> ## 🔒 只读约定 —— 使用本技能时务必遵守
>
> 本技能目录(SKILL.md / manifest.yaml / scripts/ / references/)是只读交付资产。
> 使用过程中禁止用 Edit / Write / SkillManage 修改、重写、"顺手优化"或"修正"其中任何文件,
> 也禁止把使用心得、排查经验直接写进本目录 —— 那是维护者的工作,不是使用者的工作。
>
> - 发现本技能有错误 / 过时 / 缺步骤 → 向用户报告并等确认,不要就地改文件;
> - 用户确认要改 → 先 bash scripts/protect.sh unlock,改完 bash scripts/protect.sh lock;
> - 用户想留存经验 → 写到用户自己的记忆文件(如 ~/.workbuddy/MEMORY.md),不要写进这里;
> - 唯一例外:用户本轮明确要求修改本技能,且已解锁。
>
> 完整性校验:python3 scripts/selfcheck.py(与 INTEGRITY.txt 基线比对,能查出被改/被删/新增的文件)。
全民K歌
四个能力:歌曲推荐、榜单、AI 写歌、AI 翻唱,以及写歌/翻唱完成后的发布到 K 歌。全部通过全民K歌网关调用,需要 API Key。
> 消耗权益的两个能力(AI 写歌 / AI 翻唱)必须先 python3 scripts/client.py --check 确认 Key 可用
> (2026-09-15 用户定版):写歌在动笔写歌词之前检查,翻唱在向用户确认点数之前检查。
> Key 没配好就先出歌词 / 先谈消耗,都是无效流程。具体见第三、四节。
首次使用:配置 API Key
第一步永远是检查配置:
python3 scripts/client.py --check
返回 "ok": true 直接用。返回 false(退出码 2)时:
- 告诉用户:打开 https://kg.qq.com/kgSkills/index.html (需登录),创建并复制 Key(格式
kgk_xxx)
—— 给用户看的话术固定为:「去 https://kg.qq.com/kgSkills/index.html 创建一个 API Key 给我。」 - 用户提供后执行(不要在其他命令里内联明文 Key):
python3 scripts/client.py --set-key "<用户提供的Key>"
- 再
--check确认。
> 安全纪律
> - Key 存在 ~/.workbuddy/credentials/quanmin-kge.json(600 权限),不写进 shell profile。
> - 输出只显示掩码 kgk_xxxxxx_****。
> - 清除:python3 scripts/client.py --logout。
网关地址(2026-09-16 起):默认 http://idcgate.kg.qq.com/api/kg/skills/server(仅换域名,
/api/kg/skills/server 及之后的接口路径与之前完全一致)。需要临时指向别处时用
python3 scripts/client.py --set-endpoint "http://<host>/api/kg/skills/server" 或环境变量 KG_API_BASE;
已用 --set-endpoint 保存过的会覆盖默认值,排查异常时先确认当前生效地址。
> 单一域名,全部接口都一样(2026-09-16 定版):/recommend、/chart_list、/chart_detail、
> /song_query、/cover_query、/song_generate、/cover_generate、/publish、/apikey_query 全部走
> http://idcgate.kg.qq.com/api/kg/skills/server,没有例外、无需分流。
> (此前记录的「/recommend 要走 idcgate.y.qq.com」是当时网关配置未同步导致的,已更正。)
>
> Key 与网关环境绑定:不同环境的 Key 不通用。换域名后若报「API Key 无效」(i_code:0),
> 不是 Key 坏了,是需要在新环境重新创建 Key;--set-endpoint 回退旧域名即可验证这一点
> (旧域名:https://testapi.wesingapp.com/api/kg/skills/server)。
一、歌曲推荐(同步)
python3 scripts/recommend.py --limit 5 --format table # 默认就 5 首
python3 scripts/recommend.py --scene personal --period week --limit 5
python3 scripts/recommend.py --scene personal --limit 10 --format links # 用户要求更多时
默认推荐 5 首(用户明确偏好):没说数量时一律 --limit 5,不要自作主张拉 10/20 首刷屏;
用户明确说要几首再按他说的来。
scene:本期服务端仅支持 personal(猜你喜欢);传 daily/hot/new 会被判为 -400。
personal 强依赖用户身份(由已配置的 API Key 绑定 uid),拿不到身份返回 -401,不会降级为热歌。
period:day/week/month,默认 week。
只给歌名 + 歌手,不给链接、不发音频文件,也不要提示用户"试听" —— 推荐的是他人作品,
受版权保护;服务端也不下发 play_url。回复里禁止出现「试听 / 试听一下 / 可以听听 / 去听听 /
点开听 / 播放链接」等任何引导话术(见「输出规范」)。
主题 / 节气 / 热点类需求(如"中秋歌单""最近最多人在唱的歌"):不走本接口,
由你(LLM)直接产出歌名 + 歌手名的歌单;只有"猜你喜欢 / 个性化推荐"才调用 recommend.py。
二、榜单(同步)
python3 scripts/charts.py # 榜单列表(全部模仿榜)
python3 scripts/charts.py --period imitate_week # 只列模仿周榜
python3 scripts/charts.py --chart-id imitate_week_001 --song "稻香" # 按歌查榜(必须带 --song)
榜单目录只下发翻唱模仿榜(imitate_week_001 / imitate_month_001);--period 只接受
imitate_week / imitate_month,传 day/week/month 会被服务端判为 -400。
查模仿榜详情必须带 --song(歌曲名):模仿榜是「某首歌的翻唱作品排名」,没有歌名时
服务端没有可查的伴奏 mid。脚本会直接拦截、不发请求(退出码 2),并提示补 --song。
用户还没定歌名时,先用 recommend.py 挑一首,再带歌名来查榜——不要拿不带 --song 的
调用去试探。
榜单只有歌曲维度(按歌曲查榜),不提供按歌手维度(如"唱得像周杰伦的人"这类需求不支持)。
同样只给链接、不下载音频文件;榜单链接默认展示(区别于推荐的默认不展示,见「输出规范」)。
周期只有两个:周榜(imitate_week_001)和月榜(imitate_month_001),无日榜/年榜。
- 用户没说周期时默认拉周榜,并在回复结尾固定补一句引导:「要看更长周期的沉淀可以再拉一份月榜。」
- 用户已经指定某周期时,结尾补一句另一周期:「想看短期新鲜度可以换周榜。」(月榜→周榜)/「想看长期沉淀可以换月榜。」(周榜→月榜)
- 不要替用户擅自同时拉两份——两份都是 20 条,一起给会淹掉重点;先出一份,用户要再补。
榜单回复只输出「名次 + 歌手 + 作品链接」(2026-09-15 用户定版)。
三、AI 写歌(异步,约数十秒到几分钟)
python3 scripts/compose.py --prompt "海边日落,轻快吉他,温柔女声" --style pop
python3 scripts/compose.py --prompt "..." --lyrics "[Verse]..." --title "夏末" --duration 60
python3 scripts/compose.py --prompt "..." --output-dir ./我的音乐 # 指定保存位置
python3 scripts/compose.py --prompt "..." --no-wait # 只提交,稍后查
python3 scripts/compose.py --task-id tsk_xxx # 续查已有任务
编排要求(必须由你完成,服务端不做两阶段接口):
第 0 步(2026-09-15 用户定版,不可跳过):先确认 API Key 已配置,再动笔写歌词。
python3 scripts/client.py --check
ok: true→ 才进入第 1 步(写歌词)。false→ 停下来,不要生成任何歌词,先把「首次使用:配置 API Key」那节的引导发给用户,
等配好 Key、再--check通过后,才开始写词。- 理由:写歌/翻唱要消耗权益,Key 没配好就先出歌词,用户配完 Key 还得再走一遍确认,
等于白做一轮;更糟的是用户会误以为"词已提交、正在生成"。歌词属于提交前的产物,只在该账号真的能提交时才产出。
- 先根据用户需求写歌词并确定风格,把歌词 + 风格展示给用户等待确认;
- 用户确认后再调用
compose.py提交,未经确认不要提交(会消耗权益)。
给用户确认时的消耗说明(2026-09-16 定版):固定说
「AI 写歌 + AI 翻唱共享 2 次免费额度(账号级,用完为止),用完后再生成需要消耗全民K歌点数。」
不再提「剩余次数查不到 / 额度用尽则扣 100 点」—— 只说清楚这条规则即可,具体见第五节。
音色:写歌默认使用系统音色,无需也不支持选择音色。
提交姿势(重要,防丢 task_id):写歌实测约 2–3 分钟,前台轮询常在工具默认超时被 SIGKILL
(退出码 137、无任何输出),此时任务已在服务端提交、权益已扣,却拿不到 task_id,只能重提。
两步走:
python3 scripts/compose.py --prompt "..." --no-wait # 秒回,拿到 task_id
python3 scripts/compose.py --task-id tsk_xxx # 用后台方式(run_in_background=true)续查
歌词较长时用 --lyrics "$(cat /tmp/lyrics.txt)"(先 Write 到临时文件),避免 shell 引号踩坑。
返回字段以服务端为准:--title / --duration 只是建议值,服务端可能改写
(实测传「秋分以后」返回「秋意释怀」,传 90 秒返回 222 秒);work_url 也可能为空,
此时别编链接,给 audio_url + local_path 并说明「接口本次未返回作品页链接」。
--prompt 越具体越好:情绪、场景、乐器、人声、节奏、风格。用户描述笼统时先补 2–3 个维度再提交。
⚠️ 慎用 --duration(2026-09-14 实测):传了会失败,且不报参数错误,而是伪装成服务端故障 ——
先是笼统的 AI 写歌生成失败(<1s 秒回),同日重试又变成 Inference failed: 提交任务失败(约 25s)。
关键对照:同一份歌词,带了 --duration 90 → 失败;去掉后(其余参数不变)→ 跑满 5 分钟成功,返回 120 秒成品。
--duration 是那两次的唯一变量,判定为失败诱因。而且它本来也没用(见上一条:时长由服务端决定)。
判据:写歌失败时先去掉 --duration / --instrumental 这类可选参数再提一次。
注意:当时服务端也有独立抖动(同期推荐服务报「暂不可用」,且一次纯 --prompt 提交同样秒失败),
所以去掉参数仍失败 ≠ 一定是 duration 的锅,要连续两次同样失败才判定服务端故障。
交付(2026-09-14 用户定版:写歌与翻唱一致,只交付音频文件,不做播放页 HTML):
脚本默认下载真实音频到本地 output/(按下游返回后缀命名,mp3/m4a 都有),并给出歌词与 audio_url。
⚠️ AI 写歌与 AI 翻唱都不会写入全民K歌草稿箱(2026-09-18 用户更正:翻唱也不再入草稿箱):
产物只以 audio_url + 本地文件形式交付,服务端不会在用户账号下建作品。禁止告诉用户
"已存入全民K歌草稿箱",也不要暗示能在 App 里找到它 —— 这是错的,会造成误会。
写歌与翻唱两条链路行为一致(见第四节),要上架公开作品必须显式发布(见第六节)。
work_url 常为空(实测 1/1 为空),
为空时别编作品页链接,改给 audio_url + 本地路径并如实说明"接口本次未返回作品页链接"。
写歌交付步骤:present_files 直接给音频文件(先 mp3、再原始音频),不做播放页 HTML,
歌词用 Markdown 代码块贴在回复里即可。网关常下发 .m4a(AAC),Electron 预览面板无 AAC 解码器,
所以先转一份 mp3 一起给:
ffmpeg -hide_banner -loglevel error -i 歌曲.m4a -codec:a libmp3lame -b:a 192k -y 歌曲.mp3
(--output-dir 建议指向用户工作区,别堆在技能目录的 output/。)
超时(5 分钟)时用 --task-id 续查,不要重复提交 —— 会重复消耗权益。
写歌 succeeded 后,主动问用户「是否要发布到全民K歌」,确认后再发布(见第六节)。
四、AI 翻唱(异步)
python3 scripts/cover.py --keyword "晴天" # 按关键词搜歌后翻唱
python3 scripts/cover.py --keyword "晴天" --no-wait # 只提交,秒回 task_id
python3 scripts/cover.py --task-id tsk_xxx # 续查(同样建议后台跑,避免 137 被杀)
> ⚠️ task_id 是内部标识,绝不展示给用户。它只用于续查,不当作交付内容展示,
> 回复里只说歌名 + 歌手,不要把内部 ID 写进交付说明、表格或任一对用户可见的位置。
和写歌一样:长任务用「--no-wait 拿 id + 后台 --task-id 续查」,别在前台干等。
不要用 cmd --no-wait || cmd 这种兜底写法——第一个失败会自动触发第二次提交,造成重复预扣。
音色:AI 翻唱一律使用用户自己的音色(服务端按账号已训练音色自动取用,无需传音色参数)。
提交前必须确认消耗:告知用户「AI 写歌 + AI 翻唱共享 2 次免费额度(账号级,用完为止),
用完后再生成需要消耗全民K歌点数」,用户确认后再提交。
只讲「总共 2 次、两者共享、用完后消耗点数」这条规则,不要报剩余次数、也不要说"查不到剩余"(详见第五节)。
同样先跑 python3 scripts/client.py --check(与写歌第 0 步一致):Key 未配置时先引导配置,
配好再走「确认点数 → 提交」,别在 Key 都没通的情况下跟用户谈消耗。
翻唱字段实测提醒(2026-09-14):
- 返回的 duration 可能是 0,不代表失败,真实时长用 afinfo <file> 读(实测 0 → 实际 253s);
- 不传 --title 时作品名会被服务端写成固定值「AI 翻唱作品」,落盘即 AI 翻唱作品_2.m4a,
想要正常歌名就主动传 --title;
- 搜歌与提交是同一个接口,提交即扣/预扣,所以「提交前确认点数」这条无法绕过,别想着先搜后确认。
前置条件:账号需已有 AI 音色,否则返回 -400 "无音色,去k歌训练音色"(退出码 8),
应先引导用户去 K 歌训练音色,不要反复重试提交。歌曲不支持翻唱时返回 -400 "该歌曲暂不支持AI翻唱"。
交付(2026-09-14 用户定版:翻唱只交付音频文件,不再生成播放页 HTML):
脚本默认把成品音频下载到本地 output/
(按下游返回的真实后缀命名,翻唱产物通常是 .m4a;local_path 即落盘路径)。
⚠️ AI 翻唱不会写入全民K歌草稿箱(2026-09-18 用户更正:此前"翻唱入草稿箱"的描述已失效):
交付只给 audio_url + 本地音频文件。禁止说"作品已存入全民K歌草稿箱""App 里能直接听/能看到"
—— 这是错的,会造成误会。写歌与翻唱行为一致(都不入草稿箱),不存在"本来就在草稿箱、
再发布会多出一份"的前提;要上架公开作品必须显式发布(见第六节,且需用户同意)。
输出里有 origin_song(原曲的歌名与歌手),
展示时只说"翻唱自 XXX(原唱 XXX)",不要提供原曲音频地址;
origin_song 缺省时不可当作失败。
只想拿链接不落盘时用 --no-download。
翻唱交付步骤:present_files 直接给音频文件本身,不要生成播放页 HTML
(写歌同样不需要,2026-09-14 起统一取消)。文件顺序:先 mp3、再 m4a 原文件——
网关下发的是 .m4a(AAC),Electron 预览面板无 AAC 解码器,所以先转一份 mp3 更稳妥:
ffmpeg -hide_banner -loglevel error -i 歌曲.m4a -codec:a libmp3lame -b:a 192k -y 歌曲.mp3
转码只是为了兼容,不是必须步骤;用户明确只要原文件时可直接给 local_path。
翻唱 succeeded 后,同样主动问用户「是否要发布到全民K歌」,确认后再发布(见第六节)。
五、权益与免费额度
免费额度的说法(2026-09-16 定版:只讲规则,不提"查不到")
对用户只有一句话:
> AI 写歌 + AI 翻唱共享 2 次免费额度(账号级,用完为止),用完后再生成需要消耗全民K歌点数。
- ❌ 不要报剩余次数:「你还剩 N 次」「这次应该还免费」—— 剩余量没有查询接口,凭空报数字
会出现"说还剩 2 次、实际却扣了点数"的信任事故。 - ❌ 也不要说"剩余次数我查不到"(2026-09-16 用户定版):这句话会把注意力引向"我能力不足",
用户关心的只是「用完了要不要花钱」。只说规则,不提查不到这回事。 - ✅ 提交后以
points_cost为准如实说:0= 本次走免费额度、没扣点数;>0= 本次消耗了 N 点。
不要预判、不要安慰(如"应该还是免费的")。 - 用户主动问"我还有几次免费" → 仍只讲规则(共享 2 次、用完消耗全民K歌点数),
不要为了查额度去提交任务;他要看自己的情况就引导去全民K歌 App。
> 内部事实(不要转述给用户):免费额度是账号级资产,跟 uid 走,与技能安装次数 / 机器 / 重装无关;
> 是否还剩,本地无从判断,只能由提交响应的 points_cost 反推。所以才只讲规则。
服务端规则(内部参考,勿转述给用户):写歌 + 翻唱共享账号级免费额度,具体次数由服务端下发
(FreeQuota 默认 10,实测账号侧也可能是 2),本地猜不准,一律以提交响应为准。
- 提交前确认到位依然必要:写歌必须歌词+风格过用户确认,翻唱必须明示"用完免费额度后需要消耗全民K歌点数",
一次成功比反复重试重要得多。 - 免费链路的
points_balance同样不可信:它名义上是"剩余免费次数",但实测出现过
「显示剩余 79,下一次提交立刻报点数不足」——免费额度实际已耗尽、走了预扣链路、点数余额不足。
以实际提交返回为准,报退出码 7 就引导充值,不要重试。 - 免费用尽后自动回落点数预扣,单次写歌 / AI 翻唱各消耗约 100 点数(实际以服务端
PointsCost为准,
曾实测返回 200),points_cost>0为本次实际消耗点数,仅任务成功才扣。
向用户报价只说"消耗全民K歌点数",不报具体点数(服务端定价会变);任务完成后只复述响应里的
points_cost,不要自己换算或复述余额。 - ⚠️ 别把提交响应里的
points_balance当最终余额(2026-09-14 实测踩坑):提交返回
points_balance=700,任务成功后用户实查只剩 500 —— 该字段是本次扣费前的可用余额,成功核销后
还要再减掉本次消耗。任务成功后不要报"剩余 N 点"(查询响应task里根本没有points_balance字段);
用户问余额就让他以全民K歌 App 为准。
compose.py之前在同一进程内「提交+轮询」时会把提交时的submit_balance复用到完成提示里(多报一次消耗),
已在 v2.3.11 修复:完成态只输出points_cost,不再带余额。 - 任务失败 / 超时 / 服务重启中断不扣(免费额度退还占用)。
- 充值到账有延迟:2026-09-14 实测用户在 App 充值后,网关仍连续报「点数不足」约 14 分钟才生效。
用户说"充好了"但提交仍报退出码 7 时,先等 10–15 分钟再重试,别急着判定"测试/正式环境点数不互通",
也别让用户重复充值。 - 点数不足(退出码 7 /
i_code=-403,err_msg:"点数不足,去K歌充值")时,引导用户前往全民K歌「AI 歌声」主页充值点数。
六、发布到 K 歌(写歌 / 翻唱完成后)
AI 写歌 / AI 翻唱任务 succeeded 后,先问用户「是否要发布到全民K歌」,用户确认后再发布。
> 默认都不在账号下建作品(2026-09-18 定版):不发布会只留本地文件 + audio_url,
> 不会出现在全民K歌草稿箱或「我的作品」里;只有下面的 publish 才会生成一份公开作品。
> 问用户时不要说"本来就在草稿箱,再发布会多一份" —— 该前提已不成立(写歌、翻唱都不入草稿箱)。
python3 scripts/publish.py --task-id tsk_xxx
- 发布是同步的,一次调用即出结果,成功即发布完成(服务端不返回作品 id)。
- 发布参数由服务端按任务类型自动决定,客户端无需区分:翻唱 → 取翻唱歌曲伴奏 mid、不传歌词;
写歌 → 走清唱 mid、传歌词。 - 前置条件:任务必须已
succeeded;未完成会报「任务尚未完成,暂不能发布」,不要重试,先等任务跑完。 - 发布前必须征得用户同意:未经确认不要发布(发布会生成公开 UGC 作品)。
- 失败处理:
任务不存在→ 检查 task_id 是否属于当前用户;任务类型不支持发布→ 仅写歌/翻唱任务可发布;音频上传失败/发布失败→ 服务异常,可稍后重试一次(重复发布可能产生重复作品,重试前先向用户说明)。- 发布成功不返回作品 id 与作品链接;告知用户"已发布到全民K歌,可在 App 查看"。
错误处理
脚本用退出码区分错误,按表处理:
| 退出码 | 含义 | 处理 |
|---|---|---|
| 2 | 未配置 Key | 引导配置 |
| 3 | Key 无效/失效 | 请用户重新配置;不要重试 |
| 4 | 限流 | 稍后再试 |
| 5 | 业务失败/服务异常 | 看 error 字段,最多重试 1 次 |
| 6 | 网络不通 | 检查网络后重试 1 次 |
| 7 | 点数/权益不足 | 引导用户去全民K歌「AI 歌声」主页充值点数,不是 Key 的问题,别让用户重新配置 |
| 8 | 翻唱无音色 | 引导用户先去 K 歌训练音色,不是 Key / 权益问题 |
网关用 i_code 表达结果:1 成功,0 或负数为失败(-400 参数、-401 鉴权、-403 点数不足、-404 不存在、-500 内部错误),失败原因在 err_msg。
> 实测提示:部分鉴权/业务错误也会以 i_code: 0 + err_msg 返回,脚本已按关键词兜底归类,按退出码处理即可。
补充两个非脚本错误码:
- 退出码 137:进程被 SIGKILL,通常是工具前台超时杀掉了还在轮询的异步任务。任务其实已提交并消耗权益,
但没有 task_id 可续查。处理:改用「--no-wait拿 id + 后台--task-id续查」,并告知用户可能产生重复任务、重复消耗权益。 status: timeout大概率是「客户端假超时」,不要当成任务失败(2026-09-16 定版):
cover.py/compose.py的--max-wait默认 600 秒(10 分钟),到点就返回
等待超时,用 --task-id 续查,服务端并没有放弃这个任务。实测翻唱卡 46 分钟后仍翻成succeeded
(产物正常)。所以:- 超时后一律用
client.py --post /cover_query --json '{"task_id":"..."}'(或/song_query)续查,
绝不重复提交 —— 重提不会加速,只会多占一次额度; - 想一次等到位就加大
--max-wait 1800(30 分钟),配合后台任务跑(run_in_background=true),别在前台干等; - 判断任务死活看
task.error,别只看status:曾出现status=running卡住而
error="生成失败:翻唱推理超时 ... infer_task_id=xxx: context deadline exceeded",
这个状态仍可能后续成功,隔 20–40 分钟再查一次。 - 推荐接口偶发
i_code: 0/err_msg: 推荐服务暂不可用(退出码 5):重试 1 次基本就成功,属服务端抖动,不是参数问题。
若连续失败可换--period(实测某次 week/day 全挂而month正常),并可用charts.py探活网关以区分「整个网关挂了」还是「只有推荐服务挂了」。 - 任务失败后的预扣不会立刻释放:「生成失败」的任务,本次预扣的点数要 60–90 秒才退回,期间再提交会被
误报成「点数不足」(退出码 7)。判据:刚失败过就报 7 = 预扣未释放,等 90 秒再试,别急着让用户充值,
也别连续提交(会叠多层预扣,看起来像余额凭空消失)。 - 翻唱 2 秒就
succeeded不要当成异常:实测《晴天》翻唱从提交到成功只用了 2 秒(正常要几十秒~几分钟),
落盘文件是真实的(4.9MB / 314 秒),推测服务端有缓存或结果复用。交付前核一下产物时长和内容是否对得上,
并在回复里提示用户"如果音色/歌曲对不上,可能是缓存串了"。 AI 写歌生成失败这类笼统 error:先做最小化判别——去掉--lyrics/--duration/--style,只留--prompt提一次。
仍秒失败(<1s)说明是服务端生成链路坏了(与参数、歌词无关),应反馈服务端查后端日志,不要在客户端反复重试。
正常成功的任务要跑满 2–3 分钟,秒回失败一定不是超时。
2026-09-14 实测复盘:两次提交(含全参与「只留--prompt」)都 <1s 秒失败、同报AI 写歌生成失败,
当时判定为服务端故障 —— 后来发现--duration至少是诱因之一(去掉后同样的歌词立刻成功,
且它是那次成功/失败的唯一变量,详见第三节那条 ⚠️)。但注意:纯--prompt那次也失败了,
说明当时确实叠加了服务端抖动(同期推荐服务也报「暂不可用」)。
教训:最小化判别时别只剥到--prompt,--duration这类"看起来无害"的可选参数也要单独排除;
且"全参失败 + 最小也失败"不能一口咬定是服务端故障 —— 要等抖动过去后做单变量对照才能定性。
输出规范
- 推荐(用户偏好:不要链接、不要提示试听、默认 5 首):Markdown 表格,只列排名 + 歌曲名 + 歌手名,
默认只给 5 条。用户明确说过"推荐歌曲只需要展示歌曲名和歌手名,不需要链接"。
2026-09-18 定版:不要提示用户试听 —— 回复里不得出现「试听」「试听一下」「可以听听」
「去听听」「点开链接听」「播放」等任何引导(推荐的是他人作品,且服务端不下发play_url);
只给歌名 + 歌手就算交付完毕,不要补"点开 / 试听 / 播放"类建议。用户主动要链接时才附。 - 榜单(2026-09-14 用户定版:默认给出作品链接):翻唱榜的价值就是"去听别人的翻唱作品",
必须附上play_url(实测 9/9 条都有下发),不要套用推荐那条"不贴链接"的规则 ——
2026-09-14 就因为混用这两条规则,把榜单链接全筛掉了,被用户当场指出。
歌手为「未知歌手」时照常展示。
一句话区分:推荐不贴链接,榜单贴链接。
榜单回复只给「名次 + 歌手 + 链接」三列,不写任何数据缺失类说明(2026-09-15 用户定版):
热度 / 相似度等字段服务端常不下发,这属于内部事实,不要写进回复(如"热度字段服务端没下发,
照实留空""相似度评分未返回""只能按名次推荐,补不出播放量"等一律禁止),更不要反复声明。
也不要在该列填-、N/A之类占位符 —— 直接不设这一列。-score 缺失不影响交付。 - AI 写歌 / AI 翻唱(2026-09-14 用户定版:统一只给音频):先说文件保存在哪,再
present_files
直接给音频文件(先 mp3、再原始音频),一律不生成播放页 HTML;歌词用代码块贴在回复里。
翻唱要额外标注原曲(翻唱自 XXX(原唱 XXX))。 - 不要编造歌曲、歌手和链接。
work_url经常为空,此时别编作品页链接,改给audio_url+ 本地路径并如实说明"接口本次未返回作品页链接"。
音频格式与兼容(2026-09-14 起:不再做播放页)
> 定版规则:写歌与翻唱都只交付音频文件(先 mp3、再原始音频),不再生成播放页 HTML。
> 原因:播放页这条路踩了两个连环坑(相对路径 404 → base64 内嵌后 AAC 又解不了),维护成本高、收益低,
> 用户 2026-09-14 决定直接下线。歌词用 Markdown 代码块贴回复里即可。
仍然要做的:m4a → mp3 转码。网关下发常是 .m4a(AAC),部分播放环境(含 WorkBuddy 的
Electron/Chromium 预览)无 AAC 解码器,会「控件在、没声音、还不报错」。所以交付时附一份 mp3:
ffmpeg -hide_banner -loglevel error -i 歌曲.m4a -codec:a libmp3lame -b:a 192k -y 歌曲.mp3
用户报"无法播放"时的排查顺序(照这个查,别瞎猜):
1. afinfo <file>(macOS)/ ffprobe -v error -show_entries format=duration -of default=nw=1 <file>
—— 读得出时长说明文件本体没问题,问题在播放环境;
2. 优先让用户下载 mp3 到本地播(本地播放器解码器齐全),m4a 播不动就转 mp3;
3. 产物大小明显异常(几十 KB)或 afinfo 读不出时长 → 下载不完整,重跑 --task-id 或换 --output-dir。
> 历史坑(已下线,仅供理解为什么别再走老路):① 播放页用 <audio src="相对路径"> 必然 404,
> 因为预览服务只拷 HTML 不拷音频;② 改 base64 内嵌后仍播不了,根因是 AAC 无解码器,不是路径。
> 排查时 grep SKILL.md 可能因二进制探测返回空,加 -a 参数。
分发打包(给其他人用)
Skill 是纯文本,改完 SKILL.md 立即生效,不需要"重新编译/打包"。但要对外分发时按下面做:
- 必须排除
output/—— 运行产物,不属于技能本体,别发出去。
且会持续膨胀(2026-09-14 实测已 46 MB / 12 个文件,且含用户自己的翻唱作品,有隐私风险)。
建议定期清理,或把产物目录改到技能目录之外。 - 排除
__pycache__/.DS_Store。
另:发布到 skillhub(skillhub publish)时服务端会校验文件后缀白名单 —— 曾因
MANIFEST.sha256(后缀.sha256)被拒:「不允许的文件类型: MANIFEST.sha256」(2026-09-16)。
故基线文件已改名INTEGRITY.txt(内容格式不变:sha256 相对路径)。
以后新增文件不要引入冷门后缀(.sha256/.lock/.bin之类),优先.txt/.json/.md/.py/.sh/.yaml。 - 发货前脱敏:
grep -rniE "kgk_[a-z0-9]{6}_[A-Za-z0-9]{20,}" .—— 文档示例里曾误留过明文 Key
(references/GATEWAY_API.md的 apikey_create / apikey_query 响应样例),一律改成kgk_xxxxxx_****。
真实凭据本身在~/.workbuddy/credentials/,不在技能目录内,天然不会外泄。 - 版本号 +1:同步改
SKILL.mdfrontmatter 的version和manifest.yaml的version。 - 打包前重生成完整性基线(内容变了基线就得跟着变,否则对方一跑自检就报漂移):
python3 scripts/selfcheck.py --init - 打包(
INTEGRITY.txt/scripts/protect.sh/scripts/selfcheck.py都要带上,是防篡改机制的一部分):
cd ~/.workbuddy/skills && \
zip -r /tmp/quanmin-kge-<version>.zip quanmin-kge \
-x "*/output/*" -x "*__pycache__*" -x "*.DS_Store"
对方安装(目标:全程只弹 1 次授权):
unzip quanmin-kge-<version>.zip -d . # 在对方当前工作区内解压 —— 零授权
bash quanmin-kge/scripts/install.sh # 一次做完:备份删旧→装→清接管→校验→上锁→体检→审计
两条关键点,缺一个弹窗就会从 1 次变成二十次:
- 让对方先把 zip 放进自己的工作区再解压。沙箱对工作区内读写不弹授权,
工作区外(~/Downloads、~/.workbuddy/)每次工具调用都要单独确认。 - 告诉对方可跑
python3 quanmin-kge/scripts/audit.py验货,不必逐个读scripts/源码。
逐文件读是授权弹窗的主要来源(12 个文件 = 12 次读),audit.py一次给出等价结论
(依赖 / 网络目标 / 危险调用 / 写入点 / 凭据 / 完整性)。
项目级:bash quanmin-kge/scripts/install.sh --target .workbuddy/skills。
装完让对方跑 python3 scripts/client.py --check 配自己的 API Key。
装完建议提醒对方上锁(防使用过程中被 agent 就地改写):
cd <skill 安装目录> && bash scripts/protect.sh lock
# 校验是否被动过:python3 scripts/selfcheck.py
只是使用不需要改注册表;若要让对方能用 SkillManage 修改,再往
~/.workbuddy/skills/agent-created-skills.json 的 skills 数组加一条。
参考
- 实际接口文档:
references/GATEWAY_API.md(10 个接口,全 POST +i_code;字段/默认值/错误分支以backend/server/kg_skills_server代码为准) - 服务端接口实现与 10 接口文档:
backend/server/kg_skills_server/(handler_*.go/common.go/free_quota_store.go/task.go)