全模态语音输入:多模态理解、MoE 与流式文本输出
一次技术拆解:Loqua 如何把听觉、多模态任务理解和文本渲染结合起来,同时不让语音输入变慢。
要点速览
全模态语音输入不是一个巨大的音频模型。Loqua 是一款 Mac 原生语音输入工具,把语音识别、多模态指令规划和文本渲染拆成一个流式文本输出流水线。这样的拆分让我们可以放入音频编码器 MoE、本地屏幕上下文和 200ms 级交互,同时也不会暗示产品只是某个第三方模型的包装。
我是 Shuran,Loqua.ai 的创始人。这篇文章是我们全模态语音输入栈背后的深入工程笔记:为什么要把声学识别、多模态任务理解和文本渲染分开,条件专家在哪些地方有帮助、在哪些地方没有,以及为什么流式输出几乎决定了每个架构选择。
为什么单体音频 LM 会撞上 Mac 墙
单体音频语言模型在纸面上很诱人:把音频、屏幕帧和文本都喂给一个大模型,让它直接输出最终听写结果。但在日常 Mac 使用里,这个设计会同时撞上三堵墙。第一,延迟预算非常残酷。如果第一个有用 token 要等到说话人停顿之后才出现,语音输入就会像批量转写,而不是打字。
这是我们给自己的预算。从麦克风到第一个可见字符,大约只有 200 毫秒。其中约 40ms 是平台开销(Core Audio 缓冲、进程间传递和最终渲染),60ms 给声学特征提取和前端推理,70ms 给指令规划器解码第一个 partial,剩下 30ms 留给文本渲染器发出一个安全的早期提交。让一个 1B+ 参数的音频 LM 在每一步都跑进这个窗口,在基准机器上也许可行,但在一台同时开着浏览器、IDE 和 Zoom 的真实笔记本上并不可靠。
第二,用户可见的任务不只是自动语音识别。同一句话可能要变成 Slack 段落、Git commit 标题、Cursor prompt,或一条 Python 注释。单体模型可以训练成都会做,但它必须为每个应用重新学习格式惯例,并在光标每次移动时重新推断。第三,模型必须留在笔记本的散热和内存包络里。一个 7B 音频-视觉-文本模型和 IDE 索引一起换入 VRAM,很容易带来降频和风扇噪音。Whisper 这类公开工作和近期全模态模型报告帮助整个领域理解稳健音频和多模态建模,但要交付一个本地产品,必须做更窄、更有取舍的选择。
我们的结论很简单:全模态语音输入应该是一条流水线,而不是一个包办一切的模块。流水线让每一层都有小目标、可衡量的失败模式和有边界的运行成本。
多模态指令流水线
在这条产品路径里,Loqua 没有语音回复或 TTS 层。指令规划器消费流式音频特征、选定的屏幕上下文、活动应用元数据、光标附近最近文本,以及当前指令 prompt。它输出一个紧凑的文本输出计划:意图、实体、编辑模式、目标格式和不确定性。
文本渲染器把这个计划转成文本。它不生成音频、语音回复或 TTS 输出。它只判断这个计划应该变成 Markdown checklist、自然句子、代码注释,还是结构化指令。这个分离让昂贵的跨模态推理不必进入最热路径,尤其当用户只是普通听写正文时。
每一层不该做什么
纪律体现在我们删掉了什么。声学前端不尝试理解目标格式;它只输出带时间和置信度的音频 token。指令规划器不写最终正文;它生成足够小、便于低成本修订的结构化计划。文本渲染器不重新读取音频,也不合成语音;它信任计划、目标上下文和指令 prompt。早期原型里,任何一层一旦越界,几乎都是延迟回退最常见的原因,因为跨层反馈循环会悄悄把流式处理变成批处理,直到你读 trace 才发现。
| 层 | 主要输入 | 输出 | 我们关注的失败 |
|---|---|---|---|
| 声学前端 | 流式麦克风帧 | 带时间的音频 token | 低声说话和嘈杂房间漏识别 |
| 指令规划器 | 音频 token + 屏幕上下文 + 指令 prompt | 意图和文本输出计划 | 错误的目标场景假设 |
| 文本渲染器 | 计划 + 应用约束 | 仅最终文本 | 过度格式化或修正太晚 |
好处是可调试性。当输出错了,我们能判断是识别器听错词,指令规划器选错意图,还是文本渲染器用了错误语气。相比解释一条漫长的隐藏轨迹,这容易得多。在我们的内部 triage 里,大约三分之二的上线后质量修复可以清晰归因到单层;剩下的需要跨层改动,但也是在诊断清楚之后才做。
音频编码器上的 MoE
音频编码器是条件计算真正产生收益的地方。不是每句话都需要同样的声学处理。低声说话、带口音的英语、中英混说、代码标识符和会议室背景噪声,会压测模型的不同部分。一个小型 mixture-of-experts 路由器,可以让编码器把更多容量花在困难区域,而不是让每一帧都变贵。
我们的路由保持保守。路由器基于声学统计和浅层词汇线索,而不是个人用户画像。专家会专门处理低振幅语音、快速技术听写、code-switching 等模式。我们拒绝更大的专家池,因为路由不稳定会让流式行为变差:一个准确但会在半句话里改变风格的模型,对打字并不可用。
我们试过又放弃的东西
有两个想法在研究上很吸引人,但在产品里失败了。第一,每用户专家适配:为重度用户训练一个小 adapter。它在内部测试集上把冷启动准确率提高了几个点,但让冷启动内存占用翻倍,也让隐私边界更模糊。第二,让路由器把活动应用标识作为强信号。它会过拟合少数常见应用,并在新应用里悄悄退化。我们用现在的声学和词汇路由替代了它,并把应用感知行为上移到指令规划器和文本渲染器,因为那里才是它应该存在的位置。
实际效果是,音频编码器 MoE 帮助 Loqua 保持常见路径快速,同时让长尾不那么脆弱。它的产品价值不是基准技巧,而是减少库名、低声短语或双语片段打断工作流的时刻。
流式:逐 token 还是逐短语
流式输出是最难的取舍。逐 token 输出感觉灵敏,但会暴露过早猜测。逐短语输出更干净,却显得慢。我们使用混合方式:低风险片段早出 partial,需要屏幕上下文的实体和编辑则延迟提交。
例如,当你说“change the fetch profile function to return early if the auth client is missing”,系统可以快速流出普通词。但 fetchProfile 和 authClient 附近的 token 会等待上下文层确认可见标识符。这就是为什么文本渲染要和识别分开:它可以在提交文本前修订一个小片段,而不必重启整句话。
句中双语输入是相关场景。当你说“把这个 retry 函数改成指数退避”时,指令规划器会生成一个文本输出计划,把中文片段和一个英文代码 token 交错起来。文本渲染器会在中文字符通过内部置信阈值后立刻输出,同时在英文标识符上多等几毫秒,用可见 IDE buffer 做确认。用户会先看到中文流出,然后看到标识符,最后看到剩下内容。重排输出也许更快,但感觉不对;眼睛期待从左到右的打字过程。
提交边界是另一个旋钮。提交边界指的是文本渲染器承诺不再修订的时刻。我们把它钉在自然停顿、句末标点,以及段落、列表项或代码块这类结构转换上。在提交边界以内,文本渲染器可以自由修订;一旦提交,从用户视角看文本就是最终结果。这个契约让流式输出显得诚实,而不是抖动。
结果是一套看起来立即响应、但不会把每个早期声学猜测都当成最终答案的流式语音栈。对语音输入来说,这个折中比原始转写速度更重要。
开放研究背景
我们密切关注开放研究,因为它能让我们更准确地描述产品里遇到的问题。关于音频语言建模、多模态编码器和偏好优化的论文与报告,正在让领域逐渐收敛到责任拆分、流式约束和跨模态对齐。背景阅读可以从 Whisper、Qwen2 技术报告 和公开的 Qwen2.5-Omni 概览 开始。
重要的产品边界是:这些引用是文献背景。Loqua 的生产栈是我们为 Mac 听写原创训练和优化的研究成果。我们引用开放工作是为了解释领域,而不是暗示来源。
我们已经构建了什么,下一步是什么
从这个方向已经落地的是一套窄系统:Mac 语音输入,能够聆听、读取本地屏幕上下文,并写出应用感知文本。下一步是更严格的评测。我们需要公开的方法论来评估延迟、技术 NER、多语言 WER 和屏幕上下文消歧,让这些主张可以在内部 dogfooding 之外被审视。
第二个方向是更好的校准。当模型不确定某个可见标识符时,它应该通过选择安全输出形式或保留原始短语,减少用户负担。我们也在探索文本渲染器里的轻量不确定性标记,让下游 UI 可以高亮低置信片段,方便用户用键盘快速修正,同时不打断听写节奏。
第三个方向是非词语音。这项工作还处在原型阶段,我们在有意义的声音里单独讨论。它和我们在强化学习以及多模态监听器上的工作一起,构成了 Loqua 全模态语音输入栈下一年的议程。
常见问题
今天就试试 Loqua
免费开始。Mac 原生。由每天都在使用它的算法研究者打造。
下载