# 项目协作主控文件 (doco/CLAUDE.md)

<!--
  Claude Code 每次开 session 自动读取。两个角色：

   1. 给 AI 的项目说明书（项目是什么、用什么、怎么跑）

   2. 双账号/多工具之间的"活快照"交接区（现在到哪了、下一步、刚做的决定）
        维护原则：增量更新，不要整篇重写。改动写进对应小节即可。

   3. 每次结束session或通哥明确说结束今天的工作，应由Claude修改更新本文件，交接备注和待确认等部分内容应为最新。

      -->

---

## 🔖 状态栏 (STATUS — 每次结束 session 前必须更新这三行)

- **最后更新**：Claude Code ｜ 2026-07-03
- **当前状态一句话**：**doco 子项目阶段性收工。** 22 期融合A稿全部产出，成品在 `doco/deliverables/`。主项目寄存条交接核查已完成（2026-07-03）：寄存条状态表已更新、api_credentials_inventory 已更新（MiMo/讯飞）。下一步由主项目侧执行批量导入知识库。
- **下一个动手的人从这里开始**：doco 开发完成，无待办。主项目侧批量导入走 Phase 3 上传/embedding 链路。

---

## 🤖 给 AI 的工作约定 (READ FIRST)

- 开始任何工作前，先读完本文件，特别是「状态栏」「交接备注」「关键决策」三节。
- 完成一段工作后**增量更新**：「已完成」追加一条、「待办」勾掉或新增、必要时更新「关键决策」，最后改「状态栏」三行。不要重写整份文件，不要删历史，只追加/勾选。
- 涉及发送、发布、删除、改权限、付款等不可逆操作前，先在对话里跟通哥确认。
- **本文件与项目设计文档并列，构成 doco 子项目宪法。冲突时优先级：本文件 STATUS/交接 > `doco_P3续接快照_C1完工C2起步.md` > `doco_P3_设计稿.md` > `doco_project_design.md`（越新越权威，尤其"LLM 提供方"那条以快照为准）。** 新对话接手时连同上述文档一并阅读。

------

**Claude（Opus 高级技术顾问）与制片人刘通协作原则**

1. 全程使用简体中文。
2. 通哥不懂代码编程，解释要简明扼要，说人话，不堆术语。
3. 为了节省Claude的Token，你主要的工作是plan和审核cline的报告，除非cline3次及以上修改过不去的坎，你可以直接动手改代码。
4. 给 Cline 的指令一律用代码块封装，并明确标 ACT / Plan，方便复制。
5. 回复简明扼要，不解释太多中间过程；**独立核验 Cline 自报结果，对着源数据查证，不轻信口头诊断**（本项目已多次靠核验抓到 Cline 偷换模型/术语源、误判字数）。
6. 长批处理（OCR/ASR）放独立终端跑，不要让 Cline 盯。
7. git 直接推 main，不开分支。

---

## 1. 项目概览

- **项目名**：doco 文稿整理模块（《军事科技》TPS 工作台子模块）
- **目标**：把 **A稿（编导定稿）+ B稿v2（屏幕字幕，P2 产出 743 行）+ ASR（口语实况）** 三方交叉融合，产出两个内容一致、形态不同的交付物：
  - **融合B稿**（给爱德华）：碎句 + 密集字幕级时间戳 `[XmYs] 文本`
  - **融合A稿**（给编导）：公文格式 docx，保留【导视】【主持人N】【解说N】【专家N】【隔断】分段
  - 一致性约束：融合A稿**由融合B稿生成**（按 A 稿分段归拢 + 套格式），不是事后比对硬凑。
- **范围边界（别越界去做）**：本模块只做"三方文本 → 终版"。**不做** OCR 识别（P2 已产出 B稿v2）、**不做**音频转码本身（属 ASR 适配层）、**不做**档案入库、**不做**编导确认 UI。

---

## 2. 技术栈与运行方式

- **语言**：Python（openai SDK 走 DeepSeek 兼容端、ffmpeg、python-docx）
- **关键目录**：
  - `src/doco/llm.py` — DeepSeek 统一客户端，`chat(messages, model=None, thinking=None, max_tokens=...)`，从 `doco/.env` 读 key/base_url/model
  - `src/doco/term_extract.py` — C1：规则层 + AI 层提取 + 词典累积 + 热词清洗
  - `src/doco/asr_adapter.py` — C2 讯飞 ASR 适配层（已完工）：`get_hot_words(episode_id)` 读热词表、`transcribe()` 返回 `(句子, raw)`、`write_asr_result()` 落 `asr_v2_timed.txt` + `asr_result_raw.json`
  - `src/doco/video_split.py` — P1：含 `extract_audio()`（16kHz/单声道/16bit WAV），C2 直接复用，不另写 ffmpeg
  - `src/doco/fusion_review.py` — C3：B稿v2 ⊕ ASR 交叉复审 → 融合B稿(743行) + fusion_review.csv
  - `src/doco/cli.py` — 命令入口（split / terms / asr / fuse / compose / skeleton / **run**）
  - `src/doco/templates/` — P1/P2 stage 脚本模板（`doco run` 自动复制到 episode 目录）
  - `doco/data/term_dict.json` — 累积词典（当前 267+ 条）
  - `doco/programs/<episode_id>/` — 每期产物（热词表、各阶段输出）
