Skip to content

fix(qwen): 去掉字幕里的 language Chinese / asr_text 模板残留 - #442

Open
learning-kai wants to merge 3 commits into
buxuku:mainfrom
learning-kai:codex/fix-qwen-asr-language-prefix
Open

fix(qwen): 去掉字幕里的 language Chinese / asr_text 模板残留#442
learning-kai wants to merge 3 commits into
buxuku:mainfrom
learning-kai:codex/fix-qwen-asr-language-prefix

Conversation

@learning-kai

@learning-kai learning-kai commented Aug 15, 2026

Copy link
Copy Markdown

问题

用 Qwen3-ASR 转写时会出现三类脏输出:

  1. 字幕里时不时冒出 language Chinese,有时还带着 <asr_text>
    这是 Qwen3-ASR 的聊天模板漏进正文。模型每段先报语种,再跟听写内容,sherpa-onnx 把整段解码原文交出来,SmartSub 原先原样写入 SRT。

  2. 转写直接报 SyntaxError: Bad control character in string literal in JSON at position 59
    原生识别结果是一段 JSON 字符串。Qwen 正文里偶尔带未转义的换行、Tab、NUL 等 C0 控制字符,JSON.parse 直接炸掉,整段字幕失败。

  3. 静音/幻听时整条 cue 变成英文碎片,例如 demanddetract.imageUrlFinds.
    这不是模板前缀,是模型在无语音段乱吐拉丁词。只剥 <asr_text> 去不掉。

真实课字幕里的脏形态例如:

language Chinese<asr_text>所以
language Chinese<asr_text>正态分布
**language Chinese<asr_text>开玩笑 **
demand
detract.
imageUrl

变更内容

  • 新增 sanitizeQwenAsrText,剥掉语种头、<asr_text> 和偶发 markdown 星号;剥完为空的 cue 丢弃
  • 没有汉字的单 token 默认当幻听丢掉;公式字母(p / DXY / EX1)、数学符号名(sigma)和口语短回应(OK)保留
  • 正常英文句子(如 language is important)不会被误删
  • qwenEngine 写 SRT 前清洗
  • sherpa worker 对 qwen3_asr 同步剥一层
  • 新增 json-result.js,解析原生 JSON 前把字符串里的裸控制字符转义;decodeAsync / getResult 都走这条路径

验证

  • npm run test:engines:810 passed, 0 failed
  • npm run test:sherpa-json:4 passed

已有字幕文件不会自动改写,需要重新转写才会变干净。

Qwen3-ASR sometimes emits chat-template crumbs such as language Chinese<asr_text> into cue text. Sanitize at the worker and engine exits, and drop empty leftover cues.
@learning-kai
learning-kai marked this pull request as ready for review August 15, 2026 10:45
@learning-kai

Copy link
Copy Markdown
Author

问题

用 Qwen3-ASR 转写时会出现三类脏输出:

  1. 字幕里时不时冒出 language Chinese,有时还带着 <asr_text>
    这是 Qwen3-ASR 的聊天模板漏进正文。模型每段先报语种,再跟听写内容,sherpa-onnx 把整段解码原文交出来,SmartSub 原先原样写入 SRT。

  2. 转写直接报 SyntaxError: Bad control character in string literal in JSON at position 59
    原生识别结果是一段 JSON 字符串。Qwen 正文里偶尔带未转义的换行、Tab、NUL 等 C0 控制字符,JSON.parse 直接炸掉,整段字幕失败。

  3. 静音/幻听时整条 cue 变成英文碎片,例如 demanddetract.imageUrlFinds.
    这不是模板前缀,是模型在无语音段乱吐拉丁词。只剥 <asr_text> 去不掉。

真实课字幕里的脏形态例如:

language Chinese<asr_text>所以
language Chinese<asr_text>正态分布
**language Chinese<asr_text>开玩笑 **
demand
detract.
imageUrl

