---
name: video-script
description: >
 对已完成分析的视频进行导演与剪辑策划，再写带时间戳的中文解说并校验；也处理已有短片的
 宣发标题、花字修订和外部文案回填。普通策划输入 work_dir 的 agent_narration_brief.md 与
 vlm_analysis.json；文案返修输入当前成片的工程与内容证据。策划输出 recap_story_plan.json、visual_audio_board.json、
 可选 style_card.json、cut 模式需要的 clip_plan.json，以及通过校验的 narration.json；仅宣发文案任务交付提案或回填既有包装计划。
 外发说明：只有建议型评审 review.py 联网，它把旁白稿全文与理解证据、策划文件的文字摘录发到 MiMo chat 接口
 （MIMO_API_KEY / MIMO_API_URL，不发视频、图片或音频）；单独使用时只在显式执行时运行，端到端编排默认在 TTS 前运行一次，
 可用 --no-review-narration / REVIEW_NARRATION=0 关闭（严格评审开启时除外）；validate.py 与 lint 仅在本地运行。
 触发词：解说词、写解说、视频旁白、宣发标题、花字修订、文案回填、
 narration script、写稿、解说文案、剪辑思路、导演思路。
---

## 1. 定位

只改宣发标题、封面、花字或回填外部文案时，直接读 `references/promotional-copy.md`，
按当前短片的观看理由和兑现位置处理指定文字层，不重做下述策划/旁白链。
普通解说写作不因此增加平台调研或包装任务。

本技能负责：创作方向、画面/声音计划、旁白写作与校验。Agent 不是 JSON 填写器，而要依次扮演：

1. 导演
2. 故事编辑
3. 画面剪辑师
4. 声音/旁白编辑
5. 第一次观看的观众

Agent 先记录简洁决定，再写时间线产物。`validate.py` 负责对理解索引做机械校验，从不改写 Agent 的稿子：段落顺序、数量、时间、文本、停顿和扩展元数据原样保留，只依据现有声音证据回写实测的 `overlaps_speech`。文本装不下时间窗、段落未按时间排序等问题以 error 退回给 Agent 修改。

下面的 `scripts/...` 均相对于本技能目录。若执行器从仓库根目录启动，请给脚本路径加上本技能的绝对目录。

### 1.1 创作控制模式

先根据用户要求和 `work_dir` 判断本轮模式；它不是 `full|cut|dub` 渲染模式：

- **CREATE**：首次创作。比较至少两个真正可行的故事/剪辑假设后再选择。
- **DIRECTED**：用户已指定结构、镜头、台词或表达。忠实落实，不为满足“创作流程”虚构替代方案。
- **REVISION**：用户针对已有版本看片修改。最新反馈是当前事实来源；未点名部分默认冻结。

REVISION 先明确本轮修改项与冻结项，再编辑对应层：表达、口语节奏、字幕反馈更新 `style_card.json`；镜头、入出点、表演和声音分工更新 `visual_audio_board.json`；只有观众承诺、POV、主线或 beat 改变时才更新 `recap_story_plan.json`。被删除的镜头、原声或文案也要从相关计划中删除，不能保留过期锚点。不要把看片修改重新做成一次 CREATE。

## 2. 远程调用与数据外发

本技能只有一个脚本会联网：`review.py`（§7.1 的建议型语义评审）。其余全部在本地运行：`validate.py` 及其 lint、去 AI 味检查只读写 `work_dir` 里的文件，不发任何网络请求；策划与写稿由 Agent 直接读写 `work_dir` 完成。

`review.py` 发给谁：

- MiMo 的 OpenAI 兼容 chat 接口 `<MIMO_API_URL>/chat/completions`，用 `MIMO_API_KEY` 认证，模型为 `MIMO_MODEL`（默认 `mimo-v2.5`）。未设 `MIMO_API_URL` 时，按量计费 key 发往 `https://api.xiaomimimo.com/v1`，`tp-` 开头的 Token Plan key 发往 `MIMO_TOKEN_PLAN_CLUSTER` 选定的 token-plan 集群（默认 `https://token-plan-cn.xiaomimimo.com/v1`）。
- 未设 `MIMO_API_KEY` 时直接退出，不构造也不发送任何请求。

`review.py` 发送什么（只有文字，不发视频、帧图片或音频文件）：

- `narration.json` 全部旁白段的时间与文字。
- 理解索引的文字证据：开头、中段、结尾、长空档与各旁白时间窗附近选出的时间段内，`vlm_analysis.json` 的场景描述与 `frame_facts` 文字，以及对白转写（有 `asr_clean.json` 时用它，否则 `asr_result.json`）。
- `background_research.json` 的梗概、角色、关系与剧情线摘录。
- 存在时附上 `packaging_plan.json`、`recap_story_plan.json`、`visual_audio_board.json`、`style_card.json`（各截前 3000 字）和 `original_subtitles.json` 的原声字幕文字。
- 内容较长时分块，每块一次请求；返回的意见只写回本地 `narration_review.json` / `narration_review.md`。

