doco P3-C2: 讯飞ASR适配层接入 + 修复 doco/programs 忽略规则

- C2: asr_adapter 并入讯飞上传/轮询/解析,复用 extract_audio 分离音频,
  新增 doco asr 命令读 C1 热词;真转写 ep001 出 310 句带时间戳
- 补提交 C1 代码(llm.py / term_extract.py)与累积词典
- .gitignore: 挡 doco/programs 下 png/wav/mp4 及中间帧目录、本地 settings

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
simonkoson
2026-06-17 10:19:08 +08:00
parent 487877fa08
commit 1372600d71
30 changed files with 9422 additions and 21 deletions
+144
View File
@@ -0,0 +1,144 @@
# Doco 文稿整理子项目 Brief
> 主项目 → 子项目的"交接宪法":红线、技术栈、出入口接口
> 起草日期:2026-06-12
> 状态:主项目签发,子项目内部不修改
---
## 一、为什么做
**痛点**:《军事科技》每期节目播出后,产出一份贴近实际播出的终版文稿,过去靠人工对照 A 稿(编导定稿)+ 录音转写 + OCR 字幕反复核对,**单期 4-6 小时**,且容易遗漏错字、整段重写、编导笔误。
**升级目标**:把"三方文本 → 终版文稿 + 差异报告"做成可自动化的流水线模块。
**现状起点**:demo 已用《现代防空反导大对决》一期跑通,效果达标。栏目组改变了素材输出方式——直接产出"黑底白字+干净人声"的特殊视频——使物理输入从 3 路(A 稿+B 稿+ASR)收敛到 2 路(A 稿+视频),但**逻辑上仍然三方融合**(A 稿+B 路视频字幕 OCR+音轨 ASR)。
---
## 二、做什么(功能边界,只列名)
| 模块 | 简述 |
|---|---|
| 视频双路拆分 | ffmpeg 抽帧+字幕变化检测+OCR → B 稿;ffmpeg 抽音轨 → 16k WAV |
| 讯飞 ASR 适配层 | 复用 demo 跑通的 `xfyun_asr_standard.py`,替换凭证 |
| 三方融合引擎 | 规则层(Python)+ AI 层(Claude Sonnet 4.6),demo 已锁 |
| 单期处理接口 | CLI + Python API,契约见主项目回复 §Q3 |
| Golden test | 用 demo 那期视频做零回归验证 |
| 23 期批量调度入口 | 单期接口的批量调用方式,具体编排不在本子项目 |
**具体怎么做,子项目内部讨论,本 Brief 不预设方案。**
---
## 三、怎么用(目标流程)
**单期处理**:
1. 责编/编排层在 episodes 表落库该期,拿到 INT 主键
2. 拼出 episode_id:`ep{NNN}_{YYYYMMDD}_{slug}`
3. 把视频和 A 稿放进 `programs/{episode_id}/source/`
4. 调 doco:`doco.process_episode(episode_id, video_path, a_draft_path, output_dir)`
5. 产物落 `programs/{episode_id}/output/`:终版 docx + 差异报告 + needs_review JSON
6. 编导/制片人审 needs_review 队列,决定接受 / 拒绝 / 编辑
**23 期批量**:用同一接口循环调用 23 次,失败重试 + 进度跟踪由调用方负责。
**降级回退**:若某期视频不符合"黑底白字+干净人声"条件,通过 feature flag 切换到旧流程(独立 mp3 + 上游 OCR)。
---
## 四、不做什么(红线)
子项目设计任何方案,**绝不**触碰以下:
-**不动 demo 已锁的算法层**(三方融合优先级 / 时间戳容忍 ±5s / 口语清理三档 / 改动确信度阈值 0.85/0.6 三段)
-**不引入 `ffmpeg-python` 等 wrapper**(用 subprocess 调系统 ffmpeg)
-**不替换 AI 融合层模型**(Claude Sonnet 4.6 已锁,不临时换 DeepSeek 等)
-**不复用主 project 那把 DeepSeek key**(Cline 工具用的 key 跟生产业务分开)
-**不把凭证写进主 project 的 `backend/.env`**(子模块自治,各自 .env)
-**不假设 episode_id 命名规则**(由调用方给定,doco 只当字符串用)
-**不引入主 project 没有的技术栈**(继续 Python 3.x + Claude API + 讯飞 API)
-**不自己实现批量调度**(失败重试 / 并发控制 / 进度跟踪属于编排层职责)
-**不在子项目里写中台密钥服务**(那不属于 doco 范围)
---
## 五、技术栈约束(继承主 project)
- **运行环境**:Python 3.x(版本对齐 backend),Windows / Linux 都可跑
- **系统依赖**:ffmpeg ≥ 4.x(子模块文档明确列出)
- **AI 融合**:Anthropic Claude API,模型 `claude-sonnet-4-6`
- **OCR**:DeepSeek Vision API(主项目已批,Q1)
- **ASR**:讯飞开放平台 录音文件转写标准版(**不要用大模型版**——`language` 参数被阉割)
- **音频规格**:16kHz / 单声道 / 16bit WAV(讯飞规格)
- **数据格式**:句子级时间戳统一 `[Nm Ns] 句子` 格式
---
## 六、交付什么(出口接口)
子项目最终交付物以下面形式回到主 project,**必须满足以下接口**:
### 6.1 模块交付物
- `doco/` Python 包,可被主 project 编排层 import
- CLI 入口:`doco process ...`
- 单元测试 + golden test(demo 那期零回归)
- `README.md`:系统依赖、安装、使用、配置
### 6.2 配置与凭证
- doco 自己的 `.env`(讯飞 + DeepSeek + Claude 三套 key 各自管)
- 主项目仓库根目录的 `docs/api_credentials_inventory.md` 登记一行 doco 的凭证元信息(不存真实 key)
### 6.3 数据资产沉淀(可入 TPS 中台共享)
- A 稿术语表(每期处理时从 A 稿提取,可入主 project 数据库的 terms 维度)
- OCR 错字映射表(demo 已整理,固定字形混淆对)
### 6.4 不接受
- 子项目自己改主 project 的 backend 代码、schema、迁移
- 子项目自己写主 project 的前端 UI(编导确认 UI 属于上层,不在 doco 范围)
- 子项目自己定 episode_id 命名规则(由主项目给定)
---
## 七、入口接口(子项目要知道的主 project 现状)
- **主项目没有 OCR 子模块**(已确认,Q1)
- **主项目没有视频处理服务**(已确认,Q2)
- **主项目没有节目素材批量编排层**(已确认,Q3)
- **主项目没有中台密钥服务**(已确认,Q4)
- **主项目没有节目素材目录规约**(已确认,Q5,本子项目带头定 `programs/{episode_id}/...`)
- **主项目有 episodes 表**(INT 自增主键),doco 用 episode_id 时假定调用方已落库
---
## 八、未决项(归子项目自己拍板)
以下不在本 Brief 内预设,**由子项目和制片人讨论决定**:
- 视频抽帧的具体策略(关键帧检测算法 / 抽帧密度 / 字幕变化阈值)
- DeepSeek Vision 调用的具体 prompt 设计
- 规则层 OCR 错字映射表的具体扩展机制
- AI 融合层的具体提示词迭代
- needs_review JSON 的具体字段(在 demo 基础上演化)
- 单元测试覆盖率目标
- 子项目内部的 git 分支策略 / 版本号约定
---
## 九、决策史指向(不重复内容)
子项目讨论前必读:
- `doco_project_design.md / .docx`(三方融合算法层,demo 沉淀,已锁)
- `doco_xfyun_integration_notes.md / .docx`(讯飞 ASR 接入 15 个坑)
- `doco_handoff_to_opus_chat.md / .docx`(子项目立项前的 opus 接手提示)
- `PRD_doco_文稿整理子项目_v2.md`(当前子项目的请求稿)
- `主project对Doco_PRDv2的回复.md`(主项目的批复)
---
## 十、本 Brief 自身的修订规则
- 本 Brief 不在子项目内修改
- 红线 / 技术栈如有变更,**主 project 这边发新版**,通过寄存条同步
- 子项目可在自己的 chat 里讨论"建议主 project 调整某条红线",调整动作只能在主 project 发生
- 子项目每次出新版 PRD,主项目顾问发新版回复;两者形成 PRD vN ↔ 回复 vN 的对照关系
@@ -0,0 +1,295 @@
# PRD · Doco 文稿整理模块 · 视频源改造方案
> 版本:v2
> 日期:2026-06-12
> 作者:子 project ClaudeOpus 4.7
> 状态:草稿 / 待主项目审
> 上一版:v1(2026-06-12 同日,更早一稿)
> 主要变化:
> 1. **子模块正式定名为「Doco 文稿整理模块」**(v1 沿用旧称"TPS 三方融合子模块",本版起全部改用 Doco;Q6 已由制片人拍板,不再列为待决问题)
> 2. **Q1(OCR 选型)方向更新**:制片人确认"软件离线刚需"已不成立,云端方案可接受。补充查清的关键事实:**讯飞 OCR 与 STT 共享鉴权凭证但套餐/余额各自独立**——按制片人原判据"如果能通用 token 订阅和余额就选讯飞"不成立。**新推荐方案:DeepSeek API**(制片人手上已有),并向主项目确认是否已有 OCR 能力或 LLM 视觉调用基础设施
> 3. 其他 QQ2/Q3/Q4/Q5)保持不变
---
## 〇、一段话摘要(给主 project 顾问的最短摘要)
Doco 文稿整理模块(前称"TPS 三方融合子模块")此前的物理输入是 A 稿 docx + B 稿 OCR txt + ASR txt(mp3→讯飞)。栏目组现在改了素材输出方式:责编一次性输出 23 期"特殊视频"——画面是黑底白字唱词字幕、声音是无 BGM/音效的干净人声(极少同期声杂音)。**B 稿与 ASR 现在同源派生于一个视频文件**,物理输入从 3 个收敛到 2 个(A 稿 + 视频)。
**核心判断**:下游"三方融合引擎"(规则层 + AI 层)算法**零侵入**——只要新增的"视频双路拆分"模块输出与旧版 B 稿/ASR txt 格式完全一致(`[Nm Ns] 句子`),下游所有逻辑不动。**逻辑上仍然是三方融合**——A 稿术语权威 + B 路视频字幕 OCR + 音轨 ASR,三路独立验证的关系没破。
子项目这边需要主 project 在开工前对 5 件影响接口/数据形态的事拍板。详见 §4。
---
## 一、变更前后对照
### 旧设计(demo 已验证)
```
[A 稿 docx] ──────────────────────┐
[上游既有 OCR 流程] ── B 稿 txt ──┤
├─► [三方融合引擎] ─► 终版.docx + 差异报告.docx
[节目纯净人声 mp3] │
│ │
└─► [讯飞 ASR] ── ASR txt ─┘
```
### 新设计(本 PRD 提出)
```
[A 稿 docx] ─────────────────────────────────────┐
[节目特殊视频文件] │
画面:黑底白字字幕 │
声音:干净人声 │
│ │
├─► [视频双路拆分子模块 · NEW] │
│ │ │
│ ├─► ffmpeg 抽帧+字幕变化检测 │
│ │ │ │
│ │ └─► [OCR] ── B 稿 txt ──────┤
│ │ (待 Q1 拍板用 DeepSeek) ├─► [三方融合引擎] ─► 终版 + 差异
│ └─► ffmpeg 抽音轨 → 16k/mono WAV │ (复用)
│ │ │
│ └─► [讯飞 ASR · 复用] │
│ └────── ASR txt ──────┘
└─ (后续接 23 期批量调度)
```
### 关键变化清单
| 项 | 旧 | 新 |
|---|---|---|
| 子模块名称 | TPS 三方融合子模块 | **Doco 文稿整理模块** |
| 物理输入 | A 稿 + B 稿 + ASR | A 稿 + 视频 |
| 逻辑融合方 | 三方 | **仍然三方**A / B 路 / ASR 路) |
| B 稿来源 | 上游既有 OCR 流程 | 视频抽帧 → OCR |
| ASR 来源 | 节目 mp3 | 视频音轨 |
| 算法层 | 规则层 + AI 层 | **不变** |
| 新增模块 | — | 视频双路拆分(ffmpeg + OCR |
| 处理粒度 | 单期 | 单期接口 + 23 期批量调度 |
---
## 二、子项目这边已经拍板的事(不需要主 project 决策,仅告知)
### 2.1 制片人已明确的决策
1. **子模块定名****Doco 文稿整理模块**"Doco" 取意"Documentation Consolidation"——文稿整合)。本 PRD 起所有引用旧称("TPS 三方融合子模块"、"三方融合")的地方都用新名替换。
2. **离线不再是刚需**:原 v1 推荐 PaddleOCR 本地的核心理由(离线刚需)失效;OCR 选型转向云端。
### 2.2 demo 已验证或设计文档已定义的事
不变:
1. **三方融合优先级**(A 稿术语权威 / ASR 口吻 / B 稿验证)—— 见 project_design.md §5.1
2. **时间戳容忍度 ±5 秒** —— project_design.md §5.2
3. **口语清理三档**keep_all / medium / clean)—— project_design.md §5.3
4. **改动确信度阈值**0.85 自动 / 0.60.85 黄标 / <0.6 待确认)—— project_design.md §5.4
5. **AI 融合层模型**Claude Sonnet 4.6 起步
6. **差异报告格式**demo 黄金对照已落定
7. **A 稿术语表数据结构**project_design.md §9.1
8. **OCR 错字映射表**:复用 demo 整理的字形混淆对
---
## 三、子项目这边的开发计划草案(待主 project 审)
| Phase | 内容 | 产物 | 主依赖 |
|---|---|---|---|
| P1 | 视频双路拆分预处理(单期)| `video_split.py`:视频 → 抽帧+字幕变化检测+OCR → B稿.txt;视频 → 音轨 → 16k WAV | ffmpeg、OCR(待 Q1 拍板,倾向 DeepSeek API |
| P2 | 讯飞 ASR 适配层 | 复用 `xfyun_asr_standard.py`,替换新凭证、做成可被 P1 流水线调用的模块 | 讯飞开发端密钥(待 Q4 拍板) |
| P3 | Doco 融合引擎 | 规则层 Python + AI 层 Claude API;输入 A/B/ASR 三方 txt,输出终版 docx + 差异报告 + needs_review JSON | Claude API |
| P4 | 单期端到端 golden test | 用 demo 那期《现代防空反导大对决》的视频跑一遍,与 demo 产物比对,确认零回归 | 上面三 phase |
| P5 | 23 期批量 | 单期模块包成可被批量调度调用的接口(CLI / Python API 二选一,待 Q3 拍板) | — |
---
## 四、需要主项目决策的 5 个问题
> 每条都给了子项目这边的推荐答案 + 理由。主项目顾问可以照单批,也可以推翻。
### Q1. OCR 方案选型?
**问题**:B 路 OCR 用什么?这是新引入的依赖,绕不开。
#### Q1.A 关键事实(更新自 v1
子项目这边查清了几件事,供主项目决策参考:
1. **讯飞 OCR 与 STT 的共享情况**
- 鉴权凭证(开发者账号、APPID、APIKey、APISecret**可共享**,一组凭证可同时调用多个服务
- 但**套餐/计费/余额各自独立**:STT 是"5 小时免费试用包"独立购买;OCR 是另一套计费体系(参考 SaaS 零售价 4.9 元/10 张、9.9 元/50 张)。**不存在用 STT 的余额跑 OCR 这种事**
- **按制片人原判据("如果能通用 token 订阅和余额就选讯飞")此条件不成立**
2. **制片人手上已有的 API**
- **DeepSeek**:有专门的 `DeepSeek-OCR` 模型(OCR 2.0 方案,对中文文档识别准确率行业领先)+ DeepSeek Vision 识图模式(官方 API endpoint)。**推荐**
- **MiniMax**:有 MiniMax-VL 视觉模型,理论上可 OCR,但 OCR 专项优化不如 DeepSeek-OCR
- **小米 MiMo**:主推推理模型,未见明确的视觉/OCR 能力
3. **本场景的 OCR 难度评估**
- 输入图像是**黑底白字纯字幕画面**——OCR 最简单的子集(高对比度、印刷体、无图案干扰、无版面分析)
- LLM 视觉模型在此场景几乎不会出错;用 LLM 做 OCR 在这里不是杀鸡用牛刀,因为成本极低且无新增依赖
4. **成本估算(23 期总量)**
- 单期约 300 条字幕(demo 数据),23 期 ≈ 6900 张图
- DeepSeek Vision API 单张成本约几厘到一分,**总成本预估 50–150 元**
- 对比讯飞 OCR SaaS 零售价:6900 张 × 0.198 元/张 ≈ 1370 元
- 对比 PaddleOCR 本地:0 元但需要维护本地环境(已不是刚需)
#### Q1.B 选项与推荐
**选项**
- A. **DeepSeek API(视觉模型)**——制片人已有 API,无新增订阅
- B. **讯飞 OCR**——与 STT 同账号但余额独立
- C. **PaddleOCR 本地**——零成本但需本地环境
- D. **主 project 已有的 OCR 服务**——如果存在
**子项目推荐**ADeepSeek API
**理由**
- 满足制片人"用现有 API 资源"的方向
- 黑底白字字幕是 LLM 视觉模型的舒适区,识别质量有保障
- 与讯飞 STT 解耦,单点故障范围小
- 成本最低(除 PaddleOCR)且无本地环境维护成本
**请主项目确认**
- 主 project 是否已有 OCR 子模块/能力?(制片人初步判断"主 project 应该没有 OCR 配置",请顾问最终确认)
- 主 project 的密钥管理体系是否已经接入 DeepSeek?还是需要 Doco 子模块自行管理 DeepSeek 凭证?
- 6900 张图调用 DeepSeek 是否会影响主 project 其他模块的 DeepSeek 配额/限流?
---
### Q2. 视频处理依赖(ffmpeg)的入口?
**问题**:抽帧与抽音轨都依赖 ffmpeg。
**选项**
- A. Doco 子模块自带 ffmpeg 系统依赖 + Python 封装(`ffmpeg-python` 或 subprocess
- B. 主 project 有统一的视频处理服务/适配层
**子项目推荐**A,使用 subprocess 调用系统 ffmpeg
**理由**:ffmpeg 是标准工具,独立部署足够简单。
**请主项目确认**:是否已经有视频处理子模块/服务在规划中?如有,应改走中台接口。
---
### Q3. 23 期批量 vs 单期接口?
**问题**:本次实际场景是 23 期,Doco 子模块的接口应该长什么样?
**选项**
- A. 子模块只负责单期,输入 = 1 视频 + 1 A 稿;批量调度交给主 project 编排层
- B. 子模块内部支持批量,输入 = 视频目录 + A 稿目录
- C. 两者都提供
**子项目推荐**A(子模块只对单期负责)
**理由**
- 单一职责原则。批量调度涉及失败重试、进度跟踪、并发控制——属于编排层职责
- 单期接口在出错时定位简单(哪期失败、失败在哪个 phase 一目了然)
- 主 project 未来可能有节目档案库流水线、报题单流水线等,统一在编排层做并发更合理
**请主项目确认**
- 主 project 是否计划做"节目素材批量处理编排层"?如有,Doco 的单期接口应满足什么样的契约(CLI?Python API?返回值结构?)
- 23 期这次是先单期跑通再批量,还是要求一次性 batch?
---
### Q4. 讯飞密钥更新/续费策略?
**问题**:demo 凭证可能已过期(制片人提示"讯飞试用可能也过了"),且需要区分**消费端**(讯飞听见 App)与**开发端**(开放平台)。
**选项**
- A. Doco 子模块各自申请新凭证、各自管理
- B. 接入中台统一密钥服务(如果有)
- C. 申请新凭证 + 写入"中台密钥资产文档"作为登记
**子项目推荐**C
**理由**:现阶段中台密钥服务可能未完成,但密钥归属、过期日期、所属子模块应该有统一登记入口。
**请主项目确认**
- 中台是否已有密钥管理子模块?
- 新申请的讯飞开发端密钥(包括 APP_ID、SECRET_KEY、5 小时免费试用包激活状态)应该登记在哪里?
- 如果 Q1 拍板用 DeepSeekDeepSeek API key 是否也按同样规则登记?
---
### Q5. 目录结构与命名规约?
**问题**:23 期素材、抽帧产物、wav、终版 docx 的存储路径需要规约,否则后期归档混乱。
**子项目草案**
```
programs/
└── {episode_id}/ # 例:20260612_防空反导
├── source/
│ ├── video.mp4 # 原始特殊视频(黑底白字+干净人声)
│ └── a_draft.docx # A 稿(编导定稿)
├── work/ # 中间产物(可清理)
│ ├── frames/ # 抽出的字幕关键帧
│ ├── audio_16k.wav # 抽音轨标准化产物
│ ├── b_draft.txt # OCR 产物(带 [Nm Ns] 时间戳)
│ ├── asr_result.txt # 讯飞 ASR 产物
│ └── asr_result_raw.json # 讯飞原始返回
└── output/ # 主产物(入档案库)
├── final.docx # 终版文稿
├── diff_report.docx # 差异报告
└── needs_review.json # 待编导确认队列
```
**请主项目确认**
- TPS 中台是否对节目素材的目录有统一规约?
- `episode_id` 命名规则是什么?(日期+栏目+期号?UUID?编导给定?)
- `work/` 中间产物保留多久?是否要自动清理?
---
## 五、不向主项目提问、但需要告知的事
1. **demo 的 5 份 fixture 仍然有效**A 稿 / B 稿 / ASR / 终版 / 差异报告 在新流程下都可作为单元测试与集成测试的黄金对照
2. **讯飞 ASR 脚本仍然有效**`xfyun_asr_standard.py` 只需替换凭证、把"音频文件路径"参数从"独立 mp3"改为"视频抽音轨产物 wav"
3. **23 期之后的常态化问题**:栏目组以后是否每期都按"黑底白字+干净人声"特殊视频出素材?还是只这一批 23 期是特例?这影响 Doco 子模块要不要保留旧的 mp3+独立 OCR 流程作为回退路径。**建议主项目顾问直接跟栏目组确认**
---
## 六、给主项目顾问的"接手提示词"
如果主 project 顾问要审这份 PRD,最重要的三件事:
1. **算法层零侵入是 demo 验证过的硬保证**。任何改动如果会让 P3(Doco 融合引擎)必须改算法,需要警惕——大概率是输入数据形态没对齐。
2. **OCR 选型(Q1)和目录规约(Q5)是会广泛影响其他子模块的事**,主项目顾问视野更大,应该是这两条的最终拍板人。如果主 project 有 OCR 能力或 DeepSeek 集成基础设施,Q1 的子项目推荐随时可被覆盖。
3. **23 期是一次性还是常态化(§5 第 3 条)是个产品问题,不是技术问题**,建议主项目顾问直接跟栏目组确认后再批 PRD。
---
## 七、回复期望
子项目这边等以下确认才正式开干:
- Q1(OCR 选型)—— 必须先答,否则 P1 不能开始
- Q4(讯飞密钥)—— 必须先答,否则 P2 不能开始
- Q5(目录规约)—— 必须先答,否则 P1 输出无处可放
- Q3(接口契约)—— P5 之前必须答
- Q2 —— Doco 子模块可以先按推荐方案开干,主项目随后批
收到主项目 Claude 顾问的回复 md 后,Doco 子项目这边出 PRD v3(含元信息块标注"上一版:v2"),再启动 P1。
---
## 八、版本变更摘要
| 版本 | 日期 | 主要变化 |
|---|---|---|
| v1 | 2026-06-12 | 首版。6 个待决问题(含子模块命名 Q6)。OCR 选型推荐 PaddleOCR 本地 |
| **v2** | **2026-06-12** | 子模块定名"Doco 文稿整理模块"Q1 改推荐 DeepSeek API(基于讯飞 OCR/STT 余额独立的事实查清 + 制片人手上已有 API 资源);Q6 已拍板移出待决列表;剩余 5 个待决问题 |
---
*v2 由 Doco 子 project ClaudeOpus 4.7)于 2026-06-12 拟稿,遵照《跨 project 协作提示单 v1》§三的规范。*
+141
View File
@@ -0,0 +1,141 @@
# Doco 子项目 · P1 完工快照 · 交接给 P2(OCR 阶段)
> 起草:Opus 顾问(本对话)
> 日期:2026-06-15
> 用户:制片人刘通(《军事科技》栏目 TPS 中台主理人)
> 上一份:`doco_handoff_to_next_chat.md`(P1 调试中,bug 未解)
> 状态:**P1 dry-run 已通过自检与人工验收,P1 完工**
---
## 一、一句话总结
P1(视频双路拆分预处理)的抽帧→空白过滤→去重→关键帧流水线已跑通并通过全部验收,可进入 OCR 阶段。OCR 选型方向已更正为**本地部署 DeepSeek-OCR(经 Ollama)**。
---
## 二、P1 做了什么
- **输入**:一期节目视频(黑底白字字幕 + 干净人声)+ A 稿
- **本阶段产物**:`frames/`(308 张字幕关键帧)+ keyframes 数据;音轨/B 稿是后续阶段
- **流程**:ffmpeg 抽帧(1fps,crop 到下方 20% 全宽)→ 空白帧过滤 → dHash + IoU 去重 → 关键帧
- **demo 视频**:《现代防空反导大对决》(26 分钟)
---
## 三、本轮修复的 3 个 bug(都已修好、已验收)
交接前的核心卡点是「`is_blank_frame()` 读的图 ≠ frames/ 里的图」,自检反复中止。逐层挖下去,实际是三个独立 bug:
### bug 1 · off-by-one 索引错位(核心)
- **根因**:`extract_frames()``enumerate()` 从 0 开始编号(frame_index 0-based),但下游清理逻辑和自检逻辑都从文件名提取 1-based 编号(`frame_0008.png` → 8),两套编号差 1。
- **后果**:空白帧因编号错位被错误保留;自检重读图片取到正确像素,却去查 CSV 错误的行 → 报告不一致、中止。
- **不是**"读了不同目录的图"——所有判断函数始终从同一个 frames/ 读图,是编号体系不一致。
- **修复**:改为直接从文件名提取 1-based 编号。commit `bc5a30e`
### bug 2 · 去重计数器恒为 0
- **根因**:`decision_stats[...] = decision_stats.get(..., 0)` 缺了 `+ 1`,每次迭代没递增。
- **后果**:统计永远显示"筛掉 0 张 IoU + 0 张哈希",看起来像去重没生效。**但去重逻辑本身一直是对的**,只是计数显示错。
- **修复**:加 `+ 1`。commit `d55f029`
### bug 3 · 2 个失败的单元测试(均与本次修改无关的旧断言问题)
- `test_subtitle_frame`:切片比例写错(注释说 1% 实际取了 10%),修改前就在失败。改 `arr[:100,:10]``arr[:10,:10]`
- `test_ratio_ok_but_max_brightness_too_low`:亮度阈值从 200→240 后,亮度 220 的像素不再算白像素,旧断言没同步。改断言为 `white_ratio == 0.0`
- **修复**:commit `82973a7`,17/17 全过。
---
## 四、验收数据(已核对,数字闭环)
本次 dry-run 全帧去向(1620 帧):
| 类别 | 张数 |
|---|---|
| 空白帧(blank) | 227 |
| IoU 重复(duplicate-iou) | 321 |
| 哈希重复(duplicate-hash) | 764 |
| 首帧保留(kept) | 1 |
| 关键帧(keyframe-hash) | 307 |
| **合计** | **1620** ✅ |
- 最终关键帧 = 1 + 307 = **308 张**,与 `frames/` 实际文件数一致。
- 旁证:demo 这期约 300 条字幕,关键帧 308 ≈ 字幕数,基本一对一,说明去重准确(每条字幕保留一张、砍掉重复画面)。
- **人工验收**:制片人手动翻遍 frames/,确认无任何纯黑帧。
- **单元测试**:17/17 通过。
---
## 五、遗留小尾巴(不影响完工,有空再收)
- `test_ratio_ok_but_max_brightness_too_low` 这个测试名与它现在实际测的行为对不上了(白像素阈值提到 240 后,原本想测的"白像素够但亮度不够"场景已构造不出来,断言被改成了 white_ratio==0)。建议以后让 Cline 给这个测试改名或重构。纯属测试整洁度,不影响流水线正确性。
---
## 六、重要更正 · OCR 选型(覆盖 PRD v2 的 Q1 表述)
**PRD v2 写的"DeepSeek 有 OCR 模型 + Vision 识图模式(官方 API endpoint)"——这条事实有误,作废。**
查证结果(2026-06-15,DeepSeek 官方文档):
- DeepSeek 官方 API 只有 `deepseek-v4-flash` / `deepseek-v4-pro` 两个模型,**纯文本,不收图像输入**(功能仅:文本对话 / Tool Calls / JSON 输出 / FIM)。Cline 里 DeepSeek 预设没有 image 选项是正常的,不是配置漏项。
- `DeepSeek-OCR`(3B,中文文档识别强)是**开源模型,只能本地部署**(vLLM 或 Ollama),**没有现成云端 API**,需自备 GPU。
- 因此制片人手上的 DeepSeek API key **调不了 OCR**
**新方向(已与制片人确认)**:本地部署 DeepSeek-OCR,经 **Ollama** 运行。
- 理由:制片人有 RTX 4090D 24GB(跑 3B OCR 模型富余);零调用成本、不限量;栏目全年 52 期常态化使用,长期划算;**且制片人另有一个离线小机房,只能用本地方案,云端 API 调不通**。
- Windows 上优先 Ollama(原生支持、免 WSL),不碰 vLLM(Windows 上一般要 WSL,门槛高)。
> 候选备选(若本地部署受阻):MiniMax-VL 云端 API、讯飞 OCR。但离线小机房只能本地。
---
## 七、下一步(P2 / OCR 阶段)
1. **本地部署 DeepSeek-OCR(Ollama)**:在制片人栏目主机上搭起来,确认能识别黑底白字字幕关键帧;之后同样流程部署到离线小机房。
2. **OCR 适配层 `ocr_adapter.py`**:把 308 张关键帧批量送入本地 OCR 服务,产出带 `[Nm Ns]` 时间戳的 **B 稿 txt**(格式必须与旧版完全一致,下游 P3 才零侵入)。
3. **去掉 `--dry-run`**,跑正式 OCR,产出 B 稿 + audio_16k.wav + keyframes.json,P1 整体产物齐活。
4. 之后才进 P2 的讯飞 ASR 适配、P3 的 Claude 融合层。
---
## 八、关键决策(已锁,沿用)
| 项 | 值 |
|---|---|
| 字幕裁切区域 | `crop=iw:ih*0.2:0:ih*0.8`(全宽 × 下方 20%),写死,全年 52 期一致 |
| 抽帧密度 | 1fps |
| 哈希算法 | dHash 阈值 5,pHash 阈值 2 |
| IoU 保底阈值 | 0.95 |
| 空白帧双条件 | `max_brightness >= 240` AND `white_pixel_ratio >= 0.005` |
| 凭证管理 | doco 自管 `doco/.env`,主仓库根 `docs/api_credentials_inventory.md` 只记元信息 |
| **OCR 提供方** | **本地 DeepSeek-OCR(经 Ollama)** ← 本次更正,原 PRD 的"DeepSeek 云端 Vision API"作废 |
| ASR 提供方 | 讯飞录音文件转写标准版(P2 才用) |
| AI 融合层 | Claude Sonnet 4.6(P3 才用) |
| 抽帧/抽音轨 | subprocess 调系统 ffmpeg,不引入 ffmpeg-python |
| Git workflow | 按宪法直接推 main,不开分支 |
---
## 九、环境信息
- Windows + Python 3.12.10 + `.venv`(主项目共享虚拟环境)
- VS Code + Cline 插件(编程模型已从 Minimax M3 换为 **deepseek-v4-pro**,响应更快)
- 仓库根:`E:\tps-dashboard\`(VS Code 必须打开仓库根,不能打开子目录)
- 主仓库:`simonkoson/tps-dashboard`(自部署 git,localhost:3000)
- ffmpeg:winget 安装,系统 PATH
- GPU:**NVIDIA RTX 4090 D 24GB**(本地 OCR 部署用)
- 另有一台**离线小机房**(无外网,本地部署的主要落地环境之一)
- 正确 CLI:`doco split --episode-id XXX --input-video XXX --output-dir XXX [--dry-run]`
---
## 十、本轮 commit(按时间)
| commit | 内容 |
|---|---|
| `bc5a30e` | extract_frames:frame_index 从 0-based enumerate 改为 1-based 文件名提取(核心 bug) |
| `d55f029` | decision_stats 计数加 `+ 1`(去重计数器恒为 0) |
| `82973a7` | 修复 2 个测试断言(切片比例 + 阈值适配),17/17 通过 |
---
*P1 至此完工。下一轮从「Ollama 本地部署 DeepSeek-OCR」开始。*
+118
View File
@@ -0,0 +1,118 @@
# Doco 子项目 · P2 完工快照
> 模块定位:《军事科技》中台组成部分。将节目稿件半成品(A稿)、OCR 扒词文档(B稿)、节目纯净声音(ASR)交叉验证融合,产出最接近播出的终版文稿。
> P2 阶段目标:本地部署免费 OCR + 重写字幕扒词流水线,产出高质量 B 稿。
> 状态:**已完工**。本期(ep001 现代防空反导大对决)B稿_v2.txt 定稿,743 行。
> 快照时间:2026-06-15
---
## 一、本阶段为什么存在 / 解决了什么
接手时,旧帧筛流水线把本期七百多条字幕压成了 **308 张关键帧,悄无声息丢了约六成**,且被"308 张 ≈ 300 条字幕,基本一对一"这个**巧合**骗过了验收。
经逐帧决策表(frame_analysis_debug.csv)定位,根因是**用像素启发式去猜字幕变没变**,两个独立 bug:
- **空白过滤误杀**:`max_brightness>=240 AND white_ratio>=0.005` 把字幕淡入/切换的暗帧判成空场(如"我是主持人蓝皓"被剔除)。
- **dHash 距离去重误并**:字幕版式全相同(黑底白字底部居中),仅文字不同,感知哈希分不开"同一条的不同帧"与"两条不同字幕";阈值 5 把不同内容误判为重复(如"欢迎收看《军事科技》"被砍)。
**结论**:像素相似度无法承担"字幕是否改变"的裁判职责。改为 **OCR 优先 + 按文本去重**——把裁判权交给文本(唯一真相层)。
---
## 二、部署:Ollama + DeepSeek-OCR(可复制到离线小机房)
- DeepSeek-OCR 已是 **Ollama 官方库正式模型**,无需第三方 GGUF。`ollama pull deepseek-ocr`
- 模型 `deepseek-ocr:latest` = `:3b`,**6.7GB**(显存占用约 7.8GB),8K context,Text+Image。
- 要求 Ollama **≥ v0.13.0**;本机实测 0.30.8。
- GPU:制片人主机 RTX 4090D 24GB,`ollama ps` 确认 `100% GPU`
- 调用:`POST http://localhost:11434/api/generate`,body `{"model":"deepseek-ocr","prompt":"Free OCR.","images":[<base64>],"stream":false,"keep_alive":-1}`,读 `response` 字段。
- **Prompt 用 `Free OCR.`**;实测黑底白字单行字幕识别准、标点干净、无需图像预处理。**禁用** `<|grounding|>...markdown`(会输出 bounding box / markdown 污染纯文本)。
### 部署踩坑结论(每条都是教训,务必保留)
- **`keep_alive:-1`**:不设的话模型闲置 5 分钟被踢出显存,下个请求遇重载空档报 HTTP 503。设 -1 永久常驻。
- **Windows 控制台编码**:脚本顶部 `sys.stdout.reconfigure(encoding="utf-8", errors="replace")`;进度打印**只打数字、不打中文**,否则 GBK 控制台 `UnicodeEncodeError` 崩进程。
- **写文件用 UTF-8**:jsonl 写入显式 `encoding="utf-8"`(`ensure_ascii=False` 可选,转义不影响 `json.loads` 解回)。
- **GPU 利用率低是正常的**:单张小图 OCR,GPU 算一下歇一下,空隙是读图/编码/HTTP/写盘。判断在不在 GPU 看 `ollama ps` 的 PROCESSOR 列和显存占用,**不看利用率百分比**。
### 离线小机房搬运(待执行)
小机房无外网,不能 `ollama pull`。在主机 pull 后,整拷 `C:\Users\<用户名>\.ollama\models`(或经 `OLLAMA_MODELS` 指定的目录)到小机房同路径。
---
## 三、流水线架构:两阶段解耦
**设计原则**:Stage A(贵、慢、易中断)与 Stage B(纯文本、秒级、可反复重跑)完全解耦,中间用 `ocr_raw.jsonl` 缓存衔接。这套设计在本期实战中扛住了 Cline 反复掐进程,**一帧 OCR 没有白跑**。
### Stage A — `stage_a_extract_ocr.py`(抽帧 + OCR)
- ffmpeg 1fps 抽帧 + crop 下方 20%(滤镜 `fps=1,crop=iw:ih*0.2:0:ih*0.8`),输出 `frames_v2/frame_%04d.png`(1-based,`t_sec = NNNN-1`)。
- **每帧都 OCR,绝不做亮度判空、绝不做 dHash/IoU 过滤**(这是硬约束,丢六成的元凶)。
- 结果逐帧追加写 `ocr_raw.jsonl`:`{"idx","t_sec","text"}`,异常帧写 `{..,"text":"","error":...}`
- **健壮性三件套**:① 启动读已有最大 idx 断点续跑,绝不从头/覆盖;② 单帧 `try/except Exception + continue`,异常绝不冒泡崩主循环;③ 503/超时指数退避重试。
### Stage B — `stage_b_dedup_output.py`(文本去重 + 出稿)
- 只读 `ocr_raw.jsonl`,可反复重跑调阈值。
- **连续段折叠**(裁判在此,基于文本不基于像素):只合并**时间相邻**帧,文本 difflib 相似度 **≥ 0.85** 视为同一条;**只折叠连续段,绝不全局去重**(片头片尾都出现"军事科技"是两条合法记录)。段内**多数投票**取最终文本,**取最早 t_sec** 为时间戳。
- 出稿前清 markdown 残留:`re.sub(r'^[#*\->`\s]+','',text).strip()`(DeepSeek-OCR 偶发 `# ` 行首标题)。
- 输出:`B稿_v2.txt`(`[XmYs] 文本`,格式同旧 B 稿)、`dedup_debug.csv`(逐帧判决)、`blank_filtered.txt`(被判空场的非空文本存档,供审计)。
### `is_blank_ocr(text)` — 空场判定(三选一即空场)
DeepSeek-OCR 在空白/黑场帧会**幻觉**出固定的英文财报表 `<table>...As of December 31...Total return...</table>`,不返回空串。故空场判定基于文本:
1. `strip()` 后为空串;
2. 含 HTML 标签(`<``>`);
3. 不含任何汉字(`\u4e00-\u9fff`)。
真字幕是纯中文短句,三条都不会误伤。
---
## 四、本期验收数据(ep001)
| 指标 | 数值 |
|---|---|
| 抽帧总数 | 1620 |
| OCR 帧数 / error 帧 | 1620 / **0** |
| 空场数 | 207 |
| 被判空场但非空(去重) | **1 条**(`<table>` HTML 幻觉,正确拦截) |
| **B稿_v2.txt 行数** | **743**(旧 B 稿 742,基本持平) |
| 行首 `#` 残留 | 0 |
**质量结论(对旧 B 稿)**:
- 完整性:从灾难性的 308 → 743,**捞回六成丢失字幕**。两条标志性失踪字幕("欢迎收看《军事科技》""我是主持人蓝皓")均恢复。
- 准确性:归一化后约 589 行与旧稿完全一致;约 130 条差异**绝大多数是新流水线把旧 OCR 错字改对**(盹→吨、肘空→时空、差导→差异、领士→领土、短柝→短板、交镎→交锋、范匡→范围、娈得→变得…),且中文引号更规整。**新 B 稿质量明显高于旧版。**
---
## 五、实战教训(开发期摩擦,非代码缺陷)
- **Cline 反复掐进程**:Cline 作为开发期 agent,天性"检查→行动→再检查",每次"出报告/改加固/重启续跑"都会掐掉前台 OCR 进程,崩点一路从 idx 249→503→756 后移,误以为是 bug。**解法**:让 Cline 彻底松手,改由**制片人自己在独立终端**(`.venv` 激活后 `python stage_a...py`)运行,Cline 碰不到。→ 一次性稳定跑满 1620。
- **进度监控靠文件、不靠终端**:`(Get-Content ocr_raw.jsonl).Count` 数行数判进度;停在某数不动=进程停了,数字在涨=在跑。
- **"503"歧义**:一度把"jsonl 写到 503 行"误读为"HTTP 503",白做一版重试补丁。教训:先看清是行数还是错误码。
> 关键认知:Cline 是脚手架不是运行时。脚本本身是确定性程序,产品化后无人值守运行不会再有被掐现象。本期"自己开终端跑"已提前演练了脱离 Cline 的运行方式。
---
## 六、待办(P2 收尾遗留,进 P3 后并行处理)
- **并发版 Stage A 提速**(制片人已提出):当前严格串行,~30 分钟/期,GPU 大量空转。首选方案:并发 4~8 路请求(`OLLAMA_NUM_PARALLEL`,3B 模型在 24G 卡轻松扛),预计压到十分钟出头。可选叠加"像素完全相同(hash 距离=0)帧复用上帧结果"无损省 OCR。vLLM 原生批量是高上限方案但 Windows 难装、破坏可移植性,非瓶颈不碰。
- **离线小机房模型搬运**(见第二节)。
- **空场清单/审计文件**已落地(`blank_filtered.txt`)。
---
## 七、P3 衔接:三方交叉融合(下一阶段)
**三路输入(本期实测体量与粒度)**:
- **A 稿**(`A稿_..._定稿.docx`,实为 markdown 文本):138 段**散文脚本**,按【导视】【主持人N】【解说N】【专家N】【动画N】【隔断】分段。编辑书面版,**与播出是改写关系**(例:A稿"欢迎来到这一期的《军事科技》节目"→播出"欢迎收看《军事科技》")。**不可逐行 diff**,只宜段落/话题级对齐。
- **B 稿 v2**(`B稿_v2.txt`):743 行**逐条短字幕**,屏幕实况。
- **ASR**(`asr_result_timed.txt`):306 行**句级口语实况**(每行一时间戳起点,常含多句)。
**三方可信维度(P3 融合的核心依据)**:
- A 稿 → 管**结构与书面措辞**(分段、专有名词的规范写法、完整语义)。
- ASR → 管**口语实况**(主持人/专家实际念了什么,含临场改词)。
- B 稿 → 管**屏幕术语拼写**(型号/番号/武器名,如"箭-3""萨德""见证者-136",ASR 常听岔)。
**P3 目标**:以时间戳为轴对齐三方,按各自可信维度投票/择优,产出最接近播出的终版稿。比 P2 复杂,需单独设计。
---
*P2 完工。下一步:P3 设计。*
+146
View File
@@ -0,0 +1,146 @@
# Doco 子项目 · P3 设计稿(三方交叉融合)
> 模块定位:《军事科技》中台终极目标。把 A 稿(脚本)、B 稿 v2(屏幕字幕)、ASR(口语实况)三方交叉融合,产出最接近播出的终版文稿。
> 前置:P1(帧筛)、P2(本地 OCR + 扒词流水线,产出 B稿_v2.txt 743 行)已完工。
> 状态:**设计冻结,待开发**。
> 设计时间:2026-06-15
> 本稿在原 `doco_project_design.md` 基础上,据 P2 成果与制片人最新决策修订。冲突处**以本稿为准**。
---
## 一、两个交付物(内容一致,形态不同)
| 交付物 | 给谁 | 形态 | 用途 |
|---|---|---|---|
| **融合 B 稿** | 爱德华(另一项目) | 逐条碎句 + **密集字幕级时间戳** `[XmYs] 文本` | 字幕/片段定位,时间精度优先 |
| **融合 A 稿** | 编导 | **公文格式 docx**,保留【导视】【主持人N】【解说N】【专家N】【隔断】分段 | 最像播出版的存档稿,方便编导复用 |
**一致性约束**:融合 A 稿**由融合 B 稿生成**(按 A 稿分段结构归拢 + 套格式),所以两者内容天然一致——是"构造出来的一致",不是事后比对硬凑。
---
## 二、流水线四阶段(注意依赖顺序:术语提取必须在 ASR 之前)
```
A稿 ──► ①术语提取·更新词典 ──► 本期热词表
视频 ──► ②分离音频·转码WAV ──► 讯飞STT(热词+pd=mil) ──► 新ASR(带时间戳)
B稿v2 ⊕ 新ASR ──► ③交叉复审 ──► 融合B稿(碎句·时间戳·给爱德华) + fusion_review留痕
融合B稿 + A稿(语义对齐分段) ──► ④LLM语义融合 ──► 融合A稿(公文docx·给编导)
```
沿用 P2 的工程纪律:**各阶段解耦、中间产物落缓存、可断点续跑、可单独重跑**。
### Stage C1 — 术语提取 + 词典更新(规则层 + 轻量 AI)
- 从 A 稿提取专有名词(武器/型号/番号/人名/机构/技术概念),并入中台**累积词典**。
- 词典字段:`term / domain / first_seen_episode / frequency`。**逐期累积,越用越准**。
- demo 词典(34 条热词)作为**基线**,本期在其上增量更新(如 F/A-18E/F 大黄蜂、歼-10、黄貂鱼无人加油机…)。
- 产出本期**热词表**(讯飞上限 200 个,每个 2–16 字,`|` 分隔)供 Stage C2 用。
### Stage C2 — 音频分离 + 讯飞 ASR(适配层)
- ffmpeg 从视频分离音频 → 转码 **16kHz / 单声道 / 16bit WAV**(讯飞硬要求,否则报误导性 `language verify fail`)。
- 调讯飞「**录音文件转写(标准版)**」(**不要大模型版**,免费包阉割 `language` 参数)。参数:`pd=mil``eng_smoothproc=true``eng_colloqproc=true``hotWord=<C1热词>`
- 签名:`signa = base64(HmacSHA1(MD5(appid + ts), secretKey))`。上传 `/upload` 拿 orderId,轮询 `/getResult` 到 status=4。
- 产出新 ASR(句级,带时间戳,格式 `[XmYs] 文本`),落缓存 `asr_v2_timed.txt` + 原始 json。
- 起点代码:`xfyun_asr_standard.py`(demo 已跑通),按下方安全要求改造。
### Stage C3 — B⊕ASR 交叉复审 → 融合 B 稿(给爱德华)
- **形态:甲 — 保持 B稿v2 的碎句粒度(743 行),密集字幕级时间戳原样保留**;ASR 只做复审纠错,**不改变行结构、不并到句级**。
- 复审做三件事:① 用 ASR 比对查 B 稿有没有重复行/错漏;② 屏幕术语拼写以 B稿v2 为准(见优先级表);③ B 稿明显 OCR 错字而 ASR 对的,用 ASR 覆盖。
- **不机械逐字标记**:由 LLM 读语义判断"是否真分歧",一两个字的等价差异不算分歧。
- 产出:`融合B稿.txt`(碎句+时间戳) + `fusion_review.csv`(机器做了选择/拿不准的地方留痕)。
### Stage C4 — 语义融合 → 融合 A 稿(给编导)
- 拿融合 B 稿,**按 A 稿分段结构语义对齐**归拢成段(参照未融合的 A 稿语义,不是机械时间切分)。
- LLM 读 A 稿语义,理解后重组为"最像播出版"的段落文本,套公文格式。
- **全自动,不阻塞主流程**:拿不准的段落照常产出 + 进 `fusion_review`,不卡住出稿。
- 产出:`融合A稿.docx`(公文格式)。
---
## 三、三方权威优先级(据 P2 修订 —— B 稿地位升级)
> **关键变化**:原设计把 B 稿定为"OCR 错字多,仅作交叉验证,权威最低"。P2 后 DeepSeek-OCR 产出的 B稿v2 屏幕术语拼写又准又干净,故 B 稿**升级加权**。
| 看什么 | 信谁 | 说明 |
|---|---|---|
| **屏幕术语 / 型号 / 番号**(箭-3、萨德、见证者-136) | **B稿v2 ≈ A稿 并列权威** | B 稿是屏幕实际打出的字,常更贴近播出;A 稿是规范写法。两者一致则采用,冲突进 review |
| 口语实况 / 语序 / 语气 | **ASR** | 主持人/专家实际怎么念(含临场改词) |
| 书面结构 / 完整语义 / 分段 | **A 稿** | 编导书面版,管【…】分段与段落语义骨架 |
| 同音事实错(美以→美伊) | **A 稿 / B稿v2** 覆盖 ASR | ASR 同音误识,句子通顺但语义荒谬,靠 A/B 发现 |
| 整段是否存在 | **B稿v2 + ASR 联合**判定 | 两者都无 → A 稿该段为未播出,标记删除 |
---
## 四、融合 A 稿公文格式(Stage C4 出稿规范)
| 层级 | 字体 | 字号 / 其他 |
|---|---|---|
| 大标题 | **方正小标宋** | — |
| 一级标题 | **黑体** | — |
| 二级标题 | **仿宋,加粗** | — |
| 正文 | **仿宋** | **四号(14pt),1.25 倍行距** |
> ⚠️ 字体可用性:方正小标宋是商业字体,生成 docx 的机器若未安装,Word 会静默回退、版式垮掉(同字体类坑)。出稿前须确认制片机/编导机已装方正小标宋(公文常用,多半有);否则在脚本里配置回退字体(如标宋→宋体加粗)。用 docx skill 实现时按 GB/T 9704 党政机关公文格式参照。
---
## 五、留痕与人工策略
- **全自动产出,绝不阻塞主流程**。编导拿到的是干净成品,无需做对齐/确认工作。
- 机器做了选择或拿不准的地方(B/ASR 分歧、低把握段落改写、疑似整段删除、疑似编导笔误)**全部甩进 `fusion_review.csv` 留痕**,供编导/爱德华事后复核,不卡出稿。
- 改动类型枚举沿用原设计:`unchanged / minor_edit / term_normalize / rewrite_large / segment_delete / segment_add / editor_typo`;带 confidence,低把握的进 review 文件。
---
## 六、安全:密钥外置(开发第一件事)
- **现状问题**:`xfyun_asr_standard.py` 第 41-42 行明文硬编码了讯飞 APP_ID / SECRET_KEY,且该文件已进 project,凭证已暴露。**建议在讯飞控制台轮换该密钥**。
- **正式做法**:凭证只存制片机本地(`xfyun_credentials.json` 或环境变量,且加入 `.gitignore` 不进库),脚本运行时读取。代码与对话中**只引用变量名,绝不出现真值**。
- 三套密钥互不相通:讯飞**消费端**(听见 App)≠ 讯飞**开发端**(开放平台,本模块用)≠ Claude API。本模块用开发端 + Claude API。
---
## 七、外部依赖
- **讯飞开放平台**「录音文件转写(标准版)」—— 开发端密钥已就绪(制片机本地)。
- **Claude API**(Stage C4 语义融合)—— 建议 `claude-sonnet-4-6` 起步;单期上下文 A稿~6k + ASR~5k + B稿~5k ≈ 16k 字,窗口充裕;每期 1–3 次调用。
- **本地 DeepSeek-OCR / Ollama** —— 由 P2 提供 B 稿,本阶段不直接调。
- **ffmpeg** —— 音频分离 + 转码。
---
## 八、数据资产(中台共享,逐期累积)
1. **A 稿术语表 / 词典**(核心):讯飞热词来源 + 规则层纠错字典,双重身份;逐期累积,识别质量滚雪球上升。基线 = demo 34 热词。
2. **OCR 错字映射表**:肘→时、娈→变、夭→天… 随版本扩展(P2 已观察到 DeepSeek-OCR 错字远少于旧引擎,此表主要兜底)。
3. **编导风格档案**(远期):累积每位编导删改偏好,用于参数自适应。
---
## 九、待办与风险
- **并发版 Stage A 提速**(P2 遗留):OCR 串行 ~30 分钟/期,GPU 空转;首选并发 4–8 路(`OLLAMA_NUM_PARALLEL`),预计压到十分钟出头。P3 期间并行处理。
- **离线小机房模型搬运**(P2 遗留):拷 `.ollama/models` 文件夹。
- **方正小标宋字体可用性**:出稿前确认,见第四节。
- **讯飞单源依赖**:远期接阿里云 ASR 做备份/交叉。
- **整段删除/编导笔误判定**:高风险,全自动产出但务必进 review 留痕。
---
## 十、本期可复用资产(demo / P2 产物)
| 文件 | 用途 |
|---|---|
| `B稿_v2.txt`(743 行) | Stage C3 输入(屏幕字幕,P2 产出) |
| `A稿_..._定稿.docx`(实为 md 文本) | Stage C1 术语源 + Stage C4 分段语义参照 |
| `xfyun_asr_standard.py` | Stage C2 起点代码(须先密钥外置) |
| demo 词典(34 热词) | Stage C1 词典基线 |
| 源视频 | Stage C2 音频源 |
| `现代防空反导大对决_终版.docx` / `差异报告.docx` | 集成测试黄金对照 |
---
*P3 设计冻结。下一步:按 Stage C1→C4 拆任务给 Cline,沿用解耦+缓存+可重跑工程纪律。*
+216
View File
@@ -0,0 +1,216 @@
# Doco 子项目续接快照 · 交接给下一轮 Opus 顾问
> 起草:Opus 4.7 顾问(算力即将耗尽)
> 日期:2026-06-12
> 给:接手本对话的下一个 Opus 实例
> 用户:制片人刘通(《军事科技》栏目 TPS 中台主理人)
---
## 一、先看哪些文件(读完这几份再接话)
主 project 知识库里这几份**必读**:
1. `Claude协作原则.md` — 跟制片人说话的规矩(简体中文、说人话、不堆叠技术语言、Cline 指令要代码块、宪法第 3 条不架空 Cline 的 Plan 模式)
2. `Doco子项目_Brief.md` — Doco 子项目宪法,红线全在这
3. `doco_project_design.md` — Doco 算法层设计(P3 阶段才用,但先了解全貌)
4. `主project对Doco_PRDv2的回复.md` — 主项目顾问对 5 个问题的批复
5. `跨子项目协作规则.md` — PRD 版本管理
---
## 二、当前进度坐标
- **阶段**:Doco P1(视频双路拆分预处理)
- **节点**:dry-run 调试,**核心 bug 未解决,不能进入 OCR 阶段**
- **git 状态**:多个 commit 已推 origin/main,按宪法直接推 main 不开分支
P1 任务回顾:
- 输入:一期节目视频(黑底白字字幕,干净人声)+ A 稿
- 输出:B 稿 txt + audio_16k.wav + keyframes.json
- 流程:ffmpeg 抽帧(1fps,crop 到下方 20%全宽)→ 空白帧过滤 → dHash+IoU 去重 → 关键帧 → (OCR)→ B 稿
---
## 三、当前 bug 状态(最重要)
### 已确认未修复的核心 bug
**`is_blank_frame()` 读取的图像 ≠ `frames/` 目录里最终保存的图像**
证据链:
1. 制片人上传 `frame_0008.png`(从 frames/ 直接复制)
2. Opus 用 Python 实测:整张图 414720 像素**全部为 0**(`arr.max() == 0`)
3. CSV 报告 frame_8:`max_brightness=255, white_pixel_ratio=0.0124`
4. **两者无法同时为真** → 检测时读的不是这张图
最新 dry-run 自检也证实了这点:
```
[WARNING] 自检失败: frame_0938.png 像素数据与 CSV 不一致!
CSV: max_brightness=255, white_ratio=0.007603
实际重读: max_brightness=255, white_ratio=0.022852
[error] 已中止
```
### Cline 已经修了但没修对的事
Cline 把这个 bug 解释成"CSV 列写入顺序错位",但**那只是表层**——
列对齐改完后,frame_8 的 CSV 数据 `max_brightness=255、white_ratio=0.0124` 仍然跟"全 0 像素"对不上。
Cline 已修复(列错位)是真问题,但**根本原因没找到**。
### 推测根因(交接给你)
很可能 video_split.py 的抽帧流程是分两步,而不是 ffmpeg 一步搞定:
- ffmpeg 把帧抽到某临时目录(可能是全尺寸 1920×1080,**没**裁切)
- Python 用 PIL 后裁切到 1920×216 再存到 frames/
- `is_blank_frame()` 读的是裁切前的临时全画面(里面有节目画面有亮像素 → max=255)
- `frames/` 里是裁切后的小图(字幕区域纯黑 → 像素全 0)
但这只是**推测**,你应该让 Cline 自查代码确认。
---
## 四、下一步建议指令(给你参考,可调整)
发给 Cline 的 ACT 模式指令,大致这样:
```
[ACT 模式]
P1 dry-run 自检揪出严重 bug:is_blank_frame() 读的图 ≠ frames/ 里保存的图。
证据:
1. 制片人确认 frame_0008.png 是全 0 纯黑(Python 实测 arr.max()=0)
2. CSV 仍报告 max_brightness=255, white_ratio=0.0124
3. dry-run 自检也确认 frame_0938.png 数据不一致
上次"列错位"修复是真 bug 但不是这次的根因,这是另一个独立的 bug。
请逐行排查 video_split.py 的抽帧流程,回答以下问题(在汇报里贴具体代码):
1. ffmpeg 命令是什么?是否真的有 vf "fps=1,crop=iw:ih*0.2:0:ih*0.8" 过滤器?
2. ffmpeg 输出到哪个目录?
3. is_blank_frame() 接收的 image_path 是哪个目录?
4. compute_hash() / compute_binary_matrix() 接收的 image_path 是哪个目录?
5. 最终保存到 frames/ 的图,是从哪里来的?
如果发现某个判断函数读了"非 frames/ 里的图",修复方法:
- 选项 A:让 ffmpeg 用 vf crop 直接输出裁切后的小图到 frames/,
所有判断函数都从 frames/ 读(单一数据源)
- 选项 B:保持当前流程,但把所有判断函数的 image_path 改为 frames/ 里的路径
修复后重新跑 dry-run,自检通过 + 制片人验证 frames/ 里没纯黑帧才算成功。
不要再加新功能,这次只修这一个 bug。
```
---
## 五、协作风格备忘
- **制片人不懂代码**,你要"说人话",不堆叠 Python 术语
- **简明扼要**,他明确要求不要解释中间过程
- **给 Cline 的指令一律用 ``` 代码块封装**,方便他复制粘贴
- **明确标 ACT 或 Plan 模式**——前几轮制片人提醒过这点
- 不要架空 Cline 的 Plan 模式(宪法第 3 条),但小 bug 修复可以直接 ACT
- 制片人现在的疲劳度比较高(连续 dry-run 多轮),回复要紧凑,不要长篇大论
- 制片人的 git 工作流:直接推 main 不开分支(宪法规定)
---
## 六、关键决策(已锁,不能动)
| 项 | 值 |
|---|---|
| 字幕裁切区域 | `crop=iw:ih*0.2:0:ih*0.8`(全宽 × 下方 20%),写死,栏目全年 52 期一致 |
| 抽帧密度 | 1fps |
| 哈希算法 | dHash 默认,阈值 5 |
| IoU 保底阈值 | 0.95 |
| 空白帧双条件 | `max_brightness >= 240` AND `white_pixel_ratio >= 0.005` |
| 凭证管理 | doco 自管 `doco/.env`,主仓库根 `docs/api_credentials_inventory.md` 只记元信息 |
| OCR 提供方 | DeepSeek Vision API(P1 dry-run 阶段还没正式调用) |
| ASR 提供方 | 讯飞录音文件转写标准版(P2 才用) |
| AI 融合层 | Claude Sonnet 4.6(P3 才用) |
| 不引入 ffmpeg-python wrapper | subprocess 调系统 ffmpeg |
| Git workflow | 按宪法直接推 main |
---
## 七、环境信息
- Windows + Python 3.12.10 + `.venv`(主项目共享虚拟环境)
- VS Code + Cline 插件
- 仓库根目录:`E:\tps-dashboard\`
- 主仓库:`simonkoson/tps-dashboard`(自部署 git,localhost:3000)
- ffmpeg:已通过 winget 安装,系统 PATH
- demo 视频:`programs/ep001_20260612_fangkong_fandao/source/video.mp4`(《现代防空反导大对决》26 分钟)
doco 子模块目录结构(已建好):
```
doco/
├── src/
│ └── doco/ ← src layout 的包目录
│ ├── __init__.py
│ ├── cli.py
│ ├── video_split.py ← 当前 bug 在这
│ ├── asr_adapter.py ← P2 用
│ └── ocr_adapter.py ← P2 用
├── tests/
│ ├── test_video_split.py
│ └── fixtures/
│ └── mini_test.mp4
├── .env.example
├── .env(本地,不进 git)
├── pyproject.toml
└── README.md
```
---
## 八、踩过的坑(不要重蹈)
1. **Python 包配置 src layout**:Cline 第一次配置时忘了在 src/ 下建 doco/ 子目录,导致 `doco --help``ModuleNotFoundError`。后来 git mv 文件到 src/doco/ 下才好。
2. **系统 Python vs .venv**:制片人第一次 `pip install -e doco/` 装到了系统 Python 3.12,而不是 .venv 里。VS Code 重启后 .venv 自动激活,需要在 .venv 里重装。
3. **VS Code 工作目录**:必须打开 `E:\tps-dashboard\`(仓库根目录),不能打开 `docs\` 子目录,否则 Cline 看不到 .clinerules 和其他子模块。
4. **ffmpeg PATH 不生效**:winget 装完 ffmpeg 后,旧的 VS Code 终端 PATH 还是旧的,要完全关闭 VS Code 重开。
5. **Cline 的命令示例有错**:Cline 多次给的命令示例是错的(用 `python -m doco.src.doco.cli` + `split-video` 子命令),正确命令是 `doco split --episode-id XXX --input-video XXX --output-dir XXX [--dry-run]`
6. **frames/ 目录的语义**:Cline 第一次实现把 1620 张原始抽帧全部保留在 frames/,让制片人误以为"筛选没生效"。后来改为只保留最终关键帧。
7. **pHash 阈值**:dHash 阈值 5,pHash 阈值 2,IoU 阈值 0.95——这些是实测调好的,不要再动。
8. **CSV 列对齐**:Cline 加列时只改 header 没改写入顺序导致全部错位。这次修复了,但**不是核心 bug**。
---
## 九、最新一次 dry-run 的统计(自检失败那次)
```
[stats] 原始抽帧: 1620 张
[stats] 空白帧过滤后: 1393 张 (筛掉 227 张纯黑)
[stats] pHash/dHash 去重后(IoU保底): 308 张 (筛掉 0 张IoU相同 + 0 张哈希相同) ← 统计 bug 还在
[stats] 最终关键帧: 308 张
[debug] frames/ 目录实际文件数: 308 (预期: 308)
[WARNING] 自检失败: frame_0938.png 像素数据与 CSV 不一致!
[error] 已中止
```
注意:
- "筛掉 0 张IoU相同 + 0 张哈希相同" 这个统计还是 0,说明**之前那个 stats 计数 bug 也没真正修好**——或者是这次新加的双条件改变了流程,导致 IoU/hash 没被触发(走了空白帧分支?)。需要复查。
最近的 commit(按时间倒序):
1. Cline 双条件 + 自检 + 测试 + CSV 列对齐
2. Cline numpy 加速 + CLI 参数
3. Cline IoU 保底 + dHash + 诊断 CSV
4. Cline 第一次修空白帧检测(用 brightness=200,后来发现不够,改 240)
5. Cline 加 crop 写死 + dry-run flag
6. Cline 初始化 doco 子项目 P1
---
## 十、给下一个 Opus 的最后嘱托
- 这个 bug 排查过两轮了(Cline 都没真正定位到),所以这次**让 Cline 自查代码并贴出关键函数的实际代码**,不要轻信他的口头解释
- 制片人有耐心,但已经累了,你接手后**第一句话就给方案,不要重复诊断**
- 修完这个 bug 后,P1 dry-run 应该能通过自检,接下来就是填 DeepSeek API key 跑正式 OCR(去掉 --dry-run)
- 如果 dry-run 通过,P1 就算完工,可以进入 P2(讯飞 ASR 集成)
祝接力顺利。
@@ -0,0 +1,214 @@
# 主 project 对 Doco PRD v2 的回复
> 来源:TPS 主 project 顾问(Claude Opus 4.7)
> 日期:2026-06-12
> 针对:`PRD_doco_文稿整理子项目_v2.md` 第四章 5 个问题
> 性质:正式批复,Doco 子项目据此出 v3 后启动 P1
---
## 总体意见
**5 个问题的子项目推荐**,**整体方向 4 个批准、1 个调整**:
- Q1 OCR 选型:**批准 DeepSeek API,但有附加条件**(见下)
- Q2 ffmpeg 入口:**批准,有补充约束**
- Q3 单期 vs 批量接口:**批准,加交付契约**
- Q4 密钥管理:**调整方案** — 走"自管 + 中台登记 inventory"
- Q5 目录规约:**批准草案,但 episode_id 命名规则需主项目定**
5.3 §"23 期是否常态化":**这是一个产品问题,我代制片人答不了。子项目可以按"先按 23 期一次性做完、保留兜底回退路径"开干,常态化的事跟栏目组确认后再回头审。**
---
## Q1. OCR 方案选型 — 批准 DeepSeek API
### 主 project 现状
- **主 project 没有 OCR 子模块/能力**(已确认)。旧设计里 B 稿来自"上游既有 OCR 流程",指的是栏目组那边人工跑的 OCR,不是 TPS 内部的服务。
- 主 project `backend/.env` 里有 `DEEPSEEK_API_KEY`,但**只用作 Cline 工具的 Plan/Act 模型**——没有任何业务代码在调 DeepSeek。所以 doco 调 DeepSeek 视觉模型相当于**新增一条业务调用**,不是"复用现有基础设施"。
### 批复
**Doco 子模块新增 DeepSeek Vision 调用,自管 API key。**
- API key 独立申请,**不复用 Cline 用的那把 key**。理由:Cline 的 key 是开发工具用的,doco 是生产服务,生命周期、配额、监控都该独立。
- 6900 张图 × 几厘 / 张 ≈ 50-150 元,在制片人手上的 API 余额范围内,**不存在配额冲突问题**(主 project 业务代码侧没人在花)。
- 黑底白字字幕场景对 LLM 视觉模型确实是舒适区,**实施前先在 demo 那期视频上跑 10 帧抽样,验证识别准确率达到目标后再扩到 23 期**。
- **降级路径**:如果 DeepSeek Vision 在某些边角 case 上不达标(比如有同期声同步显示双行字幕、字号变化等),回退到 PaddleOCR 本地。**不要回退到讯飞 OCR**(余额独立 + 单期 ~13 元成本太高)。
---
## Q2. 视频处理依赖(ffmpeg)入口 — 批准方案 A
### 主 project 现状
- **没有视频处理子模块/服务**(已确认),dev_plan 也没规划。
### 批复
**Doco 自带 ffmpeg 系统依赖 + Python subprocess 调用。**
附加约束:
- **不引入 `ffmpeg-python` wrapper**——subprocess 足够,不增加新依赖。
- ffmpeg 命令封装在 `doco/video_split.py` 内,**对外只暴露 Python 函数接口**,不让上层调用方直接 shell 调 ffmpeg。
- 在 doco 的 `README.md` 明确写"系统依赖:ffmpeg ≥ 4.x"——这是部署的前置条件。
- 未来如果主 project 真做了统一的视频处理服务,**doco 的封装方式应该使迁移成本最低**(把 subprocess 调用换成 HTTP 调用即可,业务逻辑不动)。
---
## Q3. 23 期批量 vs 单期接口 — 批准方案 A,加契约
### 主 project 现状
- **没有节目素材批量处理编排层**(已确认)。dev_plan 提到的"批量导入工具 v1/v2"是收视数据和报题单的批量,跟视频处理是两回事。
### 批复
**Doco 只对单期负责。同时提供 CLI 和 Python API 两套接口。**
**接口契约(子项目正式实现时按这个出)**:
```python
# Python API
def process_episode(
episode_id: str, # 调用方给定,doco 不管命名规则
video_path: Path, # 输入视频
a_draft_path: Path, # A 稿 docx
output_dir: Path, # 产物存放目录
cleanup_level: str = "medium", # keep_all / medium / clean
) -> ProcessResult
# ProcessResult 必含字段
@dataclass
class ProcessResult:
episode_id: str
status: Literal["success", "partial", "failed"]
final_docx_path: Optional[Path]
diff_report_path: Optional[Path]
needs_review_json_path: Optional[Path]
errors: List[str]
stage_timings: Dict[str, float] # 哪个 phase 用了多久
```
```bash
# CLI 等价形式
doco process \
--episode-id ep023 \
--input-video /path/to/video.mp4 \
--input-a-draft /path/to/a_draft.docx \
--output-dir /path/to/output \
--cleanup-level medium
```
**关于 23 期推进**:**先单期跑通再批量**——拿 demo 那期《现代防空反导大对决》视频做 golden test(P4),与 demo 已有产物比对零回归,再扩到 23 期。一次性 batch 23 期风险太大。
未来主 project 若开节目素材批量处理编排层,**会反过来调 doco 的单期接口**,不会改 doco。
---
## Q4. 讯飞密钥管理 — 调整方案:自管 + Inventory 登记
### 主 project 现状
- **没有中台密钥管理子模块**(已确认),也不在 dev_plan 排期内。
- 主 project 凭证都在 `backend/.env`,各模块走环境变量。
### 批复
不走子项目推荐的"申请新凭证 + 写入中台密钥资产文档"(因为中台密钥服务不存在,不能凭空捏一个)。改为:
**1. Doco 自管凭证。** 讯飞、DeepSeek 各自的 API key 放 doco 自己的 `.env`,**不放进主 project 的 `backend/.env`**——保持子模块自治。
**2. 主 project 仓库根目录新增一份登记文档:`docs/api_credentials_inventory.md`**
这份文档:
- **不存储真实凭证**(那些在各自子模块的 .env 里)
- 只存储**元信息**:每个外部 API 用了什么、所属子模块是谁、开通时间、激活状态、到期日、续费责任人
- 子模块申请新 key、key 到期更换,都更新这份文档
- 这是主 project 帮所有子模块做"凭证清单"的最小落地——比起"中台密钥服务",轻得多,但解决"忘了谁管哪把 key"的问题
**3. Inventory 文档的字段**(供子项目和未来其他子模块参考):
```markdown
| 子模块 | API 服务 | Key 类型 | 开通日 | 激活状态 | 到期日 | 责任人 | 备注 |
|---|---|---|---|---|---|---|---|
| doco | 讯飞开放平台 - 录音文件转写 | APP_ID + SECRET_KEY | 2026-06-12 | 已激活 5h 试用包 | 2027-06-12 | 制片人 | demo 凭证已过期,新申请 |
| doco | DeepSeek Vision | API_KEY | 2026-06-12 | 已激活 | — | 制片人 | doco OCR 用 |
| (主) | Anthropic Claude API | API_KEY | ... | ... | ... | ... | AI 融合层用 |
```
**4. 制片人现在要做的事**(代答 Q4 里"新申请的讯飞开发端密钥应该登记在哪里"):
- 去讯飞开放平台 console 申请新的开发端凭证
- 走 0 元购买流程激活 5 小时免费试用包
- 把 APP_ID / SECRET_KEY 写到 doco 的 .env 里
-`docs/api_credentials_inventory.md` 登记一行
---
## Q5. 目录结构与命名规约 — 批准草案,episode_id 命名由主项目定
### 主 project 现状
- **没有节目素材目录的统一规约**(已确认)。主 project 处理的是数据维度的 `episodes` 表,episode_id 是 INT 自增主键。
- 但是,**素材文件名跟数据库 ID 是两层东西**,不能直接套。
### 批复
**子项目提的 `programs/{episode_id}/source|work|output/...` 结构批准。**
**关于 episode_id 命名规则**——这是主项目要定的规约,定如下:
**episode_id 格式:`ep{NNN}_{YYYYMMDD}_{slug}`**
- `ep{NNN}` = episodes 表自增 INT 主键,零填充到 3 位(如 ep023)
- `{YYYYMMDD}` = 首播日期
- `{slug}` = 节目名称的拼音缩写或简称中文(无空格、无特殊字符)
举例:
- `ep023_20260612_haishang_xianfeng`
- `ep007_20260201_马年军事图鉴`
**理由**:
- INT 主键稳定可关联到 episodes 表
- 日期方便文件系统按时间排序
- slug 让人眼可读
**约定**:
- episode_id **由调用方给定**(主项目编排层 / 责编 / CLI 调用者),doco 不生成
- doco 只把它当一个字符串使用
- 主项目责编/编排层在调 doco 前**先在 episodes 表落库该期、拿到 INT ID**,再拼出 episode_id
**work/ 中间产物保留时长**:
- 节目入档案库后保留 **30 天**,然后可清理(由编排层负责,doco 不管)
- 出问题需要复查时,30 天窗口够定位
---
## §5 第 3 条:23 期是常态化吗?
**这是产品问题,不是技术问题。我代制片人答不了。**
**建议子项目的处理**:
- 按"23 期一次性"开干,先把这批做完
- 算法层零侵入是硬保证,即使将来变常态化,流水线也能继续跑
- **保留旧的 mp3+独立 OCR 流程作为回退路径**(代码不删,加 feature flag 或环境变量切换)。万一某期视频不符合"黑底白字+干净人声"特殊条件,降级到旧流程
- 23 期完成 + 制片人跟栏目组确认后,再决定是否清理回退路径
---
## 子项目可以启动的部分
按 PRD v2 §七 的回复期望:
-**Q1 已答**(DeepSeek API + 附加条件) → P1 可开始
-**Q2 已答**(subprocess + 不引入 wrapper)→ P1 可开始
-**Q3 已答**(单期 + 契约) → P5 推进时按契约
-**Q4 已答**(自管 + inventory 登记) → 制片人先申请讯飞新凭证 + 建 inventory,P2 可开始
-**Q5 已答**(目录草案批准 + episode_id 规则) → P1 输出有处可放
**Doco 子项目现在可以出 PRD v3,启动 P1。**
---
## 顺便给子项目的两条提醒
1. **demo 的 5 份 fixture 必须进 git**(transcripts / A 稿 / B 稿 / ASR / 终版 / 差异报告),作为单元测试 + golden test 黄金对照。这件事属于 Doco 内部自治,主项目不管,但提醒一下。
2. **AI 融合层模型选 Sonnet 4.6**(PRD v2 §二 已锁),不要在子项目里临时换成别的模型。主项目这边也不会推翻这个选择。
---
*主 project 顾问(Claude Opus 4.7)签发,2026-06-12*
@@ -0,0 +1,104 @@
# 寄存条:Doco 文稿整理子项目已外迁
> 留在主 project,让未来的 Claude 一眼知道:Doco 文稿整理模块已拉出去单独立项了。
> 外迁时间:2026-06-12
---
## 外迁了什么
**Doco 文稿整理模块**(Document Organization,前称"TPS 三方融合子模块")整体作为子项目。
聚焦:视频双路拆分 + 讯飞 ASR 适配 + 三方融合引擎(规则层 + AI 层 Claude Sonnet 4.6) + 单期处理接口 + 23 期批量调度入口。
demo 已用《现代防空反导大对决》一期跑通,设计文档(`doco_project_design.md`)已沉淀算法层全部决策。
---
## 主 project 不再讨论的话题
聊到下面这些词,**先问一句"这是 Doco 子项目的事还是主 project 的事?"**——是子项目的事,提醒制片人切到 Doco 子项目 chat。
关键词:doco / 文稿整理 / 三方融合 / 视频双路拆分 / A 稿 / B 稿 / ASR / 讯飞 / DeepSeek OCR / DeepSeek Vision / 终版文稿 / 差异报告 / needs_review / 段落对齐 / 改动类型 / 编导笔误 / 口语清理 / OCR 错字字典 / A 稿术语表
---
## 主 project 仍在管什么
- Phase 0-3 已落地的主干代码(episodes、editors、收视诊断基础、知识库)
- backend 全部 schema、API、迁移脚本
- 前端全部 React 代码实施
- 主干 Bug、性能优化、新需求(Doco 以外的)
- Cline 的全部 Plan + Act 操作
- 看板分析升级子项目的接收(见 `寄存条_看板升级已外迁.md`)
- **api_credentials_inventory.md 的维护**(本次 doco 立项时新引入,见下)
---
## 本次立项给主 project 带来的三件新事
### 1. `docs/api_credentials_inventory.md`(新增)
主 project 仓库根目录加一份"凭证清单",登记所有子模块用的外部 API 的元信息(不存真实 key)。
字段:子模块 / API 服务 / Key 类型 / 开通日 / 激活状态 / 到期日 / 责任人 / 备注。
**这不是中台密钥服务**——只是一份让所有子模块"忘了谁管哪把 key"问题最小化的登记表。子项目自己 .env 里存真凭证,主 project 这里只存元信息。
未来其他子模块申请外部 API,也按这个规则登记。
### 2. 节目素材目录规约(主项目第一次定)
`programs/{episode_id}/source|work|output/...`,episode_id 格式 `ep{NNN}_{YYYYMMDD}_{slug}`
详见 `主project对Doco_PRDv2的回复.md` §Q5。
未来其他涉及节目素材的子模块,沿用同一规约。
### 3. `跨子项目协作规则.md`(新增)
主 project 根目录新增一份通用规则文件,首条规则是 **PRD 版本管理**(主 project 只留最新版 PRD + 对应回复,旧版子项目那边归档保留)。
未来其他跨子项目通用规则在这份文件里累加,**所有外迁子项目都适用**(看板升级、Doco 等)。
---
## 接收子项目交付物的接口
Doco 子项目交付物的接口约定,见**子项目主 Brief §六**(交付什么)。简要回顾:
- **Doco Python 包**:可被主 project 编排层 import,CLI + Python API 两套接口
- **凭证管理**:子项目自己 .env,主项目这边 inventory 登记元信息
- **测试**:单元测试 + golden test(demo 那期零回归)
- **数据资产**:A 稿术语表 + OCR 错字映射表可入 TPS 中台共享(走主项目 schema 流程)
**纪律**:
- 子项目不改主 project backend 代码 / schema
- 子项目不写主 project 前端 UI(编导确认 UI 是另一码事)
- 实施完成后回访子项目登记状态
---
## 开局口径
进主 project 新 chat:
> "续接 TPS 主项目。读 `寄存条_doco子项目已外迁.md` 和 `寄存条_看板升级已外迁.md`。当前要做 [具体任务]。"
---
## 万一子项目记录丢了
降级方案:
1. **主 project 这边保留 doco 系列资产副本**:`doco_project_design.md/.docx``doco_xfyun_integration_notes.md/.docx``doco_handoff_to_opus_chat.md/.docx`、最新一版的 `PRD_doco_文稿整理子项目_vN.md``主project对Doco_PRDvN的回复.md`
2. demo 已跑通的代码和 fixture(`xfyun_asr_standard.py` + 5 份 fixture)在制片人本地有副本
3. 决策史在 Gitea 有 git 历史
4. **主 project 这边不要凭记忆重建子项目内容**——找制片人或重建子项目
---
## 当前外迁的子项目清单(随更新)
| 子项目 | 外迁日期 | 寄存条文件 | 状态 |
|---|---|---|---|
| TPS 看板分析升级 | 2026-06-12 | `寄存条_看板升级已外迁.md` | 设计阶段 |
| Doco 文稿整理 | 2026-06-12 | 本文件 | 等子项目出 PRD v3 启动 P1 |
+38
View File
@@ -0,0 +1,38 @@
# 跨子项目协作规则
> 主项目维护的通用规则,所有外迁子项目都适用。
> 起始日期:2026-06-12
---
## 规则 1:PRD 版本管理(立于 Doco 立项时)
### 1.1 主 project 这边的规则
- **只保留最新版 PRD + 对应回复**,旧版本一律删
- **PRD 与回复一一配对**:PRD v2 ↔ 回复 v2;PRD v3 ↔ 回复 v3
- 任何时点,主 project 不允许同时存在同一子项目的两个 PRD 版本
### 1.2 子 project 那边的规则
- **保留所有版本**(包括 PRD v0 / v1 / v2 / v3...)
- 旧版本可移到 `archive/` 子目录或加 `_archived` 后缀,但不删
- 每份新 PRD 在元信息块标注"上一版:vN"
### 1.3 版本翻新的操作
子项目出 PRD vN+1 时:
1. 制片人在主 project 这边**先删** PRD vN + 回复 vN
2. **再传** PRD vN+1 + 主项目顾问写的回复 vN+1
3. 一次性,不允许"先传再删"造成短暂双版本状态
### 1.4 适用范围
- 当前所有外迁子项目(看板升级 / Doco 等)
- 未来新立项的子项目沿用
---
## (未来其他通用规则在这里累加)
> 后续规则按 `## 规则 2:xxx` `## 规则 3:xxx` 顺序追加。