语音输入架构:Loqua 三模型语音输入栈内部解析
为什么我们把语音识别、语言智能和屏幕上下文拆成三层,以及我们如何诚实看待内部指标。
TL;DR
这是一篇 blog 级别的 Loqua 语音输入架构说明,也解释为什么真正适合听写的 voice ai architecture 不是一个巨大的 ASR 模型。Loqua 由三层协作构成:语音识别、语言智能和多模态上下文。原因很简单:听写质量不只是 word error rate。一个有用的 dictation model stack 必须听清词,理解技术名称,并按目标 app 组织输出。我们的内部目标是 200ms 级响应、高技术词汇识别率,以及在支持条件下低个位数 WER;在公开 benchmark 页面发布前,请把这些视为内部测量,而不是第三方结论。
我是 Shuran,带着一个小型算法研究团队,我们每天都用语音输入。我们做 Loqua,是因为评估过的听写工具虽然大多工程质量不错,也确实有用,但一旦推到代码、中英混合和 app-aware formatting 场景,就都撞到了同一个上限。这个上限是结构性的,不是调参问题。下面是我们决定必须改变结构之后,最终走到的架构。
这不是论文,而是一篇 blog 级别的 walkthrough:每一层背后的直觉、跑出来的指标,以及我们实际拿这套栈做什么。如果你想看更深的学术背景,文末的延伸阅读会列出我们参考过的论文。
套壳 Whisper 的陷阱
OpenAI 的 Whisper 改变了英语语音识别的可能性。它是一个非常出色的模型。Whisper 论文展示了,扩展弱监督音频训练可以得到强鲁棒性的通用 ASR,覆盖口音、环境和 99 种语言,而且不需要按领域 fine-tuning。这对整个领域都是结构性进展。
但 Whisper 是 ASR 模型,不是听写产品。"准确转录语音" 和 "生成我可以不编辑就贴进邮件的文字" 之间差距很大。套壳 Whisper 的做法,也就是把 Whisper 输出再交给格式化层,能补上一部分差距。但在我们关心的三个地方会失败:
- 技术词汇。Whisper 听到 "react query" 会给你 "react query"。它不知道在你的代码库里这其实是
@tanstack/react-query,也不知道你想要的是 package import。技术词汇上的 NER 需要一个能看到周围上下文的模型,而不是只听音素的模型。 - App-aware formatting。Whisper 做转录;它不知道你在 Slack 里,还是在 Python 文件里。在上面再接 formatter,要么依赖脆弱的 heuristic,要么每次 utterance 都调用重型 LLM,既慢又依赖云端。
- 紧延迟预算下的流式输出。Whisper 很适合 batch transcription。要做低延迟、设备端、流式 Whisper,要么牺牲(更小模型、更低准确率),要么做大量和模型结构对抗的工程优化。
我们在研究原型里试过套壳 Whisper 这条路。它足够用来验证想法,但不够好到成为我们每天使用的工具。
三模型直觉:另一种语音识别栈
核心直觉是:语音输入其实是三件事,而同一个模型很难把三件都做好。这三件事是:
- 听清词。Acoustic-to-token。这是 ASR 擅长的事。
- 理解意图。清理填充词、错误开头、句中修正。识别技术实体。判断用户真正想输入什么。
- 放到正确位置。按目标 app 格式化输出。让同一个意图在 VS Code 里变成代码,在 Slack 里变成消息,在 Cursor 里变成结构化 prompt。
一个端到端模型试图同时做三件事,会遇到很硬的结构性问题:"转录音素" 的 loss surface 和 "按 Slack 格式输出" 的 loss surface 指向不同方向。联合训练会同时折中两边。我们自己的 ablation 也能确认这一点:我们试过把第 2 层和第 3 层合并成一个用 multi-task data 训练的 transformer,结果两边准确率都下降。
拆成三层,每个模型就能把一件事做好。代价是层间 hand-off 会增加一点延迟;我们通过让整条 pipeline 都跑在 Neural Engine 上把它追回来。这就是 Loqua 语音输入架构的核心:不是一个单体转录器,而是一个小型 dictation model stack,每一层都为自己负责的部分专门训练。
第 1 层:语音识别
Acoustic input → token sequence。这是一套任务特定的语音识别器,而不是直接套 Whisper。架构选择由三个我们不愿妥协的约束驱动:
- Streaming-first。说话人还没说完,输出就要开始。这排除了默认对完整音频序列做 non-causal attention 的方案,我们使用的是更适合流式的变体。
- 设备端 Neural Engine 兼容。模型大小和 operator 选择,都受限于能否通过 Apple Core ML 在 Neural Engine 上高效运行。这是一个真实约束;论文里看起来没问题的 operator,可能会掉出 Neural Engine 路径,最后变成 CPU-bound。
- 低音量鲁棒性。我们的训练数据有意包含 whisper-volume input,比如在咖啡馆、深夜小声听写。大多数通用 ASR 训练数据是正常音量;小声输入需要显式覆盖。
这一层输出的是带 timing 和 confidence scores 的 token sequence。它还不是最终文字,而是下一层要清理的原始识别结果。
第 2 层:语言智能
Token sequence → cleaned, intent-resolved text。这里是我们投入最多研究精力的地方,因为大部分用户可感知质量都在这一层。
这一层的任务是:把语音模型听到的内容,变成用户真正想写下的内容。三件事会并行发生:
- 移除填充词和错误开头。"Um, so, basically, we should — actually wait, let me start over — we should cache this" 会变成 "We should cache this." 句中修正会被保留,清嗓式内容会被去掉。
- 技术词汇 NER。这一层会学习常见库、框架、模型家族、文件扩展名、终端命令和惯用 API surface 的名称。当周围上下文是 JavaScript 文件时,"React query" 会变成
@tanstack/react-query。我们的内部目标是在 curated in-domain technical vocabulary 上达到 high-90s 识别率,让常见标识符不需要每个都进 personal dictionary 也能写对。 - 结构化成形。输出应该是一句话、项目符号列表、Markdown 表格,还是代码注释,会在这里根据下一层(多模态上下文)提供的目标位置判断来决定。
按参数量算,这一层是栈里最小的模型;但按用户体验影响,它最大。我们投入在这一层的团队工时,比另外两层加起来还多。
第 3 层:多模态上下文
App state + screen + cursor → format directive。这是 omni-modal 层,也是我们不只把 Loqua 称为“听写 app”的原因。Loqua 的工作不是转录,而是在你想写的位置,写出你真正想表达的内容。
上下文层会通过 macOS Accessibility 读取当前活动 app、选中文本(如果有)、可见的相邻文本,以及目标位置的结构线索(Gmail compose vs Slack thread vs VS Code Python file vs Cursor chat panel)。它输出一条 format directive,语言层会用它来塑造最终文本。
更深的直觉,也就是为什么 omni-modal architecture 改变了语音输入而不只是增强它,是另一篇文章的主题。如果你想沿着这个线索继续看,可以读 voice meets vision: how omni-modal models unlock context-aware dictation。
我们跟踪的指标
| 指标 | 当前内部目标 | 为什么重要 |
|---|---|---|
| 端到端延迟 | Apple Silicon 上 200ms 级 | 低于用户开始觉得“我在等”的阈值 |
| Time-to-first-token (TTFT) | 常见流式场景低于 200ms | 较长 utterance 还在说时,前几个词已经出现 |
| NER accuracy | curated in-domain technical vocab 上 high 90s | 标识符、库名和模型名必须写对 |
| Multilingual WER | 支持测试条件下低个位数 | 中英混合和带口音英语必须能在真实工作流里使用 |
这些是内部 benchmark 和 dogfooding 数字,不是第三方 benchmark suite。测试集包含我们自己的技术词汇、中英切换样例、带口音英语样本,以及日常噪声环境。下一步内容上应该发布一个公开方法论页面,让这些数字能被引用,而不是靠含糊表述支撑。
我们自己怎么用
关于这套栈,最重要的一点是:我们写它,是因为我们自己要用。团队每个人每天都会听写:commit、PR、内部 Slack、更长的技术写作,以及(对我来说)像这篇 blog 里的大部分 prose。塑造这套架构的决策来自真实使用,而不是产品规格书。
日常使用里,架构和功能清单的差异在三个地方很明显:
- 代码听写。NER 质量决定了语音是能成为代码可用界面,还是只能当玩具。想看它能做什么,可以读 代码听写指南。
- 多语言 code-switching。我们内部 Slack 有一半是中文夹英文。语言层不是绕开 code-switched data,而是直接在它上面训练;句中切换不需要模式开关。
- App-aware formatting。同一句语音在 VS Code 里变成代码注释,在 GitHub 上变成结构化 PR 描述,这就是普通语音输入和真正有用产品之间的区别。
我们是小团队。这个故事诚实的版本是:我们没有资源同时维护一个 wrapped-Whisper 产品和一个三模型研究栈。我们选择结构性路径,是因为自己需要这种质量。相比做一个更宽但质量不够的东西,我们宁愿先交付一个范围更窄、质量更好的产品。
2026 年每一层如何重建
第 1 层:语音识别。识别器围绕 streaming、低音量语音和技术词汇重新收紧;更深入的 walkthrough 在 inside our omni-modal voice stack,post-training 细节在 RL in our voice stack。
第 2 层:语言智能。语言层现在把 cleanup、entity preservation 和 app-aware structure 当作同一个 dictation model stack,而不是分开的后处理器。强化学习在这里帮助最大:选择用户最少编辑的输出。
第 3 层:多模态上下文。上下文层围绕本地屏幕证据重建:当前 app、选中文本、可见标识符和光标周围内容。架构细节见 building a listener that sees what you see。
下一条前沿是非词语音频:把 AED 和 audio captioning 作为可选、local-first 的上下文。我们在 sounds with meaning 中介绍了这个原型阶段的工作。
延伸阅读
如果你想看比这篇 blog 更深的内容:
- Whisper 论文(Radford et al., 2022):我们在第 1 层借鉴的弱监督音频训练范式。
- Apple Core ML 文档:了解 Neural Engine 部署在实践中是什么样。
- 关于第 3 层推理,可以读我们的配套文章 voice meets vision: omni-modal dictation。
- 关于哪些数据会过网、哪些不会,可以读 privacy by design with a hybrid architecture。
如果你对这套架构有问题,或者想深入聊其中某一层,欢迎给我们发邮件。我们是小团队,每封邮件都会读。
常见问题
今天就试试 Loqua
免费开始。Mac 原生。由每天都在使用它的算法研究者打造。
下载