何时运行、怎样关闭：

- 单独使用本技能时，`review.py` 只在用户或 Agent 显式执行它时运行；不执行它就没有任何外发。用户不希望稿件离开本机时，不要运行它，改为人工复核后直接跑 `validate.py`。
- 端到端编排默认在 TTS 前自动调用一次这项评审，失败不阻断；关闭方式是编排入口的 `--no-review-narration` 或环境变量 `REVIEW_NARRATION=0`。若同时开启了严格评审（`--require-narration-review` 或 `REQUIRE_NARRATION_REVIEW=1`），评审必须运行，关闭开关不生效。
- 评审是建议型的：只写报告，从不改写 `narration.json`；是否修改由 Agent 或用户决定。

## 3. 读取素材并确认状态

首先阅读：

- `work_dir/agent_narration_brief.md`：场景、时长、安静窗口与字数预算。
- `asr_writing_chunks.json`：长对白的写作分块。
- `timeline_fusion.json`：判断某段是否有对白或静音槽。
- `vlm_analysis.json` / `asr_result.json`：核对具体画面与原声证据；有 `asr_clean.json` 时以它的文本为准（lint、评审、剪辑和合成都读它）。
- brief 顶部列出的 contact sheet：不要只依赖场景摘要；反应、走位、静止和台词前后的具体时刻常常更重要。
- `production_reference.json`（仅当 `work_dir` 里有）：另一部成片拆出的可迁移方法与节奏数值，用法见 §4。

full 模式使用原片时间。cut 模式第一阶段只写 `clip_plan.json`；`edited_source.mp4` 产生后，第二阶段才按输出时间写 `narration.json`。

写任何创作产物前，直接读取 `work_dir` 判断当前阶段：

- `recap_run_manifest.json`：确认 `edit_mode`、源视频和本轮设置。
- full 模式：没有 `narration.json` 时进入写稿；存在时先复核再校验。
- cut 第一阶段：尚无 `clip_plan_validated.json` / `edited_source.mp4`，只写 `clip_plan.json`。
- cut 第二阶段：两者都存在，按 `clip_plan_validated.json.clips[]` 中的 `source_start/end` 与 `output_start/end` 核对映射，再写输出时间的旁白。

必须确认旁白没有跨越错误剪辑边界，也没有落进已删除区间。整个判断只依赖 `work_dir` 产物。

## 4. 制定创作方案

先阅读 `references/creative-editing-playbook.md`，再按创作控制模式写或更新工作产物：

1. **`recap_story_plan.json`**：导演意图、CREATE 中至少两个剪辑假设、选定的 POV / 主线，以及由“变化”定义的 beats。DIRECTED / REVISION 不强行新增假设。
2. **`visual_audio_board.json`**：每拍的画面任务、具体表演/反应、入点/出点、`audio_owner`、原声锚点与 `narration_job`。
3. **`style_card.json`（适用时）**：用户当前认可的声音、口语节奏、字幕阅读姿态和明确禁忌。收到表达或字幕反馈后更新原文件，而不是只改最终文案。

只记录决定、证据锚点、被放弃的备选方案和简短理由，不写冗长思维过程。

若 `work_dir` 有 `production_reference.json`，制定方案前先读它。它来自另一部成片，只含可迁移的方法和测得的节奏，不含本片事实，不能作为本片画面、剧情或台词的证据。优先级：用户指令 > 本片证据 > 参考。CREATE 可把它的 `structure` 当作一个候选假设，与素材自生的假设比较；DIRECTED / REVISION 默认不套用。`targets` 是参考值，不是配额：与本片的 `audio_owner`、完整台词或表演冲突时以素材为准。可在 `recap_story_plan.json` 写可选字段 `reference_methods: [{"id": "m1", "decision": "adopt|adapt|skip", "note": "…"}]`。没有这个文件就跳过本段。

### 4.1 导演判断

锁定：

- 观众承诺
- POV
- 戏剧问题
- 起始与结束情绪
- 隐瞒与揭示
- 结尾余味

### 4.2 故事编辑

CREATE 比较两个真正可行的结构后选择一个；DIRECTED / REVISION 沿用用户指定或已确认的结构，除非最新反馈明确改变故事方向。每个 beat 至少改变一项：知识、权力、目标、关系、情绪或风险。若删除后因果、人物和情绪都没有损失，该 beat 通常不应保留。

### 4.3 画面剪辑

选择具体时刻，而不是只选择事件。比较：