- **常用命令**：
  - C1（已实现）：`doco terms --episode-id <id> --a-script <path> [--no-ai]`
  - C2（已实现）：`doco asr --episode-id <id> --input-video <绝对路径mp4> --output-dir <绝对路径 doco/programs/<id>>`（`--skip-asr` 只分离不转写）。**务必传绝对 `--output-dir`，否则落到当前工作目录的 `programs/` 会与 doco 产物分家。**
  - C3（已实现）：`doco fuse --episode-id <id> [--no-ai] [--batch-size 35]`
  - C4（已实现）：`doco compose --episode-id <id> [--batch-size 25]`
  - 骨架：`doco skeleton --episode-id <id> --a-script <docx>`（需人工核验后再跑 compose）
  - **一键全流程**：`doco run --episode-id <id> --a-script <docx> --input-video <mp4> [--batch-size 25] [--skip-p1]`
  - **批量跑**：`python _batch_run.py`（16 期自动串跑，断点续跑，已有产物自动跳过）
- **环境变量 / 密钥**：只放 `doco/.env`（已在 `.gitignore`）。需要的 key：`LLM_API_KEY` / `LLM_BASE_URL` / `LLM_MODEL`（已切换到小米 MiMo 2.5 Pro）、`XFYUN_APP_ID` / `XFYUN_SECRET_KEY`。**代码与对话里只引用变量名，绝不出现真值。**

---

## 3. 当前进度（核心交接区，以最新快照为准）

- **已完成至**：**doco 子项目开发收尾。** 22 期融合A稿全部产出（ep001-022，含说话人区分 C4 重跑），已归拢至 `doco/deliverables/`（按播出顺序命名，22 份）。第01期（武器装备里的形状规律）缺A稿暂跳过。排播对照表 `doco/data/2026episode_list.xlsx` 已就位。
- **正在做**：无。doco 开发完成，等待回归 TPS 主项目。
- **卡点 / 待解**：下一步把 22 期融合A稿批量导入 TPS 主项目知识库（走 Phase 3 上传/embedding 链路）。

---

## 4. 已完成（只追加，最新在最上）

