---
name: video-edit
description: "剪辑用户【已有的】口播视频——你直接重写成片应该说什么的脚本，系统用文本对齐回原始音频时间戳。

  必备前提：用户已经有视频文件（拍好的、上传的、或给出文件路径）。

  触发：用户上传/给出视频文件 + 说'剪一下'/'去卡顿'/'变流畅'/
  '剪辑'/'剪短点'/'加字幕'/'cut'/'edit'/'trim'/'让视频更紧凑'。"
version: 1.0.1
owner_repo: Optima-Chat/optima-gen
---

# Video-Edit Skill — Descript 模式：你写脚本，系统对齐

用户给原始视频，你交付剪好的成片。

## Step 0：指令清单读回（≥ 2 个动作时必跑）

**为什么必跑**：用户反馈"发给 AI 的多个指令总有漏掉，要重新生成"。LLM 直接读多指令时容易抓 1-2 个执行，剩下的被吞。**没有 checklist 也没有回读 → 用户只能等成片出来才发现漏了 → 全流程重做**。

### 触发判定

| 用户消息 | 是否必跑 Step 0 |
|---|---|
| "剪一下" / "加字幕" / "去卡顿"（单一动作） | 跳过 |
| "剪一下，剪到 30 秒"（1 动作 + 1 修饰） | 跳过 |
| "剪一下 + 加 banner 'XYZ'"（≥ 2 个动作） | **必跑** |
| "字幕用白色 + 加 BGM + 压到 30 秒"（≥ 2 个动作） | **必跑** |
| 任何**迭代消息**改 ≥ 2 项（"banner 换成 X，字幕换成 Y"） | **必跑** |

模糊判定：宁可读回，不要漏。

### 操作（在执行任何 `video-edit ...` 命令之前）

1. **拆指令**：把用户消息切成原子动作清单（每条 ≤ 15 字）。常见类别：
   - 剪辑：剪一下 / 去卡顿 / 剪到 30 秒 / 删开头 5 秒
   - 字幕：加字幕 / 不要字幕 / 字幕用白色 / 字幕加大
   - Banner：加 banner "XYZ" / 不要 banner / banner 换成 "ABC"
   - 节奏：去 NG / 保留所有重复
   - 输出：竖版 / 横版 / 不变

2. **一条消息内输出读回 + 等确认**（**不要边读边动手**）：

   ```
   我理解你要做的：
   1. 剪一下（默认去卡顿 + NG）
   2. 加 banner "日发四万单"
   3. 字幕用粗体白色
   4. 压到 30 秒以内

   全对回"开始"/"对"；要改/补告诉我哪条。
   ```

3. **用户确认后**：按清单顺序执行；每条对应到下文 skill 流程的具体步骤；执行完一条在内部 TaskList 标记 done；**全部 done 才报"成片完成"**。

4. **交付汇报里逐条回扣**：

   ```
   成片 xxx_subbed.mp4 完成：
   1. ✅ 剪一下 → 时长 28s（原 65s）
   2. ✅ banner "日发四万单" 顶部
   3. ✅ 字幕粗体白色已烧
   4. ✅ 28s < 30s
   ```

### 严禁

- ❌ 跳过 Step 0 直接执行 ≥ 2 个动作 — ≥ 2 动作时**附带的细粒度参数（"字幕 30pt"）**最容易被漏；不读回必发生
- ❌ 读回时省掉某条 / 合并两条为"剩下默认" — 漏掉的同义词
- ❌ 全部执行完才在交付汇报里说"我做了 1/2/3" — 用户已经等了 5 分钟,这时发现漏掉要重做
- ❌ 用户给迭代指令（"banner 换成 X，字幕加大"）时直接动手 — 迭代同样要读回

**细粒度参数本身是否触发 Step 0**：单一动作 + 单一参数（"剪到 30 秒" / "字幕 30pt" 单独说）**不触发**，按上方触发表跳过。Step 0 是给 ≥ 2 个独立动作准备的；单参数请求按单动作走。

## 用户怎么说，你交付什么

**默认所有剪辑都带字幕**——中文短视频 99% 需要字幕，"剪一下"的潜台词就是"给我能直接发的成片"。

| 用户说 | 你交付 |
|---|---|
| "剪一下" / "去卡顿" / "变流畅" | `<video>_subbed.mp4`（剪 + 字幕） |
| "剪成 X 秒短视频" / "30 秒" | `<video>_subbed.mp4`（压到目标时长 + 字幕） |
| "加字幕"（不需要剪） | `<video>_subbed.mp4`（仅字幕） |
| "剪一下不要字幕" / "无字幕版" | `<video>_edited.mp4` |

只有当用户提到平台但**没说目标时长**时（如"剪成 TikTok"无时长），才追问"目标多长？"。
其他情况**直接动手**，不要废话。

