47 lines
5.0 KiB
Markdown
47 lines
5.0 KiB
Markdown
# 本机离线语音转写实施与验证
|
||
|
||
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 官方项目](https://github.com/SYSTRAN/faster-whisper) 的 CPU int8 和本地模型方式;额外要求完整 tokenizer 文件存在,以防缺文件时触发在线 tokenizer 获取。模型来自 [固定公开模型 revision](https://huggingface.co/Systran/faster-whisper-base/tree/5ff83050b029cf4730c8c89ce0c835b2ea15c6f9),不是首次启动联网下载。
|
||
|
||
SILK 解码用 [silk-python 0.2.7](https://pypi.org/project/silk-python/0.2.7/) 的 CPython 3.12 Windows wheel,导入名为 `pysilk`。AMR/WAV 由 PyAV 在内存字节流中解码,不运行外部 ffmpeg,也不把文件路径或 URL 传给解码器。
|