- [2026-07-03 ｜Claude Code] **主项目寄存条交接核查完成。** 更新内容：① 主项目 `note/寄存条Doco文稿整理子项目已外迁.md` 底部状态表从"等子项目出 PRD v3 启动 P1"更新为"开发收尾（2026-07-03）"；② `docs/api_credentials_inventory.md` doco 节更新——讯飞改已激活、DeepSeek Vision 改为 Ollama 本地 DeepSeek-OCR、新增小米 MiMo 2.5 Pro（替代原 Anthropic Claude API 条目）；③ doco CLAUDE.md 状态栏更新。交付物核查：22 份 docx + 20 份 md 在 `deliverables/`、术语词典 `term_dict.json` 10600 行、测试 `test_video_split.py` 存在。
- [2026-06-26 ｜Claude Code] **doco 子项目收尾：ep021/022 补稿 + 说话人重跑 + 成品归拢。** 本 session 完成：① **ep021（海战颠覆者）、ep022（硬核脑洞）两期新补 A 稿全流程 P1→C4 跑通**——ep021 骨架 LLM 自动生成失败（`extract_a_paragraphs()` 过滤空段后重编号，LLM 用了 raw docx 下标），手写骨架 JSON（32 段=26 normal+6 ignore 粗体章节标题）通过校验；ep022 骨架 LLM 自动通过（38 段全 normal，inline header）；ep021 C3 review 13 条（均 OCR 修正，专名零替换）、C4 punct_ok 25/26；ep022 C3 review 5 条、C4 punct_ok 全过；② **cli.py 路径 bug 修复**：`episode_dir = Path("programs")/episode_id` 相对路径在子进程中二次解析导致路径翻倍（`programs/ep021/programs/ep021/...`），加 `.resolve()` 修复——此 bug 从未触发因 ep004 用 `--skip-p1` 且 `_batch_run.py` 用绝对路径；③ **ep007、ep010 C4 说话人区分重跑**（06-24 批量重跑时这两期被遗漏，融合A稿时间戳仍为 06-23），重跑完成、产物已更新；④ **排播对照表** `doco/data/2026episode_list.xlsx`（25 期播出日期/期次/节目名/编导/收视）导入，建立 ep→播出顺序完整映射；⑤ **成品归拢** `doco/deliverables/` 文件夹：22 份融合A稿按播出顺序统一命名（`第XX期_YYYYMMDD_节目名_编导_融合A稿.docx`），缺第01期（武器装备里的形状规律，无A稿）；⑥ 通哥人工核验 ep021/022 质量高，仅两处小毛刺（ep021 一段话归错段落、ep022 一个标签应为三维动画），确认技术路径有效。
- [2026-06-23 ｜Claude Code] **批量化基础设施完工 + ep004 全流程通过 + 16 期批量跑启动**。本 session 完成：① ep004（枪王对决，最难·小剧场密·58段）全流程 P1→C4 一次通过，C4 punct_ok 58/58 全过、confidence 全 ≥0.80（batch_size=25 效果显著，无批次回退）；② 新增 `doco run` 一键全流程命令（Cline 实现，Opus 审核），参数 `--episode-id / --a-script / --input-video / --batch-size / --skip-p1`，ep004 `--skip-p1` 验证通过；③ C3 SYSTEM_PROMPT 收紧专名铁律（防 ASR 同音字替换权威 B稿专名）；④ Ollama 并发提至 16 路（`OLLAMA_NUM_PARALLEL=16`，8 路实测 GPU 96%、显存充裕）；⑤ 16 期新目录建立 + 文件拷贝 + 骨架批量生成并集中核验（真名零泄露、隔断/ignore 正确）；⑥ `_batch_run.py` 批量脚本启动跑 ep005-ep020。**doco PRD 完成标准已与通哥确定**：20 期全出稿 → 带成品回 TPS 主项目知识库批量导入。
- [2026-06-22 晚｜Claude Code] **ep002 C4 审核完毕，全流程收工**。Cline 跑完 C4 compose 出稿 `20260127潜艇的仿生之路_穆佩弦_融合A稿.docx`。Opus 审核核验：硬校验（汉字零改）全过、733 行全覆盖、标点回退 0 段。**但发现两个问题**：① Cline 自报空段名称错了两个（报"解说3/解说6"，实际是"三维动画解说3/解说8"）、隔断数也报错（报 3 个，实际 4 个）——再次印证不信 Cline 自检；② **9/19 批次（47%）LLM 对齐失败走回退**（全 confidence=0.30），本次因失败批次恰在大段中间未翻车，但属黄色预警。**分段偏差根因**：A 稿是拍摄前剧本，专家采访段与实际播出内容差异巨大（A 稿一句话提纲 vs 专家自由发挥两分钟），LLM 无法正确匹配——这是信息不对应，非算法问题。通哥手动批改分段（~10 处高亮标签），内容本身正确。**结论（通哥拍板）**：治本靠编导给贴近播出版的稿子，现阶段接受"程序保证文字零改 + 编导手调分段"的模式。
- [2026-06-22 ｜Claude Code] **ep002 C3 融合通过**。融合B稿 733行（零偏移），review 10条（5 minor_edit 全是 OCR 错字修正，专名无同音替换）。C4 compose 交 Cline 跑中。
- [2026-06-22 ｜Claude Code] **ep002（潜艇仿生）P1→P2→C1→C2 完成**。P1 全量抽帧 1620 帧（不做 dHash/IoU 去重，全部进 OCR）；P2 OCR 4路并发首秀通过（~50帧/分钟，约串行3倍），Stage B 文本去重出 B稿v2 733行；C1 术语提取 68 条热词（词典累积至 235 条）；C2 讯飞 ASR 411 句。下一步 C3 fuse。
- [2026-06-22 ｜Claude Code] **标点层 prompt 修复 + ep003 重跑出稿**。`PUNCTUATE_SYSTEM_PROMPT` 加第5条规则（陈述句结尾用句号）+ 错误/正确示范。ep003 逗号占比从 88% 降至 76%，句号从 70 涨到 118。红线未破（strip_punct 硬校验通过）。commit `fd2ef1a`。
- [2026-06-22 ｜Claude Code] **LLM 提供方从 DeepSeek 切换到小米 MiMo 2.5 Pro**。环境变量名 `DEEPSEEK_*` → `LLM_*`（通用化），涉及 llm.py / term_extract.py / video_split.py。API 已验证连通。commit `9948b7d`。
- [2026-06-22 ｜Claude Code] **Ollama 并发配置完成**（`OLLAMA_NUM_PARALLEL=4` + 重启）。4090D 24G 显存充裕（OCR 时 GPU 利用率仅 2%），后续可提到 8 路。
- [2026-06-18 晚 ｜账号A] **ep003（小尺寸大讲究）批量首期·全流程端到端跑通**（P1→P2→C1→C2→C3→C4 六阶段）。产出 `20260331枪械设计中的小尺寸大讲究_孙逸昊_融合A稿.docx`。逐阶段实测数：P1/P2 出 **B稿_v2.txt 698 行**（OCR 串行，未用并发）；C1 出 **60 热词**（词典累至 172 条，AI 候选回查 A稿无幻觉）；C2 讯飞真转写 **278 句带时间戳**（热词偏置生效，型号命中良好）；C3 fuse 698 行零偏移、仅 1 处 minor_edit、留痕 1 条；C4 compose 20 段、硬校验**汉字零改逐段通过**、1 处标点回退(seg_6)。**核验三连**：① **4 个空段(seg15–18:主持人7/解说8/主持人8/解说9)确认是播出剪掉的 VSS 微声狙击枪 + NTW-20 南非大炮整段**——融合B稿里 VSS/NTW/南非/消音/反器材**零出现**，seg14 边界抽查(593–681 全 HK416、682 起进结束语)证对齐正确，非 bug；② **C3 同音字坑已抓修**：LLM 把权威 B稿"斯泰尔"按 ASR 同音盖成"斯太尔"(519 行)，已改回，与 487 行及 A稿一致；③ **出稿命名生效**——`{原A稿stem}_融合A稿.docx`，不覆盖原始定稿。**本期间改的代码**：(a) `stage_a_extract_ocr.py` 修 GBK 崩溃(ffmpeg 子进程加 `encoding=utf-8,errors=replace`)+ 去掉 A3 硬编码 ep001 台词断言改通用中文冒烟检查 + **加并发 OCR(ThreadPoolExecutor/`OCR_NUM_WORKERS`默认4/idx-set 续跑)**——并发**未在 ep003 实跑**(ep003 走串行)，ep002 首秀；(b) `fusion_align.py` a_candidates glob 排除"融合/批改"稿(防重跑把自己输出当输入) + 出稿改名带原稿信息。
- [2026-06-18 ｜账号A] **A稿解析器升级：正则 → LLM分段骨架 + 人工确认 + ignore合并**。起因：量产三期(ep002/003/004) A稿格式与 ep001 天差地别（冒号头/正文同段、`【固摇轨】`镜头标记酷似段头、正文整段加粗、小剧场对话角色头），原 `parse_a_segments` 纯正则(`^【.+?】$`)必散架。新机制(`fusion_align.py`)：`extract_a_paragraphs`(带 bold/align 样式) → `extract_skeleton_llm`(thinking=True/max_tokens足量，**LLM 只判结构不碰正文**，输出 type/role_label/para 区间 JSON) → `validate_skeleton_coverage`(全覆盖硬校验，抓 LLM 数错下标/JSON 截断) → 落 `<id>_a_skeleton.json` + 人类预览表 → `parse_a_segments` 改读骨架按下标原样抽正文(inline 头切冒号、standalone 头取后续段、**ignore 段丢弃**、**相邻同 role_label 仅隔 ignore 的 normal 段合并**)。新命令 `doco skeleton --episode-id <id> --a-script <docx>`。**三期骨架已产并人工核验通过**：人名零泄露(刘通/孙逸昊/左鑫/穆佩弦/主持人真名都没混进 role_label)、覆盖完整、ep002 四隔断认对、ep003 九个`（出枪柜）`等归 ignore 且主持人被运镜切断后正确合并(37→20段)、ep004 小剧场卡/斯角色保留。ep001 无骨架时仍走旧正则 fallback，不受影响。
- [2026-06-18 ｜账号A] **P3-C4 第一批（保守版）收口**：`fusion_align.py` 把融合B稿743行碎句按 A稿 28 段（含 4 隔断）对齐归拢，套公文 docx 出稿 `融合A稿.docx`。**两条铁律守死**：①汉字零改——正文 100% 来自融合B稿；②标点由 LLM 按语义增删（一逗到底→正常断句）。机制：(a) **AI 对齐层**喂完整 A稿正文、seg_id 单调不减、thinking=True/4096、缓存 `.c4_cache/batch_N.json`；(b) **标点层** `punctuate_segment` 用 STRICT prompt（只许插标点、禁增删改字）+ 硬校验 `strip_punct(出)==strip_punct(入)` 不过即 3 次重试、再不过回退裸文本进 review，缓存 `punct_seg_N.json`，thinking=False/3000。**红线核验脚本逐段 strip_punct 比对 28/28 全过**（diff 全 equal）。隔断按 `^隔断：【(.+?)】` 正则识别、独立成段、line_count=0、位置正确（解说1/主持人2/3/4 后各一）。标点 `punct_ok` 26/28；seg8(专家2)、seg27(专家7) 两段 LLM 顽固插连接词触发回退、已落裸文本+标记"需人工"，归第二批。产物：`融合A稿.docx` + `c4_alignment.csv`（32行对齐表，含 start/end_line、confidence、punct_ok、note）。命令 `doco compose --episode-id <id>`。
- [2026-06-17 ｜账号A] **P3-C3 收口**：`fusion_review.py` 逐行复审 B稿v2 ⊕ ASR（22 批×35行=743行），产 `融合B稿.txt`（行数/时间戳零偏移）+ `fusion_review.csv`（5条真OCR纠错留痕）。关键机制：断点缓存 `.c3_cache/batch_N.json` 可复用；硬校验行数/时间戳/change_type枚举不过不写文件；后处理修正 `final_text==B原文` 被 LLM 误标为 `minor_edit` 的行（19→5）。`--no-ai` 全 unchanged 可验证管道。
- [2026-06-17 ｜账号A] **P3-C2 收口**：密钥外置三验收点全过（硬编码已删/读 .env/已 gitignore）+ 旧 key 已轮换；`asr_adapter.py` 并入讯飞上传·轮询·解析，复用 P1 `extract_audio` 分离 16kHz/单声道/16bit WAV；新增 `doco asr` 命令读 C1 64条热词。**真转写《现代防空反导大对决》27分钟音频 → 310句带时间戳**，萨德×6/塔米尔/爱国者/箭二箭三/见证者等术语命中良好，raw json 730KB 落盘。（顺带修 `extract_audio` 在 Windows 的 `text=True` 解码崩溃。）
- [2026-06-16 ｜账号A] **P3-C1 收口**：A稿→规则+DeepSeek AI 提取→累积词典110条→热词清洗→本期热词表64条；AI 44 候选已逐个回查 A 稿原文无幻觉，算术对账一致。
- [P2 ｜本轮之前] 本地 OCR（DeepSeek-OCR/Ollama）+ 扒词流水线，产出 **B稿_v2.txt 743 行**。
- [P1 ｜本轮之前] 帧筛流水线完工。