## 核心原则：你直接写最终脚本

剪辑工具有两种模式：
- **旧模式（`cut`）**：在 proposal 里给短语标 `#` 删除——只能删整段
- **新模式（`smart-cut`）✅ 默认用这个**：你直接写"成片应该说什么"，系统找原始音频里对应的位置剪出来

新模式的杀手锏是：你**不受短语边界限制**，可以：
- 跳过填充词（"嗯"/"那个"）
- NG 重拍中只保留你认为最好那遍
- 把同一句话的不同片段拼起来
- 保留某句的前半 + 另一句的后半

**约束**：
- 不能凭空造内容——脚本里的字必须出自原始转写，否则匹配不上
- 不能调换顺序——内容按原片时间顺序保留
- 字面错别字会"匹不上"被丢——必须用转写文本里的原字（whisper 怎么识的就怎么写）

## 你的内部流程

### 默认剪辑（剪 + 字幕，最常见）

```bash
video-edit analyze <video>
```

读 `<video>.work/cut_proposal.md` 了解原片内容（每行是个短语单元，带时间戳和文本）。

然后基于这些短语，**自己写一份最终脚本**到 `<video>.work/final_script.txt`：

**格式**：每个短语段两行——中文一行（带 `**关键词**` 标记，1-2 个），英文翻译一行；段与段之间空行。

```
做**跨境电商**是有**捷径**的
Cross-border e-commerce has shortcuts

我最不缺的
What I lack the least

就是**囤货**
It is just hoarding goods
```

写脚本时的判断：

**你的唯一任务:识别 NG 重拍 + 完全无意义填充语,删掉。其它一律保留。**

> ⚠️ 这条规则 2026-05-08 重写过(之前是"保留率 ≥ 60% + 三类必留 + 两类才删"):
>
> 实证显示百分比硬规则不工作 — LLM 心算字数飘 + 用户感觉"删多了"的真实原因是误删了非 NG 的内容句。改成**只删两类,边界明确**,不再设字数门槛。**卡顿/停顿由工具阈值控制**(下方 smart-cut 已放宽),不是 prompt 层判断。

**只删两类**(明确边界 + 反例):

1. **NG 重拍** = whisper 转写中**字面相同字串 ≥ 4 字**且**位于相邻短语**(在 cut_proposal 里前后挨着)。通常因为主播照稿子念时卡顿,从前面几个字重念。
   - ✅ 删:相邻短语 `我们在义乌开了一个TK日更100条视频的` + `我们在义乌开了一个TK日更100条的实操训练营` → 字面 13 字相同,删第一遍
   - ✅ 删:同短语内 `我们在我们在做跨境电商` → 删一份 `我们在`(留 `我们在做跨境电商`)
   - ✅ 删:同短语内 `批量打爆批量打爆你的店铺` → 删一份 `批量打爆`

   **以下不算 NG,默认全部保留**(过去因这一点删多了,严格收窄):
   - **2-3 字相同但不是 NG**(如 `没有单量` 出现在钩子句和条件句两处、`实操` 出现在两段、`如果` 句式重复)— 中文 2-3 字相同太常见,**不删**
   - **跨非相邻短语的相同字串**(中间隔了 ≥ 1 个其它短语)— 不是当场 NG 重拍,**不删**
   - **同义不同字**(如 `我做这个挺久了` / `做了三四年了`)— 字面不重复,**留两个**(留措辞流畅那个亦可,不强制)
   - **同事实换措辞强调**(如 `日发四万单` + `我们日均出货四万`)— 字面不重复,**不删**
   - **同概念不同角度**(如 `海外货盘` + `一件代发`)— 信息有增量,**全留**
   - **判定阈值**:字面相同字串 **≥ 4 字 + 相邻短语**,跟 smart-cut 工具 HARD 阻断保持一致
   - 关键启发:**LLM 不可靠判断"同义",但可靠判断"字面相同长字串"。NG 收窄到字面级 + 长度 ≥ 4 + 相邻**

2. **完全无意义填充语**:`嗯`/`啊`/`呃` 单字**独立成短语**(不嵌在句中)。嵌在句中作为停顿语气词的**不删**。

**其它一律保留**:钩子条件句、背景前缀、重申/铺垫、引导词、CTA、即使你觉得"不那么核心"的句子。**不确定就留**。

**卡顿/短停顿/词尾喘气由 smart-cut 工具自动处理**(silencedetect + RMS 软停顿 + 残留验证三层),**不是你的工作**。短停顿(< 0.30s)是自然语流,工具会保留;长停顿才删。

**通用约束**(写法层):
- **中文必须用原文措辞**——不要润色或改写,否则匹配不上
- **每行 ≤ 10 个汉字**——超过会显示挤、可能溢出视频边界
- **`**关键词**` 标记 1-2 个**:挑能传达这句"重点信息"的实词(名词/动词),不要标助词、口头禅;短语里没明显重点就 0 个标记
- **英文翻译要地道、口语化**——给海外用户看的,不是直译。短句即可,可省略主语。

