qwen-subtitle — 百炼字幕智能纠错 + 多语言出海配音
它解决什么问题
录屏 / 教程类视频的字幕,无论用哪家 ASR,专有名词永远会被听错:Claude 听成 cloud、Codex 听成 class q、html2pptx 听成 html to ppt、OOXML 听成 OOCML。靠一份手工维护的术语表去替换,既累又补不全。
本 skill 的核心洞察:录屏视频里,正确的词往往就明明白白写在屏幕上。所以——让视觉模型按时间戳去看那一帧,读屏幕上的真实文字来纠正。这是单通道(纯语音)工具做不到的事。
判断标准(避免按字面例子过拟合):凡是"屏幕上写着、却被听成同音词"的术语 / 产品名 / 代码 / 命令 / 文件名 / 路径,都属此类。
组合了千问的哪些能力
| 能力 | 模型 | 走的命令 | 角色 |
|---|---|---|---|
| 🎙️ 语音识别 | FunAudio-ASR | bl speech recognize |
出字幕,带句级+词级毫秒时间戳——后续抽帧、断句、对齐配音都靠它 |
| 🧠 标错 | qwen3.7-max | bl text chat |
高精度扫出"明显听错"的点,分 screen / semantic 两类 |
| 👁️ 看屏纠正 | qwen3-vl-plus | bl vision describe |
对 screen 类按时间戳抽帧、读屏上真实文字定夺,带画面证据 |
| ✨ 去水词 | qwen-plus | bl text chat |
删呃啊嗯、口吃重复,顺滑 |
模型分工是实测定下来的:标错用 qwen3.7-max(精度命门,plus 会漏真错、且重新误标);去水词用 qwen-plus(低风险,快 5 倍)。这些在脚本顶部 FLAG_MODEL / VL_MODEL / FILLER_MODEL 常量里,需要时直接改。
流程(5 步,一条命令跑完)
视频
├─[1] FunAudio ASR ───────► 字幕轨道(句+词级毫秒时间戳)
├─[2] qwen3.7-max 标错 ───► 可疑点(screen=看画面 / semantic=纯语义)
├─[3] qwen3-vl-plus 看帧 ─► screen 类逐帧读屏纠正(带证据)
│ └ 取不到证据 → 保留原文+标记待确认〔零误改〕
├─[4] 词级时间戳断句 ─────► 按标点拆长句(>24字 / >6秒)
├─[5] qwen-plus 去水词 ───► 删语气词/口吃,顺滑
└─► corrected.srt + transcript.json + report.md(画面证据)
准备
认证:bl 已登录就什么都不用设——脚本直接用 bl 自己的认证(bl auth status 验证)。克隆配音那步要 curl 直调原始 API,脚本会自动从 ~/.bailian/config.json 读 key(或环境变量 DASHSCOPE_API_KEY,两者皆可)。不需要每次手动 export,也绝不把 key 写进任何文件。仅当 bl 从未配置过时,才 export DASHSCOPE_API_KEY=sk-xxx。
依赖:
bl(百炼 CLI,v1.4+;bl --version验证)ffmpeg(抽音频 + 抽帧;脚本自动从 PATH 找,或设FFMPEG环境变量)python3flask(仅预览页需要:pip install flask;纠错/翻译/配音不需要)
bl speech recognize 和 bl vision describe 都直接吃本地文件路径(CLI 内部已处理上传),所以不需要手动上传文件拿 URL。
第一步:先问需要哪些语言(默认行为,菜单一律用中文问)
每次运行,先用中文问用户要哪些语言(给编号,可多选如 1,3,5)。问答和 tab 标签都用中文(用户不一定看得懂外文/原生写法):
请选择要生成的语言(可多选,用逗号分隔):
〔配音 + 字幕〕用你的克隆声音说外语
1. 中文(原声,仅纠错字幕)
2. 英语 3. 日语 4. 韩语
〔仅字幕〕qwen 翻译,视频保留原声
5. 西班牙语 6. 葡萄牙语 7. 阿拉伯语
8. 印尼语 9. 越南语 10. 泰语
- 只选「中文」→ 纯纠错(A)。选了配音语言(英/日/韩)→ A + 克隆配音(B)。选了仅字幕语言 → A + 翻译出字幕(B,不配音,视频留原声)。
- 配音只支持 中/英/日/韩(CosyVoice 限制);其余语言只出字幕(qwen 支持 92 语种,这里列几个出海主流,要别的随加)。
- code 对照:英 en / 日 ja / 韩 ko / 西 es / 葡 pt / 阿 ar / 印尼 id / 越 vi / 泰 th(脚本
LANGS表里改)。
A. 纠错中文字幕(基础,所有情况都先跑)
python3 scripts/subfix.py <video.mp4> [--out DIR]
参数:--out DIR(默认 <视频名>.subfix/)、--max-seconds N(试跑前 N 秒)、--lang zh、--reuse(复用 asr.json/suspects.json 只重跑后续)。
产出:corrected.srt(纠错+断句+去水词的中文字幕)、transcript.json([{start,end,text}])、report.md(每处改动 + 画面证据 + "取不到证据保留原文待确认"清单);另有 corrected.json(句级结构化结果)与 report.json(机器可读证据)。中间产物:asr.json/suspects.json。
给用户看结果时:念
report.md的改动+证据(最有说服力);诚实区分"已改(有画面铁证)"与"保留原文待确认",别把后者说成已修复。
B. 多语言出海:克隆原声 + 翻译 + 配音
用户选了外语时,在 A 的基础上跑:
python3 scripts/dub_multi.py <video.mp4> \
--transcript <A的transcript.json> --langs en,ja,es,pt --out <DIR> [--clip-seconds N] [--voice-id ID]
⚠️
--clip-seconds默认 0 = 整片;它会限制字幕/配音的实际范围(不只是裁预览),只在试跑前 N 秒时才设。要整片就别带它(且上游subfix.py也别带--max-seconds)。
--langs 传 code(en/ja/ko/es/pt/ar/id/vi/th…)。脚本自动判档:
- 配音语言(
DUB={en,ja,ko}):克隆原声(扒 18s →bl file upload→ 复刻 API → voice_id,--voice-id可跳过)→ 按整句、限定词数翻译(配音=字幕同一份文案,要在镜头时长内说完)→ 克隆音色 TTS →atempo卡时长 → 出<code>.json+<code>.m4a(配音轨)。 - 仅字幕语言(其余):只翻译 → 出
<code>.json;视频保留原声。 - 全选了仅字幕时不克隆(省一步)。产出
manifest.json+ 各语言 transcript(+配音轨) +clip.mp4。
翻译分两路(脚本 translate() 按 for_dub 自动选):配音语言走 qwen-plus——同一份稿子既配音又当字幕,按词数(≈时长×2.6)压到能在时长内说完;纯字幕语言走 qwen-mt-turbo(TRANSLATE_MODEL 常量,百炼专用翻译模型,忠实、品牌/型号天然保留)。支持:配音=中/英/日/韩(CosyVoice v2 官方);字幕=92 语种(LANGS 表里加 code 即可)。英文配音已充分验证;日韩配音用同一克隆音色,质量需实跑确认。
配音的三条关键设计(踩坑换来的,改前先懂):
- 按整句念,不按字幕碎段——碎段会念成半句、听着"没说完";所以先把字幕碎段合并回整句再配音。
- 字幕 = 配音同一份译文,按句计时——否则屏幕显示的字和嘴里说的对不上。
- 只压缩不拉慢(atempo 上限 1.5):译文短了就自然播放留空,不拉慢拖沓。
多语言预览(tab 切字幕 + 切音轨)
预览页已自带(scripts/preview_editor.py,基于 Flask 的本地交互页)。需要 flask(pip install flask;纠错/翻译/配音本身不需要)。把 manifest 交给它:
python3 scripts/preview_editor.py <out>/manifest.json # 多语言(manifest)
python3 scripts/preview_editor.py <video> <transcript.json> # 单语言
预览页右上角按语言出 tab,切 tab → 右侧换该语言字幕、左侧视频换该语言音轨(源语言用原声,外语用克隆配音轨,与视频帧同步)。人工过一遍后保存,再交烧录。
设计原则:零误改优先
字幕工具里,错误的"纠正"比不纠正更糟——把对的词改成错的会主动污染字幕。所以这套流程的每一步都在"宁可不改,不可改错":
- 标错别越界:只标明显听错的;本身通顺、像名字的中文(哪怕疑似音译)和本身正确只是"不够具体"的词,都不标。
- VL 禁幻觉:只认画面上能逐字读到的文字;靠图标/logo/常识推断的一律不算数。
- 最小替换 + 发音一致:只替换听错的那几个字,不带进相邻路径段;改后的词必须和原文读音对得上。
- 证不了就留原文:screen 类取不到画面证据,保留原文 + 标记待确认,绝不套用模型盲猜。
这些闸门是踩了一轮坑(误标 ooxml、把"超级麦吉"改成"超级码力/Supermario")才加上的。每条闸门的来龙去脉、对应的提示词写法,见 references/design-gates.md——改提示词前务必先读,否则容易把精度调回原形。
调参(脚本顶部常量)
FLAG_MODEL / VL_MODEL / FILLER_MODEL—— 三阶段模型MAX_CHARS=24/MAX_DUR_MS=6000—— 断句阈值(单条字幕最大字数/时长)MIN_BREAK_CHARS=12—— 到标点且已够这么长就断- 甜区:录屏 / 屏幕共享 / 教程类(术语都写在屏上)。纯口播、谈话头(术语不在画面)时,VL 取不到证据会更多保留原文——这是预期行为,不是 bug。