---

## 5. 待办（按优先级，做完打勾）

- [x] **C3**：B稿v2 ⊕ ASR 交叉复审 → 融合B稿(743行时间戳零偏移) + fusion_review.csv(5条留痕)。
- [x] **C4（全部收口）**：融合B稿按 A稿28段对齐归拢 → 融合A稿 docx（公文格式）。汉字零改 + LLM 按语义加标点，红线 28/28 段逐字一致。`doco compose --episode-id <id>`。**OCR 漏字决定不补**（讨论见关键决策）；seg8/seg27 两段标点接受一逗到底。
- [x] **A稿解析器升级**：正则 → LLM分段骨架+人工确认+ignore合并；三期骨架已产并核验通过。`doco skeleton`。
- [x] **批量化 ep001-004 全流程完工**：四期均已出稿。ep004（最难·小剧场密·58段）C4 全过，batch_size=25 效果显著。
- [x] **【已完成】标点层 prompt 修复**：加第5条规则+示范，ep003 逗号占比 88%→76%，commit `fd2ef1a`。
- [x] **【已完成】配 Ollama 并发**：已从 4 路提至 16 路（`OLLAMA_NUM_PARALLEL=16`），8 路实测 GPU 96%、显存充裕。
- [x] **【已完成】C4 对齐层 batch_size 调优**：已降至 25，ep004 实测 confidence 全 ≥0.80，无批次回退。批量脚本 `_batch_run.py` 固定用 25。
- [ ] **【产品 backlog】原稿缺【专家N】/【三维动画N】标签**：ep003 复查发现编导原稿里就没有专家、后续三维动画的分段标注，骨架如实反映=融合A稿里也没有（通哥手工补了高亮）。不赖程序（原稿零信号，LLM 与人一样猜不准），**不让程序猜**。治本=提醒编导/责编原稿把【专家N】等段头标全；50 期前在导入须知里写清。
- [x] **【已完成】C3 prompt 收紧**：`fusion_review.py` SYSTEM_PROMPT 新增专名铁律——厂名/型号/番号/国名/人名/机构名遇 B稿与 ASR 同音异写，一律以 B稿为准，零容忍采 ASR。
- [x] **【已完成】串一键脚本**：`doco run` 命令（P1→P2→C1→C2→C3→C4 六阶段串联），ep004 验证通过。模板脚本放 `src/doco/templates/`。
- [x] **批量跑 22 期（完成）**：ep001-020 共 20 期 + ep021/022 新补 2 期，全部出稿。说话人区分 C4 重跑覆盖全部 22 期。成品归拢至 `doco/deliverables/`（22 份，按播出顺序命名）。
- [ ] **doco 收工后回 TPS 主项目**：22 期融合A稿批量导入知识库（走 Phase 3 已有的上传/embedding 链路），为 Phase 4a 语义搜索提供冷启动数据。成品位于 `doco/deliverables/`。
- [x] **【已修复】`fusion_align.py` align_batch 崩溃 bug**：`_parse_align_json` 调用在 `try/except` 之外，LLM 返回空字符串时进程崩溃。Cline 已修，移入 try/except 内，错误时优雅回退。
- [ ] **骨架小瑕疵 backlog（不拦路，后期顺手或跳过）**：ep002 演播室主持人 vs 主持人 是否统一；ep004 小剧场角色编号乱(斯9/斯通纳10、卡8跨场)直接改 JSON 即可，不必重调 LLM；ep003 行内`【固摇轨】`留在参照正文(无害，不进输出)。
- [x] **C2** 讯飞 ASR 适配层（密钥外置 + asr_adapter 并入 + `doco asr` 命令 + 真转写310句）
- [x] P3-C1 术语提取 + 词典 + 热词表
- [x] P2 本地 OCR + B稿v2
- [x] P1 帧筛

