fix: whisper 重复伪影整条删除,避免翻译层空响应失败

whisper 在单个窗口内卡住重复时会把一个单元写满整条 cue(实测 30 秒整、重复
74–446 次,如 `チン`×111)。这类正文会让下游 LLM 跟着循环、把输出预算耗在思考上,
最终 `content` 返回空串并报 `translation alignment failed … Expecting value:
line 1 column 1 (char 0)`;它也可能直接渲染成超长字幕行。

- `nodes/subtitle_cleanup.py` 新增 `remove_repetition_entries`:**纯模式判据、无字符
  词表**——展示时长 ≥15s 且同一 1–6 字单元连续重复 ≥6 次且覆盖正文 ≥70% 的 cue 整条
  删除;真实短促呻吟(`ぇ`×15、`ああああああ`)靠时长区分,零误删。
- `nodes/whisper.py` 在转写产出处应用该清洗(VAD 开关都生效,它与套话幻觉无关)。
- 真实数据验证:本地全部真实转写 3605 条 cue 只删 5 条 30 秒伪影;对照实验同批次
  伪影截短后 5/5 成功、原样 1/5。
This commit is contained in:
2026-09-18 22:23:25 +08:00
parent f656ec98c5
commit 3f4478523e
6 changed files with 247 additions and 12 deletions
+11 -11
View File
@@ -300,24 +300,24 @@ def invoke(request: InvokeRequest) -> InvokeResponse:
chunk_seconds / chunk_elapsed if chunk_elapsed > 0 else 0.0,
time.monotonic() - transcribe_started,
)
# decode_full(无 VAD)的副作用清理:无语音段的长时寒暄幻觉与纯语气词
# 碎片都是噪声,整条删除(序列号重排,不留 '-' 占位污染下游);判据与
# 细节见 nodes/subtitle_cleanup.py。仅 decode_full 时启用。
if decode_full:
from nodes.subtitle_cleanup import (
clean_japanese_hallucinations,
remove_short_moan_entries,
)
# 噪声清理:重复伪影(窗口内卡住重复,VAD 开关都会有)整条删除;
# decode_full 另加无语音段的长时寒暄幻觉与纯语气词碎片都是整条删除
# 序号重排,不留 '-' 占位污染下游;判据见 nodes/subtitle_cleanup.py。
from nodes.subtitle_cleanup import (
clean_japanese_hallucinations,
remove_repetition_entries,
remove_short_moan_entries,
)
body = clean_japanese_hallucinations("\n".join(lines))
body = remove_repetition_entries("\n".join(lines))
if decode_full:
body = clean_japanese_hallucinations(body)
# short_moan_max_chars:有效假名 ≤ 该值的纯呻吟碎片整条删除,
# 设 0 关闭(真实短对话不会命中,判据见 subtitle_cleanup)。
body = remove_short_moan_entries(
body,
max_chars=int(request.params.get("short_moan_max_chars", 3)),
)
else:
body = "\n".join(lines)
output_path = output_dir / "transcript.srt"
output_path.write_text(body, encoding="utf-8")
return InvokeResponse(status="completed", outputs={"srt_uri": str(output_path)})