Files
vrsub/docs/待办-翻译上下文复用与语义纠错.md
cat-shark 5b263171f2 docs: 归档翻译上下文/字幕审校方案的评审结论与实测数据
- 待办:会话式长上下文方案标记为已技术评审且不实施;补记问题 A 的阶段性结论(暂缓)
- 新增 docs/实验数据-字幕审校定位与修复实验.md:LLM 定位触发率/召回、合成正样本、逐条修复前后对照、成本与复现方式
- 新增 docs/实验数据-字幕重生成成本与耗时.md:单价口径、功率画像、全量推算
- decisions:补充两条决策(会话式长上下文否决、审校定位+定向修复暂缓)
- scripts:归档一次性探测脚本 probe_subtitle_review_stage0.py(含 rebuild 子步骤)
- AGENTS/README:补充实验与改动许可规则、文档索引
2026-09-19 18:33:28 +08:00

180 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 待办:翻译上下文复用与语义纠错
本文件记录**尚未解决**的问题与用户给出的需求规格,供后续对话接着做。
已完成的改动见 git 历史(分支 `master` 上的 `fix/review-improvements` 合并结果)。
---
## ⛔ 方案状态:已技术评审,**不实施**
「一、需求规格」与「五、落地这个需求时需要先想清楚的点」所描述的**会话式长上下文
方案经技术评审否决,后续不要再按本方案修改代码**。结论与依据见
[decisions.md](./decisions.md#翻译会话式长上下文方案否决)
1. **前提不成立**:需求 5 假设"长上下文能让模型发现并改掉语义错",但本文件自己
记录的两次实验(纠错节点 ±60s 上下文、加强提示词 A/B)全部失败。错因是 ASR
听岔、语境里没有声学证据——加长上下文只是增加同类证据,不能修掉问题 A。
2. **成本放大 3×~70×**:会话式每批原样重发全部历史(随批数二次增长),该档 Qwen
在 SiliconFlow 无 cached-input 折扣(价格表缓存列为 `-`);按实测口径推算,
剩余 187 小时素材的 API 费用从 ¥5.59.4 涨到约 ¥300400。
3. **压缩阈值踩计费档**`>150k 触发` 跨过 `[128k,+∞)` 档(输入 ¥0.4→¥1.6、
输出 ¥3.2→¥12.8,整请求按 4× 计价),阈值应低于 128k。
4. **与现有不变式/机制冲突**:文件夹级可变共享状态(节点只经产物 URI 交换、每
run 独立产物目录)、批量非 GPU 阶段并行(共享会话要求按文件夹串行)、"暂停后
整节点重跑、不留半成品"的暂停语义,都要新增子系统并改动语义。
本文件**保留为需求与问题记录**(问题 A/B/C、需要知道的坑、数据现状仍然有效)。
问题 A 的可行方向(ASR 侧低置信度 + 文件夹级热词二次解码等)**尚未评审、未决定**,
需要修时另行提出方案与代价。
---
## 一、需求规格(用户 2026-09-19 明确)
1. **翻译阶段要复用对话**:不再每批(20 条)都是无状态独立请求,而是把同一视频的
多批放进同一个会话上下文里,让模型能看到前面译过的内容。
2. **文件夹级共享上下文**:同一文件夹下的多个视频(同番号多集)共用一个上下文,
让各集之间复用信息(人名、称呼、场景设定等)。
3. **术语稳定**:因为共用上下文,整片/整文件夹的人名、专名、称呼能固定下来,
不因批次不同而漂移。
4. **上下文压缩规则**(小模型上下文有限):
- 上下文占用 **超过 150k** 时触发压缩;
- 压缩对象主要是**前 100k**:调用 LLM 对前 100k 做总结,**重点保留名字、场景**等信息;
- **后 50k 原样保留**,避免丢失"与当前高度相关"的近期信息。
5. 预期收益:模型在长上下文里更容易发现"语义不顺/与前后矛盾"的句子,从而修掉
下面的问题 3(ASR 听岔成"成句但语义错")。
---
## 二、现状(代码事实,2026-09-19 核对)
| 事实 | 位置 |
| --- | --- |
| 翻译按 `CHUNK_SIZE=20` 分块,**每块一次独立请求**:请求体只有 `system` + `user` 两条消息,无历史、无跨批信息 | `nodes/llm.py::translate_lines` / `_translate_batch` / `_call_llm` |
| 动态提示词**按批计算**:本批正文命中词表才追加规则;同一视频其它批次命中的词不会带过来 | `nodes/llm.py::_translate_batch` + `nodes/proper_nouns.py::build_proper_noun_rule` |
| 一批的"上下文"只有本批 20 条(约 1–2 分钟对话) | 同上 |
| 译文**整批返回并入库**(不是只取目标句):`_parse_translations` 要求返回的 id 集合与请求完全一致,缺项/多项/重复/空文本都判失败并重试 3 次 | `nodes/llm.py` |
| 纠错节点是**逐条调用**、输出中文译文(不是改日语),未接入任何工作流 | `nodes/subtitle_correction.py``workflows/*.json` 中无引用) |
---
## 三、问题清单
### 已解决(供参考,不必再动)
| # | 问题 | 处理 | 证据 |
| --- | --- | --- | --- |
| 1 | whisper 重复伪影(30 秒被同一单元填满;短时 3.7 秒填 111 个假名) | 清洗规则删除(长条 ≥15s 且重复 ≥6 次;或重复 ≥20 次) | `nodes/subtitle_cleanup.py::remove_repetition_entries` |
| 2 | 静音段幻觉(`こんにちは`/`おはようございます`/`東京都交通局8800形電車`…) | 开启 `hallucination_silence_threshold=2.0` + `word_timestamps`(工作流 v3);片头 120 秒由 15 条降到 4 条 | 见 `data/experiments/regen_plan/REPORT.md` 第九节 |
| 3 | 批量任务被误标 COMPLETED(明细写入竞态) | 任务先以 `CREATING` 入库,明细写完才置 QUEUED;启动清理遗留 | `src/wov_app/batch.py::create_job` |
| 4 | 历史产物损坏:CN 里出现"无正文的 cue",下一条的序号+时间轴被吸进正文(ASS 同源损坏,播放器加载失败) | 全库 112 个:85 个旧字幕随重跑修好;27 个新字幕 + 1 个命名特例已单独重做 | 见本文件"四、数据现状" |
### 未解决(本次对话的核心遗留)
**问题 A:ASR 把词听岔成"语法通顺但语义错"的句子,中文照字面直译**
实例(`kiwvr-886/masex.tv@kiwvr00886_2_8k.mp4`00:15:4500:16:20):
| 产品里的 JA(错) | 复核后的 JA(两个模型一致) | 现译(错) | 实际含义 |
| --- | --- | --- | --- |
| `じゃあ、ママの声をおなかに入れますね` | `じゃあ、まままんこにおなほに入れますね` | 那我让妈妈的声音进入你肚子里了哦 | 那妈妈用小穴(把它)套进去哦 |
| `写真してもこんなにカチカチなんですね` | `射精してもこんなにカチカチなんですね` | 也会变得这么僵硬呢 | 就算射了也还是这么硬呢 |
| `あくまで、あくまでください` | `奥まで、奥までください` | 再吃一个,再吃一个吧 | 再深一点,再深一点 |
| `あくまで入ってしまいました` | `奥まで入ってしまいました` | 它已经全都进来了 | 已经顶到最里面了 |
| `生鼻を使わさせていただきますね` / `口紋かでもご奉仕` / `生おなほ` / `おなほまんこで` | 均为 オナホ/マンコ 一类词的走音 | — | — |
已实测**失败**的修法(不要再重复试):
| 尝试 | 结果 |
| --- | --- |
| 纠错节点(`Qwen/Qwen3.6-35B-A3B``Qwen/Qwen3.5-35B-A3B`,±60s 上下文) | 均输出"那我把妈妈的声音放进肚子里哦"——**没改对** |
| 加强提示词 A:"语义不合常理/与动作链矛盾即判为误听" | 仍保留"声音" |
| 加强提示词 B:"上下文反复出现 オナホ/マンコ/チンポ;同位置的怪词应改写" | 仍保留"声音"(只把"身体/肚子"换了个说法) |
| 审校节点:LLM 定位问题 cue + 定向修复(输入日文原文 + 现有译文 + ±2 邻句 + 系列词表) | 定位召回 0/3(仅中文)~ 1/3(加日文);修复依次输出"声音传送到身体里""继续吃""已经完全进去了",**仍全部保留错译**,另有脑补改坏 |
上述失败实验的完整数据(定位触发率/合成正样本召回/逐条修复前后对照)与
原因分析见 [实验数据-字幕审校定位与修复实验.md](./实验数据-字幕审校定位与修复实验.md)。
**问题 B:翻译无跨批/跨视频上下文**(对应上面"需求规格"要解决的对象)。
直接后果:一批里同时出现多处坏转写时(`写真しても``あくまで`×2),模型既无整片
术语线索、也看不到前后动作链,只能按字面翻译。
**问题 C`_call_llm` 不重试网络错误**。实测云端 API 偶发"挂住 600 秒读超时"
(2/634 批,两批集中在同一分钟),直接把整段翻译判失败、视频白跑一遍。
用户明确选择**暂不修**,留待后续决策;全量 349 个约 5000 次调用,按此失败率会有
10–20 个视频需要重跑(重跑方式:建库级任务,缺字幕的视频会自动判 PENDING)。
### 需要知道的坑
- **任务行的 `updated_at` 会被读操作刷新**`GET /api/batch/jobs*` 会触发
`sync_batch_job_progress` 重算并写回时间戳,统计耗时请用日志的"批量任务 … 完成"行
或明细的 `updated_at`,不要用任务行。
- **产物损坏的根因**:某一步"只删正文行、留下空 cue",后续解析器把下一条的
序号+时间轴当成正文吸走。产出端(当前 pipeline)已验证不会再产生:`serialize_srt`
对空正文会写空行,`clean_srt_text` 也能正确处理空文本 cue。
- **`savr-1054/…_1_8k` 的产物名曾带 `666` 后缀**`…_1_8k666.CN.srt`),批量引擎按
"文件名含视频主名"仍能匹配,但严格按 `<视频名>.CN.srt` 匹配的脚本/播放器会漏掉。
---
## 四、数据现状(2026-09-19
- 媒体库:`/mnt/fnOS/123`NAS `192.168.123.199:/vol1/1000/123`),524 个视频 / 287.27 小时。
- 待重生成(CN 字幕早于 2026-09-01):**349 个 / 187.86 小时 / 1417.8 GiB**。
- 已按新管线重做:20 个样本 + NKKVR-175 两个 + 28 个损坏修复 = 50 个视频。
- 每个视频现有产物:`<视频名>.JA.srt`ASR 后的日语转写)、`<视频名>.CN.srt`
`<视频名>.CN_dual_eye.ass`;旧字幕备份为 `*.old-20260918` / `*.corrupt-20260918`
---
## 五、落地这个需求时需要先想清楚的点(新对话用;**本节方案已否决,不再作为实施依据**)
1. **会话形态**:把 `messages` 从"system+user"扩成多轮,是把**上一批的 JSON 原文与译文**
作为 assistant/user 消息追加,还是只追加"术语表 + 摘要"?前者上下文增长快(每批 ~800
tokens),后者省但信息有损。
2. **对齐与重试**:现有实现靠"每批独立、id 对齐、失败重试 3 次"保证不错位;改成会话后
某批失败/重试会污染会话历史,需要明确回滚策略(例如失败批不写入历史)。
3. **压缩触发与实现**`>150k` 触发、总结前 100k、保留后 50k。要确定 token 口径
(用接口返回的 usage 还是本地估算)、总结用哪个模型(同模型/更便宜的模型)、
总结产物如何缓存复用(同一文件夹复用意味着总结要能跨视频复用)。
4. **文件夹级共享**:按文件夹串行处理是前提(当前批量引擎是分组的,同组视频可能并行),
否则两个视频同时读写同一会话会互相污染;还要定义文件夹内视频的处理顺序。
5. **暂停/断点续跑**:run 恢复时上下文是否重建?重建口径(从已有 CN.srt 复原历史?)。
6. **成本与限额**:长上下文会显著抬高输入 token(每批都带上历史),要重算
`docs/实验数据-字幕重生成成本与耗时.md` 里的单价;注意不少接口对超长输入有倍率价。
7. **与现有清洗/词表的关系**`subtitle_cleanup`(重复伪影/寒暄幻觉/短呻吟)与
`proper_nouns`(词表规则)仍然保留,会话上下文是补充而非替代。
8. **验收用例**:至少覆盖本次的 `kiwvr-886_2` 00:15:4500:16:20 那段——目标句应译成
"那妈妈用小穴(把它)套进去哦"这类含义,而不是"声音进入肚子";同时不能把正常句
改坏(例如呻吟/短句不应被"脑补"成新意思)。
## 六、问题 A 的阶段性结论:暂缓(模型能力不足)
已实测过两条路子,均不能解决问题 A:
1. **翻译侧审校**(LLM 定位问题 cue + 定期修复:输入日文原文 + 译文 + ±2 邻句 +
系列词表)。实测:定位召回 0/3(只给中文)~ 1/3(加上日文);修复后仍保留错译,
且出现无依据脑补改坏正常句。**有效的只有术语/忠实性类**(如 `阴道``小穴`)。
2. **硬规则定位**(词表违背 + 近音匹配):严格口径目标召回 0/3,宽松口径命中率
48.6% 全是假阳性——硬规则泛化不了,不作为定位手段。
原因:`字面通顺但语义错`的句子中文本身读得通,模型没有怀疑动机;即使怀疑,
可依据的日文原文也是错的,模型只能沿错译换词。**判定为当前 LLM 能力不足,暂缓。**
实测数据、成本与复现方式:
[实验数据-字幕审校定位与修复实验.md](./实验数据-字幕审校定位与修复实验.md)。
若将来重开,应把方向挪回 **ASR 侧**(只审日文/给候选读法/先建场景再对照),
那里才有声学证据;三个可测方向见该文档第六节。
## 七、相关代码与文档
- 翻译节点:`nodes/llm.py``translate_lines` / `_translate_batch` / `_call_llm` / `_system_prompt`
- 审校实验脚本(一次性,已归档):`scripts/probe_subtitle_review_stage0.py`
- 词表规则:`nodes/proper_nouns.py``PROPER_NOUNS` / `ONOMANOPOEIA` / `ADULT_EUPHEMISMS`
- 纠错节点(原型,未接入):`nodes/subtitle_correction.py`
- 清洗规则:`nodes/subtitle_cleanup.py`
- 批量引擎:`src/wov_app/batch.py`、资源门控 `src/wov_app/resources.py`
- 决定性样本与提示词快照:`data/experiments/regen_plan/prompt_dump/`
- 全量实测记录:`data/experiments/regen_plan/REPORT.md`
- 成本与耗时:`docs/实验数据-字幕重生成成本与耗时.md`