---

## 6. 关键决策（为什么这么做 — 跨 session 最易丢，别推翻）

- [2026-06-23] **doco PRD 完成标准：20 期全出稿 → 带成品回 TPS 主项目。** ep004 跑通 + 一键脚本就绪 = 流水线可用；剩余 16 期在 doco 子项目内批量跑完（一方面压力测试积累问题，一方面 20 期成品对 TPS 知识库冷启动有价值）。回归路径：融合A稿作为 manuscript 类文档 → 走主项目 Phase 3 知识库上传/embedding 链路 → Phase 4a 语义搜索。
- [2026-06-23] **C4 batch_size=25 为量产默认值。** ep004 实测 58 段全过（confidence 全 ≥0.80，punct_ok 58/58），相比 ep002 的 batch_size=40（47% 批次回退）大幅改善。`_batch_run.py` 和 `doco run` 默认均为 25。
- [2026-06-23] **Ollama OCR 并发提至 16 路。** 8 路实测 GPU 利用率 96%（4090D 24GB 显存充裕），16 路为批量跑默认值。环境变量 `OLLAMA_NUM_PARALLEL=16`（系统级）+ `OCR_NUM_WORKERS=16`（批量脚本内设）。
- [2026-06-22] **C4 分段偏差的根因是 A 稿与播出版内容差异大，非算法问题，现阶段不追技术方案。** ep002 实证：专家采访段 A 稿只有一句话提纲，实际播出专家自由发挥两分钟，内容/角度/篇幅全变，LLM 拿提纲匹配实录当然对不上。解说段偏差轻，基本能对。**B 稿（唱词字幕 OCR）里没有隔断标题**（隔断是画面美术字不走字幕轨道），所以也没法用隔断做硬分界线。**通哥拍板**：治本靠编导给贴近播出版的稿子（尤其专家段录完后更新）；差异大的期接受"程序保证文字零改 + 编导花十几分钟手调分段标签"。
- [2026-06-22] **MiMo 2.5 Pro C4 对齐层批次失败率偏高（ep002 实测 9/19=47%）。** 失败批次全 confidence=0.30，回退机制把该批所有行分配给 `min_normal_seg_id`。ep002 运气好——失败批次全在大段中间，回退恰好分对了。但 ep004 段切换密，边界批次失败会导致行归错段。**建议 ep004 前把 batch_size 从 40 降到 25-30**，或排查 MiMo 返回空/JSON 截断的具体原因。
- [2026-06-18] **A稿段头识别改用「LLM 判结构 + 人工核骨架」，不用死正则。** 编导写法千变万化(方括号/冒号/前缀杂质`Xr`/`【固摇轨】`镜头标记/小剧场对话头)，正则分不清`【固】`(镜头)与`【主持人1】`(段头)——这是语义判断，交 LLM。**铁律：LLM 只输出结构(type/role_label/para 区间)，绝不复述正文；正文一律代码按下标从 docx 原样抽**(护住"汉字零改"红线)。配两道闸：① `validate_skeleton_coverage` 全覆盖硬校验(title 后每段恰好被覆盖一次，自动抓 LLM 数错下标/JSON 截断)；② 人类预览表给制片人肉眼核(重点抓真人姓名泄露/ignore 漏多/隔断认全)。`doco compose` 无骨架即报错(ep001 例外走正则)。
- [2026-06-18] **role_label 只标"段落类型"，严禁带任何真人姓名/配音员/编导署名。** 解说词只标【解说N】，不标谁配音(踩过 Cline 把"解说：刘通"标错)。但**小剧场里被演绎的历史人物/角色保留**(卡=卡拉什尼科夫、斯=斯通纳、小剧场N-主持人)——那是剧情内容不是配音员实名。有借鉴价值的修饰也留(三维动画解说/演播室主持人)，只清杂质(`Xr`/编号/空格)。
- [2026-06-18] **解析层合并"被 ignore 隔断的同角色段"。** 编导用`（出枪柜）（换枪）`镜头切换把一个主持人的话从中间切断(ep003)，骨架会切成两个同名`【主持人N】`。parse 后处理：相邻 normal 段 role_label 全等、中间仅隔 ignore，则合并(正文顺序拼接、ignore 内容丢弃)，避免输出里冒重复段头。合并条件要严，ep002/004 无 ignore 不受影响。
- [2026-06-18] **OCR 漏字决定不补（通哥拍板），不让 LLM 补词。** 三条理由:① 漏字的根在**上游抽帧/OCR**——缺的"那么/此外/一方面"都是两三字单独一屏、停留短被整屏漏掉,是真实信息丢失,文稿层无法"还原";② **LLM 补词=猜词=破"汉字零改"红线**,对专业性强、最贴近播出版的稿子风险不可接受;③ 口子守不住——seg8/seg27 标点回退正是 LLM 一有机会就想塞连接词被硬校验拦下,证明给余地它就动手。**真要科学补,唯一正路是 ASR 回捞**（这些连接词主持人/解说真说了,大概率在 ASR 310 句里）,而非 LLM 猜,但属 C3 级返工、本期不值;真做时先验 ASR 有无再说。**治本=提醒编导/责编别让两三字单独一屏**;本期连接词缺了能读懂,接受。
- [2026-06-18] **`融合A稿_批改.docx`(通哥手批)只当"看缺了什么"的参照,绝不当"让 docx 去贴它"的对齐目标。** 手批稿可能含其他润色,一旦去对齐就等于润色,违背"只守零改汉字+加标点、不润色其他部分"。
- [2026-06-18] **seg8(专家2 行169-216)/seg27(专家7 行584-608) 两段标点接受一逗到底（通哥选 B）。** 这是标点没点好,不是缺字,不碰汉字;若日后要美化由人工手点。
- [2026-06-18] **C4 红线由"汉字润色"转为"汉字零改、标点交 LLM 按语义增删"**（通哥拍板："转变后的红线更精准、更科学"）。理由：润色会让关键军事表述有不准确风险；保守做法正文 100% 来自融合B稿，只在字符间插标点。**硬校验 `strip_punct(LLM输出)==strip_punct(输入)`** 是这条红线的机器守门员（strip_punct 去掉所有 unicode 'P' 类标点+空白），不过即重试、再不过回退裸文本。**第一批先做保守版，润色/漏字回填留第二批。**
- [2026-06-18] **thinking 要按任务分用，不是越开越好——这是 C1"thinking不关content为空"的孪生坑。** 两个独立故障：(a) **开 thinking 时 max_tokens 必须给足**：thinking 与正文 `content` 共享同一配额，配额不够 thinking 吃光 → 正文空输出（C4 标点层 @2000 实测吐 0 字）；(b) **浅层活（标点/抽取）开 thinking 会又慢又被推理空转吃光**：标点这种活开 thinking 单段 10+ 分钟还超时，关掉 thinking/3000 单段 6.9s 通过。**规则：语义对齐/融合这类深推理开 thinking 且给足 token（C4 对齐 4096）；标点、抽取这类浅层确定性活必须关 thinking。**
- [2026-06-18] **A稿分段编号有重复（解说2/解说10 各出现两次）——任何按段头文本做 key 的字典都会把两次出现合并，必须按"文档出现顺序"切块/取块。** C4 红线核验脚本初版犯此错（解说2 行61-77 与行151-168 被并成一段、误报破线），改用 occurrence 队列后 28/28 全过。对齐表 c4_alignment.csv 用 seg_id 区分，不用段头文本。
- [2026-06-17] **C2 音频分离复用 P1 `video_split.extract_audio()`，不另写 ffmpeg。** WAV 落 `programs/<episode>/audio_16k.wav`，已存在则复用（避免 1GB 视频重复解码）。实测产物 `pcm_s16le/16000Hz/1ch/16bit/1620s`，符合讯飞硬要求。
- [2026-06-17] **`doco asr` 不传 `--output-dir` 会落到当前工作目录的 `programs/`**（命令在 repo 根 `E:\tps-dashboard` 跑时尤其坑），与 doco 产物分家。**命令一律用绝对 `--input-video` + 绝对 `--output-dir`。**
- [2026-06-17] **ASR 输出是口语形态，型号不规范属正常**（如「箭二箭三」非「箭-3」、「高超音速/高超声速」混用）。按设计稿口语信 ASR、型号规范信 A/B 稿，**这些归一交给 C3，别回头改 C2**。
- [2026-06-22] **AI 融合层 LLM 已从 DeepSeek v4-pro 切换到小米 MiMo 2.5 Pro。** 环境变量名通用化：`LLM_API_KEY` / `LLM_BASE_URL` / `LLM_MODEL`（旧 `DEEPSEEK_*` 已废弃，代码不再读取）。接入：OpenAI 兼容，`/chat/completions`，模型名 `mimo-v2.5-pro`。thinking 关闭方式与 DeepSeek 相同（`extra_body={"thinking":{"type":"disabled"}}`）。Cline 端也已换 MiMo 2.5 Pro。**（此条覆盖先前 DeepSeek v4-pro 的决策。）**
- [2026-06-22] **抽取类任务必须关 thinking（规则不变，适用于 MiMo）。** 做法：`chat(..., thinking=False)` → `extra_body={"thinking":{"type":"disabled"}}`，给足 `max_tokens`（C1 用 4000）。**C4 语义融合可开 thinking**（单轮调用，质量优先）。
- [2026-06-16] **C1 术语源 = A稿（规范写法），不是 B稿。** Cline 曾擅自换成 B稿v2，已纠正。
- [2026-06-15] **讯飞用「录音文件转写标准版」，不要大模型版**（免费包阉割 `language` 参数，会报误导性 `language verify fail`）。参数锁定：`pd=mil`、`eng_smoothproc=true`、`eng_colloqproc=true`、`hotWord=<C1热词>`、`language=cn`。签名 `signa=base64(HmacSHA1(MD5(appid+ts), secretKey))`；`/upload` 拿 orderId，轮询 `/getResult` 到 status=4。
- [2026-06-15] **B稿v2 权威升级**：屏幕术语/型号/番号 **B稿v2 ≈ A稿 并列权威**（B稿是屏幕实打的字，更贴近播出）。口语/语序/语气信 ASR；书面结构/分段信 A稿；同音事实错（美以→美伊）用 A稿/B稿v2 覆盖 ASR。
- [2026-06-18 晚] **C3 会拿 ASR 同音字盖权威 B稿专名——必须核验留痕、专名一律以 B稿/A稿为准。** ep003 实证：B稿"斯泰尔"(奥地利厂名)被 C3 LLM 按 ASR 同音改成"斯太尔"，造成 487/519 行不一致。已手改回。这正是上条权威规则的反例落地：**厂名/型号/番号遇 ASR 同音异写，零容忍采 ASR**。50 期批量前应在 C3 prompt 收紧（见待办 backlog）；每期 fuse 后扫一眼 `fusion_review.csv` 的 minor_edit 是不是专名同音改。
- [2026-06-18 晚] **C4 出现空段≠对齐 bug，先查"是不是播出剪了这段"。** ep003 有 4 个空段(seg15–18 整段 VSS 微声狙击枪 + NTW-20)，一度疑似对齐错乱。核验路径：去**融合B稿**里 grep 该段专属词(VSS/NTW/南非/消音/反器材)，零命中=播出确实剪了；再抽查相邻实段边界确认没把别段内容错塞过来。结论是 C4 如实留空、对齐正确。**空段是常态（播出删戏），别当 bug 去"修"。**
- [2026-06-18 晚｜通哥复查] **C4 标点层整篇偏保守是真缺陷，须在批量铺开前修——但不破红线。** ep003 复查实测逗号 511 vs 句号 70（断句标点 88% 逗号），语义该断处全逗号。根因：标点层关 thinking + STRICT prompt 只强调"别改字"未强调"句子结尾用句号"。**红线（汉字零改）由 strip_punct 守着没破，所以标点层尽可放开 prompt 让它正常断句、不怕越界**。修复列 ep002 前必做，修完重跑 ep003 compose 刷 docx。这条把先前"seg8/seg27 一逗到底可接受"的范围收窄：个别段一逗到底可接受，但整篇 88% 逗号不可接受、必须修。
- [设计期] **分层不变**：OCR 错字纠正、术语统一、时间戳对齐走**规则层**（确定性）；段落归属、整段删除判定、编导笔误识别走 **AI 层**（需语义判断）。
- [设计期] **顺序约束**：术语提取（C1，产热词）必须在 ASR（C2）之前——已成立。
- [2026-06-17] **`fusion_review.csv` 定位 = 留痕档案（给爱德华/复核），不是编导读物。** 编导读的是 C4 docx。CSV 可读性不优化，只看 final_text 准不准。
- [2026-06-17] **`final_text==B原文` 强制归 `unchanged` 不进 review。** LLM 有时把"考虑后决定保留 B 稿原文"标成 `minor_edit`，但实际没改字。`fusion_review.py` 的 `_normalize_unchanged_when_no_edit()` 在写文件前修正（review 19→5）。
- [2026-06-17] **`change_type` 的 `minor_edit`/`editor_typo` 边界 LLM 把握不稳**（OCR 错字两类混标）。仅 metadata 标签不一致，`final_text` 全对，不影响融合B稿正文。本轮决定忽略；若 C4/爱德华按 `change_type` 做逻辑分流，届时在 prompt 里收紧两个枚举的边界定义。
- [工程纪律] 各阶段解耦、中间产物落缓存、可断点续跑、可单独重跑；长批处理放独立终端。

