# 方案：人机协同配音流水线（Roll-until-Pass + 机器质检 + 审听台）

> **状态（2026-06-12）**：本文是 2026-06-10 的设计方案，留档备查。P1 已落地且现状远超本方案（声纹主指标、0.95 无例外、并行施工、复利四件套——现状见 `使用说明.md`，演进史见 `创造全流程.md`）。**P2 审听台仍未做，§5 仍是设计蓝本**；P3 学习层已以"听点回流 + 反馈台账"的形式落地。文中具体数字（黄红阈值 0.870/0.827、auto_reroll=3、CER 判级等）已过时，以 `config.json` 为准。

> **v2 修正（2026-06-10，P1 已落地）**：经确认，主要翻车模式不是错字，而是**音色不像本人 / 腔调形变**。
> 因此主指标从 ASR-CER 调整为**声纹相似度（像本人分数）**：本地 CAM++ 模型提取声纹，与"你历次认可段"的质心算余弦相似度，阈值由认可段分布自适应（当前黄线 0.870 / 红线 0.827）。
> 校准实验验证：你 roll 过的 002 段被机器独立判入低分区 ✓。已知边界：<2.5s 短段声纹不可靠 → 只标黄交人，永不自动重roll。
> ASR-CER 降级为 P2 可选辅助指标。P1 已实现：质检.py（打分/判级/锚点进化）+ 配音.py（roll-until-pass+质检报告+SRT）+ 重roll.py（自动挑最像）。

> 目标：解决"配音不稳定"。不是换引擎、不是调参，而是翻转流程——
> **现在：机器生成 → 人听全部 → 人发现坏段 → 人手动重roll → 人再听 → 人手动覆盖文件。**
> **以后：机器生成 → 机器听全部 → 机器自动重roll到合格 → 人只裁决机器拿不准的 3~5 段 → 网页一键定稿。**
> 人从"全量质检员"升级为"仲裁者/导演"。

---

## 1. 诊断：不稳定到底出在哪

| 环节 | 现状 | 问题 |
|---|---|---|
| 生成 | Fish Audio S2 Pro，temperature 0.6 | 自回归 TTS 随机性是本质属性：偶发吞字/多字/重复、语速漂移、怪腔、电音。**调参只能降概率，永远压不到 0** |
| 质检 | ❌ 无 | 坏段要靠人耳从 45 段里听出来——漏听一段，成品就带伤 |
| 修复 | `重roll.py` 出 4 版，人听 4 版挑 1 | 重roll 是"人发现后"才触发；4 版可能全坏或第 1 版就好（浪费） |
| 定稿 | 手动复制覆盖 NNN.mp3 + 重跑合并 | 纯体力活，容易覆盖错文件 |
| 一致性 | 每段独立请求 | 跨段语速/响度漂移无人监控 |

**结论：瓶颈不在"生成不稳"，在"质检全靠人"。**

---

## 2. 新架构：三层流水线

```
文案.txt
   │
   ▼
[0] 播音稿预处理（已有：分句/破折号/数字归一 → 保留）
   │
   ▼
[1] 生成层  ──→ 每段合成 1 版
   │
   ▼
[2] 机器质检层（新增，全自动，本地免费）
   │    每段音频 → 本地 ASR 转写 → 与原文比对 + 声学指标
   │    ┌─────────────────────────────────────────┐
   │    │ ✅ 绿：CER=0 且指标正常 → 直接通过，人不用听 │
   │    │ 🔴 红：明确翻车 → 自动重roll（最多3次），   │
   │    │        roll 完即检，合格就停 → 还不行才升级 │
   │    │ 🟡 黄：机器拿不准（CER低但非0/指标边缘）     │
   │    └─────────────────────────────────────────┘
   ▼
[3] 人工仲裁层（人只处理 黄+红，预计 45 段里 3~5 段）
   │    本地网页"审听台"：播放/看转写diff/一键换版本/一键重roll/改稿重生成
   ▼
[4] 定稿层：一键合并 + 同步产出 SRT（按已知段时长+gap 精确打点，行尾无标点）
   │
   ▼
[5] 学习层（裁决记录 → 翻车模式库 → 高风险段预判，越用越省）
```

---

## 3. 机器质检指标（核心新增能力）

全部本地运行、零 API 费用：

| 指标 | 检测什么 | 实现 |
|---|---|---|
| **CER 字错率** | 吞字、多字、重复字、念错 | 本地 ASR（sherpa-onnx paraformer-zh，中文最准最轻；备选 faster-whisper）转写后与原文对齐。比对前同做数字归一，避免"七十"vs"70"误报 |
| **语速比率** | 语速漂移 | 字符数 ÷ 音频时长，偏离全篇中位数 ±25% 判异常 |
| **异常静音** | 段内卡顿、句首句尾留白 | ffmpeg silencedetect，段内 >1.5s 静音判异常 |
| **响度** | 爆音、过轻、段间忽大忽小 | ffmpeg loudnorm 测 LUFS，偏离全篇中位数 ±3LU 判异常 |
| **时长合理区间** | 整段截断、跑飞（生成出 2 倍长的胡话） | 预估时长 = 字符数 × 全篇均速，超出 ±40% 判红 |

