文言文教学视频制作
0. 定位:在父技能之上加了什么
文言文(小学「小古文」、初中文言文)的教学难点与白话课文完全不同:学生听不懂、不认识器物、读不准字音。
父技能解决的是「把课文做成带配音的视频」,本技能解决的是「把文言文讲明白」。
| 父技能 story-scenario-video | 本技能(文言文) | |
|---|---|---|
| 分支 | A/B 逐段混用 | 固定分支A(全片画面人物不开口) |
| 结构 | 按 beat 一路讲下去 | 三遍法:通读 → 精讲 → 回读 |
| 通读/回读素材 | 各做各的 | 复用精讲画面(做一遍视频、讲三遍) |
| 每句镜头 | 一个 | 两个:朗读镜(文言)+ 讲解镜(白话) |
| 音色 | 单一/分角色 | 双音色:文言庄重慢读 / 白话亲切讲解 |
| 字幕 | 台词 | sub_text 带生字拼音,line 保持纯文本 |
| 画面文字 | 无 | 标注层(引线标签/名词卡/拼音)后期叠加 |
| 文本锁 | 人物长相 | 追加比例锁(器物尺寸必须支撑情节动作) |
| 读音 | note 字段 |
--style 显式钉读音并重听(见 §4) |
> 本技能不重复父技能的规则。 分支机制、H3 生成、混音、静帧集、铁律 S1–S20 全部继承并继续生效。
> 本技能只写增量:W1–W10(§8)与四条专属流程。冲突时以本技能为准。
0.5 依赖与目录
~/.workbuddy/skills/
├── 故事情景教学视频制作/ ← 父技能(H3、gen_v2_A、gen_segments、make_stills_pdf)
├── 朗诵与配音duomi-TTS/ ← GEM-3.1 配音(含 --style 钉读音)
├── knowledge-picturebook/ ← I25S 出图
└── 文言文教学视频制作/ ← 本技能
脚本自动定位父技能(相对路径上溯两级);如需覆盖:
export WENYAN_PARENT_SKILL="/abs/path/故事情景教学视频制作"
1. 核心机制一:三遍法 + 双镜分离
1.1 三遍法(结构骨架)
| 遍 | 环节 | 目的 | 画面 | 配音 |
|---|---|---|---|---|
| ① | 通读感知 | 先建立整体语感,不解释 | 复用精讲朗读镜 | 文言慢读(整句/整篇) |
| ② | 逐句精讲 | 逐句落实字词与句意 | 每句两镜:朗读镜+讲解镜 | 文言/白话交替 |
| ③ | 回读巩固 | 无字幕或纯原文回读 | 复用通读(= 精讲朗读镜) | 同 ① |
> W1(硬规则):通读与回读必须复用精讲段的画面,禁止另做一套「长卷/全景」素材。
> 理由(2026-10-10《司马光》实测教训):给通读段单独做宽景,学生第一遍看到的是 A 套画面、第二遍精讲看到的
> 是 B 套画面——图文对不上,语感铺垫白做。正确做法是「做一遍视频、讲三遍」。
> 实现:build_read.py 按 plan 的 read_groups 把精讲朗读镜拼接、贴图内标注层、落回 h3/T*.mp4。
1.2 双镜分离(这是「讲解放哪」的标准答案)
每句拆成两个镜头,听觉与视觉各归其位:
朗读镜 <句号>a |
讲解镜 <句号>b |
|
|---|---|---|
| 画面 | 该句场景(人物在做什么) | 该句核心词的特写/示意 + 名词卡 |
| 字幕 | 文言原文(大字、带生字拼音) | 白话译文 |
| 配音 | 文言慢读(音色A,抑扬顿挫) | 白话讲解(音色B,亲切自然) |
| 时长 | 短(约 4~5s) | 长(约 7~10s) |
为什么不选其它两种:
| 备选 | 否决理由 |
|---|---|
| 同镜内先读后讲 | 文言与白话听觉上突然切换,学生分不清哪句是课文、哪句是老师说的 |
| 讲解全塞字幕 | 小学中段识字量有限,读字幕本身就吃力,等于没讲 |
> W2:禁止在同一镜头内混用文言音与白话音;W3:两个音色必须分开且经闸口确认。
2. 核心机制二:图文标注层(本技能最大增量)
图文结合是文言文学习的关键法门——学生不认识的器物(瓮、牍、磬…)必须在画面上指出来。
2.1 三种标注,各管各的
| 类型 | 用途 | 形态 | 数量控制 |
|---|---|---|---|
| 引线标签 | 指认单个实物(瓮、人物、道具) | 从物体拉一条金线到标签框 | 每段 ≤2 个,多了画面被线割碎 |
| 名词卡 | 字词释义(庭=庭除、皆=都) | 顶部横条(半透明深蓝+金边) | 每讲解镜 1 张 |
| 生字拼音 | 瓮(wèng)、没(mò)、迸(bèng) | 写进 sub_text,随字幕显示 |
只给难字注音 |
布局铁律:名词卡顶置(y≈0.045),字幕底置(y≈0.88–0.94),两者绝不打架。
2.2 ⚠️ 标注绝不能进 H3(W4)
带字的图喂给视频模型 = 字被重新渲染成乱码/抖动。 正确分工:
静帧(干净无字) ──▶ H3 出视频 ──▶ 成片段上贴标注层(ffmpeg overlay)──▶ 烧字幕
唯一例外:静帧集 PDF 的封面页是给人看的印刷品,可以带标题(用 PIL 叠在 2K 原图上)。
2.3 定位流程(禁止盲猜坐标)
出图 → 亲自读图确认物体在画面哪个区域 → 写归一化坐标 → 叠加 → 合成预览图 → 目视核对 → 微调 1~2 轮
坐标一律用归一化 (0~1) 写在 overlay_spec.json,脚本按目标分辨率换算——换分辨率不用改数字。
2.4 拼音只进 sub_text,绝不进 line(W5)
line 是送 TTS 的纯文本。若在 line 里写「足跌没(mò)水中」,TTS 会把括号拼音念出来。
正确做法:line 保持「足跌没水中」,sub_text 写「足跌没(mò)水中」。
3. 核心机制三:文本锁的比例陷阱(血泪教训)
这是本技能最难、也最容易翻车的一环。
3.1 相对特征 ≠ 绝对尺寸
《司马光》实测:我把「瓮=口小肚大」这条相对特征写成了 narrow small mouth + about chest-high to a child,
结果模型画出瓮口窄于孩童肩宽、瓮矮于孩童——学生一眼看出「这孩子根本掉不进去」,直接破坏课文逻辑自洽。
判据:凡文本锁里出现「小/窄/矮/大/高」这类相对形容词,必须追问:
它在画面里要支撑哪个情节动作?绝对尺寸够不够?
3.2 尺寸自检(写锁时必做)
为课文中每个涉及尺寸、容纳、高低的动词,反推所需的尺寸关系并写进锁里:
| 课文动作 | 必须成立的尺寸关系 |
|---|---|
| 「登瓮」 | 瓮须高于孩童(要爬上去) |
| 「跌没水中」 | 瓮口须宽于孩童肩宽(掉得进);瓮须够深(淹得没) |
| 「持石击瓮」 | 瓮须不矮于执器的孩童,否则举石砸不到 |
| (通用) | 任何「钻进/装下/爬上/淹没」都照此反推 |
写法:把相对形容词改成可验证的比较句——
a wide open round mouth clearly wider than a child's shoulders so a small child could fall through it +
standing taller than a small child + big enough to hold a whole child inside。
保留辨识特征:「口 < 肚」仍要写,它描述的是瓮的外形比例,不等于「口小到塞不下人」。
> W6:相对特征不得写成绝对尺寸错误;器物尺寸必须自洽支撑情节动作。
> 这条错误缩略图看不出来——必须放大逐张核对,且让学生能钻牛角尖的地方一律要自查。
3.3 两个「回潮」坑(改词时必查)
改文本锁时最容易把旧坑放回来,每次重出图后按此表自查:
| 坑 | 症状 | 防法 |
|---|---|---|
| 配角变装 | 跨段反复出现、且与主角同框的配角(如落水孩童)各段衣服颜色不一、甚至穿现代服装、撞主角配色 | 配角也要上独立文本锁(固定年龄/服装颜色/禁现代服饰),与主角同等对待 |
| 背景漂移 | 场景从「庭院」漂成「山村/茅屋/远山」 | NEG 里写死背景硬约束(禁茅屋/村落/农田/远山/现代服装),并每段前置「Set in <主场景>」 |
> W7:跨段共现的配角同样上文本锁;NEG 必须含场景与服饰硬约束。
> 这两条在《司马光》项目中都复发过一次(改锁时漏掉了),重出 10 张图才修掉。
4. 核心机制四:多音字/生僻字钉读音
GEM-3.1 在无上下文的短句里会读错多音字与生僻字(《司马光》实测:1:02 处「足跌没水中」的「足」被读成 xū)。
4.1 唯一有效手段:--style 显式钉读音
python {TTS技能}/scripts/tts.py speak \
--text "足跌没水中" --voice 毅恒 --speed 很慢 --rhythm 抑扬顿挫 \
--style "「足」读作 zú,是足球的足,不要读成 xū;「没」读作 mò,是淹没的没" \
--name s3a --out tts_out
写入 plan 时,把这句纠正话存在段的 style 字段(tts_batch.py 自动透传)。
父技能的 note 字段是给分支B 台词块用的,本技能固定分支A,读音纠正一律走 style。
4.2 出片前必查清单
- 通读全文,标出所有多音字(没 mò/méi、为 wéi/wèi、行 xíng/háng、少 shǎo/shào、长 cháng/zhǎng…)与生僻字(瓮 wèng、迸 bèng、牍 dú…);
- 每处写进对应段的
style; - 通读段与精讲段都要钉——同一个字在片中出现两次,只改一处等于没改;
- 必须听审(无本地 ASR,只能人耳)——替换后重合成,重点听那几处。
> W8:多音字/生僻字一律 --style 钉读音并听审;同字多处出现须全部钉。
5. 工作流
Step 0 立项与分句 ──▶ Step 1 文本锁(含比例自检 W6/W7)──▶ Step 2 锚点图 + 双音色试听 ──▶【闸①】
──▶ Step 3 分镜静帧(逐张放大核对比例/服装/背景)──▶【闸②】
──▶ Step 4 派生 v2 + 逐段配音(style 钉读音)
──▶ Step 5 H3 逐段生成 ──▶ Step 6 标注层(读图定位 + 预览核对)
──▶ Step 7 通读/回读复用 ──▶ Step 8 合成(标注层+拼音字幕+GEM 原声)──▶【闸③】成片 + 静帧集
| Step | 动作 | 产物/命令 |
|---|---|---|
| 0 | 分句、填 sentences.json(每句 wenyan/baihua/名词卡/note) |
init_project.py 生成 plan.json 骨架 |
| 1 | 写 design_bible 文本锁(画风句 + 主角 + 群演 + 核心器物(比例自检) + 主场景),逐字嵌入所有提示词 |
design_bible.json |
| 2 | 出锚点图(人物设定图 + 器物图)+ 两条试听音(文言/白话各一) | stills/anchor_*.png、tts_audition/*.wav |
| 闸① | 呈设定图 + 试听音,确认画风、两个音色、是否加拼音、片尾是否拓展 | AskUserQuestion |
| 3 | 出分镜静帧(run_stills 式并发 + 重试);逐张放大核对 |
stills/still_*.png |
| 闸② | 呈总览拼版 + 关键帧,重点核对比例/服装/背景 | present_files |
| 4 | gen_v2_A.py 派生(四项自检全 PASS)+ tts_batch.py 逐段配音 |
prompts_v2/、tts_out/ |
| 5 | gen_segments.py(改提示词前必删旧 mp4,S17) |
h3/*.mp4 |
| 6 | 读图定位 → 写 overlay_spec.json → gen_overlay.py → 叠到静帧上目视核对 |
overlay/*.png |
| 7 | build_read.py 复用精讲画面生成通读/回读 |
h3/T*.mp4、h3/R*.mp4 |
| 8 | finalize_wy.py 合成 + make_stills_pdf.py 出静帧集 |
成片 + PDF |
| 闸③ | 呈成片 + 静帧集 PDF(S18:两者一并交付,缺一不合格) | present_files |
验收三项(沿用父技能,出片前必测):音轨 aac/24000/1;分支A 闭口旁证 mean_volume < -30dB(超标时回到画面抽帧验口型,H3 自生成环境音可接受);旁白末字未被截。
6. plan.json 扩展字段
父技能字段全部保留,本技能新增/约定:
{
"project": "司马光",
"workdir": "E:/.../司马光教学视频",
"h3_dir": "h3",
"design_bible": { "画风": "...", "主角": "...", "群演": "...", "核心器物": "...", "主场景": "..." },
"voices": { "wenyan": "毅恒", "baihua": "栀栀" },
"voice_params": {
"wenyan": { "speed": "很慢", "rhythm": "抑扬顿挫", "emotion": "平静", "tag": "朗诵" },
"baihua": { "speed": "偏慢", "emotion": "平静", "tag": "讲述" }
},
"read_groups": {
"T1": ["s1a", "s2a", "s3a"],
"T2": ["s4a", "s5a", "s6a"],
"reuse": { "R1": "T1", "R2": "T2" }
},
"segments": [
{ "key": "s3a", "image": "stills/still_s3a.png",
"source_prompt": "prompts/s3a.txt", "prompt": "prompts_v2/s3a.txt",
"mode": "A", "audio": "none", "role": "VO",
"line": "足跌没水中",
"sub_text": "足跌没(mò)水中",
"style": "「足」读作 zú;「没」读作 mò,是淹没的没",
"voice": "wenyan", "duration": 4, "sub": true,
"scene_name": "足跌没水中", "scene_kind": "瓮边 · 中景" },
{ "key": "R1", "reuse_from": "T1", "voice": "wenyan" }
]
}
sub_text:烧进字幕的文本(可含拼音);缺省回落到line。style:该段的读音纠正句,透传给 TTS 的--style(W8)。voice:wenyan/baihua,查voices表取音色名。reuse_from:该段音频直接复制自另一段(回读复用,不重复调 TTS)。read_groups:通读组由哪些精讲朗读镜拼成;reuse回读映射。- 每段必须
"mode": "A"+"audio": "none"(全片分支A)。
7. 脚本速查
| 脚本 | 用途 | 关键参数 |
|---|---|---|
init_project.py |
立项:由 sentences.json 生成三遍法骨架 plan.json + overlay_spec.json + design_bible.json 占位 + 目录 |
--workdir --config --group-size |
gen_overlay.py |
标注层生成(引线标签/名词卡/封面标题),数据驱动 | --spec overlay_spec.json --out overlay --size 1344x768 |
build_read.py |
通读/回读复用(W1):按 read_groups 拼精讲朗读镜、贴标注层、对齐配音时长 |
--plan --h3-dir --tts-dir --ov-dir |
tts_batch.py |
双音色逐段配音,自动透传 style 钉读音,支持 reuse_from |
--plan --out --only --force |
finalize_wy.py |
合成:贴标注层 + 烧拼音字幕 + 混入 GEM 原声(冻结对齐 S20)+ concat | --plan --cover --out --ov-dir --font-size |
父技能脚本继续用:gen_v2_A.py(分支A 派生 + 四项自检)、gen_segments.py、make_stills_pdf.py、h3_video.py。
8. 铁律(W 系列;S1–S20 继续生效)
| # | 铁律 |
|---|---|
| W1 | 三遍法 + 通读/回读复用精讲画面。禁止为通读段另做一套素材(长卷/全景);「做一遍视频、讲三遍」。实现见 build_read.py 与 plan 的 read_groups |
| W2 | 朗读镜/讲解镜分离:朗读镜=文言字幕+文言音,讲解镜=白话字幕+白话音。禁止同镜内混用两种语音 |
| W3 | 双音色:文言庄重慢读、白话亲切讲解,两音色必须分开且经闸口确认(受 S11 门控);中途不得更换 |
| W4 | 标注层后期叠加,绝不喂给 H3:静帧保持干净无字;标注在合成阶段用 ffmpeg overlay 贴上。带字静帧进 H3 = 字被重渲染成乱码。唯一例外是静帧集 PDF 封面页(给人看的印刷品) |
| W5 | 拼音只进 sub_text,绝不进 line:line 是 TTS 纯文本,夹拼音会被念出来。sub_text 缺省回落到 line |
| W6 | 比例文本锁:文本锁里的相对形容词(小/窄/矮/大/高)必须能支撑情节动作——为「登/跌入/淹没/击」这类动词反推尺寸关系并写成可验证比较句。保留外形辨识特征,但不得让它否定情节自洽 |
| W7 | 配角也上锁 + NEG 背景硬约束:跨段共现且与主角同框的配角,须与主角同等对待上独立文本锁(年龄/服装/配色/禁现代服饰);NEG 写死禁茅屋/村落/农田/远山/现代服装,并每段前置「Set in <主场景>」 |
| W8 | 多音字/生僻字一律 --style 钉读音并听审;同一字在通读段与精讲段多处出现时全部要钉。钉完重合成后必须人耳复听(无本地 ASR) |
| W9 | 每句讲解镜必须让该句核心词「看得见」:核心器物/人物/动作用画面呈现出来(必要时特写或示意),再配名词卡释义。图文结合是本课型的第一法门 |
| W10 | 分镜静帧必须逐张放大核对(比例/服装/背景三项),缩略图看不出变装与比例错误;核对完再进闸② |
| W11 | 通读/回读字幕必须复用精讲朗读镜的逐句字幕:plan.json 中 T*/R* 段必须 "sub": false 且 sub_text 留空,由 build_read.py 在拼接时按成员 s<i>a 的 sub_text 逐句烧入;禁止把整组全文写进一个 sub_text 让 finalize 静态烧满整段(会导致字幕严重超框) |
9. 排障速查
| 症状 | 根因 | 处置 |
|---|---|---|
| 学生质疑「人比器物口还大,怎么掉得进去」 | 文本锁把相对特征写成绝对尺寸(W6) | 按 §3.2 反推动作所需尺寸,改锁为可验证比较句,重出所有含该器物的图 |
| 通读段画面和精讲段不一样 | 给通读段另做了素材,违反 W1 | 用 build_read.py 按 read_groups 复用精讲朗读镜 |
| 通读/回读字幕超长、超出屏幕边界 | T*/R* 段 sub_text 写了整组全文并被 finalize 静态烧满整段(违反 W11) |
plan 中 T*/R* 改 "sub": false 且 sub_text 留空,由 build_read.py 烧入逐句字幕;必要时同步更新 references/wenyan-structure.md |
| 字幕里出现「(mò)」被念出来 | 拼音写进了 line(W5) |
拼音移到 sub_text,line 保持纯文本重配音 |
| 某个字读错(如「足」读 xū) | GEM-3.1 在短句无上下文处读偏 | 该段加 style 钉读音(W8),同字多处全部改,重配音后人耳复听 |
| 画面里器物上的字是乱码/抖动 | 把带标注的图喂给了 H3(W4) | 静帧去字重出,标注改在合成阶段 overlay |
| 配角各段衣服颜色不一/穿现代装 | 改锁时漏了配角锁(W7) | 补配角独立文本锁,重出相关图 |
| 场景漂成山村/茅屋 | NEG 缺背景硬约束(W7) | NEG 补禁项 + 每段前置「Set in <主场景>」,重出相关图 |
| 标注框挡住字幕或压住人物 | 坐标盲猜 | 名词卡顶置 y≈0.045、字幕底置 y≈0.88;出图后读图定位,先叠到静帧上目视核对再合成 |
| 改了提示词重生成画面没变 | gen_segments 幂等(S17) | 先删该段 mp4 再重跑 |
| 成片音轨 32000Hz/2ch | 误用 H3 重渲染音轨(S15) | 用 finalize_wy.py 混入 GEM 原声,输出强制 -ar 24000 -ac 1 |
10. 参考文件
| 文件 | 内容 |
|---|---|
references/wenyan-structure.md |
三遍法与双镜分离详解、讲解放置决策表、时长配比、语感培养要点 |
references/annotation-layer.md |
标注层三种类型规范、overlay_spec.json 字段、定位流程与布局避让 |
references/lock-and-pronunciation.md |
比例文本锁自检法、配角锁与背景约束模板、多音字钉读音写法与常见字表 |