- 说话者与倾听者
- 动作与反应
- 早进与晚进
- 早出与多停半秒

在不破坏理解的前提下晚进早出，同时保留不可替代的表演、停顿、失误、动作声和完整台词。

### 4.4 声音与旁白分工

先指定 `audio_owner`，再写字。旁白只允许承担以下 `narration_job`：

- `context`
- `causal_link`
- `foreshadow`
- `interpretation`
- `transition`
- `none`

画面、原声或沉默已经足够时使用 `none`，不要默认铺旁白。

### 4.5 cut 模式第一阶段

cut 模式先根据 `recap_story_plan.json` 与 `visual_audio_board.json` 写原片时间的 `clip_plan.json`，此时不要写 `narration.json`：

```json
{
  "target_duration": "10m",
  "clips": [
    {
      "start": 12.0,
      "end": 38.0,
      "reason": "b01 | hook | knowledge: unknown→threat | POV=主角 | 保留倾听反应 | 入点=问题已问出 | 出点=沉默落地"
    }
  ]
}
```

`reason` 统一使用：

```text
beat_id | function | change | POV | preferred moment | 入点 | 出点
```

片段顺序必须构成一条完整故事线，而不是无序高光。可使用 0–1 个 cold open，随后回到因果清楚的 setup → turn → escalation → payoff。片段长度服从具体时刻，不使用统一秒数模板；片尾必须保留完整台词或动作。密集 scene-change 候选的来源判断与处理规则由剪辑阶段定义，写计划时遵循同一规则，不制造人工闪切。

## 5. 撰写旁白

full 模式直接按原片时间写；cut 第二阶段先查看 `edited_source.mp4` 与剪后故事板，补充 `visual_audio_board.json` 的输出时间并重新确认 `audio_owner` / `narration_job`，再按输出时间写：

```json
[
  {
    "start": 5.0,
    "end": 12.0,
    "narration": "解说文本。",
    "pause_after_ms": 250,
    "overlaps_speech": true,
    "emotion": "紧张"
  }
]
```

字段说明：

| 字段 | 含义 |
|------|------|
| `start` / `end` | full 模式为原片时间；cut 第二阶段为输出时间。与上一块间隔不超过 1.6 秒的块属于同一段落，assemble 会让它紧接上一块的实际结尾（间隔 0.35 秒）播放，最多比 `start` 提前 1.2 秒，且提前的那一段不会进入原声对白；段落首块从 `start` 开始，上一块超时则顺延到上一块结尾加停顿之后 |
| `narration` | 解说文本 |
| `pause_after_ms` | 段后停顿，默认 250ms |
| `overlaps_speech` | 是否与原对白重叠；连续铺底窗口通常为 `true`，真正静音槽才为 `false` |
| `emotion` | 整个解说块的 MiMo TTS 情绪/语气标签 |

### 5.1 写作规则

1. **先有 `narration_job`，后有句子**：没有明确任务就不写；旁白不是默认音轨。
2. **按连续思路写**：旁白拥有一个 beat 时，用一个或少量完整句子完成“前提 → 触发动作 → 变化/意义”，并在一次 TTS 中合成。句号服从口语思路和呼吸，不服从字幕换行；不要固定句数，也不要“一句一停”。
3. **7:3 不是配额**：只在素材判断不足时作为避免墙到墙旁白的粗略首稿参考。实际比例服从 `audio_owner`；强对白、动作声或沉默可以完整拥有一个 beat。
4. **视听接力**：旁白若引出原声，块尾要让观众想听；原声结束后的下一块要承接它造成的变化。
5. **按有效语速控量**：用 `字数 / brief 头部 speech budget` 估算朗读秒数，每块再加约 0.45 秒 TTS 首尾静音。brief 里每个窗口标的字数没扣这段静音，2–3 秒的短窗口要比它少写一两个字；装不下时删减或拆分叙事任务，不用加速堆字。
6. **不看图说话**：旁白只增加上下文、因果、预期、证据支持的解释或跨越。
7. **人物与证据优先**：优先使用已知角色名；关系、动机、潜台词和结果必须指向 visual / ASR / research / user context，且不能把背景资料伪装成当前画面事实。
8. **写给耳朵听**：使用具体名词和动词，句子完整、口语可听；避免字幕腔、半句、空泛拔高和破折号。TTS 文本先保证听感连续，字幕再按阅读宽度拆分，不能反过来把朗读稿切碎。
9. **避免模板化纠偏**：“不是 A，而是 B”只在确实存在一个观众可能相信、而素材又要纠正的判断时使用。它不是禁句，但不能靠先否定再肯定制造假洞察；优先直接写人物的动作、因果和后果。

### 5.2 解说结构

