Adds a bundled local ASR model catalog for the macOS app with one-click Sherpa Qwen3 / SenseVoice downloads (pause/resume, inline actions) and a shared model storage directory used by MLX Qwen3. Fixes the light-mode sidebar material and makes the menu-bar icon follow the system appearance with a refreshed status mark. Renames the built product to OSGKeyboard.app. Bumps version to 0.5.2 (build 19).
23 KiB
OSGKeyboard 本地 ASR 技术架构
文档状态:架构规划(非实现规格)
适用范围:macOS 本地听写;与 iOS 键盘扩展、云 ASR 路径的关系见各节说明。
核心结论:短期不换主模型,优先打通 词库感知管道;中期用 POC 验证 Sherpa Qwen3 hard hotwords 是否值得成为热词主线。
1. Executive Summary
OSGKeyboard 的本地 ASR 竞争力不来自单一模型,而来自:
- 用户 PersonalDictionary(term / aliases / iCloud)
- 内置技术词库(
phrases.tsv≈ 1 万词,iOS 已用于 Apple CLM) - 分层 bias:ASR 偏置 → 后处理纠错 → Polish 保真
- 可替换的 Local ASR Provider(Qwen3 MLX 主线,Sherpa / SenseVoice / Apple Speech 对照)
当前最大缺口: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 端到端数据流
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
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— 本地分支未传personalDictionaryOSGKeyboardMac/MacLocalASRService.swift— Qwen3 / Apple Speech 二选一OSGKeyboardShared/Models/PersonalDictionary+ASRBias.swift— 云侧asrHotwords/asrPromptBias/ 阿里热词表OSGKeyboardShared/Services/PolishingService.swift— 润色层消费dictionary.promptFragment()
3.3 iOS 词库资产(macOS 不可直接复用)
- 内置词库:
OSGKeyboard/Resources/CustomLanguageModel/v1/phrases.tsv(约 10,301 行,含word/pinyin/source/weight) - Apple CLM:
OSGKeyboardShared/Services/CustomLanguageModelManager.swift→SFSpeechLanguageModel.Configuration,仅 iOS 26+ 中文本地 ASR 路径 - 结论:macOS 需从 TSV +
PersonalDictionary重新适配为 Qwen3 prompt / Sherpa hotwords / polish fragment,不能加载OSGKeyboardCLM.bin
3.4 本地模型现状
- 默认路径:
~/Library/Application Support/OSGKeyboard/models/qwen3-asr-1.7b-mlx - 校验:
config.json、model.safetensors、vocab.json、merges.txt - Qwen3 引擎:
MacQwen3LocalASR.swift/MacQwen3ASREngine.swift— 无 prompt / hotword 入参
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 的启示
- 学架构,不学「本地已完整支持热词」的 README 叙事(OpenLess 本地 Qwen3 缺口与 OSG 类似)。
- SayIt 证明:sherpa-onnx
OfflineQwen3ASRModelConfig.hotwords可在 recognizer 创建时注入;热词变化需 重建 recognizer(缓存 key 含 hotwords 字符串)。 - Typeflux 证明:
activeTerms()上限 500 + 动态排序;但 Sherpa Qwen3/SenseVoice 命令行路径未传 vocabulary prompt。 - OpenBroca 证明:runtime 不得静默选「目录里第一个模型」;须
selectedModelId+ manifest。
5. 目标架构
5.1 管道总览
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)为可选能力,非第一期必做。
建议协议(概念层):
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
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 为 单一事实来源,新增例如:
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 保证 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
qwen3-mlx-1.7b/ # 当前 MLX 布局(可与现路径兼容)
sherpa-qwen3-0.6b/
sherpa-sensevoice-small/
10.3 Catalog 条目(概念)
{
"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 产品分层
主线: Qwen3 MLX + LocalASRBiasAdapter + Polish/Correction
重点 POC: Sherpa Qwen3 hotwords
对照: SenseVoice(速度)、Apple Speech(fallback)
参考上限: Cloud ASR + PersonalDictionary
团队部署: Qwen3-ASR vLLM(SayIt 式,非客户端主线)
暂不主推: FunASR Paraformer hotwords、纯 OpenLess 本地词典叙事
11.2 详细对比
| 维度 | Qwen3 MLX | Sherpa Qwen3 | SenseVoice | Apple Speech | Cloud |
|---|---|---|---|---|---|
| 离线 | ✅ | ✅ | ✅ | ✅ | ❌ |
| 中英混合技术词 | 强(经验性) | 待 POC | 中 | 中 | 强 |
| Hard hotwords | ❌→prompt | ✅ recognizerScoped | ❌ | ❌ | ✅ 因 provider 异 |
| 实现成本 | 低(已有) | 高(runtime 体积) | 中 | 低 | 已有 |
| 模型体积 | ~1.3GB+ | 类似 | ~350MB 级 | 0 | N/A |
| 隐私 | 本地 | 本地 | 本地 | 本地 | 依配置 |
11.3 Sherpa POC 通过阈值(建议)
相对 当前 Qwen3 MLX + 仅 polish 基线:
| 指标 | 建议阈值 |
|---|---|
| 用户热词召回率 | 提升 ≥ 20% |
| 误触发率(未说热词却被改成热词) | ≤ 2% |
| 30s 音频端到端延迟 | ≤ 基线 × 1.5 |
| 内存峰值(8GB Mac 目标机) | 可接受且无 OOM |
| 安装成功率 | 普通用户可完成 ModelScope/HF 下载 |
未达阈值:保留 Qwen3 MLX 主线,Sherpa 仅作高级选项。
12. POC 评测计划
12.1 测试集
| 类别 | 内容 | 目的 |
|---|---|---|
| A 普通中文 | 日常口语 50 句 | 基线 WER / 误触发 |
| B 技术术语 | SwiftUI、Cursor、Qwen3-ASR 等 50 句 | 专名召回 |
| C 用户词典 | 模拟 PersonalDictionary 20 词 × 多句 | 热词核心场景 |
| D 长句润色 | 30s+ 口语 | polish 兜底 |
| E 噪声 / 短句 | 低 SNR、<2s | 防污染规则 |
12.2 对照矩阵
| 配置 | 说明 |
|---|---|
| Baseline | Qwen3 MLX,无 bias |
| B1 | Qwen3 MLX + promptBias |
| B2 | B1 + polishFragment + correction |
| POC1 | Sherpa Qwen3 + hardHotwords |
| POC2 | SenseVoice,无 hotwords |
| Ref | 云 ASR + PersonalDictionary |
12.3 指标
- Raw CER/WER(中文可用字错误率)
- Hotword recall(用户词是否出现在 raw 或 final)
- False hotword rate
- Final accuracy(用户主观或编辑距离)
- Latency:record end → text inserted
- Memory / CPU、模型加载时间
- 离线可靠性(无网络完成全流程)
13. 失败回退策略
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 权重缺失 | Apple Speech(现状) |
| Qwen3 推理失败 | 可配置:重试一次 → Apple Speech |
| Sherpa 未安装 | 不回退云;提示下载 |
| 模型 manifest 损坏 | 标记 invalid,禁止设为默认 |
| Polish 失败 | 使用 raw(现状) |
| 用户禁用云 | 不静默切云 |
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 |
15. 风险与待确认问题
| 风险 | 缓解 |
|---|---|
| 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 作轻量选项 |
待确认:
- MLX Swift API 是否暴露等价
setPrompt(对标 Open-Less/qwen-asrqwen_set_prompt) - Sherpa-ONNX Swift/SPM 与 App Store 公证路径
phrases.tsvTop-N 是否按weight静态裁剪即可,或需按 App 动态检索
16. 相关代码索引
| 主题 | 路径 |
|---|---|
| Mac 听写管道 | OSGKeyboardMac/MacDictationPipeline.swift |
| 本地 ASR 入口 | OSGKeyboardMac/MacLocalASRService.swift |
| Qwen3 MLX | OSGKeyboardMac/MacQwen3LocalASR.swift, MacQwen3ASREngine.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. 修订记录
| 日期 | 说明 |
|---|---|
| 2026-03-31 | 初版:基于 OSG 代码审计 + typeless-alternative 竞品源码 + 计划评审(ModelScope、hotwordMode、回退策略) |