{
  "timestamp": "2026-05-13T01:22:06+0800",
  "status": "ok",
  "summary": "completed",
  "mode": "claude_api_multi_turn",
  "multi_turn": true,
  "payload_text": "请根据以下 SKILL.md 规则和 step1 分析数据，对歌词进行改写。\n\n## 🎯 创作纲要（Turn 1 已确认）\n\n**核心情感真相**：你以为沉默是一种保留，但其实那一刻你们都知道，已经没有什么值得挽回了。\n  ↑ 这是这首歌想留在听众心里的感受。不是写作指令，是目的地——场景、意象、hook 都从写作过程中有机发现，不要预先计划。\n\n**结构引擎**：C（用此引擎的骨架逻辑推进全曲，具体场景和意象在写的过程中自然生长）\n\n**写法镜像（方向参考，非照搬）**：①句式节奏：短句为主，多3-6字疑问句与陈述句交替，节奏克制而带压迫感；②意象类型：极度日常的人际细节为主（一条简讯、一个低头的动作），几乎无宏观景象；③叙事视角：直接向对方质问与倾诉，'我'与'你'的对位贯穿全曲；④情绪传递方式：靠具体行为细节堆积张力（低头/沉默/不解释），不命名情绪；⑤修辞密度：每行几乎只有一个动作或一个细节，密度低但精准，留白制造压力。\n  ↑ 在同一审美频道创作，但**禁止直接复制节奏模式**：若原曲以三字句为主，新词也可短促，但须变换句长节奏（如三字/五字/七字混搭）；若原曲密集用具体小物件，新词同类但物件体系必须完全不同。\n  ⚠️ **此项优先级低于所有原创度规则**：SKILL.md 的叙事重构四维度（事件/逻辑/关系/形式全新）、禁止1:1照搬原词骨架、禁用意象列表等硬约束不受此项影响，冲突时以原创度规则为准。\n\n⚠️ 行长：Verse 每行 8-13字；Chorus 每行 ≤10字\n⚠️ 副歌词汇：日常用语，小学生能理解。文学词/技术词 → 移到 verse。\n⚠️ 押韵：写词时让内容自然带出押韵；写完每段后逐行检查末字，同段内保持一致韵部即可。\n\n---\n## SKILL.md 规则\n\n# music-pipeline SKILL.md\n\n## 用途\nPGC → AI 翻唱生成完整工作链路。接收用户给出的歌曲信息、PGC 歌词、音频或平台链接，自动串联 **音乐获取 → 音频预处理 → Gemini 分析 → Claude 改写（含封面方案）→ Suno 生成 → 封面生成** 六个阶段，输出可试听的 AI 生成歌曲及专辑封面。\n\n这个 skill 的目标不是\"随便跑出一首歌\"，而是把整个工作流固定下来：\n- 上游素材怎么取\n- 中间 JSON 怎么长\n- 每一步的职责边界是什么\n- 遇到错误时怎么回退\n\n---\n\n## 触发场景\n- 用户说\"跑一下音乐生成链路\"\n- 用户说\"帮我生成这首歌的 AI 版\"\n- 用户给出歌名/歌手，希望自动找音频和歌词\n- 用户给出 PGC 歌词 + 音频，希望生成可直接喂给 Suno 的版本\n- 用户在开发模式下调试某一步（Step 0 / Step 1 / Step 2 / Step 3 / Step 4）\n\n---\n\n## ⚠️ 强制规则：禁止手动 curl，必须用脚本\n\n**禁止手动 curl 调汽水/网易云/QQ 音乐 API。所有操作必须通过脚本执行。**\n\n工作目录：`cd /home/debian/.openclaw/workspace/skills/music-pipeline/scripts`\n\n### 默认：用 pipeline 跑完整链路（三阶段）\n\nStep 2（歌词改写）通过 OpenClaw subagent 执行，不是纯 Python 脚本，所以 pipeline 分三段跑：\n\n```bash\ncd /home/debian/.openclaw/workspace/skills/music-pipeline/scripts\n\n# 准备歌单 CSV（歌名,歌手[,平台]）\necho \"稻香,周杰伦\" > /tmp/songs.csv\n\n# ===== 阶段 1：Step 0 → 1（全自动，约 2-3 分钟/批）=====\npython3 pipeline.py /tmp/songs.csv -o /srv/music-files/pipeline/batch-主题-YYYYMMDD-HHMM --step1-only\n\n# ===== 阶段 2：Step 2 歌词改写（spawn subagent）=====\n# 见下方【阶段 2 流水线编排规范】\n\n# ===== 阶段 3：Step 3 → 5（全自动，--resume 继续）=====\npython3 pipeline.py /tmp/songs.csv --resume /srv/music-files/pipeline/batch-主题-YYYYMMDD-HHMM\n```\n\n> ⚠️ **批次目录命名规范（必须遵守）**：\n> - `-o` 必须指向**最终的、有意义的批次目录**，格式：`/srv/music-files/pipeline/batch-{主题}-{YYYYMMDD}-{HHMM}`\n> - **禁止**先建父目录再传 `-o 父目录/output`——`batch_name` 取的是目录名，这样会导致批次名变成 `output`\n> - **禁止**在同一个父目录下建多个 `output2`/`output3`——失败了应该换新的 batch 目录重跑\n> - 歌单文件放在 `/tmp/songs.csv` 即可，不要放在批次目录里\n\n阶段 1 和 3 全自动（搜歌、下载、Gemini 分析、Suno 生成、混音、封面、飞书导出）。\n阶段 2 通过 spawn subagent 改写歌词。\npipeline 含 checkpoint，任意阶段中断可 `--resume` 续跑。\n\n### 阶段 2 流水线编排规范（必须严格遵守）\n\n**subagent 参数（固定，不要改）：**\n```\nruntime: subagent\nmode: run\nmodel: anthropic/claude-sonnet-4-6\nthinking: low\nrunTimeoutSeconds: 600\n```\n\n**为什么这样设：**\n- Sonnet（不是 Opus）：创作任务不需要深度推理，Opus 的 extended thinking 会在 5 分钟内超时被 abort\n- thinking=low：避免 thinking 阶段消耗过多时间\n- 600s timeout：Sonnet 通常 2-4 分钟完成，600s 留足余量\n\n**subagent task 模板：**\n```\n你是歌词改写 agent。先 read {skill_path}。\n然后读取 {song_dir}/step1_json_a.json\n改写后输出到 {song_dir}/step2_json_b.json\nprompt 不改、[section] 和 (annotation) 保留、唱出来的英文替换为全新内容、英文≥5%时保持中英比例。完成后输出摘要。\n```\n> `{skill_path}` 由 orchestrator 调用 `get_step2_skill_path()` 动态填入：\n> - base profile → `/home/debian/.openclaw/workspace/skills/lyrics-rewriter/SKILL.md`\n> - xiachen profile → `profiles/xiachen/lyrics-rewriter-SKILL.md`\n\n**流水线批次编排（3 首一批）：**\n1. 将需要 step2 的歌曲按 3 首分批\n2. spawn 批次 1 的 3 个 subagent\n3. 批次 1 全部完成后：\n   - 更新 `_state.json` 为 `step2_ready`，写入 `step2_subagent` 元数据\n   - **不要**提前把 `step2` 写进 `completed_steps`\n   - 后台启动 `--resume` 跑批次 1 的 Phase 3\n   - 同时 spawn 批次 2 的 3 个 subagent\n4. 重复直到所有批次完成\n5. subagent 并发上限 5 个（OpenClaw 限制），所以 3 首一批最安全\n\n**subagent 错误处理：**\n\n超时（timed out）：\n- 先检查 step2_json_b.json 是否已写入磁盘\n- 文件存在且 JSON 合法 → 视为成功，继续\n- 文件不存在 → 等 30 秒后重新 spawn 一次\n\nAPI 过载（overloaded_error / LLM error）：\n- **这不是文件问题，是 Anthropic API 暂时过载**\n- 不要检查文件、不要分析原因，直接等 60 秒后重新 spawn\n- 如果连续 2 次过载，降低并发：从 3 个一批改为 2 个一批\n- 过载通常几分钟自行恢复\n\n通用重试规则：\n- 任何 subagent 返回非 \"completed successfully\" 的状态，先检查输出文件是否存在\n- 存在 → 继续；不存在 → 等待后重试，最多重试 2 次\n\n**state 更新（每首歌 step2 写盘后必须做）：**\n```python\nimport json\ns = json.load(open(f'{song_dir}/_state.json'))\ns['status'] = 'step2_ready'\ns['step2_subagent'] = {\n  'agent': 'v3',\n  'output_file': f'{song_dir}/step2_json_b.json'\n}\njson.dump(s, open(f'{song_dir}/_state.json', 'w'), ensure_ascii=False, indent=2)\n```\n> `step2` 真正进入 `completed_steps` 的时机，仍然是 `pipeline.py --resume` 跑过 Step 2.5 原创度校验之后。\n\n**发送结果（全部完成后）：**\n- 每首歌发封面图 + 2 个 mixed mp3\n- 命名：`{新歌名}_V{1|2}_mixed.mp3`\n- 通过飞书 `im/v1/files` 上传 + `im/v1/messages` 发送\n- **封面优先级**：`cover_seedream.jpg`（SeeDream 生成）> `cover_seedream.png` > `suno_cover_0.jpg`（Suno 自带封面，最后 fallback）\n\n### 仅调试/迭代单个步骤时用单步脚本\n\n```bash\ncd /home/debian/.openclaw/workspace/skills/music-pipeline/scripts\n\npython3 steps/step0_fetch.py \"歌名\" \"歌手\"                    # Step 0: 搜歌\npython3 steps/step05_download.py <audio_url> <output.mp3>      # Step 0.5: 下载\npython3 steps/step1_gemini.py <audio.mp3> <lyrics.txt> -o out  # Step 1: Gemini\npython3 steps/step3_suno.py <json_b.json> -o out               # Step 3: Suno\npython3 steps/step35_mix.py <input.mp3> <output.mp3> --json    # Step 3.5: 混音\npython3 steps/step4_seedream.py \"cover prompt\" -o out           # Step 4: 封面\npython3 steps/step5_feishu.py <output_dir>                      # Step 5: 飞书导出\n```\n\npipeline 内部就是 import 这些单步脚本，逻辑完全一致。\n\n---\n\n## 总体原则\n1. **必须用 step 脚本**，不要手动 curl 各音乐平台 API\n2. **PGC 歌词是真值**，Gemini 不负责自由听写整首歌词\n3. **音频负责风格信息**，歌词负责文字真值\n4. **Step 1 的标准输出是 JSON_A**：`{ song_id?, prompt, lyrics }`\n5. **Step 2 的标准输出是 JSON_B**：`{ song_id?, prompt, title, lyrics, cover }`（cover 含封面 prompt）\n6. **Step 4 用 JSON_B 的 `cover_prompt`（顶层字段） 调 SeeDream API 生成封面图**\n7. **开发模式默认回传每一步原始结果**，尤其是 Gemini 原始 JSON、Suno 原始返回和封面图 URL\n\n---\n\n## 完整链路\n\n```\n[输入] 用户给歌名/歌手/链接，或直接给 PGC 歌词 + 音频\n    ↓\n[Step 0] 音乐获取：调用音乐获取 skill，拿到音频链接 + 歌词 + 基本元数据\n    ↓\n[Step 0.5] 音频预处理：下载音频，必要时转成 mp3\n    ↓\n[Step 1] Gemini 分析：分析音频 + 以 PGC 歌词为真值 → 生成 JSON_A（prompt + lyrics）\n    ↓\n[Step 2] Claude 改写：按 lyrics-rewriter 规则 → 生成 JSON_B（prompt + lyrics + title + cover）\n    ↓\n[Step 3] Suno 生成：用 JSON_B 生成歌曲 → 返回 2 条试听链接\n    ↓\n[Step 4] 封面生成：用 JSON_B 的 cover_prompt（顶层字段） 调 SeeDream API → 返回封面图 URL\n    ↓\n[输出] 歌名 + 试听链接 + 封面图 + 中间结果（开发模式）\n```\n\n---\n\n## Step 0：音乐获取（固定方案）\n\n### 目的\n把后续步骤真正需要的素材补齐：\n- 音频链接\n- PGC 歌词\n- 标题 / 歌手 / 平台 / song_id\n\n### 这一阶段解决什么问题\n用户通常不会一次性给全：\n- 可能只说\"跑《特别的人》方大同\"\n- 可能只发一个平台链接\n- 可能只发歌名不发歌词\n\nStep 0 的作用就是把这些零散输入补齐成结构化结果，供 Step 1 使用。\n\n### 输入\n支持这些输入形式：\n- 歌名 + 歌手\n- 平台链接\n- 单独歌名\n- 用户直接给出的音频 + 歌词（此时可跳过 Step 0）\n\n### 输出（推荐 JSON）\n```json\n{\n  \"title\": \"歌曲名\",\n  \"artist\": \"歌手名\",\n  \"platform\": \"netease|qq|qishui|other\",\n  \"song_id\": \"平台歌曲ID或空\",\n  \"audio_url\": \"可访问的音频链接\",\n  \"lyrics\": \"完整 PGC 歌词\"\n}\n```\n\n### 调用策略\n1. **优先统一 skill**：`music-search`\n2. 如果用户明确指定平台，再落到对应平台专用 skill：\n   - `netease-cloud-music`\n   - `qq-music`\n   - `qishui-music`\n\n### 注意事项\n- 能拿到歌词真值时，不要把\"歌词识别\"压力丢给 Gemini\n- 能拿到稳定音频地址时，尽量保留原始来源信息\n- 如果只拿到平台试听链接但不可稳定访问，后续要进入 Step 0.5 下载/转码\n\n### 示例\n#### 示例 A：用户只给歌名\n用户：`帮我跑《特别的人》方大同`\n\nStep 0 应拿到：\n- title: 特别的人\n- artist: 方大同\n- audio_url: 某平台音频链接\n- lyrics: 完整歌词\n\n#### 示例 B：用户给平台链接\n用户：`把这个网易云链接做成 AI 版`\n\nStep 0 应做：\n- 解析链接\n- 抽取 song_id\n- 拉取歌词与音频信息\n\n---\n\n## Step 0.5：音频预处理\n\n### 目的\n把 Step 0 拿到的音频，变成 Step 1 最稳可用的输入格式。\n\n### 固定策略\n- 优先使用 **本地 mp3**\n- 如果拿到的是 flac / m4a / 其他格式，先转成 mp3\n- 如果拿到的是不稳定外链，先下载到本地再处理\n\n### 当前已验证的工程结论\n在官方 Gemini API 这条链路里，最稳的是：\n1. 下载到本地\n2. 转成 mp3\n3. 再转 base64 data URL\n4. 放进 `messages[].content[].image_url.url`\n\n### 示例\n```bash\nffmpeg -i source.flac -codec:a libmp3lame -q:a 2 -ar 44100 song_pgc.mp3\n```\n\n---\n\n## Step 1：Gemini 分析（固定方案）\n\n### 目的\n把\"音频里的风格信息\"和\"PGC 歌词里的文字真值\"融合成 Suno 可消费的 JSON_A。\n\n### 这一阶段解决什么问题\nStep 1 不只是转写，而是把素材变成两个关键产物：\n1. **prompt**：给 Suno 的风格蓝图\n2. **lyrics**：带 section label + producer notes 的结构化歌词\n\n### 职责边界\n- **音频负责**：\n  - 曲风\n  - BPM / Key / Rhythm\n  - 编曲元素\n  - 演唱方式\n  - 结构\n  - 情绪\n  - producer notes\n- **PGC 歌词负责**：\n  - 文字真值\n  - 中文歌词内容本体\n\n### 固定原则\n1. **必须传入 PGC 歌词**，不要只给音频\n2. **以 PGC 歌词为 ground truth**，不要让模型自由听写整首歌词\n3. **音频用于推断**：section、编曲提示、演唱方式、结构与风格特征\n4. 当前主链默认模型是 `gemini-2.5-pro`，实际运行模型可由 `GEMINI_MODEL` 覆盖\n5. `max_tokens` 固定为 `32000`\n6. 官方 Gemini API 下，音频输入最稳妥方式为：\n   - 本地音频转 **mp3**\n   - 再转成 **base64 data URL**\n   - 放入 `messages[].content[].image_url.url`\n\n### Step 1 固定 Prompt（推荐版）\n\n```text\nYou are a world-class music producer and hit-songwriter, acting as a \"style chameleon\". Your task is to perform a deep analysis of an input audio file.\n\nYou are given:\n1. The original PGC audio\n2. The original PGC lyrics\n\nImportant rule:\n- Use the provided PGC lyrics as the ground-truth text.\n- Use the audio to infer genre, arrangement, vocal delivery, song structure, emotional arc, and section-level musical cues.\n- Do NOT freely re-transcribe the entire song from audio unless resolving obvious discrepancies.\n\nYour output must be valid JSON in the following format:\n{\n  \"prompt\": \"...\",\n  \"lyrics\": \"...\"\n}\n\nRequirements for \"prompt\":\n1. Strict format:\nGenre: <Genre>; BPM: <BPM>; Key: <Key>; Rhythm: <Detailed Rhythm description>; Instruments: <Detailed Instruments list with timbre descriptions>; Vocals: <Detailed Vocal description including timbre, technique, gender, processing>; Mood: <Detailed Mood description>; Production: <Production description including mixing, space, effects>; Structure: <Song Structure description>; Language: <Language>; Highlight: <the most noteworthy element>\n2. Keep the entire prompt under 1000 characters.\n3. Highlight must be the final field.\n\nRequirements for \"lyrics\":\n1. Use the provided PGC lyrics as the text source.\n2. Add standard structure markers such as [intro], [verse], [chorus], [bridge], [outro].\n3. Immediately after each section marker, insert producer-note style musical guidance in parentheses.\n4. These notes must be keyword-only, not full sentences.\n5. Include musical details such as melody contour, melody rhythm, vocal performance, mix, instrumental interplay, and structural events.\n6. No pinyin.\n7. No timestamps.\n8. Keep the entire lyrics field under 5000 characters.\n\nReturn JSON only. No markdown fences. No explanation.\n```\n\n### Step 1 当前固定实现\n- endpoint: `https://generativelanguage.googleapis.com/v1beta/models/{GEMINI_MODEL}:generateContent`\n- authentication: `?key=${GEMINI_API_KEY}`\n- default model: `gemini-2.5-pro`\n- max_tokens: `32000`\n\n### 已验证的工程结论\n1. 纯文本请求：成功\n2. 音频最小理解（语言/时长/曲风）：成功\n3. `prompt only`：成功\n4. `lyrics only`：成功\n5. `prompt + lyrics`：默认设置下不稳定；**加 `max_tokens: 32000` 后可成功**\n\n### 标准输出（JSON_A）\n```json\n{\n  \"song_id\": \"可选，推荐保留\",\n  \"prompt\": \"严格格式化后的风格提示词\",\n  \"lyrics\": \"带 [section] 和英文括号 producer notes 的结构化歌词\"\n}\n```\n\n### 示例\n#### 示例输出片段\n```json\n{\n  \"prompt\": \"Genre: Mandopop / Soul Ballad; BPM: 70; Key: E-flat Major; ...\",\n  \"lyrics\": \"[verse]\\n(Melody-Contour: ...; Vocal-Perf: ...; Mix: ...)\\n爱一个人或许要慷慨...\"\n}\n```\n\n### 回退策略\n如果官方 Gemini API 返回：\n- 429\n- 500\n- 502\n- 503\n\n则优先回退到分步方案：\n1. 第一次只拿 `prompt`\n2. 第二次只拿 `lyrics`\n3. 本地合并 JSON_A\n\n---\n\n## Step 2：Claude 改写（lyrics-rewriter）\n\n### 目的\n把 Step 1 的结构化歌词改写成\"新主题但保留风格与结构\"的版本，并同时生成专辑封面方案，产出 JSON_B。\n\n### 这一阶段解决什么问题\nStep 1 给的是\"原曲风格 + 原曲文本整理版\"；\nStep 2 的任务是：\n1. 变成\"**同风格、全新内容**\"的新歌词，用于后续 Suno 生成\n2. 根据新歌词情绪，生成可直接调用图片 API 的封面 prompt\n\n### 输入\n标准输入是 JSON_A：\n```json\n{\n  \"song_id\": \"...\",\n  \"prompt\": \"...\",\n  \"lyrics\": \"...\"\n}\n```\n\n### 输出\n标准输出是 JSON_B：\n```json\n{\n  \"song_id\": \"...\",\n  \"prompt\": \"...\",\n  \"title\": \"2-5字歌名\",\n  \"lyrics\": \"重写后的结构化歌词\",\n  \"cover\": {\n    \"template_id\": 1,\n    \"template_name\": \"模板名称\",\n    \"scene_description\": \"画面描绘内容\",\n    \"cover_prompt\": \"完整可用的封面生成 prompt\",\n    \"selection_reason\": \"选择理由\"\n  }\n}\n```\n\n### 固定规则摘要\n- `prompt` 原样保留\n- `song_id` 原样保留（若存在）\n- 段落标签原样保留\n- 英文括号提示词原样保留在原位置\n- 只重写中文歌词\n- 新增 `title`\n\n### 重点注意\n- 中文内容必须彻底改写，不能只是换同义词\n- 新主题必须契合原曲情绪和曲风\n- 副歌要更有传播性和张力\n- 要重点防止：\n  - 丢失英文括号提示词\n  - 重复 section 标签\n  - 结构顺序混乱\n\n### 示例\n#### 输入片段\n```text\n[verse]\n(Structure:Verse, Vocal-Perf:Intimate-breathy, ...)\n爱一个人或许要慷慨\n若只想要被爱\n```\n\n#### 输出片段\n```text\n[verse]\n(Structure:Verse, Vocal-Perf:Intimate-breathy, ...)\n窗外的灯一盏一盏暗下去\n那是谁的夜晚还没散去\n```\n\n---\n\n## Step 3：Suno 生成\n\n### 目的\n把 JSON_B 的 `prompt + lyrics` 直接喂给 Suno，拿到最终可试听样本。\n\n### 输入\n```json\n{\n  \"prompt\": \"...\",\n  \"title\": \"...\",\n  \"lyrics\": \"...\"\n}\n```\n\n### 调用方式\n```bash\n~/.openclaw/workspace/skills/suno/suno.sh custom \"{prompt字段内容}\" \"{lyrics字段内容}\"\n```\n\n### 输出\n- 默认 2 首歌曲\n- 每首包含：\n  - 任务状态\n  - 时长\n  - MP3 链接\n  - 封面链接\n  - song ID\n\n### 示例\n```text\n第1首: https://cdn1.suno.ai/xxx.mp3\n第2首: https://cdn1.suno.ai/yyy.mp3\n```\n\n### 注意事项\n- prompt 太长时，先压缩\n- lyrics 太长时，可能被 Suno 截断\n- 如果只是验证链路，优先先跑一版，不要一开始过度清洗\n\n---\n\n## Step 3.5：混音/母带（参考曲匹配模式）\n\n### 目的\n对 Suno 生成的原始音频做混音/母带处理，以原曲（PGC）频谱为参考方向，温和调整频谱平衡、动态和声场。\n\n### 脚本\n```bash\npython3 steps/step35_mix.py <input.mp3> <output.mp3> --ref <原曲.mp3> --json\n```\n\n### 信号链路\n```\n输入 → M/S分离 → Side链路 → Mid链路 → M/S重组 → Side增益\n→ 并行压缩 → 主链路EQ → 主压缩 → Limiter → 二次校验 → MP3编码(alimiter 0.95)\n```\n\n### M/S 链路（基准参数）\n\n| 通道 | 模块 | 参数 |\n|---|---|---|\n| Side | HighpassFilter | 120 Hz |\n| Side | HighShelfFilter | 10kHz, 0 dB（基准关闭） |\n| Side | 整体增益 side_gain_db | 0 dB（参考曲模式动态调） |\n| Mid | PeakFilter | 60 Hz, +0.5 dB, q=1.0 |\n| Mid | PeakFilter | 100 Hz, +0.5 dB, q=1.0 |\n| Mid | PeakFilter | 1000 Hz, +0.5 dB, q=0.5 |\n\n### 主链路（基准参数）\n\n| 序号 | 模块 | 参数 |\n|---|---|---|\n| 1 | Pre-gain | -1.5 dB（参考曲模式归零） |\n| 2 | HighpassFilter | 30 Hz |\n| 3 | 去闷 PeakFilter | 300 Hz, -1.0 dB, q=1.5 |\n| 4 | 低频增强 LowShelf | 80 Hz, +1.5 dB |\n| 5 | 收紧 PeakFilter | 120 Hz, -1.5 dB, q=1.5 |\n| 6 | 盒音切除（条件触发） | 800 Hz, -1.0 dB, q=1.0 |\n| 7 | 空气感 HighShelf | 16 kHz, +0.5 dB |\n| 8 | 主压缩 Compressor | -10 dB / 2.0:1 / atk 20ms / rel 100ms |\n| 9 | Limiter | -1.0 dB / rel 100ms |\n\n### 并行压缩（纽约压缩，M/S重组后、主链路前）\n\n| 参数 | 基准值 |\n|---|---|\n| threshold | -20 dB |\n| ratio | 8.0:1 |\n| attack | 5 ms |\n| release | 150 ms |\n| mix | 10% |\n\n### 参考曲匹配模式（`--ref` 原曲路径）\n\n分析原曲频谱作为**方向指引**（不追精确数字），温和调整：\n\n| 维度 | 死区 | 偏多时 | 偏少时 | 安全范围 |\n|---|---|---|---|---|\n| **低频** | ±5% | 去闷 max -2.0dB, sub 最低归零 | sub +1.0~+2.0, 去闷归零 | 去闷 0~-2.0, sub 0~+2.0 |\n| **高频** | ±2% | 空气感 min -0.5dB | 空气感 max +1.0dB | -0.5~+1.0 |\n| **LowMid(250-1k)** | ±3% | 600Hz cut max -0.5dB, Q=0.7 | 600Hz boost max +0.5dB, Q=0.7 | ±0.5 |\n| **Mid(1k-4k)** | ±3% | 2.5kHz cut max -0.5dB, Q=1.0 | 2.5kHz boost max +0.5dB, Q=1.0 | ±0.5 |\n| **Air(8k+)** | — | 额外降空气感 max -0.5dB | — | 叠加在高频调整上 |\n| **宽度** | ±2% | Side gain max -2.0dB | Side gain max +2.0dB | ±2.0 |\n| **动态** | ±3dB | 阈值 max -14dB, PC max 20% | 阈值 min -5dB, PC min 2% | 阈值 -5~-14, PC 2~20% |\n\n参考曲模式下 pre_gain 归零。\n\n### 二次校验（仅参考曲模式）\n\n处理完后重新分析六段频谱，按需补偿：\n\n| 检测项 | 触发条件 | 频点 | Q | 斜率 | 上限 |\n|---|---|---|---|---|---|\n| 低频补偿 | 输出 < 目标 8% | LowShelf 80Hz | — | Δ×0.06 | **+1.0dB** |\n| LowMid补偿 | 输出 < 目标 8% | Peak 600Hz | 0.7 | Δ×0.03 | **+0.5dB** |\n| Mid切削 | 输出 > 目标 5% | Peak 2.5kHz | 1.0 | Δ×0.03 | **-0.5dB** |\n| Air补偿/切削 | 输出偏差 >2% | HighShelf 10kHz | — | Δ×0.08 | **±0.5dB** |\n\n### 输出\nMP3 320kbps，ffmpeg `alimiter=limit=0.95` 防 intersample peak。\n\n### Pipeline 集成\nPipeline 自动从 Step 0 下载的原曲传入 `--ref`，无需手动指定。\n\n---\n\n## Step 4：封面生成（SeeDream API）\n\n### 目的\n用 Step 2 输出的 `cover_prompt`（顶层字段） 调用 SeeDream 图片生成 API，拿到专辑封面图片 URL。\n\n### 这一阶段解决什么问题\nStep 2 已经生成了完整的封面 prompt，Step 4 只负责把它丢给图片 API 拿回图。\n\n### 输入\n从 JSON_B 中取顶层 `cover_prompt` 字段（注意：不是 `cover.cover_prompt`，是顶层直接字段）。\n\n### API 调用方式\n\n```bash\ncurl -X POST 'https://api.acedata.cloud/seedream/images' \\\n-H 'accept: application/json' \\\n-H 'authorization: Bearer ${SEEDREAM_TOKEN}' \\\n-H 'content-type: application/json' \\\n-d '{\n  \"action\": \"generate\",\n  \"model\": \"doubao-seedream-4-5-251128\",\n  \"prompt\": \"<JSON_B.cover_prompt 的内容>\",\n  \"size\": \"2048x2048\",\n  \"watermark\": false\n}'\n```\n\n### Python 调用方式\n\n```python\nimport requests\n\ndef generate_cover(cover_prompt: str, token: str, model: str = \"doubao-seedream-4-5-251128\") -> str:\n    resp = requests.post(\n        \"https://api.acedata.cloud/seedream/images\",\n        headers={\n            \"accept\": \"application/json\",\n            \"authorization\": f\"Bearer {token}\",\n            \"content-type\": \"application/json\"\n        },\n        json={\n            \"action\": \"generate\",\n            \"model\": model,\n            \"prompt\": cover_prompt,\n            \"size\": \"2048x2048\",\n            \"watermark\": False\n        },\n        timeout=180\n    )\n    data = resp.json()\n    if data.get(\"success\"):\n        return data[\"data\"][0][\"image_url\"]\n    raise Exception(f\"SeeDream API error: {data}\")\n```\n\n### 固定参数\n- **API endpoint**: `https://api.acedata.cloud/seedream/images`\n- **默认模型**: `doubao-seedream-4-5-251128`\n- **备选模型**: `doubao-seedream-4-0-250828`\n- **图片尺寸**: `2048x2048`（正方形，专辑封面标准）\n- **水印**: `false`\n\n### ⚠️ 关键注意事项\n- 模型名用**短横线** `-` 而非点号 `.`（文档写的是点号，实际要用短横线）\n- API 调用耗时约 30-90 秒，需要足够的 timeout\n- Token 需在 [Acedata 平台](https://platform.acedata.cloud/documents/seedream-images) 单独开通 SeeDream 服务\n\n### 输出\n```json\n{\n  \"success\": true,\n  \"task_id\": \"...\",\n  \"data\": [\n    {\n      \"prompt\": \"...\",\n      \"size\": \"2048x2048\",\n      \"image_url\": \"https://platform.cdn.acedata.cloud/seedream/xxx.jpg\"\n    }\n  ]\n}\n```\n\n取 `data[0].image_url` 写回 JSON_B 的 `cover.image_url`。\n\n### 回退策略\n- 如果 4.5 模型失败，回退到 `doubao-seedream-4-0-250828`\n- 如果 API 返回 429（限频），等待 Retry-After 后重试\n- 如果 API 返回 400，检查 token 是否已开通 SeeDream 服务\n\n### 已验证的工程结论\n1. Token `82c5a4b2bf8c4a369aca02f9e12f4bd8` 已开通，4.5 和 4.0 均可用\n2. 中文 prompt 正常出图\n3. 三个模板的封面 prompt 均已验证出图效果\n4. 出图耗时约 30-60 秒\n\n---\n\n## 完整执行流程（主 agent 操作步骤）\n\n```\n1. 收到用户输入（歌名/歌手/链接，或直接给 PGC 歌词 + 音频）\n2. 如果素材不完整，先走 Step 0：音乐获取 skill → 拿到 audio_url + lyrics + metadata\n3. 进入 Step 0.5：下载音频，必要时转 mp3\n4. Gemini Step 1：分析音频 + 结合 PGC 歌词 → 得到 JSON_A\n5. Claude Step 2：按 lyrics-rewriter 改写 JSON_A → 得到 JSON_B（含 title + cover）\n6. Suno Step 3：生成歌曲 → 得到音频链接\n7. SeeDream Step 4：用 cover_prompt（顶层字段） 生成封面 → 得到封面图 URL\n8. 回复用户：\n   - 歌名：{title}\n   - 试听链接 1：{url1}\n   - 试听链接 2：{url2}\n   - 封面图：{cover_image_url}\n   - 开发模式下附中间结果\n```\n\n> Step 3 和 Step 4 可以并行执行（Suno 生成音乐 + SeeDream 生成封面互不依赖）。\n\n> 默认进入开发模式时，应回传每一步原始结果，尤其是 Gemini 原始 JSON、Claude 改写结果、Suno 原始返回。\n\n---\n\n## 输入格式要求\n\n用户支持两种输入方式：\n\n### 方式 A：直接给完整素材\n- **PGC 歌词**：原始完整歌词文本（或飞书文档链接）\n- **音频**：优先本地文件；若是外链，建议先下载并转 mp3\n\n### 方式 B：只给歌曲信息\n- 歌名\n- 歌手\n- 歌曲链接 / 平台链接（可选）\n\n此时先走 Step 0，用音乐获取 skill 补齐：\n- 音频链接\n- 歌词\n- 元数据\n\n可选：\n- 指定新歌主题方向（否则 Claude 自主选择）\n- 指定生成数量（默认 2 首）\n- 指定只调某一步（如\"只调 Step 2\"）\n\n---\n\n## 代码仓库与版本管理\n\n**本地路径**：`/home/debian/.openclaw/workspace/skills/music-pipeline/`\n**GitHub**：`git@github.com:hycccc/music-pipeline3000.git`\n**当前开发分支**：`v2.0-dev`\n**稳定版快照**：`tag/v1.0-stable`（acedata Suno + v1 step1 prompt）\n\n### 改代码后必须做：\n```bash\ncd /home/debian/.openclaw/workspace/skills/music-pipeline\ngit add .\ngit commit -m \"描述改了什么\"\ngit push origin HEAD:v2.0-dev\n```\n\n### 分支说明：\n| 分支/Tag | 内容 |\n|----------|------|\n| `v2.0-dev` | 当前开发版，自建 Suno API（默认） |\n| `main` | GitHub 原有历史 |\n| `tag/v1.0-stable` | v1 完整快照，acedata Suno |\n\n**⚠️ 不要用 rsync 同步，不要在 workspace 的大 git 里 commit pipeline 代码，只用这个独立 repo。**\n\n---\n\n## v1 vs v2 版本对比\n\n### 三条链路一览\n\n| 链路 | Step 1 Prompt | Step 3 Suno 接口 | 触发方式 |\n|------|--------------|-----------------|---------|\n| **v1** | 通用 base prompt（style chameleon） | acedata `/suno/audios`，chirp-v5 | 切换 step3 文件 |\n| **v2 通用** | 同 v1 prompt（EXACTLY 2 Chorus + Musical Arc） | 自建 `localhost:12138`，chirp-crow | `python3 pipeline.py`（默认） |\n| **v2 下沉（xiachen）** | 华语/民族专属 prompt，五声调式/hook-first | 同 v2 通用 | `python3 pipeline.py --profile xiachen` |\n\n### v1 和 v2 通用的 Step 1 prompt 差异\n\nv2 通用在 v1 基础上加了两条：\n- `EXACTLY 2 Chorus sections`（v1 是 at least 2）\n- Musical Arc 校验规则（Bridge 后必须接 Chorus / Verse 2 删了 Bridge 也要删等）\n\n### v2 下沉（xiachen）Step 1 prompt 额外内容\n\n- Genre 针对华语少数民族风格有专属 anchor（彝族/蒙古族/新疆等）\n- Instruments 必须识别民族乐器名称（erhu/马头琴/guzheng 等）\n- Melody priority: Hook-first design（副歌钩子必须一遍就能哼）\n- 文件位置：`scripts/profiles/xiachen.yaml`（`step1.prompt_template` 字段）\n\n---\n\n### 迭代时改哪个文件\n\n| 改什么 | 改哪个文件 |\n|--------|----------|\n| v2 通用 Step 1 prompt | `scripts/steps/step1_gemini.py` 里的 `STEP1_PROMPT_TEMPLATE` |\n| v2 下沉 Step 1 prompt | `scripts/profiles/xiachen.yaml` 里的 `step1.prompt_template` |\n| Step 2 歌词改写规则（通用） | `workspace/skills/lyrics-rewriter/SKILL.md` |\n| Step 2 歌词改写规则（下沉） | `scripts/profiles/xiachen/lyrics-rewriter-SKILL.md` |\n| Step 3 Suno 接口/参数 | `scripts/steps/step3_suno.py` |\n| Step 3.5 混音参数 | `scripts/steps/step35_mix.py` |\n| Step 4 封面生成 | `scripts/steps/step4_seedream.py` |\n| Step 5 飞书导出 | `scripts/steps/step5_feishu.py` |\n| Pipeline 编排逻辑 | `scripts/pipeline.py` |\n\n### 切换 v1 / v2 链路\n\n```bash\ncd /home/debian/.openclaw/workspace/skills/music-pipeline\n\n# 切换到 v1（acedata）\ncp scripts/steps/step3_suno_v1.py scripts/steps/step3_suno.py\n\n# 切回 v2（自建 API）\ngit checkout HEAD -- scripts/steps/step3_suno.py\n```\n\n---\n\n## 开发模式约定\n\n当用户明确说进入开发模式时：\n1. 每一步都要先说明当前在调哪一步\n2. 每一步都要回传原始结果\n3. 出错时优先定位\"是哪一层错了\"：\n   - 素材获取\n   - 转码\n   - Gemini 输入\n   - Gemini 输出格式\n   - Claude 改写规则\n   - Suno 接收\n4. 已验证过的工程结论要及时写回 skill\n\n---\n\n## 错误处理\n\n| 问题 | 处理方式 |\n|------|----------|\n| 用户只给歌名，没有歌词音频 | 先走 Step 0，用音乐获取 skill 补齐 |\n| 外链 flac 不稳定 | 先下载到本地并转 mp3 |\n| 官方 Gemini API 返回 429/5xx | 重试；必要时切换到 Step 1 拆分调用 |\n| Gemini 输出不是合法 JSON | 重新要求 JSON-only，或手动提取 `choices[0].message.content` |\n| `prompt + lyrics` 联合输出失败 | 保留 `max_tokens: 32000`；仍失败就拆成两次请求 |\n| Step 2 丢括号提示词 | 收紧 prompt，明确\"英文括号内容一字不动保留\" |\n| Step 2 重复 section 标签 | 增加去重修复步骤 |\n| Suno 生成失败 | 检查 prompt 长度与歌词长度，必要时缩短 |\n| 歌词太长导致 Suno 截断 | 精简歌词，保留核心段落 |\n| SeeDream 返回 400 model 错误 | 检查模型名是否用短横线（`doubao-seedream-4-5-251128`），不是点号 |\n| SeeDream 返回 400 但模型名正确 | token 可能未开通 SeeDream 服务，需到平台 Acquire |\n| SeeDream 返回 429 | 等待后重试，或降级到 4.0 模型 |\n| 封面出图有文字/人物 | 在 cover_prompt 中加强否定提示词，重新生成 |\n\n---\n\n## 迭代记录\n\n| 版本 | 日期 | 内容 |\n|------|------|------|\n| v1.0 | 2026-03-16 | 初始化，三段链路：Gemini反推 → Claude改写 → Suno生成 |\n| v1.1 | 2026-03-16 | 固定 Step 1 工程方案：PGC歌词为真值 + 音频用于风格分析；默认模型 gemini-2.5-pro；官方 Gemini API 需 max_tokens=32000；支持必要时拆分 prompt/lyrics 两次请求 |\n| v1.2 | 2026-03-16 | 补全 Step 0 / Step 0.5 / Step 1 / Step 2 / Step 3 的目的、输入输出、示例、开发模式约定与错误处理细节 |\n| v2.0 | 2026-03-17 | 新增 Step 4 封面生成（SeeDream API）；Step 2 输出新增 cover 字段（含封面 prompt）；lyrics-rewriter skill 同步升级为 v3.0；完整链路从五步扩展为六步 |\n\n\n## Step 1 分析数据 (step1_json_a.json)\n\n```json\n{\n  \"prompt\": \"Genre: Mandopop / Ballad; BPM: 140; Key: E major; Rhythm: Slow, emotive 4/4 ballad feel driven by arpeggiated piano and a simple, solid acoustic kit backbeat that enters for the chorus.; Instruments: Resonant Piano (arpeggios and block chords), Lush Strings (swelling pads and countermelodies), Clean E.Bass (root notes), Subtle Ac.Guitar (strummed texture), Punchy Acoustic Kit (soft kick; Vocals: Female, clear and emotive with a breathy quality in verses, transitioning to a powerful, soaring chest voice in the chorus, wide dynamic range; Mood: Staring out a rain-streaked window at night, remembering a past relationship's highs and lows.; Production: Polished, wide stereo mix with upfront vocals. Lush hall reverb on strings and vocals, creating an epic and cinematic space. Dynamic build from sparse verse to full-bodied chorus.; Highlight: The powerful, emotive female vocal melody in the chorus that soars over a lush string arrangement.\",\n  \"lyrics\": \"[intro]\\n(Mix: Sparse, intimate field; Piano and cello upfront)\\n(Inst: Solo cello with a melancholic melody; Resonant grand piano plays gentle arpeggios)\\n(Chords: C#m7 - G#m7 - A - Emaj7)\\n\\n[verse]\\n(Vocal-Perf: Soft, breathy, conversational mezzo-piano)\\n(Inst: Piano continues as the main harmonic support; Vocals take center stage)\\n(Chords: C#m7 - G#m7 - A - Emaj7 - C#m7 - G#m7 - A - Bsus4 - B)\\n(Melody-Contour: Gentle rising and falling phrases)\\n我问为什么\\n那女孩 传简讯给我\\n而你为什么\\n不解释 低着头沉默\\n\\n[pre-chorus]\\n(Transition: Subtle string pad enters, creating a lift)\\n(Vocal-Perf: Volume increases slightly, more sustained notes, building tension)\\n(Inst: Strings swell gently underneath the piano and vocals)\\n(Chords: F#m7 - G#m7 - A - Bsus4 - B)\\n我该相信你很爱我\\n不愿意敷衍我\\n还是明白\\n你已不想挽回什么\\n\\n[chorus]\\n(Transition: Full acoustic kit and bass enter with a soft cymbal crash)\\n(Mix: Expands to a wide, cinematic stereo image; Vocals are powerful and layered)\\n(Vocal-Perf: Strong, soaring chest voice; Mezzo-forte to forte dynamic)\\n(Inst: Drums provide a solid backbeat; Strings become prominent with a soaring countermelody)\\n(Chords: E - B/D# - C#m7 - G#m7 - A - E/G# - F#m7 - B)\\n(Melody-Harmony: Strong resolution on chord tones; Emphasizes the song's emotional peak)\\n我怀念的 是无话不说\\n我怀念的 是一起做梦\\n我怀念的 是争吵以后\\n还是想要 爱你的冲动\\n我记得那年生日\\n也记得那一首歌\\n记得那片星空\\n最紧的右手\\n最暖的胸口\\n谁记得\\n谁忘了\\n\\n[bridge]\\n(Vocal-Perf: Highly emotive, almost pleading; Use of slight vocal fry for texture)\\n(Inst: Strings and piano build in intensity; Drums use more cymbal swells)\\n(Chords: A - E/G# - F#m7 - C#m7 - A - E/G# - F#m7 - Bsus4 - B)\\n(Structure: Builds energy towards the final chorus)\\n我怀念的 是无言感动\\n我怀念的 是绝对炽热\\n我怀念的 是你很激动\\n求我原谅 抱得我都痛\\n我记得你在背后\\n也记得我颤抖着\\n记得感觉汹涌\\n最美的烟火\\n最长的相拥\\n\\n[chorus]\\n(Mix: The most powerful and dense section of the song; Climax)\\n(Vocal-Perf: Full power, sustained high notes, with subtle emotional ad-libs)\\n(Inst: Entire ensemble plays at full volume; Strings are majestic and sweeping)\\n(Chords: E - B/D# - C#m7 - G#m7 - A - E/G# - F#m7 - B)\\n(Sound-Design: A final, powerful cymbal crash to mark the peak before the outro)\\n谁爱得太自由\\n谁过头太远了\\n谁要走我的心\\n谁忘了那就是承诺\\n谁自顾自地走\\n谁忘了看着我\\n谁让爱变沉重\\n谁忘了要给你温柔\\n\\n[outro]\\n(Transition: Abrupt dynamic drop after the final chorus line)\\n(Mix: Returns to the sparse, intimate feel of the intro)\\n(Vocal-Perf: Soft, resigned, almost a whisper; Decrescendo to silence)\\n(Inst: Instruments fade out, leaving only the final piano chords and a single vocal line)\\n(Chords: E - G#m7 - A - E)\\n我记得那年生日\\n也记得那一首歌\\n记得那片星空\\n最紧的右手\\n最暖的胸口\\n我放手\\n我让座\\n假洒脱\\n谁懂我多么不舍得\\n太爱了\\n所以我\\n没有哭\\n没有说\",\n  \"song_id\": \"01_我怀念的_20260513\",\n  \"_meta\": {\n    \"provider\": \"google\",\n    \"model\": \"gemini-2.5-pro\",\n    \"usage\": {\n      \"prompt_tokens\": 12291,\n      \"completion_tokens\": 1285,\n      \"total_tokens\": 15769\n    },\n    \"generated_at\": \"2026-05-13T01:20:06.726999+08:00\"\n  },\n  \"_prompt_compressed\": {\n    \"from\": 1081,\n    \"to\": 947\n  }\n}\n```\n\n## ⚠️ 关键约束（最高优先级）\n\n1. **原创度必须 ≥ 85%**：每一句歌词都必须是全新创作，严禁复用、微调或改写原词句。\n2. **主题必须彻底重构**：不能只做同义替换（如\"星座→缘分\"、\"酒吧→KTV\"），必须选择完全不同的叙事空间。\n3. **叙事事件必须全新**：原曲中的每个具体场景/事件，新歌词中禁止出现对应事件，即使换了细节也不行。\n4. **论证逻辑必须不同**：如果原曲是\"A信X → B嘲笑 → 举例 → 反转\"，新歌词必须用完全不同的情感推进方式。\n5. **角色关系必须重新设计**：原曲的人物关系模板禁止以任何变体出现。\n6. **结构标注（括号内容）可以保留**，但歌词文本行必须全新。\n7. **自检**：写完后并排对比——如果仅凭段落结构和叙事节奏就能看出是同一首歌的改写，必须重做。\n\n请严格按照 SKILL.md 的规则输出 step2_json_b.json 的完整 JSON。只输出 JSON，不要其他内容。用 json code block 包裹输出。\n⚠️ JSON 格式要求：lyrics 等字段是 JSON 字符串值，其中的双引号必须转义为 \"，或改用 「」 代替引号，禁止出现未转义的裸双引号。",
  "agent_meta": {
    "provider": "anthropic",
    "model": "claude-sonnet-4-6",
    "usage": {
      "input": 33507,
      "output": 3945,
      "cacheRead": 0,
      "cacheWrite": 0,
      "total": 37452
    },
    "duration_ms": 59488
  }
}