4.6 KiB
4.6 KiB
记录问诊 · 本地 SenseVoice 转写接入
「记录问诊」录完整段录音后,在本机用 SenseVoice(经 sherpa-mnn 跑在 MNN 后端)离线转写成文字, 再交本地 LLM 整理成问诊小结。全程本机、无网络。
代码已全部就位且默认编译通过:未接入 sherpa-mnn 时
SenseVoiceBridge走桩,问诊自动回退系统端侧识别(SFSpeech)。 本文档是把真实 SenseVoice 跑起来的「构建 + 设备验证」步骤。
架构(按本项目模块边界 §3.1)
ConsultationSheet(UI)
→ 录音(ConsultationRecorder,m4a 落 Vault)
→ SenseVoiceASRService.transcribe(file) // 离线整段转写,无实时字幕
→ AIRuntime.runExclusiveForASR { … } // 进推理闸门 + 卸常驻 LLM/VL 腾内存(防 OOM)
→ SenseVoiceBridge(ObjC++) // sherpa-mnn C-API
→ sherpa-mnn → MNN 后端(CPU/SME2)
→ DiaryAssistService.organizeConsultation(text) // 本地 LLM 整理成问诊小结
→ 存为带「问诊」tag 的 DiaryEntry(录音挂 audio Asset)
- 引擎/模型未就绪 → 自动回退 SFSpeech;再不行 → 手动文字录入。任何一步都不卡死(红线 #5)。
- SenseVoice 是非流式:录音中只显示声纹动效,不显示实时字幕,结束后整段转写。
一、构建 sherpa-mnn.xcframework
MNN_SRC=/Users/xuhuayong/apps/MNN-src sh scripts/build-sherpa-mnn-xcframework.sh
- 复用 MNN 源码自带的
apps/frameworks/sherpa-mnn/build-ios.sh,产出Frameworks/sherpa-mnn.xcframework(已含 device arm64 + simulator,libtool 合并好的静态库 +Headers/)。 - 若 sherpa cmake 报找不到 MNN:先
sh scripts/build-mnn-xcframework.sh构建 MNN, 再export MNN_LIB_DIR=<含 libMNN.a + include 的目录>重跑。 - Apple Silicon 上若不需要 Intel 模拟器,可在
build-ios.sh里去掉simulator_x86_64段加速。
二、加进 Xcode 工程
- 把
Frameworks/sherpa-mnn.xcframework拖进 target → Frameworks, Libraries, and Embedded Content, 选 Do Not Embed(静态库)。 - Build Settings → Header Search Paths 追加(recursive,Debug + Release):
这样
$(PROJECT_DIR)/Frameworks/sherpa-mnn.xcframework/HeadersSenseVoiceBridge.mm里的#if __has_include(<sherpa-mnn/c-api/c-api.h>)命中,自动切到真实实现。 - sherpa-mnn 用到 C++ 标准库,确保 target 的 Other Linker Flags 含
-lc++(通常已隐式链接)。 MNN.xcframework仍需在工程里(sherpa-mnn 依赖它)。即便主 LLM 已切 Gemma-3n/MLX, 不要从工程移除 MNN.xcframework,否则问诊转写退回 SFSpeech。
工程用 PBXFileSystemSynchronizedRootGroup:
SenseVoiceBridge.{h,mm}放在康康/AI/MNN/下已自动参与编译, 桥接已在康康/康康-Bridging-Header.h暴露给 Swift,无需手改 pbxproj。
三、转换并安装 SenseVoice 模型
MNN_SRC=/Users/xuhuayong/apps/MNN-src sh scripts/convert-sensevoice-mnn.sh
产出 build/SenseVoice/{model.mnn, tokens.txt}(转的是 fp32 model.onnx,8bit 权重量化;勿转 int8 版)。
安装到沙盒 Application Support/Models/SenseVoice/:
- 模拟器:拷到
~/Library/Developer/CoreSimulator/Devices/<id>/data/Containers/Data/Application/<id>/Library/Application Support/Models/SenseVoice/ - 真机:经「我的 · 模型管理」旁路导入,或用调试构建预拷进沙盒。
就位判定:SenseVoiceASRService.isModelInstalled(model.mnn + tokens.txt 都在)。
引擎+模型都就绪后 SenseVoiceASRService.isAvailable == true,问诊即走 SenseVoice。
四、设备验证
- 真机打开「记一笔 → 记录问诊 → 开始录音」,说几句(含数值/药名,验数字保真)。
- 「结束并整理」后应看到「正在转写录音 · 本地 SenseVoice」,而非「本机识别」。
- 转写稿交本地 LLM 整理成问诊小结,保存后在记录详情里能回放原声、看小结。
- 故意不装模型 → 应自动回退「本机识别」(SFSpeech),功能仍可用。
备注:与 Gemma-3n/MLX 主线的关系
- 转写(SenseVoice/MNN)与文本生成(Gemma-3n/MLX)互相独立:ASR 用 MNN 后端,LLM 整理用 MLX。
二者经
AIRuntime闸门串行,不会同时常驻内存(§3.1)。 - 这是目前工程里唯一仍依赖 MNN 的路径。若决定彻底移除 MNN,问诊转写会退回 SFSpeech;
桥接的
__has_include守卫保证那时仍能编译。 - 若希望改走 sherpa-onnx(onnxruntime,不依赖 MNN),只需把
SenseVoiceBridge.mm里的SherpaMnn*C-API 换成对应的SherpaOnnx*(结构同名),其余 Swift 层不动。