Files
kefu/deploy/review-voice-20260918/asr-implementation-report.md
T
2026-09-21 10:34:06 +08:00

5.0 KiB
Raw Blame History

本机离线语音转写实施与验证

2026-09-18。范围是客户端收到语音后的本机转写基础设施;聊天读取、未回复批次选择以及向模型传递由主线程集成。没有识别真实客户语音、发送消息或修改客户配置。

实现与文件

C:/wechat_rpa 和 C:/kefu/wechat_rpa 已同步以下内容:

  • voice_transcription.py:绝对路径及账号根目录约束、SILK/AMR/WAV 解码、CPU int8 离线识别、单工作线程、有界队列、消息与音频 SHA256 隔离、失败退避和停止清理。
  • test_voice_transcription.py:26 项合成回归;真实解码使用人工生成音频,模型对象边界使用替身。
  • packaging_asr.py:明确收集模型文件、VAD 模型、CTranslate2 DLL、PyAV DLL、Python 模块及包元数据。
  • requirements.txt、wechat_rpa.spec:只追加 ASR 依赖与打包输入,保留两目录原有打包差异。
  • assets/asr/faster-whisper-base/:固定 revision 的公开模型及逐文件摘要、许可证。

原有 requirements/spec 备份在 before-asr/。依赖只安装到 C:/wechat_rpa/.build-venv,没有改全局 Python。

调用契约

VoiceTranscriptionService(model_dir=None, allowed_roots=(), max_pending=32, retry_seconds=30.0, recognizer=None, max_cache=128)。

request(account, conv_id, server_id, audio_path) 只校验文件元数据并排队,返回 status=pending|ready|error、text、error_code、message、retry_at、audio_sha256。成功另外返回 duration_seconds 和 source=local_asr。调用者重复查询相同消息即可读取完成结果;close() 停止新任务并清空结果。

缓存键同时包含账号、会话、服务端消息 ID 和音频 SHA256;文件内容变化会让旧结果失效。转写文本仅在进程内暂存,不写回企微数据库,不记录音频正文或转写日志。服务允许的音频上限是 8 MiB、120 秒;不支持的格式、空音频、低置信度或空识别均明确失败,不生成猜测文字。

已执行验证

  1. Python 3.12 原生 unittest test_voice_transcription -v:26/26 通过,包含并发快速排队、账号/会话边界、关闭时不复活缓存、文件变化、失败退避、格式/大小/时长和低置信度边界。
  2. verify_real_offline_asr.py:系统语音合成器生成同一段非客户中文语音,再转换为 SILK 和 AMR;实际本机模型全部转写成功。整个识别过程拦截 socket 与 DNS,网络尝试为 0。详情在 real-offline-asr.json。
  3. WAV/SILK/AMR 语音约 9.5 秒;实测转写耗时分别约 4.27/2.19/2.30 秒,均保留“下午三点”“预约流程”的核心意思。这只是该机器该合成样本的耗时,不承诺一般性能。
  4. 三种格式都把合成语音中的“识别”识为同音“时别”,证明输出并非逐字完全准确;保留原模型结果,不做编造性修正。未验证真实客户口音、背景噪声或业务术语准确率。
  5. 实际本地缓存只做元数据及 16 字节文件头审计:5 条语音的引用均唯一映射到本账号缓存中的 .silk 文件,5/5 文件头为 SILK,没有对这些文件解码或转写。详情 voice-reference-counts.json。
  6. 打包输入检查确认模型/VAD 与 CTranslate2、PyAV 原生库已被收集;完整安装包运行仍以冻结验证结果为准。

冻结包验证已完成

verify_frozen_offline_asr.py 已在最终 v1.4.27 dist 执行并通过(exit 0),总耗时 6.14 秒。脚本以构建 Python -I -S 启动,归档读取结束后去除构建环境 site-packages 和归档辅助模块;voice_transcription、NumPy、PyAV、pysilk、CTranslate2、faster-whisper、ONNX Runtime、tokenizers 全部确认从该 EXE 的 PYZ 导入。原生扩展、实际加载的 CTranslate2/AV/NumPy/ONNX DLL 和固定模型均来自目标 _internal,开发环境依赖泄漏、越界 DLL、网络及外部动作计数均为 0。

三个约 9.5 秒的人工语音样本均 ready,预约和三点语义检查通过:WAV 2.625 秒,SILK 1.485 秒,AMR 1.703 秒。详情是 frozen-offline-asr.json/.log。测试只覆盖人工语音,不代表真实客户音频准确率。

该次验证对应 EXE SHA256:a1387fbd74ba38ee86bba7204d8d05124376c6dc7912e8243e7f0b4622ac28d7。早前 frozen-asr-build-blocked.json 仅记录首次构建磁盘不足的历史阻塞,本次最终构建和识别验证已成功。

依赖依据

采用 SYSTRAN/faster-whisper 官方项目 的 CPU int8 和本地模型方式;额外要求完整 tokenizer 文件存在,以防缺文件时触发在线 tokenizer 获取。模型来自 固定公开模型 revision,不是首次启动联网下载。

SILK 解码用 silk-python 0.2.7 的 CPython 3.12 Windows wheel,导入名为 pysilk。AMR/WAV 由 PyAV 在内存字节流中解码,不运行外部 ffmpeg,也不把文件路径或 URL 传给解码器。