---

## 7. ⏩ 交接备注（写给下一个接手的 session）

> 下次开工读完这段应能 0 摩擦续上。接手后可清空重写。**（2026-06-26 重写）**

- **大局**：**doco 子项目开发已完成。** 22 期融合A稿全部产出（含说话人区分重跑），成品归拢至 `doco/deliverables/`（22 份，按播出顺序 `第XX期_YYYYMMDD_节目名_编导_融合A稿.docx` 命名）。缺第01期（武器装备里的形状规律，无A稿）。下一步是把成品带回 TPS 主项目知识库。
- **第一句话该干的**：切回 TPS 主项目，把 `doco/deliverables/` 里 22 份融合A稿批量导入知识库（走 Phase 3 上传/embedding 链路），为 Phase 4a 语义搜索提供冷启动数据。
- **排播对照表**：`doco/data/2026episode_list.xlsx`（25 期完整播出日期/期次/节目名/编导/收视），内部 ep ID → 播出顺序的映射已建立。
- **LLM**：小米 MiMo 2.5 Pro，环境变量 `LLM_API_KEY` / `LLM_BASE_URL` / `LLM_MODEL`。
- **Ollama**：16 路并发（`OLLAMA_NUM_PARALLEL=16`），4090D 24GB 充裕。
- **关键参数**：C4 batch_size=25，C3 batch_size=35。
- **量产经验（若后续加期）**：
  1. **C3 fuse 后必扫 `fusion_review.csv`**：专名被 ASR 同音字替换要打回。C3 prompt 已收紧专名铁律，但仍需人工复核。
  2. **C4 空段 = 播出剪了那段**，不是 bug。
  3. **C4 分段偏差是常态**（A 稿≠播出版），编导手调分段标签即可。
  4. **OCR 漏字不补**（通哥拍板），LLM 补词=破红线。
  5. Cline 自报结果一律对源数据核验，不轻信。
  6. thinking 分任务用：骨架/对齐开，标点/抽取/融合关。
  7. **骨架注意**：`extract_a_paragraphs()` 会过滤空段并重编号，LLM 生成骨架时必须基于过滤后的下标（ep021 踩过此坑，需手写骨架）。
  8. **cli.py 路径**：`episode_dir` 已改为 `.resolve()` 绝对路径，修复了子进程路径翻倍 bug。