- **钩子**：提出正文会真实兑现的问题或利害，不用无关留存话术。
- **主线**：围绕选定 POV 与主线推进，不在每个场景重新开篇。
- **递进**：后续 beat 必须提高风险、改变关系或提供新信息。
- **悬念缺口**：只预告之后真的会回收的后果。
- **收尾**：回答或有意转化开头问题，留下明确余味。
- **衔接**：旁白块与相邻原声属于同一个 beat，前者铺垫、后者呈现、下一块承接。

### 5.3 原声留白字幕

可选写 `original_subtitles.json`，使用成片输出时间：

```json
[{"start": 15.0, "end": 17.0, "text": "原声台词"}]
```

只写留白中实际听得到的台词，订正 ASR 错字与人名，每条尽量控制在一行；被旁白盖住或已经剪掉的句子不要写。省略时，合成阶段会使用保守的 ASR 映射兜底，并在成片中用 `「」` 区分原声对白与旁白。

## 6. 创作自审

在调用 LLM 评审前做以下**反事实检查**：

1. 删除每个 beat：若因果、人物、情绪或承诺没有损失，就删除。
2. 比较说话者/动作与倾听者/反应：保留更符合 POV 和情绪的时刻。
3. 静音旁白：画面与原声仍应承载可见行动、人物行为和关键情绪。
4. 只听声音：旁白应形成可听懂的主线，而不是画面字幕。
5. 把旁白换成原声或沉默：若场景自身更有力量，就让出声音所有权。
6. 检查开头问题是否真实，结尾是否回答或转化它。
7. 检查相邻短句能否合成一个连续思路；字幕分行不得成为 TTS 断句理由。

只记录并优先修复 1–3 个回报最高的问题；先改结构，再润色句子。

REVISION 还要逐项确认：用户点名的问题已经改变，未点名的冻结项没有意外变化，相关 `style_card.json` / `visual_audio_board.json` 中不存在旧镜头或旧表达。除非用户要求备选版本，不额外扩展新方向。

## 7. 评审与校验

### 7.1 建议型语义评审

```bash
python3 scripts/review.py --work-dir <work_dir>   # 会联网：外发内容与关闭方式见 §2
```

评审会自动识别 cut 模式，并在存在已校验剪辑计划时按输出时间线核对；`--timeline source` 可强制使用原片时间。打开 `narration_review.md`，逐项处理 `error`，尤其是 `category=hallucination`。

重复修改并评审，直到：

- `verdict` 为 `PASS` 且没有 `error`；或
- 对仍保留的问题做明确 override。

覆盖决定追加到 `work_dir/narration_review_override.md`：

```markdown
### 覆盖记录 — <date>
- 问题：segment 4 / category=hallucination
- 评审意见：“他早已知情”缺少画面/对白依据
- 决定：KEEP — 该事实来自用户提供的当前集背景，而非未来剧情
- 签署：<agent/human>
```

`review.py` 本身只写报告，默认调用策略为建议型、失败开放；若调用方显式开启严格评审，事实矛盾、残句、解析失败或评审不可用可在 TTS 前阻断。覆盖记录只用于审计，`review.py` / `validate.py` 不读取它。

### 7.2 确定性硬校验

```bash
python3 scripts/validate.py --work-dir <work_dir> --mode full
# cut 输出时间线由编排器使用 --mode cut_output
```

命令写出 `narration_lint.json`。full 与 cut_output 用同一套声音归属算法（原声对白区间减去安静窗口）回写 `overlaps_speech`，其余字段原样保留，不截短、不合并、不补标点、不重排。推荐字数按时间窗先扣 0.45 秒 TTS 首尾静音再计算；full 模式下某段字数超过推荐字数的 1.25 倍即报 `over_budget` error，报告里写明时间窗、`budget_chars`、`limit_chars`、`actual_chars` 和 `over_chars`：缩短文字或放宽/挪动时间窗，不要指望 TTS 替你缩稿。有 error 时命令以非零退出，逐块列出「段 N」（narration.json 里第 N 块，从 1 数；`narration_lint.json` 的 `index` 仍从 0 数）、错误码、关键数字和改法。修复所有 error 后重复运行，直到校验干净，再继续 TTS 与合成。

片名或题材明确但缺少剧情上下文时，先按本技能的 `references/research-guide.md` 写 `background_research.json`。若理解素材偏薄，brief 中的数量只能当上限：宁可少写、写实，也不要为凑数复述画面。

## 8. 能力边界

- 不运行 ASR / VLM，不合成 TTS，不渲染视频；只消费视频理解索引。
- 唯一的远程调用是 `review.py` 的 MiMo 文字评审（见 §2）；`validate.py` 与 lint 只在本地运行。
- 平台研究仅用于明确的宣发任务；不替代当前片内事实，也不默认改变解说和剪辑。
