Files
vrsub/docs/decisions.md
T
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

165 lines
12 KiB
Markdown
Raw 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.
# 设计决策与踩坑记录
本文件记录"为什么这样做":模型/参数选择的实测依据、历史事故与踩坑。
**当前生效的规则**以对应专题文档为准(本文件只解释理由):
- 节点参数与默认值:[node-protocol.md](./node-protocol.md)
- 工作流与模型配置:[workflows.md](./workflows.md) / [configuration.md](./configuration.md)
- 代码缺陷与修复过程:[代码审查问题跟踪.md](./代码审查问题跟踪.md)
## 帧文件必须按帧号数值排序(14236 帧事故)
- **现象**run_339ec7ee437f 的 14236 帧任务中,字幕时间与图像错位。
- **根因**ffmpeg `%04d` 编号超过 9999 帧后扩为 5 位,字典序 `sorted()`
会把 5 位编号排在 4 位之前。
- **结论**:帧文件必须按帧号数值排序读取(`_sorted_frame_files`),
回归测试见 `test_frame_files_read_order_matches_frame_number`
## glm-ocr 重复循环问题与源头修复
- **根因**glm-ocr 生成阶段存在已知 bugM-RoPE delta 未传递,大图触发
重复循环;GitHub #454 / #16892)。`keep_alive` 与其无关(实测无效)。
- **修复路径**(现行防护措施见
[node-protocol.md](./node-protocol.md#glm-ocr-调用与重复循环防护)):
1. `frame-extract` 裁切后把帧压缩到 720p 内——过大输入图是触发条件之一。
2. `vlm` 请求体加 `repeat_penalty` + `num_predict` 压制重复。
3. `subtitle-ocr``max_result_chars` 把超长输出判为异常并跳过该帧。
## llm-filter 的 LLM 分类层默认关闭
- **决策(2026-09**`llm-filter` 只跑确定性规则层,LLM 五类分类层默认关闭
`use_llm=0`),需显式 `use_llm=1` 才启用。
- **依据(run_ac7f480a3ccb 逐类人工审查)**LLM 层额外删除的 131 条中
**56%73 条)是真实对话**`好好教育她一番吧`/`腿不要合上`/
`这家医院 为VIP患者提供了特殊服务`),而它真正抓住而规则层抓不到的仅
58 条且大半可正则化(已下沉到规则层);`repeat` 类别 67 条判定零删除;
长文本保护等五套机制全在给不稳定分类器兜底。
- **效果**:关闭后真实数据保留 863 条(旧 588 条)、**误删真对话 0 条**
(旧 73 条)、无 LLM 调用。
- **启用时保留的机制**:每条连同前后各 `context_size`(默认 10)条**过滤后**
文本判断(上下文净化),≥`min_keep_len`(默认 12)时 noise 不构成删除依据
(长文本保护),429/5xx 指数退避 + 自适应线程池 `report_failure()` 降并发后
重试一轮,判定成功即追加 `filter_partial.jsonl` 断点存档;按文本去重。
- **复现**`scripts/regenerate_filter_ac7f480a3ccb.py`
回归数据 `tests/nodes/test_llm_filter/data/ocr_srt_run_ac7f480a3ccb.srt`
## 翻译模型默认值切换与 subtitle-correction 例外
- **默认切换(2026-09**`LLM_MODEL``Qwen/Qwen3.6-35B-A3B` 改为
`Qwen/Qwen3.5-35B-A3B`——同片 2 小时日语 ASR 全量对比,质量持平、
0.232 s/行(评测中最快)。
- **例外**`subtitle-correction` 节点**有意**保留旧兜底
`Qwen/Qwen3.6-35B-A3B`——该节点错听泛化实测新模型 0/4、旧模型 4/4
(复现:同一误听场景各跑 4 次),见 `tests/test_llm_default_model.py`
- **评测数据**`data/experiments/translate_models/REPORT.md`
工具链 `scripts/bench_translate_models.py` 等。
## whisper large-v2 vs large-v3 切换
- **决策(2026-09**:通用转写模型从 large-v3
切到 `faster-whisper-large-v2`
- **依据**savr-1054 全片 A/B 实测,无 VAD 幻觉长段归零、开头漏句救回,
VAD 链路条数与覆盖小幅领先。
- **数据**`data/experiments/whisper_v2_vs_v3/`
脚本 `scripts/compare_whisper_v2_vs_v3.py`
调研记录 [调研-whisper漏句与decode_full验证.md](./调研-whisper漏句与decode_full验证.md)。
## VR 字幕景深与遮挡方案选型
- **结论**:采用 **A-1 零视差**(字幕固定在屏幕平面,不做景深偏移)+
**B-1 顶部安全区**`an8` 顶部居中,`margin_top` 默认 700),
并用半透明填充 + 半透明描边降低遮挡感。
- **调研全文**[VR双目字幕景深与遮挡调查报告.md](./VR双目字幕景深与遮挡调查报告.md)。
## 批量任务先写明细再排队(CREATING 状态)
- **现象**:新建批量任务后偶发任务被标 COMPLETED、`done=0/N`,剩余视频永远不再
被处理(例:`batch_ac585c5458de` 唯一明细还 PENDING 而任务已 COMPLETED)。
- **根因**`create_job` 先把任务行以 QUEUED 入库(此时引擎就看得见),再逐条登记
明细(扫描媒体库时 500+ 条要数秒);引擎轮询到的快照可能没包含剩余明细,收尾时
“无未结束明细”检查也看不到它们,于是把任务标 COMPLETED。
- **结论**:任务行改以 `CREATING` 入库,明细全部登记完才置 QUEUED(引擎只取
QUEUED);登记中途异常置 FAILED 并抛给路由;启动时把上一进程遗留的 CREATING
统一置 FAILED`fail_creating_batch_jobs`),避免静默残留。
- **旧数据修复**`scripts/fix_zombie_batch_jobs.py` 仍用于处理历史
“COMPLETED 但仍有未结束明细”的脏数据。回归测试见 `test_batch.py`
`test_job_hidden_from_engine_until_details_written`
## 批量流水线按 GPU 资源调度(而不是按"是否云端"分支)
- **现象**:LLM 走线上端点时翻译阶段不占显存,但组内阶段仍是"先全部提音、再全部
转写、再全部翻译"的串行推进,翻译期间 GPU 全程空闲(实测 19.6W / 272MiB
占整轮挂钟约 40%)。
- **结论**:阶段能否启动只看资源。`wov_app.resources.stage_gpu_need_mb` 给出每个
阶段的显存需求(whisper 按权重 ×1.45、本机 LLM 端点按预留、线上端点为 0),
进程内 `GpuGate` 负责准入:不需要 GPU 的阶段立刻放行(转写与线上翻译并行);
需要 GPU 的阶段互斥,并按"阶段索引最小者优先"派发,组内因此仍是先跑完全部转写
再进翻译——本机 Ollama 模型每组只加载一次,行为与旧实现一致。
- **为什么不是"配置分支"**:代码不判断端点是不是云端,只读"这个阶段要不要占
GPU、现在够不够"。同一份代码在本机模型下自动退化为串行、在线上模型下自动并行。
- **降级**:探测不到 `nvidia-smi` 时退化为 GPU 阶段互斥(与串行结果一致);
局域网地址上的本机 Ollama 需用 `WOV_LOCAL_MODEL_HOSTS` 声明,否则会被当成远端。
- **并发写**:流水线多线程写产物/进度,SQLite 开 WAL + busy timeout
`WOV_DB_BUSY_TIMEOUT_SECONDS`)。
## 翻译"会话式长上下文"方案否决
- **决定(2026-09 技术评审)**:不实现"翻译阶段复用会话上下文 + 文件夹级共享上下文 +
150k 触发压缩"方案,见
[待办-翻译上下文复用与语义纠错.md](./待办-翻译上下文复用与语义纠错.md)。
- **前提不成立**:该方案预期"长上下文能让模型发现语义不顺从而修掉 ASR 误听",但
同一份待办文档记录的两次实验(`subtitle-correction` 节点 ±60s 上下文、加强提示词
A/B)全部失败。误听句在字面上语法通顺、局部自洽,语境里没有声学证据,模型没有
改写依据——加长上下文只增加同类证据。
- **成本**:会话式每批重发全部历史,输入随批数二次增长;该档 Qwen 在 SiliconFlow
价格表"缓存价格"列为 `-`(无 cached-input 折扣),前缀缓存无法抵消。按实测口径
(批均约 880 tokens、其中输入 529 / 输出 350434 行/素材小时 ≈ 22.5 批/小时)
推算:1.2 小时视频约 3.3×、3 小时视频约 6.5×、15 小时文件夹约 70×(约 ¥30/文件夹),
剩余 187 小时素材由 ¥5.59.4 升到约 ¥300400。
- **阈值踩计费档**`>150k 触发` 越过 `[128k,+∞)` 档(输入 ¥0.4→¥1.6、输出
¥3.2→¥12.8,整请求按该档计价),压缩阈值必须低于 128k,长上下文方案的收益结构
与计费结构天然冲突。
- **与既有设计冲突**:文件夹会话是跨 run 的可变共享状态,与"节点只通过产物 URI
交换数据、每 run 独立产物目录"的不变式、与批量"非 GPU 阶段并行"的调度成果、与
"暂停后整节点重跑、不留半成品"的暂停契约都要冲突,需要新增会话存储子系统并扩展
暂停/幂等语义。
- **未决的替代方向**:问题 A 更可能在 ASR 侧修(`word_timestamps` 已产出的词级
`probability` / `avg_logprob` 目前未被任何节点使用;低置信度片段 + 文件夹级热词
`initial_prompt` 二次解码等),但**尚未评审决定**,需要时另行提方案与代价。
- **实测来源**`data/experiments/regen_plan/app_cloud_run.log`(每批 tokens)、
[实验数据-字幕重生成成本与耗时.md](./实验数据-字幕重生成成本与耗时.md)、
SiliconFlow 价格页 `[0,128k)` / `[128k,+∞)` 分档。
- **字幕审校(定位+定向修复)的实测结论**:见
[实验数据-字幕审校定位与修复实验.md](./实验数据-字幕审校定位与修复实验.md)
## 字幕审校(定位+定向修复)方案暂缓
- **决定(2026-09**:不实施"LLM 定位问题 cue + 定向修复该 cue"的翻译审校节点,
也不改造现有 `subtitle-correction` 节点。实测数据见
[实验数据-字幕审校定位与修复实验.md](./实验数据-字幕审校定位与修复实验.md)。
- **有效的部分**:术语/忠实性类问题(词表违背、译文与日文不一致、称呼跳变)定位与
修复都成立(例:`阴道``小穴`;"生来的里面插着生来的玩具"→"小鸡鸡插进了小穴里"),
合成正样本定位召回 11/14 = 79%,增量成本约 +¥0.04/素材小时(全量约 +¥7)。
- **无效的部分**`字面通顺但语义错`(问题 A,ASR 听岔成正常句子)。实测把中文、
日文原文、邻句、系列词表全部给 LLM:
- 定位:只给中文时三条已知问题 cue 召回 0/3;加上日文也只到 1/3
(三条出问题的共性是**中文读起来完全通顺**,模型没有怀疑动机);
- 修复:三条目标依次改成"声音传送到身体里""继续吃""已经完全进去了"
**全部保留错译**;且出现无依据脑补(`お腹鸣っちゃう`"肚子饿了"→"小穴要湿了")。
- **原因判定**:不是上下文给得不够,而是**当前 LLM 能力不足以从错误的日文原文
反推真实含义**——能依据的日文本身是错的,模型只能沿错译换词。这与待办文档里
记录的前两次失败实验(纠错节点、加强提示词 A/B)结论一致。
- **副产品结论**:① 硬规则(近音匹配)泛化不了——严格口径漏报(目标 0/3),
宽松口径 48.6% 全是假阳性;② 系列自身的译法并不一致(同一个 `おなほ` 被译成
"自慰套/手办/小鼻鼻/嘴"),所以"文件夹级上下文"的价值真实存在,但**惯用译名
靠挖掘挖不出来**,需要人工或强约束固定。
- **若将来重开**:方向应从翻译侧挪回 ASR 侧(只审日文、给候选读法;先建场景再对照),
那里才有声学证据;具体三个可测方向见实验文档第六节。
## 相关文档
- 当前生效的参数与协议:[node-protocol.md](./node-protocol.md)
- 实验数据(成本/耗时、审校定位实验):[实验数据-字幕重生成成本与耗时.md](./实验数据-字幕重生成成本与耗时.md)、[实验数据-字幕审校定位与修复实验.md](./实验数据-字幕审校定位与修复实验.md)
- 运维语义与批量流程:[operations.md](./operations.md)
- 缺陷 ID 与验收标准:[代码审查问题跟踪.md](./代码审查问题跟踪.md)