- **出稿命名**：episode 目录内为 `{原始A稿stem}_融合A稿.docx`；deliverables 文件夹统一为 `第XX期_YYYYMMDD_节目名_编导_融合A稿.docx`。
- **字体坑**：大标题方正小标宋_GBK（商业字体），出稿前确认已装。
- **工具脚本清单**（一次性，跑完可删）：`_setup_episodes.py`（建目录+拷文件）、`_batch_skeleton.py`（批量生成骨架）、`_batch_run.py`（批量全流程）。
- **doco 收工后的路**：22 期成品 → TPS 主项目知识库批量导入（走 Phase 3 上传/embedding 链路）→ Phase 4a 语义搜索提供冷启动数据。

---

## 8. 待确认 / 开放问题（需通哥拍板，AI 别自行假设）

- ~~**B稿_v2.txt 就位**~~：已完成（通哥已拷进 episode 目录并随 9340edc 入库）。
- ~~**讯飞旧 key 轮换**~~：已完成（新值已在 `doco/.env`，旧 appid `84eff996` 代码内无残留）。
- **方正小标宋字体可用性**：C4 出稿前确认两台机器是否已装。
- ~~**OCR 并发提速**~~：已完成，16 路并发（4090D 24GB 充裕）。
- **讯飞单源依赖**（远期）：是否接阿里云 ASR 做备份/交叉验证。
- **ep016 骨架的宣传词被标为 break**：其他期（ep006/13）宣传词标为 normal，需通哥确认 ep016 是否应改。