# VoiceSnap 多语言与多模型总体研发规划 状态:研发规划稿 日期:2026-06-01 范围:语言检测、模型 profile 抽象、多模型下载/切换/删除、首批四模型接入 ## 1. 当前技术现状 当前实现基本是单模型架构,核心路径围绕 SenseVoice 写死。 已知硬编码点: - 模型目录固定为 `models/sensevoice`。 - 模型文件固定为 `model.int8.onnx` 或 `model.onnx`。 - `tokens.txt` 固定在同一个 SenseVoice 目录下。 - engine 初始化固定写入 `config.ModelConfig.SenseVoice.Model`。 - 下载器只识别 `sherpa-onnx-sense-voice...` 并重命名为 `sensevoice`。 - 配置项只有一个模型下载 URL 和 fallback URL。 要支持: ```text 中文默认 SenseVoice 中文高级 Qwen3-ASR English 默认 Moonshine English 高级 Parakeet ``` 必须引入模型 profile 抽象。 ## 2. 研发目标 本轮研发目标: 1. 从单 SenseVoice 模型改为多模型 profile。 2. 支持语言版本和默认模型映射。 3. 支持按系统语言首次选择默认语言和默认模型。 4. 支持模型安装状态管理。 5. 支持下载、校验、切换、删除模型。 6. 支持四个首批模型: - SenseVoice - Qwen3-ASR - Moonshine English - Parakeet 7. 保持现有录音、热键、上屏、历史、用户词库逻辑可复用。 ## 3. 核心架构设计 新增三个核心概念: ```text LanguageProfile ModelProfile ModelInstallState ``` ## 4. LanguageProfile `LanguageProfile` 表示产品语言版本和默认识别方向。 建议结构: ```go type LanguageProfile struct { ID string DisplayName string UILocale string SystemMatchers []string DefaultModelID string UpgradeModelIDs []string } ``` 首批配置: ```text zh-CN - displayName: 中文 - uiLocale: zh-CN - systemMatchers: zh, zh-CN, zh-Hans, zh-Hant - defaultModelId: sensevoice-zh - upgradeModelIds: qwen3-asr-0.6b en - displayName: English - uiLocale: en - systemMatchers: en, en-US, en-GB - defaultModelId: moonshine-en - upgradeModelIds: parakeet-en ``` 未来可扩展: ```text yue ja ko fr de es ... ``` 粤语应使用独立 `languageId`,不要简单归入中文。 ## 5. ModelProfile `ModelProfile` 表示一个可安装、可切换的 ASR 模型。 建议结构: ```go type ModelProfile struct { ID string DisplayName string BackendKind string Tier string LanguageIDs []string RecommendedFor []string Description string ApproxSize string DownloadURLs []string InstallDirName string RequiredFiles []string ProviderOrder []string NumThreads int } ``` 字段说明: | 字段 | 说明 | |---|---| | `ID` | 稳定模型 ID,不能随显示名变化 | | `BackendKind` | 决定 sherpa config builder | | `Tier` | `default` 或 `advanced` | | `LanguageIDs` | 模型支持的语言 | | `RecommendedFor` | 当前产品推荐使用的语言 | | `DownloadURLs` | 主下载和备用下载 | | `RequiredFiles` | 安装完成必须存在的文件 | | `ProviderOrder` | macOS 可为 `coreml,cpu` 或 `cpu` | ## 6. 首批 ModelProfile ### 6.1 SenseVoice ```text id: sensevoice-zh backendKind: sensevoice tier: default recommendedFor: zh-CN installDirName: sensevoice-zh requiredFiles: - model.int8.onnx 或 model.onnx - tokens.txt ``` sherpa config: ```go config.ModelConfig.SenseVoice.Model = modelPath config.ModelConfig.SenseVoice.UseInverseTextNormalization = 1 config.ModelConfig.Tokens = tokensPath ``` ### 6.2 Qwen3-ASR ```text id: qwen3-asr-0.6b backendKind: qwen3_asr tier: advanced recommendedFor: zh-CN, yue installDirName: qwen3-asr-0.6b requiredFiles: - conv_frontend.onnx - encoder.int8.onnx - decoder.int8.onnx - tokenizer/merges.txt - tokenizer/vocab.json ``` sherpa config: ```go config.ModelConfig.Qwen3ASR.ConvFrontend = convFrontendPath config.ModelConfig.Qwen3ASR.Encoder = encoderPath config.ModelConfig.Qwen3ASR.Decoder = decoderPath config.ModelConfig.Qwen3ASR.Tokenizer = tokenizerDir ``` 备注: - Qwen3-ASR 体积明显大于 SenseVoice。 - 只作为用户主动选择的高级模型。 - Qwen3-ASR 支持多语言和多种中文方言,后续可以作为更多语言的高级模型复用。 ### 6.3 Moonshine English ```text id: moonshine-en backendKind: moonshine tier: default recommendedFor: en installDirName: moonshine-en requiredFiles: - encoder_model.ort - decoder_model_merged.ort - tokens.txt ``` sherpa config: ```go config.ModelConfig.Moonshine.Encoder = encoderPath config.ModelConfig.Moonshine.MergedDecoder = mergedDecoderPath config.ModelConfig.Tokens = tokensPath ``` 备注: - 使用 sherpa-onnx Moonshine English quantized 路线。 - 不在第一阶段接 Moonshine Voice framework。 - 不做实时 partial transcript。 ### 6.4 Parakeet ```text id: parakeet-en backendKind: nemo_transducer tier: advanced recommendedFor: en installDirName: parakeet-en requiredFiles: - encoder.int8.onnx - decoder.int8.onnx - joiner.int8.onnx - tokens.txt ``` sherpa config: ```go config.ModelConfig.Transducer.Encoder = encoderPath config.ModelConfig.Transducer.Decoder = decoderPath config.ModelConfig.Transducer.Joiner = joinerPath config.ModelConfig.Tokens = tokensPath config.ModelConfig.ModelType = "nemo_transducer" ``` 备注: - 作为 English 高级模型。 - 体积大于 Moonshine English。 - 用户主动下载后可切换。 ## 7. 配置文件扩展 当前 config 需要新增字段。 建议: ```go type Config struct { // existing fields... LanguageMode string `json:"LanguageMode"` // auto | manual LanguageID string `json:"LanguageID"` // zh-CN | en | yue ... SelectedModelID string `json:"SelectedModelID"` // sensevoice-zh | moonshine-en ... } ``` 默认逻辑: ```text LanguageMode = auto LanguageID = detectSystemLanguage() SelectedModelID = defaultModelFor(LanguageID) ``` 如果用户手动切换语言: ```text LanguageMode = manual LanguageID = userSelectedLanguage ``` 切换语言时: - 如果当前模型仍适合该语言,可以不强制切换。 - 如果当前模型不适合该语言,提示用户切换到推荐默认模型。 - 不自动删除已安装模型。 ## 8. 模型安装状态 建议新增模型状态文件: ```text models/state.json ``` 示例: ```json { "installedModels": { "sensevoice-zh": { "installedAt": 1780120000000, "version": "2025.09.09", "path": "models/sensevoice-zh" }, "moonshine-en": { "installedAt": 1780120000000, "version": "2026.02.27", "path": "models/moonshine-en" } } } ``` 也可以用每个模型目录下的 manifest: ```text models//manifest.json ``` 推荐两者都支持: - `models/state.json` 用于快速列出状态。 - `models//manifest.json` 用于校验单个模型目录。 ## 9. 路径规划 旧结构: ```text models/sensevoice ``` 新结构: ```text models/ sensevoice-zh/ qwen3-asr-0.6b/ moonshine-en/ parakeet-en/ state.json ``` 兼容策略: - 如果发现旧目录 `models/sensevoice`,迁移或识别为 `sensevoice-zh`。 - 不覆盖用户已有模型。 - 迁移失败时保留旧目录,并提示重新下载。 ## 10. 下载器改造 当前下载器写死 SenseVoice 解压逻辑,需要改成通用下载器。 目标接口: ```go func DownloadModel(profile ModelProfile, progress ProgressCallback) error ``` 流程: 1. 创建临时下载目录。 2. 按 `profile.DownloadURLs` 顺序下载。 3. 解压到临时目录。 4. 在解压结果中定位 `RequiredFiles`。 5. 移动到 `models/`。 6. 写入 manifest。 7. 更新 `models/state.json`。 8. 清理临时文件。 必须保证: - 下载失败不破坏已安装模型。 - 解压失败不破坏已安装模型。 - 校验失败不破坏已安装模型。 - 切换模型失败不改变当前 active model。 ## 11. EngineFactory 改造 新增: ```go func New(profileID string) (Engine, error) ``` 或: ```go func NewWithProfile(profile ModelProfile) (Engine, error) ``` 内部根据 `BackendKind` 分发: ```go switch profile.BackendKind { case "sensevoice": buildSenseVoiceConfig(profile) case "moonshine": buildMoonshineConfig(profile) case "nemo_transducer": buildNemoTransducerConfig(profile) case "qwen3_asr": buildQwen3ASRConfig(profile) } ``` 平台文件 `engine_darwin.go`、`engine_windows.go`、`engine_linux.go` 保留 provider 差异,但不要再写死 SenseVoice。 ## 12. 服务层改造 `EngineService` 增加: ```text ListLanguages() GetCurrentLanguage() SetLanguage(languageId) ListModels() GetCurrentModel() ModelExists(modelId) DownloadModel(modelId) UseModel(modelId) DeleteModel(modelId) ``` 注意: - `DeleteModel(currentModelId)` 必须拒绝。 - `UseModel(modelId)` 要求模型已安装。 - `DownloadModel(modelId)` 成功后不一定自动使用,产品可决定。 - 如果是用户点击“下载并使用”,前端可以下载成功后调用 `UseModel`。 ## 13. 前端改造 设置页增加两个页面或区域: ```text Language Recognition Model ``` ### 13.1 Language 页面 第一阶段显示: ```text Auto 中文 English ``` 后续扩展为多语言列表。 ### 13.2 Recognition Model 页面 模型卡片字段: - 名称 - 适合语言 - 特点说明 - 大小 - 状态 - 操作按钮 状态: ```text Not Installed Downloading Installed In Use ``` 操作: ```text Download Use Delete ``` ## 14. 实现阶段 ### Phase 1:模型 profile 基础设施 目标:不改变用户体验,先把现有 SenseVoice 放进 profile 架构。 交付: - `ModelProfile`。 - `LanguageProfile`。 - `ModelRegistry`。 - `LanguageRegistry`。 - `sensevoice-zh` profile。 - `EngineFactory` 初步抽象。 - 现有 SenseVoice 路径兼容。 验收: - 当前中文路径功能不退化。 - 现有模型仍能加载。 - 当前配置仍能兼容。 ### Phase 2:语言检测和默认模型 目标:首次启动按系统语言选择语言版本和默认模型。 交付: - 系统语言检测。 - `LanguageMode`、`LanguageID`、`SelectedModelID` 配置字段。 - 中文默认 `sensevoice-zh`。 - English 默认 `moonshine-en`。 - 未支持语言默认 English。 验收: - 中文系统默认中文 + SenseVoice。 - English 系统默认 English + Moonshine。 - 用户手动切换语言后保存。 ### Phase 3:Moonshine English 接入 目标:English 默认模型可用。 交付: - `moonshine-en` profile。 - Moonshine required files 校验。 - Moonshine config builder。 - Moonshine 下载/安装/加载。 - English UI 文案。 验收: - Moonshine 模型可下载。 - 下载后可初始化。 - 英文音频可识别。 - 用户词库、历史、上屏逻辑复用。 ### Phase 4:Parakeet 接入 目标:English 高级模型可选。 交付: - `parakeet-en` profile。 - NeMo transducer config builder。 - Parakeet required files 校验。 - Parakeet 下载/安装/切换/删除。 验收: - Moonshine 和 Parakeet 可在设置中切换。 - Parakeet 未安装时不能直接使用。 - Parakeet 下载后可使用。 - 删除 Parakeet 后可回到 Moonshine。 ### Phase 5:Qwen3-ASR 接入 目标:中文高级模型可选。 交付: - `qwen3-asr-0.6b` profile。 - Qwen3-ASR config builder。 - Qwen3-ASR tokenizer 目录校验。 - Qwen3-ASR 下载/安装/切换/删除。 验收: - SenseVoice 和 Qwen3-ASR 可切换。 - Qwen3-ASR 未安装时不能直接使用。 - 删除 Qwen3-ASR 后可回到 SenseVoice。 ### Phase 6:模型删除与恢复体验 目标:完善长期使用体验。 交付: - 当前模型删除保护。 - 删除确认。 - 模型磁盘占用展示。 - 模型损坏后重新下载。 - 模型切换失败回滚。 验收: - 不会删除当前模型。 - 删除未使用模型不影响设置和用户数据。 - 模型损坏时可重新下载。 ## 15. 测试计划 ### 15.1 单元测试 - 系统语言匹配。 - 默认模型映射。 - profile required files 校验。 - 模型状态读写。 - 旧配置兼容。 - 旧 `models/sensevoice` 兼容。 - 删除当前模型被拒绝。 - 未安装模型不能使用。 ### 15.2 集成测试 - 中文默认路径。 - English 默认路径。 - SenseVoice -> Qwen3-ASR 切换。 - Qwen3-ASR -> SenseVoice 回退。 - Moonshine -> Parakeet 切换。 - Parakeet -> Moonshine 回退。 - 下载失败不破坏当前模型。 - 初始化失败不改变当前模型。 ### 15.3 手工 QA - macOS 中文系统首次启动。 - macOS English 系统首次启动。 - 修改系统语言后首次启动。 - 用户手动切换语言。 - 用户下载高级模型。 - 用户删除非当前模型。 - 用户尝试删除当前模型。 ## 16. 主要风险 ### 16.1 模型体积 高级模型体积大。Qwen3-ASR 和 Parakeet 都不适合作为默认模型。 缓解: - 默认模型轻量。 - 高级模型主动下载。 - 下载前显示体积。 - 默认模型保留。 ### 16.2 模型包结构差异 四个模型文件结构不同。 缓解: - 用 `BackendKind` + `RequiredFiles`。 - 不再写死 `model.int8.onnx`。 ### 16.3 切换失败 模型下载成功不代表初始化成功。 缓解: - 切换前保留旧 recognizer。 - 新模型初始化成功后再替换 active engine。 - 失败时回滚。 ### 16.4 未来语言扩展 如果第一版把 UI 和模型写死为中英,会阻碍后续 30 种语言。 缓解: - 语言和模型均 profile 化。 - UI 从 registry 渲染。 - 不在业务逻辑里硬编码四个模型。 ### 16.5 许可证和分发 模型能否随 App 分发、是否需要用户单独下载,需要发布前确认。 缓解: - runtime JSON 不需要保存许可证字段。 - 但发布流程中必须做模型分发许可检查。 - 优先采用用户主动下载模型包的方式。 ## 17. 参考资料 - sherpa-onnx Moonshine v2 models: https://k2-fsa.github.io/sherpa/onnx/moonshine/models-v2.html - sherpa-onnx Parakeet NeMo transducer models: https://k2-fsa.github.io/sherpa/onnx/pretrained_models/offline-transducer/nemo-transducer-models.html - sherpa-onnx Qwen3-ASR: https://k2-fsa.github.io/sherpa/onnx/qwen3-asr/index.html - sherpa-onnx Qwen3-ASR pretrained model: https://k2-fsa.github.io/sherpa/onnx/qwen3-asr/pretrained.html - Qwen3-ASR Technical Report: https://arxiv.org/abs/2601.21337