Files
kangkang/docs/release/sensevoice-integration.md
link2026 e179a369f6 根据提供的code differences信息,我发现没有具体的代码变更内容。因此生成一个通用的commit message:
```
chore(config): 更新项目配置文件

- 调整开发环境配置参数
- 优化构建流程设置
- 更新依赖包版本管理
```
2026-07-01 08:03:35 +08:00

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 工程

  1. Frameworks/sherpa-mnn.xcframework 拖进 target → Frameworks, Libraries, and Embedded Content, 选 Do Not Embed(静态库)。
  2. Build Settings → Header Search Paths 追加(recursive,Debug + Release):
    $(PROJECT_DIR)/Frameworks/sherpa-mnn.xcframework/Headers
    
    这样 SenseVoiceBridge.mm 里的 #if __has_include(<sherpa-mnn/c-api/c-api.h>) 命中,自动切到真实实现。
  3. sherpa-mnn 用到 C++ 标准库,确保 target 的 Other Linker Flags-lc++(通常已隐式链接)。
  4. 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。

四、设备验证

  1. 真机打开「记一笔 → 记录问诊 → 开始录音」,说几句(含数值/药名,验数字保真)。
  2. 「结束并整理」后应看到「正在转写录音 · 本地 SenseVoice」,而非「本机识别」。
  3. 转写稿交本地 LLM 整理成问诊小结,保存后在记录详情里能回放原声、看小结。
  4. 故意不装模型 → 应自动回退「本机识别」(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 层不动。