feat(keyboard): ship AI hint carousel, home library cards, and clipboard polish
Rotate AI idle suggestions with optional remote packs, move history/dictionary onto self-sizing Home preview cards, harden clipboard capture/prompting, and simplify keyboard chrome by dropping most liquid-glass shadows.
This commit is contained in:
+215
-541
@@ -1,585 +1,259 @@
|
||||
# OSGKeyboard 本地 ASR 技术架构
|
||||
|
||||
> **文档状态**:架构规划(非实现规格)
|
||||
> **适用范围**:macOS 本地听写;与 iOS 键盘扩展、云 ASR 路径的关系见各节说明。
|
||||
> **核心结论**:macOS 本地听写默认 **Qwen3 MLX 真流式**(mlx-audio-swift);词库经 `LocalASRBiasAdapter` 以 soft prompt + 后处理注入。Sherpa offline 已移除。
|
||||
> **文档状态**:1.7.0 代码事实 + 后续评测方向
|
||||
> **适用范围**:macOS 15+ 本地听写;iOS 26+ 使用 Apple SpeechAnalyzer。
|
||||
> **当前结论**:Mac 默认安装目录模型为
|
||||
> `qwen3-mlx-0.6b-4bit`,通过 `mlx-audio-swift` 做 Qwen3 MLX 真流式识别;
|
||||
> 模型不可用时走 Apple Speech fallback。Sherpa 已不在当前 catalog 或运行路径中。
|
||||
|
||||
---
|
||||
## 1. 架构摘要
|
||||
|
||||
## 1. Executive Summary
|
||||
本地 ASR 的专有名词质量由三层共同完成:
|
||||
|
||||
OSGKeyboard 的本地 ASR 竞争力不来自单一模型,而来自:
|
||||
1. **ASR bias**:`PersonalDictionary` 与内置技术词经
|
||||
`LocalASRBiasAdapter` 生成 Qwen3 `promptBias`。
|
||||
2. **确定性纠错**:识别后按个人词库 `aliases → term` 做边界受控替换。
|
||||
3. **Polish 保真**:把内置词参考作为补充上下文交给润色层;无 API Key 或润色失败时
|
||||
仍返回本地识别与纠错结果。
|
||||
|
||||
1. **用户 PersonalDictionary**(term / aliases / iCloud)
|
||||
2. **内置技术词库**(`phrases.tsv` ≈ 1 万词,iOS 已用于 Apple CLM)
|
||||
3. **分层 bias**:ASR 偏置 → 后处理纠错 → Polish 保真
|
||||
4. **可替换的 Local ASR Provider**(Qwen3 MLX 主线,Sherpa / SenseVoice / Apple Speech 对照)
|
||||
这三层已经接入 Mac 的 live 与 batch 路径。“Mac 本地路径不消费词库”不再是当前事实。
|
||||
|
||||
当前最大缺口:**macOS 本地路径未消费任何词库**;云路径已通过 `PersonalDictionary+ASRBias` 完整接线。
|
||||
|
||||
推荐路线:
|
||||
|
||||
| 阶段 | 动作 |
|
||||
|------|------|
|
||||
| **短期** | 保留 Qwen3 MLX;实现 `LocalASRBiasAdapter`;接 soft prompt + polish + aliases 后处理 |
|
||||
| **中期** | ModelScope 优先的本地模型 catalog;Sherpa Qwen3 hotwords POC |
|
||||
| **长期** | 按评测数据决定是否新增默认 provider 或保留 Qwen3 MLX |
|
||||
|
||||
---
|
||||
|
||||
## 2. 背景与问题定义
|
||||
|
||||
### 2.1 为什么本地 ASR 不能只讨论模型
|
||||
|
||||
语音输入的「专有名词准确率」由多层共同决定:
|
||||
|
||||
- **ASR 层**:听出 `Claude`、`SwiftUI`、`Qwen3-ASR`
|
||||
- **后处理层**:`克劳德` → `Claude`
|
||||
- **润色层**:保留品牌名、变量名,不擅自改写
|
||||
|
||||
闭源产品(Typeless 等)常把词典效果归因于云端 ASR;开源竞品(OpenLess、Typeflux、SayIt)表明:**词典必须按 backend 能力分层注入**,不能假设「一个 hotwords 数组走天下」。
|
||||
|
||||
### 2.2 OSG 相对竞品的结构性优势
|
||||
|
||||
| 能力 | OSGKeyboard | 典型开源竞品 |
|
||||
|------|-------------|----------------|
|
||||
| 用户词库 | `PersonalDictionary`(term + aliases + category + iCloud) | 多为 phrase-only |
|
||||
| 内置领域词库 | ~10k `phrases.tsv` + iOS CLM | OpenLess preset ~20 词;SayIt server hotwords.txt ~30 词 |
|
||||
| 云 ASR bias | 智谱 / 阿里 vocabulary / Whisper prompt | 单云或单 provider |
|
||||
| iOS 本地 CLM | `SFCustomLanguageModelData` | macOS 路径未等价 |
|
||||
|
||||
### 2.3 设计目标
|
||||
|
||||
- 离线、隐私友好的 macOS 本地听写
|
||||
- 复用 `PersonalDictionary` 与 `phrases.tsv` **源数据**(非 iOS `.bin` 直用)
|
||||
- Provider 可替换;能力矩阵诚实声明(尤其热词模式)
|
||||
- 模型下载可管理(**ModelScope 优先**,HF / GitHub 备用)
|
||||
- 可评测、可灰度、可回退
|
||||
|
||||
### 2.4 非目标
|
||||
|
||||
- 不立即将主路径切到 FunASR Python server 或 Sherpa
|
||||
- 不把 1 万词全量塞入 ASR prompt
|
||||
- 不把 Polish 当作唯一纠错层
|
||||
- 不承诺未 POC 验证的模型效果
|
||||
- 第一期不强制实现 Typeflux 式「自动词库学习」(仅作可选实验设计)
|
||||
|
||||
---
|
||||
|
||||
## 3. 当前架构(代码事实)
|
||||
|
||||
### 3.1 端到端数据流
|
||||
## 2. 当前端到端数据流
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph macOS["macOS"]
|
||||
Rec["MacAudioRecorder"]
|
||||
Pipe["MacDictationPipeline"]
|
||||
Local["MacLocalASRService"]
|
||||
Cloud["CloudASRClient"]
|
||||
Polish["PolishingService"]
|
||||
Insert["MacTextInsertionService"]
|
||||
end
|
||||
Rec --> Pipe
|
||||
Pipe -->|engineMode local| Local
|
||||
Pipe -->|engineMode cloud| Cloud
|
||||
Local --> Polish
|
||||
Cloud --> Polish
|
||||
Polish --> Insert
|
||||
Audio["MacAudioRecorder · 16 kHz samples"] --> Pipeline["MacDictationPipeline"]
|
||||
Dict["PersonalDictionary"] --> Bias["LocalASRBiasAdapter"]
|
||||
Lexicon["phrases.tsv · BuiltinLexiconIndex"] --> Bias
|
||||
App["Front app + locale"] --> Bias
|
||||
Bias --> Prompt["promptBias"]
|
||||
Bias --> Pairs["correctionPairs"]
|
||||
Bias --> PolishTerms["polishFragment"]
|
||||
|
||||
Pipeline -->|local + installed MLX| Live["MacMLXLiveCapture"]
|
||||
Pipeline -->|batch/recovery| Local["MacLocalASRService"]
|
||||
Live --> Qwen["MacMLXStreamingASRProvider"]
|
||||
Local --> Qwen
|
||||
Local -->|model unavailable / Apple selected| Apple["MacSpeechLocalASR"]
|
||||
Prompt --> Qwen
|
||||
Qwen --> Raw["Raw transcript"]
|
||||
Apple --> Raw
|
||||
Raw --> Correct["LocalASRTranscriptCorrector"]
|
||||
Pairs --> Correct
|
||||
Correct --> Polish["PolishingService"]
|
||||
PolishTerms --> Polish
|
||||
Polish --> Insert["MacTextInsertionService"]
|
||||
```
|
||||
|
||||
### 3.2 云路径 vs 本地路径
|
||||
|
||||
| 环节 | 云 ASR | 本地 ASR(当前) |
|
||||
|------|--------|------------------|
|
||||
| 入口 | `MacDictationPipeline.run` | 同左 |
|
||||
| ASR | `CloudASRClientFactory` + `dictionary: store.personalDictionary` | `MacLocalASRService.transcribe(samples, locale)` **无 dictionary** |
|
||||
| 词库 bias | `PersonalDictionary+ASRBias`(按 provider) | **无** |
|
||||
| 润色 | `PolishingService` + `promptFragment()` | 同左(仅用词典做 polish,不经 ASR) |
|
||||
| 默认模型 | 用户所选云 provider | Qwen3 MLX 1.7B;缺权重 → Apple Speech |
|
||||
|
||||
关键代码:
|
||||
|
||||
- [`OSGKeyboardMac/MacDictationPipeline.swift`](../OSGKeyboardMac/MacDictationPipeline.swift) — 本地分支未传 `personalDictionary`
|
||||
- [`OSGKeyboardMac/MacLocalASRService.swift`](../OSGKeyboardMac/MacLocalASRService.swift) — Sherpa catalog models / Apple Speech fallback
|
||||
- [`OSGKeyboardShared/Models/PersonalDictionary+ASRBias.swift`](../OSGKeyboardShared/Models/PersonalDictionary+ASRBias.swift) — 云侧 `asrHotwords` / `asrPromptBias` / 阿里热词表
|
||||
- [`OSGKeyboardShared/Services/PolishingService.swift`](../OSGKeyboardShared/Services/PolishingService.swift) — 润色层消费 `dictionary.promptFragment()`
|
||||
|
||||
### 3.3 iOS 词库资产(macOS 不可直接复用)
|
||||
|
||||
- **内置词库**:[`OSGKeyboard/Resources/CustomLanguageModel/v1/phrases.tsv`](../OSGKeyboard/Resources/CustomLanguageModel/v1/phrases.tsv)(约 10,301 行,含 `word` / `pinyin` / `source` / `weight`)
|
||||
- **Apple CLM**:[`OSGKeyboardShared/Services/CustomLanguageModelManager.swift`](../OSGKeyboardShared/Services/CustomLanguageModelManager.swift) → `SFSpeechLanguageModel.Configuration`,仅 iOS 26+ 中文本地 ASR 路径
|
||||
- **结论**:macOS 需从 TSV + `PersonalDictionary` **重新适配**为 Qwen3 prompt / Sherpa hotwords / polish fragment,不能加载 `OSGKeyboardCLM.bin`
|
||||
|
||||
### 3.4 本地模型现状
|
||||
|
||||
- 默认模型:`sherpa-qwen3-0.6b-int8`(catalog 管理,安装于 `~/Library/Application Support/OSGKeyboard/`)
|
||||
- 运行时:`sherpa-onnx-offline` 二进制(按架构下载,见 `local-asr-catalog.json`)
|
||||
- Sherpa 引擎:[`MacSherpaLocalASR.swift`](../OSGKeyboardMac/MacSherpaLocalASR.swift) / [`MacSherpaONNXRunner.swift`](../OSGKeyboardMac/MacSherpaONNXRunner.swift) — Qwen3 支持 recognizer-scoped hotwords
|
||||
|
||||
---
|
||||
|
||||
## 4. 开源竞品源码观察
|
||||
|
||||
基于 GitHub topic `typeless-alternative` 及关联仓库(2026-03 快照)。
|
||||
|
||||
### 4.1 对比总表
|
||||
|
||||
| 项目 | 技术栈 | 本地 ASR | 热词 / 词库 | 可借鉴 | 不宜照搬 |
|
||||
|------|--------|----------|-------------|--------|----------|
|
||||
| **OpenLess** | Tauri/Rust | Qwen3 C 引擎、Apple Speech、Sherpa(Win) | 火山 `context.hotwords`;Whisper prompt;Polish hotword block;**本地 Qwen3 未接词典** | 多 provider + polish 双层 | 本地热词叙事过度乐观 |
|
||||
| **Typeflux** | Swift/macOS | SenseVoice、FunASR、Qwen3 Sherpa CLI、WhisperKit | `VocabularyStore` cap 500;Doubao hotwords;Whisper prompt;**Sherpa 本地无热词**;自动项目词 + 编辑后学习 | Swift 原生、词库排序、自动学习(作实验) | Sherpa 仅 CLI 离线,无 hotwords 接线 |
|
||||
| **SayIt** | Tauri + FastAPI | sherpa-onnx Rust:**Qwen3 recognizer 创建时写 hotwords** | 内置主题词 + 自定义;云 Qwen corpus;server `hotwords.txt` | **本地 Qwen3 hard hotwords 实证**;跨 provider `StartOptions.hotwords` | 服务端 vLLM 非 macOS 客户端主线 |
|
||||
| **VoiceSnap** | Go/Wails | SenseVoice + sherpa-onnx | **无个性化词库** | 离线体验:静音截断、剪贴板保护、填充词过滤 | 无万级词库场景 |
|
||||
| **OpenBroca** | Electron | Sherpa 等 | Dictionary hotword/replacement | **one-shot `recognize` first**;model catalog、sha256、selected model | Electron 栈 |
|
||||
|
||||
### 4.2 对 OSG 的启示
|
||||
|
||||
1. **学架构,不学「本地已完整支持热词」的 README 叙事**(OpenLess 本地 Qwen3 缺口与 OSG 类似)。
|
||||
2. **SayIt 证明**:sherpa-onnx `OfflineQwen3ASRModelConfig.hotwords` 可在 recognizer 创建时注入;热词变化需 **重建 recognizer**(缓存 key 含 hotwords 字符串)。
|
||||
3. **Typeflux 证明**:`activeTerms()` 上限 500 + 动态排序;但 Sherpa Qwen3/SenseVoice 命令行路径**未传** vocabulary prompt。
|
||||
4. **OpenBroca 证明**:runtime 不得静默选「目录里第一个模型」;须 `selectedModelId` + manifest。
|
||||
|
||||
---
|
||||
|
||||
## 5. 目标架构
|
||||
|
||||
### 5.1 管道总览
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
audioSamples["Audio Samples 16kHz"] --> macPipeline["MacDictationPipeline"]
|
||||
personalDict["PersonalDictionary"] --> biasAdapter["LocalASRBiasAdapter"]
|
||||
phrasesTSV["phrases.tsv Index"] --> biasAdapter
|
||||
runtimeCtx["Runtime Context locale app recentHits"] --> biasAdapter
|
||||
providerCap["Provider Capability"] --> biasAdapter
|
||||
|
||||
biasAdapter --> hotwords["hardHotwords"]
|
||||
biasAdapter --> promptBias["promptBias"]
|
||||
biasAdapter --> corpusText["corpusContext"]
|
||||
biasAdapter --> correctionPairs["correctionPairs"]
|
||||
biasAdapter --> polishFrag["polishFragment"]
|
||||
biasAdapter --> diag["diagnostics"]
|
||||
|
||||
macPipeline --> recognize["LocalASRProvider.recognize"]
|
||||
hotwords --> recognize
|
||||
promptBias --> recognize
|
||||
corpusText --> recognize
|
||||
|
||||
recognize --> rawTranscript["Raw Transcript"]
|
||||
rawTranscript --> correctionLayer["Correction Layer"]
|
||||
correctionPairs --> correctionLayer
|
||||
correctionLayer --> polishingService["PolishingService"]
|
||||
polishFrag --> polishingService
|
||||
polishingService --> finalText["Final Text"]
|
||||
```
|
||||
|
||||
### 5.2 三层职责边界
|
||||
|
||||
| 层 | 职责 | 禁止 |
|
||||
|----|------|------|
|
||||
| **ASR bias** | 提高听写阶段专有名词概率 | 承担全文语法润色 |
|
||||
| **Correction** | 高置信 `aliases → term` 替换 | 凭热词表改写普通句意 |
|
||||
| **Polish** | 标点、口语转书面、热词保真 | 单独承担全部专名纠错 |
|
||||
|
||||
---
|
||||
|
||||
## 6. Local ASR Provider 抽象
|
||||
|
||||
### 6.1 One-shot first
|
||||
|
||||
借鉴 OpenBroca:macOS 听写主路径为 **录完后一次性 `recognize`**;流式预览(`transcribe` / partial)为可选能力,非第一期必做。
|
||||
|
||||
建议协议(概念层):
|
||||
|
||||
```swift
|
||||
protocol LocalASRProvider {
|
||||
var capabilities: LocalASRCapabilities { get }
|
||||
func recognize(
|
||||
samples: [Float],
|
||||
sampleRate: Int,
|
||||
locale: Locale,
|
||||
bias: LocalASRBiasPayload?,
|
||||
options: LocalASRRecognizeOptions?
|
||||
) async throws -> LocalASRResult
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 能力矩阵(须诚实声明)
|
||||
|
||||
除 `supportsStreaming`、`maxHotwordCount` 外,**必须区分热词模式**:
|
||||
|
||||
| 字段 | 含义 |
|
||||
|------|------|
|
||||
| `hotwordMode` | `none` / `promptOnly` / `perRequest` / `recognizerScoped` / `cloudVocabulary` |
|
||||
| `hotwordStrength` | `weak` / `medium` / `strong`(产品文案用,非科学绝对值) |
|
||||
| `hotwordReloadCost` | `none` / `recognizerReload` / `modelReload` |
|
||||
| `maxPromptCharacters` | soft prompt 上限 |
|
||||
| `maxHotwordCount` | hard hotwords 上限 |
|
||||
| `supportsLanguageHint` | 是否接受 locale → language hint |
|
||||
|
||||
### 6.3 各 Backend 定位(规划)
|
||||
|
||||
| Provider | 角色 | hotwordMode(规划) | 备注 |
|
||||
|----------|------|---------------------|------|
|
||||
| **Qwen3 MLX** | 短期主线 | `promptOnly`(待接 `qwen_set_prompt` 等价 API) | 已有权重路径;改动面最小 |
|
||||
| **Sherpa Qwen3** | 中期 POC | `recognizerScoped` | SayIt 同款;热词变更加载成本 |
|
||||
| **SenseVoice** | 对照 | `none` 或弱 prompt | 速度/中文基线;非热词主线 |
|
||||
| **Apple Speech** | Fallback | `none`(macOS CLM 待验证) | 系统稳定 |
|
||||
| **Cloud ASR** | 质量上限 | 各云 `PersonalDictionary+ASRBias` | 非离线 |
|
||||
|
||||
**暂不主推**:Sherpa Paraformer 作为热词主线(官方不支持 Paraformer hotwords,与 transducer/Qwen3 不同)。
|
||||
|
||||
---
|
||||
|
||||
## 7. LocalASRBiasAdapter 设计
|
||||
|
||||
### 7.1 输入
|
||||
|
||||
| 输入 | 说明 |
|
||||
|------|------|
|
||||
| `PersonalDictionary.effectiveEntries` | 用户词;最高优先级 |
|
||||
| `BuiltinLexiconIndex` | 自 `phrases.tsv` 构建;按 weight / 场景筛选 |
|
||||
| `locale` | `store.localeId` |
|
||||
| `frontAppBundleId` | 可选;技术类 App 提升 IT 子集权重 |
|
||||
| `recentHitTerms` | 历史命中统计(若已有) |
|
||||
| `providerCapabilities` | 决定输出哪些字段、如何截断 |
|
||||
|
||||
### 7.2 输出 `LocalASRBiasPayload`
|
||||
|
||||
```swift
|
||||
struct LocalASRBiasPayload {
|
||||
var hardHotwords: [String] // Sherpa Qwen3、部分云 API
|
||||
var promptBias: String? // Qwen3 MLX、Whisper 系
|
||||
var corpusContext: String? // Qwen 云 corpus 风格(若将来统一)
|
||||
var polishFragment: String // PolishingService 追加块
|
||||
var correctionPairs: [(alias: String, term: String)]
|
||||
var diagnostics: BiasDiagnostics // 供设置页 / 调试
|
||||
}
|
||||
|
||||
struct BiasDiagnostics {
|
||||
var userTermCount: Int
|
||||
var builtinTermCount: Int
|
||||
var truncated: Bool
|
||||
var truncationReason: String?
|
||||
var selectedSources: [String] // e.g. user, builtin-it, builtin-top
|
||||
}
|
||||
```
|
||||
|
||||
### 7.3 优先级与截断
|
||||
|
||||
```
|
||||
用户高频 / 最近命中
|
||||
> PersonalDictionary(全部有效 term)
|
||||
> 当前 App 相关内置词(phrases 子集)
|
||||
> 高 weight 内置技术词(Top-N)
|
||||
> 其余内置词(仅 polish / 检索,不进 ASR)
|
||||
```
|
||||
|
||||
默认建议(可 POC 调参):
|
||||
|
||||
| 输出 | 默认上限 |
|
||||
|------|----------|
|
||||
| `hardHotwords` | 100(Qwen3 Sherpa);对齐 `asrHotwords(maxCount: 100)` |
|
||||
| `promptBias` | 800 字符;复用 `asrPromptBias(maxCharacters:)` 逻辑 |
|
||||
| ASR 层内置词 | 200–500;**不全量 1 万** |
|
||||
| `correctionPairs` | aliases 全量可进后处理,但仅 **整词 / 高置信** 替换 |
|
||||
|
||||
### 7.4 防污染规则
|
||||
|
||||
- 近静音、极短音频:减少或跳过内置词,保留用户词。
|
||||
- 用户词始终优先于内置词。
|
||||
- diagnostics 必须记录「为何丢弃」某批词(超 cap、provider 不支持、场景不匹配)。
|
||||
|
||||
### 7.5 与现有云代码复用
|
||||
|
||||
扩展 [`PersonalDictionary+ASRBias.swift`](../OSGKeyboardShared/Models/PersonalDictionary+ASRBias.swift) 为 **单一事实来源**,新增例如:
|
||||
|
||||
- `localPromptBias(maxCharacters:builtinTerms:)`
|
||||
- `correctionPairs()`
|
||||
- `rankedTermsForASR(limit:builtinBoost:)`
|
||||
|
||||
避免 macOS / iOS / Cloud 三套独立拼接逻辑。
|
||||
|
||||
---
|
||||
|
||||
## 8. 词库策略
|
||||
|
||||
### 8.1 PersonalDictionary
|
||||
|
||||
| 字段 | ASR | Correction | Polish |
|
||||
|------|-----|------------|--------|
|
||||
| `term` | hotword / prompt | 标准写法 | 必须保留 |
|
||||
| `aliases` | 可进 prompt 提示 | **主战场** | 语义纠错参考 |
|
||||
| `category` | 排序权重 | — | 分组展示 |
|
||||
| `usageCount` | 排序权重 | — | — |
|
||||
|
||||
iCloud:[`PersonalDictionaryCloudSync`](../OSGKeyboardShared/Services/PersonalDictionaryCloudSync/) 保证 Mac / iOS / Extension 一致;本地 ASR 只读 `AppGroupStore.personalDictionary`。
|
||||
|
||||
### 8.2 phrases.tsv 分层
|
||||
|
||||
**不全量进入 ASR prompt。**
|
||||
|
||||
| 层级 | 用途 | 规模建议 |
|
||||
|------|------|----------|
|
||||
| L1 ASR 高价值 | `weight >= 4` 或 curated IT 品牌缩写 | 200–500 |
|
||||
| L2 场景相关 | 按 `frontApp` / 用户最近命中动态加入 | +0–100 |
|
||||
| L3 全量索引 | 后处理模糊匹配、polish 检索 | ~10k |
|
||||
|
||||
TSV 列:`word`、`pinyin`、`source`、`weight` — 构建索引时保留 weight 用于排序。
|
||||
|
||||
### 8.3 iOS CLM 与 macOS 关系
|
||||
|
||||
- iOS:TSV → export script → `.bin` → `CustomLanguageModelManager`
|
||||
- macOS:TSV → `BuiltinLexiconIndex` → `LocalASRBiasAdapter` → Qwen3 / Sherpa / Polish
|
||||
- **同一 TSV 源**,两种消费格式;不尝试把 `.bin` 喂给 Sherpa/MLX
|
||||
|
||||
---
|
||||
|
||||
## 9. 自动词库学习(可选实验,非第一期)
|
||||
|
||||
借鉴 Typeflux `WorkflowController+AutomaticVocabulary`:
|
||||
|
||||
- 听写插入后,短时观察用户在前台可编辑框内的修改
|
||||
- LLM 或规则判断是否为「专名 / 品牌 / 大小写修正」
|
||||
- 候选进入 **待确认队列**,不直接写入 `PersonalDictionary`
|
||||
|
||||
**约束(必须写进隐私说明)**:
|
||||
|
||||
- 默认关闭
|
||||
- 不自动 iCloud 同步待确认项
|
||||
- 可一键清空、可审计来源
|
||||
- 拒绝:整句改写、纯语法修正、过短词条、编辑幅度过大
|
||||
|
||||
OSG 已有 `PersonalDictionary.Entry.Source.recentEdit` 与合并逻辑,可与之对齐而非新建平行存储。
|
||||
|
||||
---
|
||||
|
||||
## 10. 本地模型管理
|
||||
|
||||
### 10.1 原则
|
||||
|
||||
- **Catalog 与 Runtime 分离**:下载源只影响安装;推理只读本地 **已验证 manifest**
|
||||
- **Selected model 显式**:禁止「扫描目录用第一个 onnx」
|
||||
- **完整性**:sha256 或 size 校验 + staging 目录原子发布
|
||||
|
||||
### 10.2 存储布局(建议)
|
||||
|
||||
```
|
||||
~/Library/Application Support/OSGKeyboard/
|
||||
LocalASRModels/
|
||||
manifest.json # 已安装模型、版本、backend、capabilities
|
||||
runtimes/sherpa-onnx-1.13.4-macos-arm64/
|
||||
models/sherpa-qwen3-0.6b-int8/
|
||||
sherpa-sensevoice-small/
|
||||
```
|
||||
|
||||
### 10.3 Catalog 条目(概念)
|
||||
|
||||
```json
|
||||
{
|
||||
"modelId": "sherpa-qwen3-asr-0.6b-int8",
|
||||
"displayName": "Qwen3-ASR 0.6B (Sherpa)",
|
||||
"backend": "sherpaQwen3",
|
||||
"sizeBytes": 1200000000,
|
||||
"recommendedLocales": ["zh-CN", "en-US"],
|
||||
"supportsHotwords": true,
|
||||
"hotwordMode": "recognizerScoped",
|
||||
"sources": [
|
||||
{
|
||||
"type": "modelscope",
|
||||
"url": "https://www.modelscope.cn/api/v1/models/.../repo?Revision=master&FilePath=...",
|
||||
"sha256": "...",
|
||||
"priority": 1
|
||||
},
|
||||
{
|
||||
"type": "huggingface",
|
||||
"url": "https://huggingface.co/...",
|
||||
"priority": 2
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/k2-fsa/sherpa-onnx/releases/download/...",
|
||||
"priority": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 10.4 ModelScope 策略
|
||||
|
||||
| 场景 | 策略 |
|
||||
|------|------|
|
||||
| 中国大陆用户默认 | **ModelScope 优先**(Qwen3-ASR、SenseVoice、FunASR 相关 ONNX) |
|
||||
| 国际 / ModelScope 失败 | Hugging Face → GitHub Releases |
|
||||
| 企业内网 | `custom` mirror URL(用户配置) |
|
||||
| 安装流程 | download → verify → extract → validate required files → rename staging → update manifest |
|
||||
| 失败 | 清理 staging / 临时文件;不留下半安装状态 |
|
||||
|
||||
MLX Qwen3 权重:可继续支持用户自选目录(现状),逐步纳入统一 catalog 的 `type: mlx` 条目。
|
||||
|
||||
### 10.5 UI / 设置需求(规划)
|
||||
|
||||
- 模型列表:体积、语言、安装状态、是否支持热词
|
||||
- 下载进度:phase(downloading / extracting / validating / finalizing)
|
||||
- 切换模型:仅允许 **installed + manifest 合法** 的项为默认
|
||||
- 诊断:当前 provider、capability、上次 bias diagnostics
|
||||
|
||||
---
|
||||
|
||||
## 11. 后端对比与决策矩阵
|
||||
|
||||
### 11.1 产品分层
|
||||
### 2.1 引擎选择与回退
|
||||
|
||||
| 项目 | 当前事实 |
|
||||
|---|---|
|
||||
| 默认模型 ID | `qwen3-mlx-0.6b-4bit` |
|
||||
| 可选 MLX 模型 | 0.6B 4-bit、1.7B 4-bit |
|
||||
| Catalog | `OSGKeyboard/Resources/LocalASR/local-asr-catalog.json` |
|
||||
| 下载源 | `hf-mirror.com` 与 Hugging Face repository files |
|
||||
| 推理 | `MLXAudioSTT.Qwen3ASRModel` |
|
||||
| Live partial | 100 ms 音频 feed;流式 session 定期 decode |
|
||||
| Batch | 同一 MLX 模型的 `generate(audio:context:language:)` |
|
||||
| Fallback | Apple Speech 的本地 `SFSpeechURLRecognitionRequest` |
|
||||
|
||||
旧 Sherpa model ID 只在偏好迁移逻辑中映射到当前 MLX 默认值,不代表 Sherpa
|
||||
backend 仍可运行。当前 catalog 的 `runtimes` 为空。
|
||||
|
||||
### 2.2 词库接线
|
||||
|
||||
`MacDictationPipeline.resolveLocalBias` 与 `MacMLXLiveCapture.resolveBias` 都读取
|
||||
`store.personalDictionary`,再调用:
|
||||
|
||||
```text
|
||||
主线: Qwen3 MLX + LocalASRBiasAdapter + Polish/Correction
|
||||
重点 POC: Sherpa Qwen3 hotwords
|
||||
对照: SenseVoice(速度)、Apple Speech(fallback)
|
||||
参考上限: Cloud ASR + PersonalDictionary
|
||||
团队部署: Qwen3-ASR vLLM(SayIt 式,非客户端主线)
|
||||
暂不主推: FunASR Paraformer hotwords、纯 OpenLess 本地词典叙事
|
||||
LocalASRBiasAdapter.adapt(
|
||||
dictionary + locale + frontAppBundleId + backend capabilities
|
||||
)
|
||||
```
|
||||
|
||||
### 11.2 详细对比
|
||||
适配器当前输出:
|
||||
|
||||
| 维度 | Qwen3 MLX | Sherpa Qwen3 | SenseVoice | Apple Speech | Cloud |
|
||||
|------|-----------|--------------|------------|--------------|-------|
|
||||
| 离线 | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||||
| 中英混合技术词 | 强(经验性) | 待 POC | 中 | 中 | 强 |
|
||||
| Hard hotwords | ❌→prompt | ✅ recognizerScoped | ❌ | ❌ | ✅ 因 provider 异 |
|
||||
| 实现成本 | 低(已有) | 高(runtime 体积) | 中 | 低 | 已有 |
|
||||
| 模型体积 | ~1.3GB+ | 类似 | ~350MB 级 | 0 | N/A |
|
||||
| 隐私 | 本地 | 本地 | 本地 | 本地 | 依配置 |
|
||||
| 输出 | 当前消费者 |
|
||||
|---|---|
|
||||
| `promptBias` | Qwen3 MLX streaming `StreamingConfig.context` 与 batch `generate(context:)` |
|
||||
| `correctionPairs` | `LocalASRTranscriptCorrector`,在 polish 前修正 aliases |
|
||||
| `polishFragment` | `PolishingService` 的 `dictionarySupplement` |
|
||||
| `diagnostics` | `LocalASRBiasDiagnosticsStore` |
|
||||
| `hardHotwords` | 为具备 hard-hotword capability 的 backend 保留;当前 Qwen3 MLX 不使用 |
|
||||
|
||||
### 11.3 Sherpa POC 通过阈值(建议)
|
||||
个人词优先;`BuiltinLexiconIndex` 从 `phrases.tsv` 选择 `weight >= 4` 的 Top-N,
|
||||
代码编辑器/终端前台场景优先 `computer_terms`。默认最多考虑 300 个内置 ASR 词,
|
||||
Qwen3 soft prompt 最长 800 字符,润色补充最多 40 个内置词。
|
||||
|
||||
相对 **当前 Qwen3 MLX + 仅 polish** 基线:
|
||||
### 2.3 Apple Speech fallback
|
||||
|
||||
| 指标 | 建议阈值 |
|
||||
|------|----------|
|
||||
| 用户热词召回率 | 提升 ≥ 20% |
|
||||
| 误触发率(未说热词却被改成热词) | ≤ 2% |
|
||||
| 30s 音频端到端延迟 | ≤ 基线 × 1.5 |
|
||||
| 内存峰值(8GB Mac 目标机) | 可接受且无 OOM |
|
||||
| 安装成功率 | 普通用户可完成 ModelScope/HF 下载 |
|
||||
Apple fallback 强制 `requiresOnDeviceRecognition = true`,系统缺少对应语言模型时会
|
||||
明确失败,不会静默切云。中文路径会准备打包的 Apple Custom Language Model;
|
||||
个人词库仍参与识别后的 alias 纠错与 polish 保真。
|
||||
|
||||
未达阈值:**保留 Qwen3 MLX 主线**,Sherpa 仅作高级选项。
|
||||
当前 `LocalASRCapabilities.appleSpeech` 声明 `hotwordMode = none`,因此不要把
|
||||
`contextualStrings` 描述为已经由个人词库动态注入。相关 API 虽有适配入口,但当前
|
||||
capability 不生成 hard hotwords。
|
||||
|
||||
---
|
||||
## 3. 代码索引
|
||||
|
||||
## 12. POC 评测计划
|
||||
| 主题 | 当前路径 |
|
||||
|---|---|
|
||||
| Mac 听写编排 | `OSGKeyboardMac/MacDictationPipeline.swift` |
|
||||
| 本地引擎选择 / fallback | `OSGKeyboardMac/MacLocalASRService.swift` |
|
||||
| MLX live capture | `OSGKeyboardMac/MacMLXLiveCapture.swift` |
|
||||
| MLX provider | `OSGKeyboardMac/MacMLXStreamingASRProvider.swift` |
|
||||
| MLX streaming session | `OSGKeyboardMac/MacMLXStreamingSession.swift` |
|
||||
| Apple Speech fallback | `OSGKeyboardMac/MacSpeechLocalASR.swift` |
|
||||
| Bias payload / capability | `OSGKeyboardShared/Models/LocalASRBiasPayload.swift`, `LocalASRCapabilities.swift` |
|
||||
| Bias 构建 | `OSGKeyboardShared/Services/LocalASRBiasAdapter.swift` |
|
||||
| 内置词索引 | `OSGKeyboardShared/Services/BuiltinLexiconIndex.swift` |
|
||||
| 用户词库 | `OSGKeyboardShared/Models/PersonalDictionary.swift` |
|
||||
| 云 ASR bias | `OSGKeyboardShared/Models/PersonalDictionary+ASRBias.swift` |
|
||||
| Apple CLM | `OSGKeyboardHostSupport/Services/CustomLanguageModelManager.swift` |
|
||||
| 内置 TSV | `OSGKeyboard/Resources/CustomLanguageModel/v1/phrases.tsv` |
|
||||
|
||||
### 12.1 测试集
|
||||
## 4. 设计目标与边界
|
||||
|
||||
### 4.1 目标
|
||||
|
||||
- Mac 本地听写离线可用,音频默认不离开设备。
|
||||
- iOS 与 Mac 复用 `PersonalDictionary` 和 `phrases.tsv` 的源数据。
|
||||
- 每个 backend 如实声明 soft prompt、hard hotword、streaming 与 reload 成本。
|
||||
- 模型下载、校验、安装和选择显式可管理。
|
||||
- 质量、延迟、内存与误触发均可量化评测。
|
||||
- 本地失败不静默回退云端。
|
||||
|
||||
### 4.2 非目标
|
||||
|
||||
- 不把一万词全量塞进 ASR prompt。
|
||||
- 不把 polish 当作唯一专名纠错层。
|
||||
- 不把尚未进入 catalog/runtime 的 Sherpa 或 SenseVoice 描述成当前能力。
|
||||
- 不承诺未经同一测试集验证的模型质量。
|
||||
- 不把 iOS 生成的 CLM `.bin` 直接喂给 MLX 或其他 backend。
|
||||
|
||||
## 5. 能力模型
|
||||
|
||||
`LocalASRCapabilities` 区分:
|
||||
|
||||
| 字段 | 含义 |
|
||||
|---|---|
|
||||
| `hotwordMode` | `none` / `promptOnly` / `perRequest` / `recognizerScoped` / `cloudVocabulary` |
|
||||
| `maxPromptCharacters` | soft prompt 上限 |
|
||||
| `maxHotwordCount` | hard hotword 上限 |
|
||||
| `supportsStreaming` | 是否提供真流式 partial |
|
||||
| `hotwordReloadCost` | `none` / `recognizerReload` / `modelReload` |
|
||||
|
||||
当前实际运行矩阵:
|
||||
|
||||
| Backend | 当前角色 | Bias | Streaming |
|
||||
|---|---|---|---|
|
||||
| Qwen3 MLX | Mac 默认 | `promptOnly`,800 字符 | 是 |
|
||||
| Apple Speech | Mac fallback / 显式选择 | ASR 层 `none`;后处理与 polish 仍接词库 | 否 |
|
||||
| iOS SpeechAnalyzer | iOS 主路径 | Apple CLM + 本地纠错 | 渐进结果 |
|
||||
| Cloud ASR | 用户显式选择 | 按 provider 使用个人词库 | 按 provider |
|
||||
|
||||
## 6. 竞品研究中仍有效的结论
|
||||
|
||||
基于 2026-03 的公开源码快照,保留以下架构结论,不把它们当作 OSG 当前实现:
|
||||
|
||||
| 项目 | 观察 | 对 OSG 的启示 |
|
||||
|---|---|---|
|
||||
| OpenLess | 多 provider;本地 Qwen3 词典接线有限 | README 不能把统一词库接口等同于 backend 已消费 |
|
||||
| Typeflux | 词库限额、动态排序、项目词学习 | Top-N 与上下文排序有价值;自动学习必须可确认 |
|
||||
| SayIt | Sherpa Qwen3 recognizer-scoped hotwords | 若未来重做 Sherpa POC,词库变化需计入 recognizer 重建成本 |
|
||||
| VoiceSnap | SenseVoice 离线与静音处理 | 可作为速度/资源基线,不代表具备个性词库 |
|
||||
| OpenBroca | 显式 model selection 与 manifest | 禁止扫描目录后任取第一个模型 |
|
||||
|
||||
这些研究只支撑未来实验。当前发布架构仍是 Qwen3 MLX + Apple Speech fallback。
|
||||
|
||||
## 7. 模型管理
|
||||
|
||||
当前安装由 catalog 描述 repository files、模型布局与体积,安装状态由 manifest
|
||||
和必要文件校验决定。设计原则保持:
|
||||
|
||||
- Catalog 与 runtime 分离。
|
||||
- `selectedModelId` 显式保存;旧 ID 有确定迁移规则。
|
||||
- 下载进入 staging,经校验后原子发布。
|
||||
- 无效或未安装模型不能假装可用。
|
||||
- 中国大陆镜像与官方 Hugging Face 可按偏好选择。
|
||||
|
||||
ModelScope、自定义企业镜像与额外 backend 可以作为后续 catalog 扩展,但不是
|
||||
1.7.0 当前下载路径。
|
||||
|
||||
## 8. 评测方法
|
||||
|
||||
### 8.1 测试集
|
||||
|
||||
| 类别 | 内容 | 目的 |
|
||||
|------|------|------|
|
||||
| A 普通中文 | 日常口语 50 句 | 基线 WER / 误触发 |
|
||||
|---|---|---|
|
||||
| A 普通中文 | 日常口语 50 句 | CER / 误触发基线 |
|
||||
| B 技术术语 | SwiftUI、Cursor、Qwen3-ASR 等 50 句 | 专名召回 |
|
||||
| C 用户词典 | 模拟 PersonalDictionary 20 词 × 多句 | 热词核心场景 |
|
||||
| D 长句润色 | 30s+ 口语 | polish 兜底 |
|
||||
| E 噪声 / 短句 | 低 SNR、<2s | 防污染规则 |
|
||||
| C 用户词典 | 20 个个人词,每词多句 | bias 与 alias 纠错 |
|
||||
| D 长句 | 30 秒以上口语 | streaming 稳定性与 finalize |
|
||||
| E 噪声 / 短句 | 低 SNR、少于 2 秒 | 幻觉与热词污染 |
|
||||
| F 中英混合 | 技术会议和代码口述 | 语言提示与专名保真 |
|
||||
|
||||
### 12.2 对照矩阵
|
||||
### 8.2 当前对照矩阵
|
||||
|
||||
| 配置 | 说明 |
|
||||
|------|------|
|
||||
| Baseline | Qwen3 MLX,无 bias |
|
||||
| B1 | Qwen3 MLX + promptBias |
|
||||
| B2 | B1 + polishFragment + correction |
|
||||
| POC1 | Sherpa Qwen3 + hardHotwords |
|
||||
| POC2 | SenseVoice,无 hotwords |
|
||||
| Ref | 云 ASR + PersonalDictionary |
|
||||
|---|---|
|
||||
| Baseline | Qwen3 MLX 0.6B,不传 bias |
|
||||
| B1 | 0.6B + `promptBias` |
|
||||
| B2 | B1 + alias correction + polish supplement |
|
||||
| Quality | Qwen3 MLX 1.7B + B2 |
|
||||
| Fallback | Apple Speech + correction / polish |
|
||||
| Reference | 用户所选 Cloud ASR + `PersonalDictionary` |
|
||||
|
||||
### 12.3 指标
|
||||
未来 Sherpa/SenseVoice 只能作为新增 POC 行,不能替换当前 baseline 名称。
|
||||
|
||||
- Raw CER/WER(中文可用字错误率)
|
||||
- **Hotword recall**(用户词是否出现在 raw 或 final)
|
||||
- **False hotword rate**
|
||||
- Final accuracy(用户主观或编辑距离)
|
||||
- Latency:record end → text inserted
|
||||
- Memory / CPU、模型加载时间
|
||||
- 离线可靠性(无网络完成全流程)
|
||||
### 8.3 指标
|
||||
|
||||
---
|
||||
- Raw CER/WER。
|
||||
- 用户词 raw/final hotword recall。
|
||||
- False hotword rate。
|
||||
- 录音结束到最终插入的延迟,以及 live partial 首次可见延迟。
|
||||
- 8 GB Apple-silicon Mac 上的峰值内存、CPU 与模型加载时间。
|
||||
- 无网络完成率、模型安装成功率、fallback 成功率。
|
||||
|
||||
## 13. 失败回退策略
|
||||
若未来评估 hard-hotword backend,建议门槛:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
start["recognize 开始"] --> qwen{"Qwen3 MLX 可用?"}
|
||||
qwen -->|是| qwenRun["Qwen3 + bias"]
|
||||
qwen -->|否| apple["Apple Speech"]
|
||||
qwenRun -->|失败| apple
|
||||
qwenRun -->|成功| post["Correction + Polish"]
|
||||
apple --> post
|
||||
post -->|Polish 失败| raw["返回 raw transcript"]
|
||||
post -->|成功| done["插入 final"]
|
||||
```
|
||||
- 用户词召回相对当前 Qwen3 MLX + B2 提升至少 20%。
|
||||
- False hotword rate 不高于 2%。
|
||||
- 30 秒音频端到端延迟不超过当前基线 1.5 倍。
|
||||
- 分发体积、签名、公证与 8 GB 设备内存均可接受。
|
||||
|
||||
| 条件 | 行为 |
|
||||
|------|------|
|
||||
| Qwen3 权重缺失 | Apple Speech(现状) |
|
||||
| Qwen3 推理失败 | 可配置:重试一次 → Apple Speech |
|
||||
| Sherpa 未安装 | 不回退云;提示下载 |
|
||||
| 模型 manifest 损坏 | 标记 invalid,禁止设为默认 |
|
||||
| Polish 失败 | 使用 raw(现状) |
|
||||
| 用户禁用云 | 不静默切云 |
|
||||
## 9. 回退与风险
|
||||
|
||||
---
|
||||
| 条件 | 当前/要求行为 |
|
||||
|---|---|
|
||||
| MLX 模型未安装 | 使用 Apple Speech fallback |
|
||||
| MLX live 失败 | 返回 batch recovery 信号 |
|
||||
| Apple 本地语言模型缺失 | 明确报错并提示系统下载,不切云 |
|
||||
| Polish 缺 Key、超时或失败 | 返回本地识别/纠错文本 |
|
||||
| 用户禁用云 | 不静默切换云 ASR |
|
||||
| 热词 dump / 近静音幻觉 | 丢弃可疑 live 结果并尝试 batch |
|
||||
|
||||
## 14. 分阶段落地路线
|
||||
主要后续风险:
|
||||
|
||||
| Phase | 内容 | 交付物 |
|
||||
|-------|------|--------|
|
||||
| **1** | 本文档定稿;`LocalASRCapabilities` + `LocalASRBiasPayload` 类型设计 | 架构文档 + ADR 可选 |
|
||||
| **2** | `MacDictationPipeline` 接入 adapter;Qwen3 MLX `promptBias`;polish + correction | 实现 PR |
|
||||
| **3** | `BuiltinLexiconIndex`;Top-N;diagnostics UI | 实现 PR |
|
||||
| **4** | Model catalog + ModelScope 下载 + manifest | 实现 PR |
|
||||
| **5** | Sherpa Qwen3 POC + 评测报告 | 决策是否默认切换 |
|
||||
| **6** | 可选:自动词库学习实验(默认关) | 功能 flag |
|
||||
1. 800 字符 soft prompt 对低频专名的提升有限。
|
||||
2. 内置 Top-N 过多会污染短句或近静音输入。
|
||||
3. alias 替换必须保持整词/高置信边界。
|
||||
4. 1.7B 模型在低配 Mac 上的内存与首载延迟需持续量化。
|
||||
5. 新 backend 必须先证明收益,再承担下载、签名和维护成本。
|
||||
|
||||
---
|
||||
## 10. 后续方向
|
||||
|
||||
## 15. 风险与待确认问题
|
||||
1. 用固定测试集持续比较 0.6B / 1.7B、无 bias / 分层 bias。
|
||||
2. 将 diagnostics 与实际命中/截断数据用于调节 Top-N 和 prompt 上限。
|
||||
3. 验证 Apple Speech fallback 的个人词动态提示能力后,再决定是否修改 capability。
|
||||
4. 只有 hard-hotword 收益达到阈值时,才恢复 Sherpa Qwen3 POC。
|
||||
5. 自动词库学习若实验,必须默认关闭、本地处理、用户确认后写入,并可审计/清空。
|
||||
|
||||
| 风险 | 缓解 |
|
||||
|------|------|
|
||||
| Qwen3 prompt bias 过弱 | POC 对比 Sherpa hard hotwords;保留 correction + polish |
|
||||
| 热词过多污染识别 | cap + 场景筛选 + diagnostics |
|
||||
| aliases 后处理误改 | 整词边界、低置信跳过 |
|
||||
| Sherpa 分发体积 / 签名 / 公证 | 单独评估;可选按需下载 |
|
||||
| Apple Speech macOS CLM | 调研 macOS 26+ 是否可接 CLM;否则仅 fallback |
|
||||
| ModelScope API 变更 | 多 mirror;manifest 可更新 URL |
|
||||
| 自动学习隐私 | 默认关、本地、待确认 |
|
||||
| 低配 Mac 内存 | 单模型常驻策略;SenseVoice 作轻量选项 |
|
||||
|
||||
**待确认**:
|
||||
|
||||
1. MLX Swift API 是否暴露等价 `setPrompt`(对标 Open-Less/qwen-asr `qwen_set_prompt`)
|
||||
2. Sherpa-ONNX Swift/SPM 与 App Store 公证路径
|
||||
3. `phrases.tsv` Top-N 是否按 `weight` 静态裁剪即可,或需按 App 动态检索
|
||||
|
||||
---
|
||||
|
||||
## 16. 相关代码索引
|
||||
|
||||
| 主题 | 路径 |
|
||||
|------|------|
|
||||
| Mac 听写管道 | `OSGKeyboardMac/MacDictationPipeline.swift` |
|
||||
| 本地 ASR 入口 | `OSGKeyboardMac/MacLocalASRService.swift` |
|
||||
| Sherpa local ASR | `OSGKeyboardMac/MacSherpaLocalASR.swift`, `MacSherpaONNXRunner.swift` |
|
||||
| Apple Speech fallback | `OSGKeyboardMac/MacSpeechLocalASR.swift` |
|
||||
| 用户词库 | `OSGKeyboardShared/Models/PersonalDictionary.swift` |
|
||||
| 云 bias | `OSGKeyboardShared/Models/PersonalDictionary+ASRBias.swift` |
|
||||
| 内置 TSV | `OSGKeyboard/Resources/CustomLanguageModel/v1/phrases.tsv` |
|
||||
| iOS CLM | `OSGKeyboardShared/Services/CustomLanguageModelManager.swift` |
|
||||
| 润色 | `OSGKeyboardShared/Services/PolishingService.swift` |
|
||||
| 文本插入 | `OSGKeyboardMac/MacTextInsertionService.swift` |
|
||||
|
||||
---
|
||||
|
||||
## 17. 修订记录
|
||||
## 11. 修订记录
|
||||
|
||||
| 日期 | 说明 |
|
||||
|------|------|
|
||||
| 2026-03-31 | 初版:基于 OSG 代码审计 + typeless-alternative 竞品源码 + 计划评审(ModelScope、hotwordMode、回退策略) |
|
||||
|---|---|
|
||||
| 2026-08-11 | 按 1.7.0 代码重写:Qwen3 MLX streaming 默认、Apple Speech fallback、LocalASRBiasAdapter/PersonalDictionary 已接线;移除 Sherpa 当前路径叙述 |
|
||||
| 2026-03-31 | 初版竞品研究与评测框架 |
|
||||
|
||||
Reference in New Issue
Block a user