**写完后必做的自查（保存前）**：把 final_script 当成一段**连续口播稿**通读一遍——不是一行行检查，是**整体读**。问自己：

1. **是否误删了内容句?**（最近几次 review 发现 LLM 倾向于把"2-3 字字面重复但不是 NG"的句子删掉。范例:钩子句 "没有单量的同学们" + 条件句 "一直没有单量" — `没有单量` 4 字虽然相同但**不在相邻短语**，是合理重复，**两段都留**）
2. **逻辑有跳跃/断点吗?**（如果有，缺哪句话补哪句，前提是原片说过）
3. **任何一段是"上一段的翻版"吗?**（删一段）

**这一步不可省略**——`smart-cut` 现在会硬阻断字面 4+ 字重复，但语义级重复（同义不同字）只能靠你这一遍通读发现。

写完自查通过后：

```bash
video-edit smart-cut <video>
```

`smart-cut` 用 difflib 把脚本对齐回原始 word 时间戳。**如果输出 `[FAIL] 检测到字面重复`**：

1. 看报错指出的"第 X 段 vs 第 Y 段"
2. 打开 final_script.txt 删掉重复的那段（一般留措辞更流畅的那遍）
3. 重跑 `smart-cut`
4. 直到不再 FAIL 才能继续

**剪完之后必跑 review**（语义级 NG 兜底检测）：

```bash
video-edit review <video>
```

如果报告里有 `HARD: repeated-content`：删 `final_script.txt` 里对应的重复段，重跑 `smart-cut` + `review`，直到 HARD = 0。

review 通过后再烧字幕：

```bash
video-edit subtitle <video>
```

### Banner（顶部持续标语横幅）

视频顶部持续显示 4-12 字大字标题，牌匾风（深酒红实心底色块 + 米白思源黑体加粗 72pt）。

**这是默认动作，不是可选**：在烧字幕**之前**直接加 banner，不要判断脸位置、不要询问用户。唯一跳过条件——用户初始 prompt 已明确说"不要标题/不要 banner"。

**关键流程**：banner 必须在烧字幕**之前**注入到 subs.ass，这样只烧 1 次（合并 banner + 字幕一并烧录），比"先烧字幕再注入 banner 重烧"省一半时间。

#### Step 1：从 final_script 自动提炼标题

读 `final_script.txt`，从中提一个 4-12 字的核心卖点作为 banner 文字。优先级：

1. **数字 + 主体**（最有冲击力）：把脚本里的具体数字（"日更100条"、"3天涨粉1万"、"7天瘦5斤"）跟主体词（产品/服务/课程名）拼在一起
2. **痛点 + 解法**：脚本前段的钩子句压成短语（"流量没起色 → 流量起飞秘籍"）
3. **品牌 + 品类**：脚本主推的品牌或产品 + 品类标签（"XX牌洗面奶 敏感肌可用"）
4. **兜底**：脚本里出现频率最高 / 加粗 `**...**` 标记的实体名词

通用规则：
- 4-12 字（汉字）
- 短语之间**用半角空格分隔**，banner-tool 会自动按空格换行成两行
- 不要写整句完整话（"我们今天来聊聊..."），banner 是招牌不是字幕
- 跟视频内容的卖点对齐，不要用脚本里没有的概念

#### Step 2：先生成 subs.ass（不烧）→ 注入 banner → 一次烧录

```bash
# 1. 只生成 ass，不烧字幕（替代原来的 video-edit subtitle）
video-edit subtitle <video> --gen-only

# 2. 注入 banner 到 subs.ass
TITLE="<上一步定的标题>"
DURATION=$(ffprobe -v quiet -show_format -of default=nk=1:nw=1 -show_entries format=duration <video>_edited.mp4)
banner-tool inject <video>.work/subs.ass --text "$TITLE" --duration "$DURATION"

# 3. 一次烧录（subs.ass 已存在 → 只烧不再重写，banner 跟主字幕一并烧出）
video-edit subtitle <video>
```

最终成片 `<video>_subbed.mp4` 顶部出现牌匾标语横幅。完成后告诉用户用了什么 banner 文字，用户不喜欢可以让你换。

**速度对比**（旧 → 新）：
- 旧流程：subtitle 烧 1 次（~60s）→ banner inject → subtitle 重烧 1 次（~60s）= 120s/视频
- 新流程：gen-only（~1s）→ banner inject → subtitle 烧 1 次（~30s 用 veryfast preset）= 30s/视频
- **4 视频从 ~8 分钟压到 ~2 分钟**

### 用户要求调整时

