From 32f90908ddf4503c3f272a50650e2290cd895fdd Mon Sep 17 00:00:00 2001 From: xiaosheng <73678111+xiaoshengbao@users.noreply.github.com> Date: Mon, 21 Sep 2026 22:58:19 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat(stt):=20=E6=8E=A5=E5=85=A5=E5=88=86?= =?UTF-8?q?=E6=A1=A3=E8=AF=AD=E9=9F=B3=E6=A8=A1=E5=9E=8B=E5=B9=B6=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E6=9C=AC=E6=9C=BA=E5=8A=9F=E8=83=BD=E9=AA=8C=E6=94=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 + desktop/package.json | 2 + desktop/scripts/build-backend.cjs | 11 + desktop/src/main.cjs | 4 +- desktop/tests/native-core-runtime.test.cjs | 3 +- docs/stt-benchmark-2026-09-21.md | 83 +++ docs/stt-integration-2026-09-21.md | 79 +++ docs/stt-model-upgrade-plan-2026-09-21.md | 116 ++++ frontend/components/SettingsDialog.vue | 44 +- frontend/components/chat/MessageContent.vue | 2 +- .../chat/VoiceTranscriptionSidebar.vue | 18 +- frontend/composables/chat/useChatMessages.js | 8 +- frontend/tests/voice-model-settings.test.mjs | 2 +- .../tests/voice-transcription-sidebar.test.js | 24 +- pyproject.toml | 15 + src/wechat_decrypt_tool/asr_backends.py | 178 ++++++ src/wechat_decrypt_tool/asr_models.py | 77 +++ src/wechat_decrypt_tool/asr_worker.py | 162 ++++++ .../resources/voice_models.json | 146 +++++ src/wechat_decrypt_tool/routers/chat_media.py | 14 +- .../voice_transcription.py | 196 ++++++- tests/test_asr_upgrade.py | 196 +++++++ tests/test_voice_transcription_manager.py | 5 +- tools/benchmark_stt_local.py | 230 ++++++++ uv.lock | 541 +++++++++++++++++- 25 files changed, 2083 insertions(+), 77 deletions(-) create mode 100644 docs/stt-benchmark-2026-09-21.md create mode 100644 docs/stt-integration-2026-09-21.md create mode 100644 docs/stt-model-upgrade-plan-2026-09-21.md create mode 100644 src/wechat_decrypt_tool/asr_backends.py create mode 100644 src/wechat_decrypt_tool/asr_models.py create mode 100644 src/wechat_decrypt_tool/asr_worker.py create mode 100644 src/wechat_decrypt_tool/resources/voice_models.json create mode 100644 tests/test_asr_upgrade.py create mode 100644 tools/benchmark_stt_local.py diff --git a/.gitignore b/.gitignore index 13098690..160c0887 100644 --- a/.gitignore +++ b/.gitignore @@ -121,3 +121,7 @@ pro-shots/ # 隔离验收数据库、真实聊天资料和本地状态快照不得进入版本库。 /tmp/deepagents-migration/ + +# 语音验收包含真实聊天音频、转写、模型权重及本地运行环境。 +/tmp/stt-benchmark-20260921/ +/tmp/stt-integration-20260921/ diff --git a/desktop/package.json b/desktop/package.json index fe18c30d..1d92093c 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -5,9 +5,11 @@ "main": "src/main.cjs", "scripts": { "dev": "node scripts/dev.cjs", + "dev:gpu": "cross-env WECHAT_TOOL_QWEN_GPU=1 node scripts/dev.cjs", "dev:static": "npm --prefix ../frontend run generate && cross-env WECHAT_TOOL_STATIC_UI=1 electron .", "build:ui": "npm --prefix ../frontend run generate && node scripts/copy-ui.cjs", "build:backend": "uv sync --no-editable --extra build --extra voice-transcription && node scripts/build-backend.cjs", + "build:backend:gpu": "uv sync --no-editable --extra build --extra voice-transcription --extra voice-transcription-gpu && node scripts/build-backend.cjs --qwen-gpu", "build:icon": "node scripts/build-icon.cjs", "build:mac:image-helper": "node scripts/build-macos-image-helper.cjs", "verify:mac:native": "node scripts/verify-macos-native.cjs --arch arm64 --require-host-arch", diff --git a/desktop/scripts/build-backend.cjs b/desktop/scripts/build-backend.cjs index de26e1eb..a0bb2797 100644 --- a/desktop/scripts/build-backend.cjs +++ b/desktop/scripts/build-backend.cjs @@ -652,11 +652,22 @@ function main() { "--collect-all", "opencc", "--collect-all", + "sherpa_onnx", + "--add-data", + pyInstallerAddData(path.join(repoRoot, "src/wechat_decrypt_tool/resources/voice_models.json"), "wechat_decrypt_tool/resources"), + "--collect-all", "watchfiles", ...aiPackagingArgs(repoRoot), entry, ]; + // CUDA/PyTorch 体积较大,仅在明确构建 Qwen GPU 版本时收集。 + if (process.argv.includes("--qwen-gpu")) { + args.splice(args.length - 1, 0, "--collect-all", "torch", "--collect-all", "transformers"); + } else { + args.splice(args.length - 1, 0, "--exclude-module", "torch", "--exclude-module", "transformers"); + } + if (process.platform === "win32") { args.splice( args.length - 1, diff --git a/desktop/src/main.cjs b/desktop/src/main.cjs index 810b3f57..9f6983f2 100644 --- a/desktop/src/main.cjs +++ b/desktop/src/main.cjs @@ -2174,7 +2174,9 @@ function startBackend() { // The desktop backend only needs runtime dependencies. Letting `uv run` // include the default dev group can block Electron startup on an unrelated // pytest/Pygments download before Python is even launched. - backendProc = spawn("uv", ["run", "--no-dev", "main.py"], { + const voiceExtras = ["--extra", "voice-transcription"]; + if (env.WECHAT_TOOL_QWEN_GPU === "1") voiceExtras.push("--extra", "voice-transcription-gpu"); + backendProc = spawn("uv", ["run", "--no-dev", ...voiceExtras, "main.py"], { cwd: repoRoot(), env, stdio: "inherit", diff --git a/desktop/tests/native-core-runtime.test.cjs b/desktop/tests/native-core-runtime.test.cjs index 0c0a0284..4d75f9c2 100644 --- a/desktop/tests/native-core-runtime.test.cjs +++ b/desktop/tests/native-core-runtime.test.cjs @@ -544,7 +544,8 @@ test("desktop startBackend clears legacy WCDB state and never starts the sidecar const startBackend = mainSource.match(/function startBackend\(\) \{([\s\S]*?)\n\}/)?.[1] || ""; assert.match(startBackend, /configureNativeCoreRuntime\(env\)/); assert.match(startBackend, /clearLegacyWcdbEnvironment\(env\)/); - assert.match(startBackend, /spawn\("uv", \["run", "--no-dev", "main\.py"\]/); + assert.match(startBackend, /spawn\("uv", \["run", "--no-dev", \.\.\.voiceExtras, "main\.py"\]/); + assert.match(startBackend, /voiceExtras = \["--extra", "voice-transcription"\]/); assert.match(startBackend, /PYTHONIOENCODING:\s*"utf-8"/); assert.match(startBackend, /WECHAT_TOOL_NODE_EXECUTABLE:\s*process\.execPath/); assert.match(startBackend, /WECHAT_TOOL_NODE_MODE:\s*"electron-run-as-node"/); diff --git a/docs/stt-benchmark-2026-09-21.md b/docs/stt-benchmark-2026-09-21.md new file mode 100644 index 00000000..273e8ec6 --- /dev/null +++ b/docs/stt-benchmark-2026-09-21.md @@ -0,0 +1,83 @@ +# 本机 STT 模型对照测试(2026-09-21) + +已完成 12 个运行配置。样本为项目已有的 20 条真实微信语音,共 202.54 秒、5 个会话。 + +## 对升级方案的修订 + +- **低配:优先 Zipformer CTC INT8。** 本批语音约 2.74 秒,Tiny 约 22.17 秒,快约 8.1 倍;与微信转写的差异率从 26.44% 降至 10.22%。CTC 进程峰值约 390 MiB,比 Tiny 的 279 MiB 高,不能宣称内存也更省。 +- **主流 CPU:Qwen3-ASR 0.6B ONNX INT4 值得接入。** 约 61.59 秒,相比 Medium CPU 的 163.63 秒快约 2.66 倍,差异率从 8.11% 降至 4.35%;代价是进程峰值从约 1.63 GiB 增至 3.65 GiB。建议先面向 16 GB 内存设备,4 GB 老电脑不以它作为默认。 +- **GPU:保留 Turbo 极速选项,增加 Qwen 质量优先选项。** Qwen 0.6B 为 38.57 秒、差异率 3.64%;1.7B 为 44.16 秒、2.94%;Turbo 为 7.87 秒、6.82%;原 Large v3 为 20.51 秒、6.93%。标准 Transformers 路径没有带来 GPU 提速,1.7B 是本批样本与参考最接近的配置,但耗时约为 Large v3 的 2.15 倍。 +- **不单列 Transducer 均衡档。** 本批结果 3.61 秒、差异率 11.16%,没有体现相对 CTC 的价值。20 条样本不足以判定它在其他数据上一定较差。 +- 这些结果足以调整工程接入顺序,不能证明真实准确率已经提升。下一步需要听原音校对参考,以及在真实低配设备上验收;当前只有本机 CPU 路径测试。 + +## 方法与适用边界 + +- 本机:Windows、Ryzen 5 5600X(6 核 12 线程)、32 GB 内存、RTX 4070 SUPER 12 GB,驱动 596.49。 +- 从 828 条已有微信转写的候选中,以固定种子 20260921 抽取。按 0.8–3、3–8、8–20、20–60 秒各选五条;实际最长时长见本地清单。仅有转写的语音会入选,存在选择偏差。 +- 统一解码为 16 kHz 单声道,所有模型使用完全相同的 WAV;音频和文本未发送到外部 ASR 服务。 +- 每模型独立进程、CPU 4 线程、单条串行,预热一条后随机顺序运行两轮。耗时是两轮热运行均值,包含特征提取和识别,不含 SILK 解码、下载、模型加载及应用界面开销。 +- 指定推理引擎使用 4 个 CPU 线程;这不等同于模拟低端 CPU,也不保证第三方库与整台电脑总共只有 4 个线程。部分测试期间后台仍在下载权重,速度作为本机初筛结果。 +- 原 Whisper 参数沿用项目:中文、beam_size=5、vad_filter=True、condition_on_previous_text=False。Qwen 指定中文、贪心解码;各模型按对应部署路径运行,并非相同架构/解码算法的微基准。 +- 差异率采用字符编辑距离 / 参考字符数;统一简繁、全半角、大小写,去空白和标点。参考是未人工校对的微信机器转写,因此该数值不能当作真实错误率,更不能用 100% 减去它声称准确率。 +- 不提供低配 CPU 的外推保证:本机仅在 CPU 路径上测试,并非 N100 或 4 GB 老电脑实测。样本量不足以证明所有方言、噪声和人名场景的整体提升。 +- 进程内存是 50 ms 采样的峰值工作集,包含推理库。Qwen 显存为 PyTorch 分配峰值;该值不等于整张显卡占用,不能与未测得显存的数据直接比较。 +- 测试用 faster-whisper 1.2.1 与项目一致;测试 CTranslate2 为 4.8.2,项目原环境为 4.8.1。其他依赖和模型 revision 已留档;因此这是沿用项目参数的独立环境对照,并非原应用端到端计时。 + +## 实测汇总 + +| 配置 | 203 秒音频耗时 | RTF↓ | 单条 P95 | 与微信转写差异率↓ | 峰值进程内存 | Qwen 分配显存峰值 | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | +| whisper-tiny-cpu | 22.17 s | 0.109 | 5.11 s | 26.44% | 279 MiB | 未测 | +| whisper-base-cpu | 21.95 s | 0.108 | 1.83 s | 17.04% | 407 MiB | 未测 | +| whisper-small-cpu | 57.80 s | 0.285 | 5.29 s | 10.58% | 593 MiB | 未测 | +| whisper-medium-cpu | 163.63 s | 0.808 | 13.75 s | 8.11% | 1674 MiB | 未测 | +| zipformer-ctc | 2.74 s | 0.014 | 0.34 s | 10.22% | 390 MiB | 未测 | +| zipformer-rnnt | 3.61 s | 0.018 | 0.43 s | 11.16% | 420 MiB | 未测 | +| qwen-onnx-cpu | 61.59 s | 0.304 | 7.87 s | 4.35% | 3737 MiB | 未测 | +| whisper-medium-cuda | 13.66 s | 0.067 | 1.52 s | 10.22% | 1607 MiB | 未测 | +| whisper-turbo-cuda | 7.87 s | 0.039 | 0.80 s | 6.82% | 1676 MiB | 未测 | +| whisper-large-v3-cuda | 20.51 s | 0.101 | 2.42 s | 6.93% | 3082 MiB | 未测 | +| qwen-06-cuda | 38.57 s | 0.190 | 4.61 s | 3.64% | 2401 MiB | 1.67 GiB | +| qwen-17-cuda | 44.16 s | 0.218 | 5.16 s | 2.94% | 4800 MiB | 4.01 GiB | + +RTF = 识别耗时 / 音频时长,越低越快。不同模型资源档位不代表质量必然单调上升。 + +每轮使用相同的随机顺序策略。差异率按第一轮输出计算;Tiny、Base 在两轮中分别有 2 条、1 条输出变化,其余配置是否变化可查原始记录。 + +## 部署中发现的问题 + +- Zipformer 的原始 ONNX 文件直接处理约 22 秒样本时,CTC 和 Transducer 均出现 Reshape 维度错误;两份原始失败日志保留在测试目录。表中结果是增加最多 15 秒、末段低能量切分后的配置,不能把它描述成无改动即可替换。 +- Qwen ONNX 上游示例硬编码的 system/user token ID 与下载模型的分词器不一致。测试入口改为用实际分词器编码提示模板;参考文本没有进入提示词。 +- ONNX CPU 候选是社区导出,官方 HF GPU 候选是另一套转换/运行路径,量化和预处理差异需要随发布版本固定。 +- Qwen CPU 特征提取与上游 PyTorch 实现做了同一条音频的数值核对:最大绝对差约 4.86e-5、平均绝对差约 3.28e-7;这验证了实现一致性,不等于量化质量验证。 + +## 冷启动参考 + +| 配置 | 加载阶段(含库导入) | 首条推理 | +| --- | ---: | ---: | +| whisper-tiny-cpu | 2.37 s | 1.43 s | +| whisper-base-cpu | 0.51 s | 6.21 s | +| whisper-small-cpu | 1.22 s | 1.83 s | +| whisper-medium-cpu | 3.13 s | 5.44 s | +| zipformer-ctc | 0.99 s | 0.02 s | +| zipformer-rnnt | 0.98 s | 0.05 s | +| qwen-onnx-cpu | 8.07 s | 2.49 s | +| whisper-medium-cuda | 2.11 s | 0.77 s | +| whisper-turbo-cuda | 2.16 s | 0.55 s | +| whisper-large-v3-cuda | 3.42 s | 0.71 s | +| qwen-06-cuda | 9.76 s | 1.24 s | +| qwen-17-cuda | 13.30 s | 1.07 s | + +首条采用同一短语音;未清空 Windows 文件缓存,因此不能视作重启电脑后的磁盘冷启动。 + +## 复现与证据 + +- 入口:`tools/benchmark_stt_local.py`。测试资产、环境版本、模型 revision、逐条结果位于 `tmp/stt-benchmark-20260921/`。 +- `summary.json` 不含聊天原文;`manifest.private.json`、`results/*.private.json` 和 `review.private.html` 含本地语音或转写,不纳入公开报告。 +- 抽样清单记录 WAV 的 SHA-256;全部 20 条音频、10 组模型资产的大小及 LFS 哈希已重新核对。12 个运行配置共 480 条推理记录,通过独立动态规划编辑距离复算;记录见 `verification.json`。 +- `model-lock.json` 记录实际下载的仓库、revision 和文件清单;`requirements-lock.txt` 留存测试环境依赖。项目 Turbo 的原仓库地址当前重定向至 `dropbox-dash/faster-whisper-large-v3-turbo`,测试使用该重定向目标。 +- 当前应用模型设置和项目主虚拟环境未更改;测试依赖安装在独立虚拟环境。 + +```powershell +tmp/stt-benchmark-20260921/venv/Scripts/python.exe tools/benchmark_stt_local.py --root tmp/stt-benchmark-20260921 --model qwen-06-cuda +``` diff --git a/docs/stt-integration-2026-09-21.md b/docs/stt-integration-2026-09-21.md new file mode 100644 index 00000000..80f21f5f --- /dev/null +++ b/docs/stt-integration-2026-09-21.md @@ -0,0 +1,79 @@ +# 语音识别升级接入说明 + +2026-09-21:源码已接入新模型,保留原 Whisper 模型 ID、用户选择及旧缓存。此次未生成或替换正式安装包。 + +## 软件中的入口 + +在设置的“语音识别模型”中下载、选择模型;聊天页的语音转写侧栏使用同一组选项。旧模型通过“兼容模型”展开,当前已选的旧模型始终可见。未安装的运行组件会显示原因,不能误选成可用模型。 + +| 档位 | 选项 | 运行设备 | +| --- | --- | --- | +| 低配极速 | Zipformer CTC INT8 | CPU;中英文、无标点 | +| 中配质量优先 | Qwen3-ASR 0.6B ONNX INT4 | CPU;建议 16 GB 内存 | +| GPU 速度优先 | 原 Whisper Turbo | NVIDIA GPU,保留原 CPU 回退逻辑 | +| GPU 质量优先 | Qwen3-ASR 0.6B / 1.7B | NVIDIA GPU,需单独的 Qwen GPU 运行组件 | + +CPU/GPU 版本是独立选项。选中新模型会设置匹配的设备;环境变量锁定设备时不会覆盖。Qwen GPU 失败会给出错误,不会悄悄切换另一模型。新后端单进程串行复用,避免批量并发创建多份模型;取消时终止工作进程,下一条任务可以重新加载。空闲 120 秒后进程自动释放。 + +本机四个新模型和 Turbo 的文件已安装到 `%APPDATA%/wechat-data-analysis-desktop/voice_models/`,新模型复制前已校验固定版本的 SHA-256。首次接入保留原选择;后续应用户要求在桌面应用验收,最终启用 Qwen3-ASR 1.7B GPU。 + +## 启动及构建 + +项目普通开发启动会安装 CPU 语音依赖。前端需要 Node 20.19+ 或 22.12+;本机系统 Node 18 太旧,本次测试和构建使用已存在的 Codex Node 运行时。使用符合版本要求的 Node 后,要启用本机已验证的 Qwen GPU,在仓库根目录运行: + +```powershell +npm --prefix desktop run dev:gpu +``` + +本机不更改系统 Node 也可以这样启动: + +```powershell +$env:WECHAT_TOOL_QWEN_GPU = '1' +& "$env:USERPROFILE/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin/node.exe" desktop/scripts/dev.cjs +``` + +也可以手动安装: + +```powershell +uv sync --extra voice-transcription +# 需要 Qwen GPU 时加上该扩展;Windows 锁定官方 PyTorch CUDA 12.8 索引。 +uv sync --extra voice-transcription --extra voice-transcription-gpu +``` + +`desktop` 的 `build:backend` 构建 CPU 版本,包含 sherpa-onnx 和新模型资产清单,排除 PyTorch/Transformers;`build:backend:gpu` 额外收集 Qwen GPU 组件。默认 `dist:win` 仍走 CPU 后端构建,不应据此宣称普通安装包已包含 Qwen GPU。完整 GPU 安装包需要沿用项目正式签名和原生核心构建流程,使用 GPU 后端构建产物。 + +## 文件及缓存 + +- `resources/voice_models.json` 固定 Hugging Face 仓库、revision、必要文件、大小和 SHA-256;下载后先校验,再原子发布。 +- `asr_models.py` 定义模型目录及能力;`asr_backends.py` 提供三种后端;`asr_worker.py` 隔离模型、内存、取消和 CUDA 探测。 +- 新模型缓存键包含模型 ID、revision、后端和缓存版本;旧 Whisper 缓存键保持原样,切换不会误用另一模型的文本。 +- 推理只读取本地权重和音频,工作进程启用 Hugging Face 离线模式。Zipformer 最长 15 秒、Qwen 最长 25 秒分段,优先在低能量位置切分。 +- Qwen ONNX 按实际 tokenizer 编码角色提示,避免社区示例的固定 token ID 不匹配;CPU 特征提取不依赖 PyTorch。 +- Windows Whisper CUDA 可以复用已安装 PyTorch 中的 CUDA 12 DLL,解决只有系统 CUDA 13 时的依赖缺失。 + +## 本机验收 + +通过项目正式 `VoiceTranscriptionService.transcribe_voice` 读取并解码 20 条真实 SILK,四模型共 80 次成功;写缓存和批量缓存查询均验证通过。缓存写入测试目录,没有改写原会话的转写缓存。音频总长 202.54 秒。 + +| 模型 | 20 条总耗时,含加载、解码和缓存 | 与微信机器参考的字符差异率 | +| --- | ---: | ---: | +| Zipformer CTC | 6.202 秒 | 10.58% | +| Qwen 0.6B CPU | 65.125 秒 | 4.47% | +| Qwen 0.6B GPU | 51.954 秒 | 3.53% | +| Qwen 1.7B GPU | 47.384 秒 | 2.82% | + +本表是单轮完整链路验收,与之前仅计推理的双轮基准计时不同,不能混算加速倍数。微信机器转写未经人工校对,差异率不是人工标注准确率;低配最低硬件、方言、噪声和长录音仍需更大语料验证。 + +回归结果:后端 132 通过、1 跳过;前端语音组件 63 通过;设置契约与桌面启动契约 28 通过;前端 Nuxt 生产静态构建成功。真实工作进程取消后约 81 毫秒完成回收,再次识别成功;Turbo 实际 CUDA 推理成功。NumPy 声学特征与上游 PyTorch 版本比较,最大绝对误差小于 0.0001。 + +界面机械检查仅发现原有进度条的 width 动画告警,没有新增告警。后续启动真实 Electron 开发应用完成桌面验收:设置中选择 Zipformer,聊天消息实际转写并显示结果;批量扫描可以启动和取消;Qwen 1.7B GPU 同样在聊天页转写成功,来源提示显示正确模型。修正了聊天来源提示残留的 Whisper 专属文案。 + +运行中的应用 HTTP API 对四个新模型各处理 3 条真实语音,并逐条验证缓存命中、模型 ID 与 CPU/GPU 设备。另将原有 20 条 SILK 复制到隔离测试账号,使用正式批量管理器完整转写:20/20 成功、失败 0,配置并发 4 时实际限制为 1;再次扫描 20/20 命中缓存。真实账号的全库扫描仅验证启动和取消,没有等待全部历史语音完成。正式安装包及其冻结工作进程尚未完成验收。 + +私人音频、参考文本、逐条输出和测试数据库保存在被 Git 忽略的 `tmp/stt-benchmark-20260921/` 与 `tmp/stt-integration-20260921/`;本文不包含会话内容。 + +## 独立 PR 基线复验 + +PR 基于上游 `main` 的 `2646cfdf`,只移入本次语音升级。上游尚无开发分支上的导出语音选项,因此没有带入相关导出界面和导出状态改动。 + +独立工作区后端相关用例为 131 通过、1 跳过、1 失败;唯一失败是 `test_export_option_is_wired_from_dialog_to_backend`,其要求的 `exportTranscribeVoice` 控件在上游不存在。已从未修改的 `origin/main` 提取该测试及其全部输入文件,独立复现相同断言失败;本 PR 不修改这项测试。语音组件 63 项、设置与桌面启动契约 28 项均通过。使用符合前端版本要求的 Node 重新安装锁定依赖后,Nuxt 生产静态构建成功,预渲染 34 个路由。 diff --git a/docs/stt-model-upgrade-plan-2026-09-21.md b/docs/stt-model-upgrade-plan-2026-09-21.md new file mode 100644 index 00000000..e93b168f --- /dev/null +++ b/docs/stt-model-upgrade-plan-2026-09-21.md @@ -0,0 +1,116 @@ +# 本地语音转文字模型升级方案 + +日期:2026-09-21。状态:已接入四个新模型及模型管理界面,完成本机真实 SILK 的正式服务验收;用户原有模型选择未自动更改。落地方式与验收范围见 [接入说明](stt-integration-2026-09-21.md),选型实测证据见 [本机测试报告](stt-benchmark-2026-09-21.md)。20 条机器参考样本不能代替发布前的人工准确率验收。下文的硬件自动推荐和更广语料验收属于后续目标,首版提供手动选型。 + +## 目标与结论 + +面向微信短语音,提供低配 CPU、主流 CPU、NVIDIA GPU 三档能力。新增 Zipformer 和 Qwen3-ASR,同时保留实测表现突出的 Whisper Turbo;用户已选模型不自动更换。 + +初版“所有档位换新”的方案经实测后调整:低配主推 Zipformer CTC,主流 CPU 增加 Qwen ONNX,GPU 将速度优先和质量优先分开。Transducer 在本次样本上比 CTC 更慢、与微信转写的差异更多,暂不单列为更高档。Qwen 0.6B 的 CPU/GPU 版本属于不同运行配置。 + +已确认的本机取舍:CTC 约 2.74 秒处理 202.54 秒语音;Qwen CPU 约 61.59 秒,比 Medium CPU 快约 2.66 倍,但峰值进程内存约 3.65 GiB;GPU Turbo 约 7.87 秒,Qwen 0.6B 约 38.57 秒,因此 Qwen 不能直接接替 Turbo 的极速定位。差异率只代表与微信机器转写的一致程度。 + +所有硬件建议都是待验收目标,不是已验证的最低配置。权重文件大小不等于运行内存,量化位数也不代表整条推理链都采用该精度。 + +## 模型与原有档位对应 + +| 新选项 | 对应旧选项 | 模型来源与版本 | 运行方式 | 已核实的主要文件大小 | 验收目标设备 | +| --- | --- | --- | --- | --- | --- | +| 低配极速 | Tiny、Base 的优先新候选;Small 保留兼容 | pkufool/zipformer-small,CTC INT8 | CPU,sherpa-onnx,必须增加分段 | ctc.int8.onnx 约 28.7 MB,另加词表 | 4–8 GB 内存、低功耗/老款 CPU 仍需另测 | +| CPU 质量优先 | Medium 的新候选 | andrewleech/qwen3-asr-0.6b-onnx,INT4 变体 | CPU,ONNX Runtime | 指定推理文件合计约 2.03 GB | 优先 16 GB 内存;本机进程峰值约 3.65 GiB | +| GPU 极速 | Turbo 保留 | faster-whisper-large-v3-turbo | CUDA,CTranslate2 FP16 | model.bin 约 1.62 GB,另加配置和词表 | 本机 12 GB 显存已测;更低显存仍需测 | +| GPU 质量优先 | 新增选项 | Qwen/Qwen3-ASR-0.6B-hf | CUDA,PyTorch / Transformers BF16 | model.safetensors 约 1.56 GB,另加配置和词表 | 本机分配显存峰值约 1.67 GiB;不是系统最低配置保证 | +| GPU 质量优先大模型 | Large v3 的新候选 | Qwen/Qwen3-ASR-1.7B-hf | CUDA,PyTorch / Transformers BF16 | model.safetensors 约 4.08 GB,另加配置和词表 | 本机分配显存峰值约 4.01 GiB,预留显存另算 | + +大小按十进制 MB/GB 表示,只包含所列权重及文件;不包含推理运行库。CPU 两档不应依赖 PyTorch 或 CUDA。Qwen GPU 档精度依据设备能力验证 BF16/FP16,不能沿用 CTranslate2 的 compute_type 逻辑。Zipformer Transducer 保留研究记录,不作为首批必上的独立档位。 + +### 选型依据和边界 + +- [Zipformer Small 模型卡](https://huggingface.co/pkufool/zipformer-small)提供中英文 CTC 和 Transducer 两种解码头;[文件列表](https://huggingface.co/pkufool/zipformer-small/tree/main)包含 INT8 文件。模型卡上 Transducer 在列出的测试集上比 CTC 更准确,但没有证明其在本项目中一定快于 Whisper。CTC 作为最低资源档、Transducer 作为均衡档,是待实测的工程选型。 +- 该 Zipformer 仓库建立于 2026-06-25,属于近期发布的模型资产;Zipformer 架构本身来自 2023 年,不能宣称是 2026 年新发明的架构。供应方[部署说明](https://pkufool.github.io/zipformer/en/deployment/)推荐 sherpa-onnx,但必须验证这里的具体导出文件和所锁定版本相容。 +- [Qwen 0.6B ONNX](https://huggingface.co/andrewleech/qwen3-asr-0.6b-onnx)是社区导出;INT4 主要用于解码器,编码器仍为 FP32。选用它是为了评估 CPU 上的资源与精度折中,不能将其他导出版本的性能数据直接套用。 +- [Qwen 0.6B HF](https://huggingface.co/Qwen/Qwen3-ASR-0.6B-hf)和[1.7B HF](https://huggingface.co/Qwen/Qwen3-ASR-1.7B-hf)是官方 Transformers 原生版本;Qwen3-ASR 属于 2026 年模型系列,HF 原生仓库于 2026 年 6 月建立。模型卡要求 Transformers >= 5.13.0。 +- Zipformer 低配档先按中英文能力展示;不能承诺 Qwen 同等的方言、多语言和热词能力。低配档标点需要单独评估,首版允许提供无标点文本,不能为了补标点默认加载大型语言模型。 +- 不将 SenseVoiceSmall 或 BELLE 作为本次“新模型”主线:它们仍可用作评测对照,但原始模型属于 2024 年。Fun-ASR-Nano、FireRedASR2 作为后续中文专项候选,首版避免引入更多推理框架。 + +## 默认推荐规则 + +1. 新安装首次打开模型设置时,根据可用内存、CPU、GPU 能力推荐一个档位,用户点击下载后才下载资产。机器总内存不能单独决定推荐结果。 +2. 低配优先验证“低配极速”CTC。首版不把 Qwen 作为所有机器统一默认,也不按模型文件体积推算运行内存。 +3. 16 GB CPU 机器提供“CPU 质量优先”,展示本机测试环境、耗时和内存;不能把 5600X 的速度直接套用到低功耗 CPU。 +4. GPU 可用时保留 Turbo 极速路径;Qwen 作为质量优先的可选路径,说明其标准 Transformers 运行方式在本机更慢。显存不足时减小并发、分段或提示切换已安装的较小模型。 +5. Qwen 0.6B 的 CPU/GPU 版本在界面中说明为同系列不同运行方式。模型卡不使用“最高准确率”等未经项目评测支持的绝对标签。 + +## 项目改造范围 + +当前 voice_transcription.py 将目录校验、WhisperModel 加载、transcribe 参数和 CPU 回退都绑定到 faster-whisper;模型列表有 Tiny、Base、Small、Medium、Large v3、Turbo 六项。pyproject.toml 已包含 onnxruntime 和 tokenizers,但没有 Qwen 所需的 PyTorch / Transformers。 + +### 一、推理接口 + +提取独立 ASR 后端接口:load、transcribe、unload、capabilities。统一输出文本、时长、检测语言(允许未知)、实际模型标识、后端版本、实际设备和耗时。 + +- 保留 WhisperBackend 处理旧模型。 +- 增加 ZipformerBackend:CTC 和 Transducer 共用模型管理,分别使用正确的特征提取及解码器。 +- 增加 QwenOnnxBackend:处理音频特征、提示词、KV cache、逐步解码和取消;不能只调用 ONNX 文件一次就当作完整识别。 +- 增加 QwenTransformersBackend:按官方 HF 接口加载 0.6B / 1.7B,关闭训练行为,规范输出中的语言标记和文本。 + +微信 SILK 解码、任务排队、进度、转写缓存及前端结果展示继续复用。音频统一到后端要求的单声道 16 kHz 格式,各后端的特征提取不可混用。 + +### 二、模型资产与配置 + +将固定 Whisper 文件白名单改为逐模型清单,字段至少包括 modelId、backend、repoId、revision、files、文件哈希、量化方式、支持设备和语言。 + +建议新 ID:zipformer-small-ctc-int8、zipformer-small-rnnt-int8、qwen3-asr-06b-onnx-int4、qwen3-asr-06b-hf、qwen3-asr-17b-hf。 + +- 精确下载指定变体所需文件,禁止整仓下载不同精度和训练检查点。 +- 下载完成校验后再原子发布目录,继续支持取消、重试、删除和占用保护。 +- 新增通用 ASR 配置;兼容旧 WECHAT_TOOL_WHISPER_* 环境变量,旧变量仍指向旧后端,不暗中重解释。 +- 模型 ID、版本和量化变体参与缓存身份;不能把原来的 medium ID 指向 Qwen 后继续读取 Whisper 结果。 +- 旧缓存和模型不删除。已安装旧模型可在“旧版模型”区域查看、使用和主动移除。 + +### 三、低配运行与 GPU 依赖 + +- CPU 默认一次只识别一条,线程数从 2 开始按设备调整,避免与任务并发相乘造成过载。 +- 采用独立推理进程,任务间复用模型;空闲后卸载,取消或异常时可终止工作进程释放内存。 +- 音频分段并限制输出长度,测试静音、尾音、重复输出和跨段文字拼接。 +- GPU 推理依赖按需安装或作为独立运行包发布;普通 CPU 安装包不强制包含 PyTorch/CUDA。 +- 当前 CTranslate2 的 CUDA 探测不能证明 PyTorch CUDA 可用;各后端分别探测。 +- GPU 故障不能直接沿用当前“同模型 CPU int8”回退逻辑。只在事先允许且模型已安装时切换明确的 CPU 档,并显示实际使用模型;否则提供可操作错误,不自动下载另一个大模型。 +- Windows 为第一验收平台;macOS/Linux 的轮子、算子兼容和打包分别验证。项目 macOS ONNX Runtime 版本不同,不能按 Windows 测试结果宣称全平台可用。 + +## 实施顺序与验收 + +### 第一步:最小评测,确定名单 + +独立评测入口已实现为 `tools/benchmark_stt_local.py`,在固定版本上进行 20 条真实语音初筛。具体结果及限制见测试报告;以下更大规模人工评测仍是发布前的下一阶段。 + +测试集至少覆盖 100 条、3–60 秒的短语音:普通话、带口音普通话、中英混合、方言、嘈杂、近静音、人名数字和语速较快的内容。公共样本可先跑;微信样本在明确选定范围后本地处理。 + +记录中文 CER、英文 WER、人名/数字错误、无语音误识别、冷启动、热启动 p50/p95 延迟、峰值进程树内存、显存、取消耗时和长批次内存增长。标点单独评价。资源分档不等于质量严格单调,跨模型质量必须由同一套数据确认。 + +建议发布门槛:低配档在目标机与现有相应档比较不明显降低准确率,同时降低占用或耗时;Qwen 中高档在主要中文场景体现可量化收益。达不到条件的候选不作为新默认。4 GB 极低配必须单独测试,不能以 8 GB 结果代替。 + +### 第二步:上线低配 CPU 档 + +完成后端接口、Zipformer CTC、模型下载管理和原有设置兼容。修复较长输入的分段问题,再覆盖低配基本需求;Transducer 暂不作为首版必需项。 + +### 第三步:上线 Qwen CPU/GPU + +验证 Qwen ONNX INT4 中文量化退化、Windows 算子兼容和实际内存;再加入官方 HF 0.6B/1.7B GPU 档及可选运行包。若社区 ONNX 版本不通过,保留 Zipformer 默认,不发布未经验证的 CPU Qwen。 + +### 第四步:迁移展示与发布 + +设置页按实测价值展示配置、实际模型名、下载大小、语言和建议设备。Turbo 保留在主要选项中;其他原模型保留兼容入口。已选模型继续生效,升级仅提示可选的新档位,避免为了凑齐档位强行增加模型。 + +必要回归:完全离线、损坏/中断下载、模型删除与正在推理冲突、取消、切换模型、缓存隔离、GPU 不可用/显存不足、旧配置启动、桌面打包启动。验收完成后才把“候选”改为“正式推荐”。 + +## 调研版本记录 + +以下为本次核查到的 HF revision,仅供实施评测锁定与复现;发布前需要按验收版本生成完整文件清单与哈希。 + +| 仓库 | revision | +| --- | --- | +| pkufool/zipformer-small | e1764e4e54504721900d1e6b99c746e7331980af | +| andrewleech/qwen3-asr-0.6b-onnx | 4fc24a1402e74db89c4d2ef256875e71680128c4 | +| Qwen/Qwen3-ASR-0.6B-hf | 7f1569a48a89f3e3f4dc3a5c9d28bddd903bc76c | +| Qwen/Qwen3-ASR-1.7B-hf | bcd2b5b7f32b480ab5790554cfa8347f246a14f3 | diff --git a/frontend/components/SettingsDialog.vue b/frontend/components/SettingsDialog.vue index 98f0e6b7..023e5f2a 100644 --- a/frontend/components/SettingsDialog.vue +++ b/frontend/components/SettingsDialog.vue @@ -263,7 +263,7 @@
推理设备
-
CPU 兼容所有设备;NVIDIA GPU 使用 CUDA 加速,初始化失败会自动回退 CPU。
+
选择新模型时会匹配所需设备。Whisper 支持 GPU 失败后回退 CPU;Qwen GPU 不会自动切换模型。
-
正在检测本地 Whisper 与 CUDA 状态...
+
正在检测语音模型与运行环境...