判级规则：
- **绿**：CER=0 且全部声学指标正常
- **红**：CER>5% 或 时长跑飞 或 静音异常 → 自动重roll
- **黄**：其余（CER 0~5%、指标边缘）→ 留给人裁决

> 注：ASR 在这里只做质检比对，**SRT 打点仍然不用 Whisper**——合并时每段时长（ffprobe）+ gap 已知，时间轴是精确计算出来的。

---

## 4. Roll-until-Pass：把重roll从"事后补救"变"事中自愈"

现状 `重roll.py` 是盲打 4 版；新逻辑：

```
合成第 N 段 → 质检
  ├─ 绿 → 下一段
  └─ 红 → 重roll 1 版 → 质检 → 合格就停（最多 3 次）
            └─ 3 次全红 → 标记升级，保留所有版本给人挑
```

**成本对比**（按 Fish Audio 字符计费）：
- 现状：1 版 + 人工发现后盲 roll 4 版 = 坏段 5 倍字符费 + 人听 5 遍
- 新方案：平均坏段 ~2 版即过 = 坏段 2 倍字符费 + 人听 0 遍
- 总成本**反而更低**，人工时间下降 ~90%

config 增加开关：
```json
"qc": {
  "enable": true,
  "auto_reroll": 3,        // 红段自动重roll上限
  "cer_red": 0.05,         // CER 红线
  "oversample": 1          // 土豪模式可设 3：每段直接生成3版机器挑最优
}
```

---

## 5. 审听台（人机协同的交互面）

`python 审听台.py` → 起本地服务 → 浏览器打开一页式工作台：

```
┌──────────────────────────────────────────────────────┐
│ 第六章配音审听台      绿 40 │ 黄 4 │ 红 1   [一键定稿] │
├──────────────────────────────────────────────────────┤
│ 🟡 012  ▶播放  原文: 他说的那句话，到今天我还记得        │
│         ASR:  他说的那句话到今天我还记得 (CER 0%, 语速+22%)│
│         [✓用这版] [换v2 ▶] [换v3 ▶] [🎲再roll] [✏改稿]  │
├──────────────────────────────────────────────────────┤
│ 🔴 027  ▶播放  3版全红: 重复字「记得记得」               │
│         [听v1] [听v2] [听v3] [🎲再roll] [✏改稿重生成]    │
└──────────────────────────────────────────────────────┘
```

- 绿段折叠隐藏（想抽查可展开）
- 转写 diff 高亮：哪个字吞了/多了一眼可见，**很多段不用听就能裁决**
- 「✏改稿」是关键逃生门：有些文本天生难念（生僻词、拗口排比），与其 roll 十次不如改一个字
- 选定即写盘：自动覆盖 `output/NNN.mp3`，点「一键定稿」= 合并 + 出 SRT
- 纯 Python 标准库 http.server + 单文件 HTML，零框架零依赖

---

## 6. 学习层：越用越聪明（协同进化）

每次人工裁决落一条 JSON 记录：段文本、翻车类型、roll 了几次、最终怎么解决。积累后：

1. **高风险预判**：合成前扫描文案，命中历史翻车特征（超长句、特定生僻词、连续感叹）的段直接走 oversample=3
2. **TTS 安全改写规则库**：把"改稿才解决"的案例沉淀为预处理规则（你现有的破折号处理就是第一条规则，这是它的系统化）
3. 数据在手，将来换引擎/换音色也能快速回归测试

---

## 7. 实施路线

| 阶段 | 内容 | 改动 | 效果 |
|---|---|---|---|
| **P1 质检闭环**（核心，建议先做） | `质检.py`：ASR+声学指标+三色判级；`配音.py` 接入 roll-until-pass | 新增 1 文件，改 1 文件；装 sherpa-onnx（pip，CPU即可） | 红段自动自愈，人只需关注黄段清单（命令行版） |
| **P2 审听台** | `审听台.py` + 单页 HTML | 新增 2 文件 | 告别手动复制覆盖文件，3 分钟定稿一章 |
| **P3 学习层** | 裁决日志 + 风险预判 | 新增 1 文件 | 翻车率随使用持续下降 |
| 顺手补 | 定稿时同步产出 SRT（行尾无标点） | 并入 P1 合并逻辑 | 配音字幕一次出 |

不做的事：不换 TTS 引擎（S2 Pro 音色你已认可）、不动现有分句逻辑（已打磨好）、不引入云服务/数据库/前端框架。

---

## 8. 风险与兜底

- **ASR 误判**（把对的判成错的）：判级阈值保守——ASR 报错但拿不准时只进黄区给人看，绝不自动毙掉；ASR 自己听错的常见字可加白名单
- **首次装 ASR 模型**：sherpa-onnx paraformer 模型 ~200MB 一次性下载，全程离线运行
- **额度**：roll-until-pass 有 3 次上限 + 总额度熔断（单章重roll 字符数超过原文 1.5 倍即停手报告）
