#2099·read-frog

[BUG] AI 分句复用字幕翻译的 provider 配置,temperature 无法为分句独立设置

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

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

本 issue 已在复测后修正。 原始版本把截图现象归因为「两条 cue 同时在屏」,该归因经实测证伪并已移除,修正说明见文末。本 issue 现聚焦 temperature 无法为分句独立设置这一设计问题。

runAiSegmentSubtitles 与字幕翻译共用同一份 provider 配置,因此 provider 上的 temperature 会被原样应用到分句调用,无法为分句单独设置。

但分句不是翻译任务。按 DEFAULT_SUBTITLES_SEGMENTATION_SYSTEM_PROMPT 的 rule 3,模型要做的 是确定性查表:找句子第一个词的 s、最后一个词的 e,输出 s --> e。这是 argmax 该做的事, 不是 sampling 该做的事。为翻译自然度调高的 temperature(DeepSeek 官方对翻译的推荐值是 1.3) 会直接降低这个结构化抽取任务的稳定性。

调用链已逐行核实:

  1. aiSegmentBlock(utils/subtitles/processor/ai-segmentation.ts)复用 session 的 videoSubtitles providerRef —— 函数上方的注释说明这个复用是有意为之(为省一次 hostedAi.status 往返)。
  2. generateTextForProviderRef(entrypoints/background/background-stream.ts:725)的 destructure 不包含 temperature
  3. 同文件 :773,...buildLocalGenerateTextParams(providerRef.config)generateText() 的参数对象里最后展开,会覆盖前面设置的任何同名字段。
  4. buildLocalGenerateTextParams(utils/providers/generate-params.ts)返回 temperature: config.temperature

因此在 payload 里传 temperature 是无效的,必须在调用点替换 ref 携带的 config。

影响范围: 只影响 local / 自定义 provider。hosted 路径(providerRef.kind === "system") 走 createHostedTextPartStream,根本不传 temperature,用内置 provider 复现不出来。

预期行为: 分句应跑在适合结构化抽取的 temperature 上,与用户为翻译选择的值解耦。


补充观测:temperature 不是唯一因素。

在同一视频、同一时间点分别用 temperature 1.3 与 0.3 复测,分句边界每次都不同(说明 aiSegmentationCache 正常 miss、确实重新采样),但两个 temperature 下都稳定产出畸形结果。 所以降低 temperature 能提升稳定性,但不足以单独解决问题 —— 分句 prompt 本身在词级输入上对 轻量模型要求过高(实测单次请求 187 个词级碎片 / 8110 字符 JSON / 覆盖 60 秒,要求逐词对齐 毫秒时间戳)。

相关的健壮性缺口

分句结果在进入缓存和渲染前没有任何一层校验,采样噪声造成的畸形输出会被完整放行:

  • parseSimplifiedVttToFragments(utils/subtitles/processor/ai-segmentation.ts)零校验 —— 不检查时间戳单调性、不检查区间重叠、不检查 rule 8 要求的 "No omission" 覆盖度。而且 prompt 里没有任何一条禁止重复,所以同一源片段进多条 cue 是完全放行的。
  • runAiSegmentSubtitles(entrypoints/background/ai-segmentation.ts)写 cache 的唯一闸门是 parseSimplifiedVttToFragments(result).length === 0 —— 只要有一条 cue 就算通过。
  • replaceProcessedChunk(entrypoints/subtitles.content/segmentation-pipeline.ts)删除旧 cue 用的是原始 chunk[chunkStart, chunkEnd),但 push 进去的是模型生成的 fragments, 没有 clamp 回这个窗口。

这三点独立于 temperature 成立。

To Reproduce

  1. 配置一个自定义 OpenAI-compatible provider(如 DeepSeek),在 provider 设置里把 temperature 调到 1.0 或更高。
  2. 把它分配给视频字幕,开启 AI 分句。
  3. 播放一个带 word-level 自动字幕(scrolling-asr 格式)的 YouTube 视频。
  4. 观察分句结果的时间戳与边界质量。

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

最小修复 —— 在分句调用点 clone 一份 ref:

typescript
const segmentationRef = providerRef.kind === "local"
  ? { ...providerRef, config: { ...providerRef.config, temperature: 0 } }
  : providerRef

(config 必须单独展开一层,浅拷贝会让副本与原对象共享同一个 config 引用。)

或者给 generateTextForProviderRef 增加一个显式的 temperature override 参数,在 spread 之后 生效。

建议同时加一个校验层,在写 cache 之前拒绝 start 非单调、区间重叠、或同一源片段出现在多条 cue 里的结果。目前坏结果会被写进 db.aiSegmentationCache,同一 config 下重放同一视频会一直 命中它。

更彻底的做法是不让模型产出时间戳:让它只返回句子边界在输入数组里的索引,时间戳由代码 回查。这样 end < start、区间重叠、片段重复在结构上都不可能发生,与 temperature 无关,顺带 可以删掉 prompt 里那段很长的 WRONG/CORRECT 示例。这与 #2100 提到的「词级时间戳不该在管线中途 被丢弃」是同一个方向。


修订说明(复测后)

原始版本把截图里「上下两段内容相同、措辞不同」归因为两条 cue 同时在屏,并推论「同一批源 片段被分配进了两条 cue、各翻译了一次」。该归因经实测证伪:

  • displaySubtitleAtom(entrypoints/subtitles.content/atoms.ts)用 .find() 返回单个 fragment;MainSubtitleTranslationSubtitle 读取的是同一个 fragment 的 .text.translation。渲染层在架构上不可能同时显示两条 cue。
  • 在页面上直接读取 shadow DOM 实测:.subtitles-main.subtitles-translation 在 DOM 中 各只有一个元素。

该现象的真实成因是 AI 分句阶段模型翻译了原文,使 fragment.text 本身变成目标语言;双语 模式下原文行因此显示译文,译文行则是对已译文本的二次翻译。已另开 issue 单独跟踪:#2101。

相关:#2100(超长 cue,根因不同)

需要可以提 PR