**如果是 ≥ 2 项调整**（"banner 换成 X，字幕加大"）→ **先回到 Step 0 读回 + 等确认**，再动手。迭代是漏指令的高发场景（上一轮的已生效项 + 这一轮的新要求容易混）。

单项调整直接做：
1. 编辑 `<video>.work/final_script.txt`（加/删内容）
2. 删 `<video>.work/subs.ass`
3. 重跑 `smart-cut` + `subtitle`

### 限时长精剪（用户给了目标时长）

写 final_script 时**额外做这些**：
- 估算字数：中文口播 ~3-4 字/秒，30 秒约 90-120 字
- 找钩子句放最前面（脚本开头那段）
- 压到目标时长（更激进地删）

### 仅字幕（用户明确说不剪）

```bash
video-edit subtitle <video>
```

没有 `_edited.mp4` 时，命令直接对原片烧字幕。

### 无字幕版（用户明确说不要字幕）

```bash
video-edit analyze <video>
# 写 final_script.txt
video-edit smart-cut <video>
```

不跑 subtitle，交付 `<原片>_edited.mp4`。

## 写脚本的心法

读 proposal 时**逐 phrase 判断**(简化决策树):

1. 这句是 NG 重拍?(口误/吞字后当场重说同一句)→ 删较差那遍
2. 这句是孤立 `嗯/啊/呃` 单字独立短语?→ 跳过
3. 其它(包括钩子/背景/重申/引导/听起来"不太核心"的句子)→ **保留**

不算字数,不设比例。**疑则留**。

**关键纪律 1**:脚本里的字**必须**来自原始转写——直接复制 proposal 里的 phrase 文本最稳。
你想怎么润色都不行(系统按字符匹配,改了字就匹不上)。

**关键纪律 2**:**只识别 NG,卡顿交给工具**。你不要在 prompt 层做"内容是不是太长" / "保留率够不够"的判断 — 那是工具阈值的事,smart-cut 会自动删卡顿/短停顿/喘气。

> ⚠️ 这条纪律 2026-05-08 重写过(经历两次反转):
> - 2026-05-07 之前:"宁可激进删,删多了用户能加回来"
> - 2026-05-07:反转为"宁可保守 + 60% 保留率",理由是开拍对比发现我们删多了内容(钩子/重申被删)
> - 2026-05-08:再次重写为"只识别 NG,其它全留",理由是 60% 硬规则 LLM 心算飘 + 工具阈值同步放宽,prompt 层不需要数字约束
>
> 现在的模型:**agent 只判断 NG;静音/卡顿由 smart-cut 工具阈值控制**(`DELETE_SILENCE_MIN=0.30s`/`RESIDUAL_PAUSE_MAX=0.50s`)。两层职责不再交叉。
>
> 详见 [P0 spec](https://github.com/Optima-Chat/video-edit-skill-spec/blob/main/docs/spec/P0-content-preservation.md) 和 [ADR](https://github.com/Optima-Chat/video-edit-skill-spec/blob/main/docs/decisions/2026-05-07-stop-pause-tightening.md)。

## 交付怎么说

简洁，**只说结果**：

- ✅ "剪好了，xxx_subbed.mp4，时长 23 秒"
- ❌ "我用了 video-edit smart-cut，写了 final_script.txt..."

## 不要做的事

- ❌ 跳过 final_script 直接 smart-cut——会失败（找不到脚本文件）
- ❌ 在脚本里写转写以外的字——匹配不上，那部分会被丢
- ❌ 用 `both` 或 `cut`——已弃用，质量差
- ❌ 把命令名/路径暴露给用户——内部细节
- ❌ 用户说"剪掉前 X 秒"/"剪掉中间一段"——定点裁剪不是去卡顿，告诉用户"我擅长去卡顿停顿，定点裁剪请用别的方式"
- ❌ **跳过通读自查直接 smart-cut**——重复内容是用户最反感的问题，省这一步省不出去
- ❌ **跳过 review 直接 subtitle**——subtitle 烧完字幕的成片重复不可逆，必须先通过 review
- ❌ **多指令场景跳过 Step 0 读回**——用户给 ≥ 2 个动作时不读回必有漏指令

## 命令参考（你内部用）

| 命令 | 用途 |
|---|---|
| `video-edit analyze <video>` | 转写 + 静音检测 + 生成 proposal（**默认入口**）|
| `video-edit smart-cut <video>` | 按 final_script.txt 对齐剪辑（**默认主力**）|
| `video-edit subtitle <video>` | 对成片重新转写 + 烧字幕，输出 `<原片>_subbed.mp4` |
| `video-edit cut <video>` | 旧模式：按 proposal 的 `#` 标记剪辑（弃用，质量差）|
| `video-edit both <video>` | 旧模式：analyze + cut 一步到位（弃用，跳过审片）|
| `video-edit review <video>` | 诊断用：检查 `_edited.mp4` |
