# Round 3 PRD:Moonshine English 默认模型 状态:v0.1 启动稿 所属子项目:`02-P-NBL` Round:Round 3 日期:2026-06-03 基线版本:`v2.1.20-build20260603.1145` 基线 commit:`f66a79670c444010a59b8513c9f61257ebc7d9b6` 关联文档: - [`../product-plan.md`](../product-plan.md) - [`../engineering-plan.md`](../engineering-plan.md) - [`../plan.md`](../plan.md) - [`../round-2/prd.md`](../round-2/prd.md) ## 1. 背景 Round 1 已完成模型 profile 基础设施,Round 2 已完成语言检测和语言版本设置。当前状态是: - 中文和 English UI 可以根据 effective language 切换。 - `LanguageProfile` 已经有 `zh-CN` 和 `en`。 - `en` 语言版本仍临时使用 `sensevoice-zh`。 - 引擎初始化仍只支持 `sensevoice` backend。 - Onboarding 下载仍依赖 config 里的 SenseVoice URL。 Round 3 要把 English 语言版本接到真正的英文默认模型 Moonshine English。 ## 2. 本轮目标 Round 3 完成后: | 场景 | effective language | 默认模型 | 期望结果 | |---|---|---|---| | 中文系统新用户 | `zh-CN` | `sensevoice-zh` | 继续中文 UI + SenseVoice | | English 系统新用户 | `en` | `moonshine-en` | English UI + Moonshine English | | 其他系统语言新用户 | `en` | `moonshine-en` | English UI + Moonshine English | | 老中文用户升级 | `zh-CN` | `sensevoice-zh` | 不重新下载、不丢数据 | | 用户手动切到 English | `en` | `moonshine-en` | 未安装则提示/下载 Moonshine | | 用户手动切回中文 | `zh-CN` | `sensevoice-zh` | 使用已有 SenseVoice | 核心目标: - 新增 `moonshine-en` model profile。 - English 语言版本默认模型改为 `moonshine-en`。 - 新增 sherpa-onnx Moonshine backend builder。 - Onboarding 按“当前应使用的模型 profile”下载模型,而不是写死 SenseVoice URL。 - Moonshine 下载失败、初始化失败、缺文件时,不破坏 SenseVoice、用户词库、历史记录和配置。 ## 3. 本轮非目标 明确不做: - 不接入 Parakeet。 - 不接入 Qwen3-ASR。 - 不接入 Moonshine Voice framework。 - 不接 Moonshine v2 多语言模型。 - 不做 realtime partial transcript。 - 不做模型选择页。 - 不做模型删除、清理和回滚 UI。 - 不做 30+ 语言列表。 - 不做粤语独立语言版本。 - 不做欧洲语言第一优选模型决策。 - 不改变用户词库、history、replacement 规则格式。 - 不做正式签名和 notarization。 ## 4. 模型选择决策 Round 3 选择: ```text model id: moonshine-en actual package: sherpa-onnx-moonshine-base-en-int8 backend kind: moonshine language: English only ``` 选择 `base-en-int8`,不是 `tiny-en-int8`,原因: - GitHub release asset 显示 `base-en-int8.tar.bz2` 约 `250807309` bytes,约 239 MiB。 - sherpa 官方文档列出的解压文件总量约 560448 blocks,仍在用户可接受的 300MB 级别附近。 - 英文用户分支需要更稳的默认准确率,base 比 tiny 更适合作为第一默认模型。 - `tiny-en-int8` 可作为工程备用候选,但本轮不暴露为独立 UI 选项。 资料依据: - Moonshine v1 官方文档明确该页模型只支持 English: - 官方模型下载 URL: ## 5. Moonshine Profile 新增 profile: ```text ID: moonshine-en DisplayName: Moonshine English BackendKind: moonshine Tier: default SupportedLanguageIDs: en RecommendedFor: en Description: 轻量英文离线模型,适合英文听写和英文日常输入。 ApproxSize: 约 239 MiB 下载包,解压后约 270-280 MiB InstallDirName: moonshine-en ProviderOrder: cpu NumThreads: 4 ``` 下载 URL: ```text primary: https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-moonshine-base-en-int8.tar.bz2 fallback: https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-moonshine-tiny-en-int8.tar.bz2 ``` 说明: - fallback URL 只用于下载源不可用时保证开发/QA 能继续验证,不表示最终产品会静默降级到 tiny。 - 如果最终产品不接受 tiny 作为降级包,研发应把 fallback 留空或换成自维护 mirror。 Required files: | Role | Required file | |---|---| | `preprocessor` | `preprocess.onnx` | | `encoder` | `encode.int8.onnx` | | `uncached_decoder` | `uncached_decode.int8.onnx` | | `cached_decoder` | `cached_decode.int8.onnx` | | `tokens` | `tokens.txt` | ## 6. sherpa-onnx 配置 当前项目锁定: ```text github.com/k2-fsa/sherpa-onnx-go v1.12.24 github.com/k2-fsa/sherpa-onnx-go-macos v1.12.24 ``` 该版本 Moonshine 是 v1 四文件配置: ```go config.ModelConfig.Moonshine.Preprocessor = resolved.Files["preprocessor"] config.ModelConfig.Moonshine.Encoder = resolved.Files["encoder"] config.ModelConfig.Moonshine.UncachedDecoder = resolved.Files["uncached_decoder"] config.ModelConfig.Moonshine.CachedDecoder = resolved.Files["cached_decoder"] config.ModelConfig.Tokens = resolved.Files["tokens"] config.ModelConfig.NumThreads = resolved.Profile.NumThreads config.ModelConfig.Provider = "cpu" config.DecodingMethod = "greedy_search" ``` 保留: ```go config.FeatConfig.SampleRate = 16000 config.FeatConfig.FeatureDim = 80 ``` 不要使用: ```text encoder_model.ort decoder_model_merged.ort MergedDecoder ``` 这些不是当前项目依赖版本对应的 Moonshine v1 接入形态。 ## 7. 默认模型解析规则 Round 3 必须修正“当前模型”的定义。 当前问题: ```text config.Default().SelectedModelID = sensevoice-zh engine.New() 直接读取 SelectedModelID ``` 这会导致 English 新用户即使 effective language 是 `en`,仍然被锁到 SenseVoice。 Round 3 目标规则: ```text CurrentModel = ResolveCurrentModel(Config, EffectiveLanguage) ``` 建议引入: ```text ModelSelectionMode = auto | manual ``` 默认: ```text ModelSelectionMode = auto ``` 解析规则: | 条件 | 当前模型 | |---|---| | `ModelSelectionMode=auto` 且 effective language=`zh-CN` | `sensevoice-zh` | | `ModelSelectionMode=auto` 且 effective language=`en` | `moonshine-en` | | `ModelSelectionMode=manual` 且 `SelectedModelID` 合法 | `SelectedModelID` | | `ModelSelectionMode=manual` 但模型非法 | 回退到 effective language 默认模型,并记录日志 | | 旧配置缺少 `ModelSelectionMode` | 视为 `auto` | 说明: - Round 3 没有模型选择 UI,因此普通用户不会进入 `manual`。 - 老用户配置里已有的 `SelectedModelID=sensevoice-zh` 应视为历史默认值,不应阻止 English 默认 Moonshine 生效。 - 后续 Round 4/5 做模型选择页时,再由 UI 把 `ModelSelectionMode` 设为 `manual`。 ## 8. 用户故事 ### 8.1 English 新用户 作为 English macOS 用户,我第一次启动 App 时,应看到 English UI,并自动准备 Moonshine English。 验收点: - effective language=`en`。 - current model=`moonshine-en`。 - 如果 Moonshine 未安装,进入 onboarding 下载 Moonshine。 - 下载完成后引擎显示 `Moonshine English · CPU` 或等价信息。 - 按 R-Alt 说英文,可以离线识别并写入 history。 ### 8.2 中文新用户 作为中文 macOS 用户,我第一次启动 App 时,应保持 Round 2 体验。 验收点: - effective language=`zh-CN`。 - current model=`sensevoice-zh`。 - 不下载 Moonshine。 - SenseVoice 可正常离线识别。 ### 8.3 老中文用户升级 作为已有中文用户,我升级到 Round 3 后,不应被要求下载 Moonshine。 验收点: - 旧 SenseVoice 目录可继续使用。 - `userdict.json` 不变。 - `history.json` 不变。 - replacement 规则继续生效。 - 热键和历史写入不退化。 ### 8.4 用户手动切到 English 作为用户,我在设置页把语言版本切到 English 后,App 应切换到英文产品路径。 验收点: - UI 立即切到 English。 - current model 解析为 `moonshine-en`。 - 如果 Moonshine 未安装,显示模型准备/下载状态。 - 如果 Moonshine 已安装,重新初始化 Moonshine 引擎。 - 切换失败时,不能删除或覆盖 SenseVoice。 ### 8.5 用户手动切回中文 作为用户,我可以从 English 切回中文。 验收点: - UI 立即切回中文。 - current model 解析为 `sensevoice-zh`。 - 若 SenseVoice 已安装,直接恢复中文识别。 - Moonshine 模型保留,不自动删除。 ## 9. Onboarding 与下载规则 Round 3 onboarding 不应再让前端从 `Config.ModelDownloadUrl` 和 `FallbackModelDownloadUrl` 读取 SenseVoice URL。 目标: ```text frontend -> EngineService.DownloadCurrentModel() backend -> ResolveCurrentModel() backend -> profile.DownloadURLs backend -> DownloadProfile(profile, urls, modelsRoot) ``` 要求: - `ModelExists()` 检查 current model,不是固定 SenseVoice。 - `DownloadCurrentModel()` 下载 current model。 - 下载完成后重新初始化 current model。 - 下载失败只影响目标模型,不删除已有模型。 - 下载进度事件继续复用 `model:download-progress`。 保留兼容: - 可以保留旧 `DownloadModel(primaryURL, fallbackURL)` API,但 Round 3 前端不应再调用它。 - `ModelDownloadUrl` / `FallbackModelDownloadUrl` 可暂时留在 config 中,不在本轮强删。 ## 10. 失败处理 ### 10.1 下载失败 期望: - 显示下载失败。 - 不删除 `models/sensevoice`、`models/sensevoice-zh`。 - 不删除已有 `models/moonshine-en` 完整目录。 - 不把 incomplete staging 目录作为可用模型。 - 用户可重试。 ### 10.2 缺文件 如果 `moonshine-en` 缺少任意 required file: - `ValidateModelDir` 返回 incomplete。 - `ModelExists()` 返回 false。 - `engine.New()` 不应崩溃。 - UI 进入 need_model 或错误状态。 ### 10.3 初始化失败 如果 Moonshine sherpa 初始化失败: - 状态为 `error` 或 `need_model`,取决于模型是否完整存在。 - 记录 backend、provider、model id、root dir。 - 不修改用户词库、历史和 SenseVoice 目录。 ## 11. 验收标准 Round 3 功能性验收 PASS 必须满足: - English fresh user 默认 Moonshine。 - Chinese fresh user 默认 SenseVoice。 - 老中文用户升级不触发 Moonshine 下载。 - Moonshine 可下载、校验、加载。 - 英文语音可离线识别,并写入 `history.json`。 - 中文 SenseVoice 回归通过。 - 语言切换会同步 current model。 - Moonshine 下载失败不破坏已有 SenseVoice。 - Moonshine 缺文件不被判定为可用。 - `go test ./...` 通过。 - `npm run build` 通过。 - QA 报告记录版本、build、commit、测试包、日志和关键证据。