{"id":"video-script","name":"video-script","summary":"完成した動画の分析を監督・編集し、その後タイムスタンプ付きの中国語解説を書き校正しました。work_dir agent_narration_brief.mdとvlm_analysis.jsonがすでに含まれている場合に使います。","body":"## 1. 定位\n\n本技能负责：创作方向、画面/声音计划、旁白写作与校验。Agent 不是 JSON 填写器，而要依次扮演：\n\n1. 导演\n2. 故事编辑\n3. 画面剪辑师\n4. 声音/旁白编辑\n5. 第一次观看的观众\n\nAgent 先记录简洁决定，再写时间线产物。`validate.py` 负责对理解索引做机械校验；full 模式还会把旁白对齐到安静窗口。\n\n下面的 `scripts/...` 均相对于本技能目录。若执行器从仓库根目录启动，请给脚本路径加上本技能的绝对目录。本技能不从其他技能目录读取参考文件或辅助脚本；外部输入只来自显式路径与 `work_dir` 产物。\n\n## 2. 读取素材并确认状态\n\n首先阅读：\n\n- `work_dir/agent_narration_brief.md`：场景、时长、安静窗口与字数预算。\n- `asr_writing_chunks.json`：长对白的写作分块。\n- `timeline_fusion.json`：判断某段是否有对白或静音槽。\n- `vlm_analysis.json` / `asr_result.json`：核对具体画面与原声证据。\n- brief 顶部列出的 contact sheet：不要只依赖场景摘要；反应、走位、静止和台词前后的具体时刻常常更重要。\n\nfull 模式使用原片时间。cut 模式第一阶段只写 `clip_plan.json`；`edited_source.mp4` 产生后，第二阶段才按输出时间写 `narration.json`。\n\n写任何创作产物前，直接读取 `work_dir` 判断当前阶段：\n\n- `recap_run_manifest.json`：确认 `edit_mode`、源视频和本轮设置。\n- full 模式：没有 `narration.json` 时进入写稿；存在时先复核再校验。\n- cut 第一阶段：尚无 `clip_plan_validated.json` / `edited_source.mp4`，只写 `clip_plan.json`。\n- cut 第二阶段：两者都存在，按 `clip_plan_validated.json.clips[]` 中的 `source_start/end` 与 `output_start/end` 核对映射，再写输出时间的旁白。\n\n必须确认旁白没有跨越错误剪辑边界，也没有落进已删除区间。整个判断只依赖 `work_dir` 产物。\n\n## 3. 制定创作方案\n\n先阅读 `references/creative-editing-playbook.md`，再写或更新两个工作产物：\n\n1. **`recap_story_plan.json`**：导演意图、至少两个剪辑假设、选定的 POV / 主线，以及由“变化”定义的 beats。\n2. **`visual_audio_board.json`**：每拍的画面任务、具体表演/反应、入点/出点、`audio_owner`、原声锚点与 `narration_job`。\n\n只记录决定、证据锚点、被放弃的备选方案和简短理由，不写冗长思维过程。\n\n### 3.1 导演判断\n\n锁定：\n\n- 观众承诺\n- POV\n- 戏剧问题\n- 起始与结束情绪\n- 隐瞒与揭示\n- 结尾余味\n\n### 3.2 故事编辑\n\n比较两个真正可行的结构后选择一个。每个 beat 至少改变一项：知识、权力、目标、关系、情绪或风险。若删除后因果、人物和情绪都没有损失，该 beat 通常不应保留。\n\n### 3.3 画面剪辑\n\n选择具体时刻，而不是只选择事件。比较：\n\n- 说话者与倾听者\n- 动作与反应\n- 早进与晚进\n- 早出与多停半秒\n\n在不破坏理解的前提下晚进早出，同时保留不可替代的表演、停顿、失误、动作声和完整台词。\n\n### 3.4 声音与旁白分工\n\n先指定 `audio_owner`，再写字。旁白只允许承担以下 `narration_job`：\n\n- `context`\n- `causal_link`\n- `foreshadow`\n- `interpretation`\n- `transition`\n- `none`\n\n画面、原声或沉默已经足够时使用 `none`，不要默认铺旁白。\n\n### 3.5 cut 模式第一阶段\n\ncut 模式先根据 `recap_story_plan.json` 与 `visual_audio_board.json` 写原片时间的 `clip_plan.json`，此时不要写 `narration.json`：\n\n```json\n{\n  \"target_duration\": \"10m\",\n  \"clips\": [\n    {\n      \"start\": 12.0,\n      \"end\": 38.0,\n      \"reason\": \"b01 | hook | knowledge: unknown→threat | POV=主角 | 保留倾听反应 | 入点=问题已问出 | 出点=沉默落地\"\n    }\n  ]\n}\n```\n\n`reason` 统一使用：\n\n```text\nbeat_id | function | change | POV | preferred moment | 入点 | 出点\n```\n\n片段顺序必须构成一条完整故事线，而不是无序高光。可使用 0–1 个 cold open，随后回到因果清楚的 setup → turn → escalation → payoff。片段长度服从具体时刻，不使用统一秒数模板；片尾必须保留完整台词或动作。\n\n## 4. 撰写旁白\n\nfull 模式直接按原片时间写；cut 第二阶段先查看 `edited_source.mp4` 与剪后故事板，补充 `visual_audio_board.json` 的输出时间并重新确认 `audio_owner` / `narration_job`，再按输出时间写：\n\n```json\n[\n  {\n    \"start\": 5.0,\n    \"end\": 12.0,\n    \"narration\": \"解说文本。\",\n    \"pause_after_ms\": 250,\n    \"overlaps_speech\": true,\n    \"emotion\": \"紧张\"\n  }\n]\n```\n\n字段说明：\n\n| 字段 | 含义 |\n|------|------|\n| `start` / `end` | full 模式为原片时间；cut 第二阶段为输出时间 |\n| `narration` | 解说文本 |\n| `pause_after_ms` | 段后停顿，默认 250ms |\n| `overlaps_speech` | 是否与原对白重叠；连续铺底窗口通常为 `true`，真正静音槽才为 `false` |\n| `emotion` | 整个解说块的 MiMo TTS 情绪/语气标签 |\n\n### 4.1 写作规则\n\n1. **先有 `narration_job`，后有句子**：没有明确任务就不写；旁白不是默认音轨。\n2. **按解说块写**：旁白拥有一个 beat 时，用 2–4 个完整句子完成一个连续想法，并在一次 TTS 中合成；不要“一句一停”。\n3. **7:3 不是配额**：只在素材判断不足时作为避免墙到墙旁白的粗略首稿参考。实际比例服从 `audio_owner`；强对白、动作声或沉默可以完整拥有一个 beat。\n4. **视听接力**：旁白若引出原声，块尾要让观众想听；原声结束后的下一块要承接它造成的变化。\n5. **按有效语速控量**：用 `字数 / brief 头部 speech budget` 估算窗口；装不下时删减或拆分叙事任务，不用加速堆字。\n6. **不看图说话**：旁白只增加上下文、因果、预期、证据支持的解释或跨越。\n7. **人物与证据优先**：优先使用已知角色名；关系、动机、潜台词和结果必须指向 visual / ASR / research / user context，且不能把背景资料伪装成当前画面事实。\n8. **写给耳朵听**：使用具体名词和动词，句子完整、口语可听；避免字幕腔、半句、空泛拔高和破折号。\n\n### 4.2 解说结构\n\n- **钩子**：提出正文会真实兑现的问题或利害，不用无关留存话术。\n- **主线**：围绕选定 POV 与主线推进，不在每个场景重新开篇。\n- **递进**：后续 beat 必须提高风险、改变关系或提供新信息。\n- **悬念缺口**：只预告之后真的会回收的后果。\n- **收尾**：回答或有意转化开头问题，留下明确余味。\n- **衔接**：旁白块与相邻原声属于同一个 beat，前者铺垫、后者呈现、下一块承接。\n\n### 4.3 原声留白字幕\n\n可选写 `original_subtitles.json`，使用成片输出时间：\n\n```json\n[{\"start\": 15.0, \"end\": 17.0, \"text\": \"原声台词\"}]\n```\n\n只写留白中实际听得到的台词，订正 ASR 错字与人名，每条尽量控制在一行；被旁白盖住或已经剪掉的句子不要写。省略时，合成阶段会使用保守的 ASR 映射兜底，并在成片中用 `「」` 区分原声对白与旁白。\n\n## 5. 创作自审\n\n在调用 LLM 评审前做以下**反事实检查**：\n\n1. 删除每个 beat：若因果、人物、情绪或承诺没有损失，就删除。\n2. 比较说话者/动作与倾听者/反应：保留更符合 POV 和情绪的时刻。\n3. 静音旁白：画面与原声仍应承载可见行动、人物行为和关键情绪。\n4. 只听声音：旁白应形成可听懂的主线，而不是画面字幕。\n5. 把旁白换成原声或沉默：若场景自身更有力量，就让出声音所有权。\n6. 检查开头问题是否真实，结尾是否回答或转化它。\n\n只记录并优先修复 1–3 个回报最高的问题；先改结构，再润色句子。\n\n## 6. 评审与校验\n\n### 6.1 建议型语义评审\n\n```bash\npython3 scripts/review.py --work-dir <work_dir>\n```\n\n评审会自动识别 cut 模式，并在存在已校验剪辑计划时按输出时间线核对；`--timeline source` 可强制使用原片时间。打开 `narration_review.md`，逐项处理 `error`，尤其是 `category=hallucination`。\n\n重复修改并评审，直到：\n\n- `verdict` 为 `PASS` / `OK` 且没有 `error`；或\n- 对仍保留的问题做明确 override。\n\n覆盖决定追加到 `work_dir/narration_review_override.md`：\n\n```markdown\n### 覆盖记录 — <date>\n- 问题：segment 4 / category=hallucination\n- 评审意见：“他早已知情”缺少画面/对白依据\n- 决定：KEEP — 该事实来自用户提供的当前集背景，而非未来剧情\n- 签署：<agent/human>\n```\n\n`review.py` 本身只写报告，默认调用策略为建议型、失败开放；若调用方显式开启严格评审，事实矛盾、残句、解析失败或评审不可用可在 TTS 前阻断。覆盖记录只用于审计，`review.py` / `validate.py` 不读取它。\n\n### 6.2 确定性硬校验\n\n```bash\npython3 scripts/validate.py --work-dir <work_dir> --mode full\n# cut 输出时间线由编排器使用 --mode cut_output\n```\n\n命令写出 `narration_lint.json`。full 模式还会根据安静窗口重写 `narration.json` 的时间。修复所有 error 后重复运行，直到校验干净，再继续 TTS 与合成。\n\n片名或题材明确但缺少剧情上下文时，先按本技能的 `references/research-guide.md` 写 `background_research.json`。若理解素材偏薄，brief 中的数量只能当上限：宁可少写、写实，也不要为凑数复述画面。\n\n## 7. 能力边界\n\n- 不运行 ASR / VLM；只消费视频理解索引。\n- 不合成 TTS，也不渲染视频。\n- 不根据平台分析做优化；先建立内容意图与剪辑一致性。\n- `review.py` 不改写 `narration.json`；是否采用严格门禁由调用方决定。\n- `validate.py` 不改写文本含义，只检查或对齐时间与安静窗口。","author":"@worldwonderer","ownerProfile":null,"authorContacts":null,"sourceUrl":"https://github.com/worldwonderer/video-recap-skills/tree/main/skills/video-script","license":"MIT","category":null,"lang":"multi","tokens":2985,"stars":0,"calls30d":1,"claimed":false,"visibility":"public","origin":"crawler","version":"0.1.0","createdAt":"2026-08-22","updatedAt":"2026-08-22","files":[{"path":"references/creative-editing-playbook.md","size":8090,"sha256":"e4710fa6d2deffefb84e7d3d63b427c0929d950de63294db0783dcd9ffbd24da"},{"path":"references/research-guide.md","size":3243,"sha256":"d239f4bc8680313184c91650cba0ee4fff2d454b91bf2edfdbeea02cd2adab48"},{"path":"scripts/agent_brief.py","size":24621,"sha256":"e55084231b06aabb9cfd05b7f8bd7e13b594783e7a76403cc96268155fab2b1f"},{"path":"scripts/agent_text.py","size":15030,"sha256":"47bc223d9c063438d1eae6c95512fa19e5cce2ec63ea340f974fd5064661f0d9"},{"path":"scripts/brief_context.py","size":16342,"sha256":"4c373ce5ba45cd0283b8adf316f9245c430889da6d61d0ea6c4554df45559f27"},{"path":"scripts/brief_inputs.py","size":10856,"sha256":"904a7423b6b9886176791c2aa2c3d9d88b574bc622879d3bcc51e8fad82dc293"},{"path":"scripts/brief_timeline.py","size":17836,"sha256":"1951db6820d8cd0780bb9f86612446e6cf1ec2314b5ab0949d2f17ef0fd1ef90"},{"path":"scripts/deslop_qc.py","size":11108,"sha256":"424e0f80859858fce7d499950a8217879a44015dbafeaff35e21030db6c51a93"},{"path":"scripts/evidence_bundle.py","size":17303,"sha256":"b65f71fbc9d327fa29566101f34f763ba7b7c8bea60ec0881cd9e9a658bffb7f"},{"path":"scripts/lib.py","size":16894,"sha256":"d700d903eab9cc916dceb24b338002bc24e78ee42113a6b7e42030ea9cadf326"},{"path":"scripts/narration_lint.py","size":27712,"sha256":"4fce3fd020783889461b3a4d9b78e30d49234d454c001cc4617266a9547b06c9"},{"path":"scripts/narration.py","size":370,"sha256":"2a562a02de57fec1a772d411c2e30379e275972c0787e45d49cbed4cab69f49d"},{"path":"scripts/review_grounding.py","size":9873,"sha256":"cda8e661ac8728d05b5dea4b661c4873c4e021aef70aac60914934ad044d5c33"},{"path":"scripts/review.py","size":1087,"sha256":"73882f2181be51e944a095174832c5b6a910600f0db9d904f6520e4888955eb6"},{"path":"scripts/review_response.py","size":33882,"sha256":"760a9f1762fde6c81fd55d3114537ada171b3d992c99a79f3fdd44930a771ec8"},{"path":"scripts/review_runner.py","size":7630,"sha256":"772816771cee5cd4e2cfc78da690e56be1f89cf8e675899e2bcec8b03ed3b8d3"},{"path":"scripts/speech_ownership.py","size":7068,"sha256":"0c30e0468b81b5947387f38be1f293357cb9cde2eaab2ecab577958ad26943b6"},{"path":"scripts/timeline_fusion.py","size":7401,"sha256":"cb9389877be260b28777c77b8bd9a34b49bbcf93ee376bd61798f5070a197d69"},{"path":"scripts/validate.py","size":6193,"sha256":"91ef0764d9992822d2b84ba95fa1089022aa55d670d8e8a27b0e1966217d6a79"}],"requires":{"mcp":[],"tools":[]},"safety":{"flags":[{"code":"net.endpoints","kind":"exfiltration","excerpt":"api.xiaomimimo.com, token-plan-ams.xiaomimimo.com, token-plan-cn.xiaomimimo.com, token-plan-sgp.xiaomimimo.com","message":"bundled scripts reach 4 external host(s)","severity":"warn"}],"scannedAt":"2026-08-22","hasScripts":true,"networkEndpoints":["api.xiaomimimo.com","token-plan-ams.xiaomimimo.com","token-plan-cn.xiaomimimo.com","token-plan-sgp.xiaomimimo.com"]}}