变更内容

  • 新增 sanitizeQwenAsrText,剥掉语种头、<asr_text> 和偶发 markdown 星号;剥完为空的 cue 丢弃
  • 没有汉字的单 token 默认当幻听丢掉;公式字母(p / DXY / EX1)、数学符号名(sigma)和口语短回应(OK)保留
  • 正常英文句子(如 language is important)不会被误删
  • qwenEngine 写 SRT 前清洗
  • sherpa worker 对 qwen3_asr 同步剥一层
  • 新增 json-result.js,解析原生 JSON 前把字符串里的裸控制字符转义;decodeAsync / getResult 都走这条路径

验证

  • npm run test:engines:810 passed, 0 failed
  • npm run test:sherpa-json:4 passed

已有字幕文件不会自动改写,需要重新转写才会变干净。

@buxuku buxuku left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

发现

  1. [P1] 多语种场景会静默删除合法的单词字幕

    main/helpers/engines/qwenText.ts:78-83 和 worker 中的 extraResources/sherpa/worker/sherpa-worker.js:313-318 将所有不含汉字、没有空格、非白名单/公式格式的 2 到 24 字符英文 token 判定为幻听。

    例如:

    Welcome -> ''
    computer -> ''
    English -> ''
    

    worker 会先删除 segment,主进程无法恢复。项目英文和中文资源文案都将 Qwen3-ASR 描述为 “multilingual” (renderer/public/locales/*/resources.json:181-182),但这条规则没有使用 source language 做 gating。英文视频中常见的单词字幕会直接消失。

    建议只在明确选择中文时启用该规则,或结合模型语言/置信度判断,并补充 Welcomecomputer 等合法英文单词测试。

  2. [P2] language Chinese 的常见变体仍会残留到字幕

    qwenText.ts:101-106 的 header 正则要求语言名首字母大写,且不能带句末标点或引号;它运行在 stripCueDecorations() 之前。以下输入都会被保留:

    language chinese
    language Chinese.
    "language Chinese"
    language Chinese:
    

    后续的单 token 判定只对含空格的文本直接放行,因此无法补救。worker 版本也有相同问题 (sherpa-worker.js:333-340)。这会使 PR 主要修复目标在真实输出带标点时失效。

  3. [P2] JSON 修复器会把非法 JSON 静默改写成另一份合法数据

    extraResources/sherpa/vendor/json-result.js:37-44 会删除字符串外所有非法 C0 控制字符,而不是拒绝损坏结果。例如:

    {"a":1<vertical-tab>2} -> {"a":12}
    

    parseJsonResult() 随后成功返回 { a: 12 }。这不仅容忍了 native 输出,还改变了数据语义并隐藏了原生结果损坏;该 parser 通过 OfflineRecognizer 对所有 sherpa 引擎生效,不只 Qwen。

    建议只处理已知的尾部 NUL,字符串外的内部非法控制字符应继续抛错,并增加结构损坏测试。

  4. [P2] 控制字符被“转义后再还原”,最终仍会写入 SRT

    json-result.js:29-30,67-68 将裸控制字符转成 JSON escape,但 JSON.parse 会把它们还原成真实字符。之后 qwenEngine.ts:144-153formatSrtContent() 原样写入字幕文件。

    因此 NUL 会进入 .srt 文件,换行会改变 cue 的文本结构。PR 当前测试还明确要求保留 NUL (scripts/test-sherpa-json-result.cjs:8-12)。如果目标只是避免 JSON.parse 崩溃,这个行为需要明确记录;如果目标是生成干净字幕,则应在写文件前删除或替换 C0 字符。

  5. [P2] 新增核心路径没有被 CI 覆盖,worker 实现也没有直接测试

    package.json:41 只注册了 test:sherpa-json,但 .github/workflows/ci.yml:36-61 没有运行它;当前 PR 的 GitHub 状态也是 no checks reported

    此外,qwenText.ts:12-109sherpa-worker.js:248-340 是两份手工复制的 sanitizer,现有 TypeScript 单测只覆盖前者,未覆盖生产 worker 的 segment 丢弃逻辑。两边未来漂移时,CI 不会发现。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants