#2101·read-frog

[BUG] AI 分句会把原文翻译掉,双语模式下原文行显示译文

Author: T0MYYYCreated Aug 18, 2026Updated Sep 19, 2026
LabelsStale

Before you report

  • I have searched existing issues to avoid duplicates
  • I have tried with the latest available version (or can reproduce on latest)

Current vs. Expected behavior

开启 AI 分句后,双语字幕的原文行显示的是译文,而译文行是对该译文的二次翻译 —— 于是屏幕上出现两段内容相同、措辞略有出入的目标语言文本。

根因: 分句 system prompt 的 rule 6 是 **No translation** - Keep the original language, 只有一行,夹在 8 条规则中间,是整个 prompt 里最弱的约束。轻量模型(实测 DeepSeek deepseek-v4-flash)在这个任务上会系统性地违反它,把 t 字段连同分句一起翻译掉。

数据流上没有任何一层能拦住:

  • parseSimplifiedVttToFragments(utils/subtitles/processor/ai-segmentation.ts)不做任何 语言校验,只要能解析出 ≥1 条 cue 就接受。
  • runAiSegmentSubtitles(entrypoints/background/ai-segmentation.ts)写 cache 的唯一闸门 同样是 length === 0,于是被污染的原文进了 db.aiSegmentationCache
  • translateSubtitles(utils/subtitles/processor/translator.ts)只写 translation 字段、 ...fragment 原样保留 text,所以污染无法在翻译阶段被覆盖或发现。
  • 渲染层 MainSubtitlesubtitle.textTranslationSubtitlesubtitle.translation (entrypoints/subtitles.content/ui/subtitle-lines.tsx),两行来自同一个 fragment。

结果是译文行等于「把已经是目标语言的文本再翻一遍」,措辞与原文行略有出入 —— 这正是双语两行 看起来像「同一句话说了两遍」的原因。

预期行为: 分句结果的 text 必须保持源语言。分句不应改写文本内容,只应决定切分点。

实测证据

在页面上直接读取 read-frog 注入的 shadow DOM(#read-frog-subtitles-ui-host):

mainCount: 1              // .subtitles-main 在 DOM 中只有一个元素
trCount:   1              // .subtitles-translation 在 DOM 中只有一个元素
main[0].hasCJK: true      // 原文行内容是中文
main[0].len:    375       // 单条 cue 375 字符
translation[0]: "翻译中"  // 译文行仍处于 pending

源视频为英文,字幕轨为 YouTube 英文自动字幕(scrolling-asr 格式),目标语言中文。 原文行本应是英文。

同一时间点分别用 temperature 1.3 与 0.3 复测,分句边界每次都不同(说明缓存正常 miss、确实 重新采样),但两次都出现原文被翻译。所以这不是采样噪声偶发,而是稳定复现。

To Reproduce

  1. 配置一个自定义 LLM provider(实测 DeepSeek deepseek-v4-flash),分配给视频字幕。
  2. 开启 AI 分句,字幕显示模式设为双语(displayMode: bilingual)。
  3. 播放一个带 word-level 自动字幕(scrolling-asr 格式)的英文 YouTube 视频,目标语言设为中文。
  4. 观察原文行 —— 显示的是中文而非英文。

触发前提是送进模型的字幕轨为词级。开启 AI 分句时 utils/subtitles/fetchers/youtube/index.ts:425 会直接 return parseStandardSubtitles(...), 绕过 parseScrollingAsrSubtitles,把滚动 ASR 轨打散成词级碎片。实测同一视频同一份 timedtext: 开启时 6468 条(平均 4.4 字符),关闭时 375 条(平均 79.9 字符)。人工上传字幕的视频输入是 句级的,不容易触发。

Which area(s) are affected? (Select all that apply)

Other

Extension version (if applicable)

1.46.0

Provide environment information

bash
- Extension channel/version: Chrome Web Store 1.46.0

Additional context

短期缓解: 强化 rule 6。目前它只有一行且位置靠后,可以移到 Output 段落顶部,并在 Critical Example 里补一个「输入是英文、输出必须仍是英文」的正例。但 prompt 层约束对轻量模型的 遵守率有限,只能算缓解。

结构性修复: 让模型只返回句子边界在输入数组里的索引,文本由代码从原始 fragment 回查 拼接。这样模型在结构上就没有改写文本的机会,原文污染、时间戳畸形、片段重复三类问题一并消失。

兜底校验:parseSimplifiedVttToFragments 之后、写 cache 之前,比对分句输出与输入 fragment 的文本覆盖度(例如归一化后的字符集合或 n-gram 重合率)。原文被整段翻译时重合率会 骤降,可以直接拒绝该结果并回退到 optimizeSubtitles 的规则分句。

相关:#2099(temperature 无法为分句独立设置)、#2100(超长 cue)。三者都指向同一个结构性 问题:模型被授权改写文本与时间戳,而管线不校验它的输出。

需要可以提 PR