Cherry Studio 语音交互一文搞定:语音输入与语音输出的 3 层实现思路
【免费下载链接】cherry-studioAI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs项目地址: https://gitcode.com/GitHub_Trending/ch/cherry-studio
在 Cherry Studio 的语音交互中,语音输入负责把你说的话变成文本喂给大模型,语音输出负责把模型回复念出来——整条链路就是"麦克风 → ASR → LLM → TTS → 扬声器"。本文用分层拆解的方式讲清楚每一层做什么、选型依据是什么、以及低延迟场景下该调哪里,帮你在 10 分钟内建立对桌面端语音交互的完整认知。
先给结论,再逐层展开:
- 输入层用浏览器原生 ASR(Web Speech API)起步,够快且零依赖;
- 推理层复用 Cherry Studio 已有的流式消息管道,语音只是另一种输入形态;
- 输出层用 TTS 引擎 + 队列管理,核心是打断、分段与音色一致性。
🎙️ 语音输入层:如何把麦克风声音变成可用文本
语音识别(ASR,Automatic Speech Recognition)是把音频流转成文字的技术。桌面客户端里最常见的两条路:
| 方案 | 原理 | 优点 | 代价 |
|---|---|---|---|
| Web Speech API | 调用浏览器/Chromium 内置识别引擎 | 零依赖、流式返回中间结果 | 依赖系统语言包,离线能力弱 |
| 云端 ASR(如 Whisper 类 API) | 音频上传到服务端识别 | 准确率高、支持中英混合 | 增加延迟与费用,涉及隐私 |
| 本地模型(DeepSpeech / faster-whisper) | 进程内跑推理 | 完全离线 | 模型体积大,需要算力评估 |
Cherry Studio 这类 Electron 应用天然跑在 Chromium 里,webkitSpeechRecognition开箱即用。关键只有一段配置:
const rec = new (window.SpeechRecognition || window.webkitSpeechRecognition)(); rec.continuous = false; // 单轮识别,说完即止 rec.interimResults = true; // 边说边出中间文本,UI 可以实时上屏 rec.lang = 'zh-CN'; // 语言要跟随用户设置,别写死 rec.start();三个容易踩的坑:
- 权限提示:首次调用
getUserMedia/ 启动识别会弹系统麦克风授权,必须在 UI 上准备好"未授权"的降级文案,而不是让用户对着一个没反应的按钮发呆。 - 中间结果与最终结果:
interimResults给出的是草稿文本,只有isFinal的那条才适合直接发往模型,否则会把半句话提交出去。 - 识别中断:
onerror的no-speech(用户没说话)和onend(识别自然结束)语义不同,前者应静默重试或给轻提示,后者才切换 UI 状态。
如果目标是对话效率而非打字替代,推流(push-to-talk)比持续监听更稳:按住说话 → 松开触发识别,天然规避了"模型说话时麦克风把 TTS 声音也录进去"的回环问题。
⚡ 推理层:语音文本如何走 Cherry Studio 既有管道
识别出的文本并不特殊——它就是用户消息。Cherry Studio 的消息链路是:渲染进程useChat()发起请求,经 Electron IPC 的 MessagePort 传送到主进程,由 AI Completion Service 调度到 AI Core / Agent SDK,再流式回推 UI 消息块并落库 SQLite:
这对语音交互的含义是:你不需要为语音单开一条推理通道。ASR 文本进入输入框后,和键盘输入共用同一套重试、流式、持久化逻辑。唯一要做的适配:
- 把语音输入标记在消息元数据里(来源字段),方便后续做"仅语音模式自动发送"或统计;
- Cherry Studio 的 provider-registry 中已定义了 speech 类模型枚举(见 src/shared 与 packages/provider-registry),选语音相关供应商时可直接按类型过滤。
📣 语音输出层:TTS 音色与语速怎么选
语音合成(TTS,Text-to-Speech)把模型回复转成音频。方案分两档:
- 系统 TTS(
speechSynthesis):零成本、零网络,音色取决于操作系统安装的声音包。适合"能不能响起来"的验证阶段。 - 云端 TTS / 本地模型(Azure、Edge、CosyVoice 等):音色自然度和一致性显著更好,支持按音色 ID 固定说话人——长对话里用户能"认"出同一个声音,体验差距就来自这里。
配置上的关键点只有三行:
const u = new SpeechSynthesisUtterance(chunk); u.voice = pickVoice(voices, 'zh-CN'); // 音色按会话固定,别每次随机 u.rate = 1.05; // 长文本略快于自然语速,读起来更紧凑 u.volume = 0.95;三个选型经验:
- 音色按会话持久化。用户选了某个声音后,整轮对话乃至整个应用会话都应复用,跳变的声音会直接劝退。
- 语速 1.0~1.15 区间。纯代码/长列表回复用 1.1 以上,情绪向内容用 1.0。
- 先剥离再念。Markdown 里的代码块、链接、表格直接读出来是灾难,送进 TTS 前先做一次"朗读版"转换(去代码块、表格转句子、公式跳过)。
🔄 两个最容易翻车的交互细节
打断(barge-in)
用户说话时 TTS 还在播,正确行为是立即停播:
rec.onstart = () => speechSynthesis.cancel(); // 识别一启动就掐掉播放反过来的时序也要处理好:TTS 播报期间麦克风要么静音、要么用 VAD(语音活动检测)判定,否则模型会被自己的声音"唤醒",形成自问自答的死循环。
队列与分段
一条 2000 字的回复直接丢给 TTS,speechSynthesis在 Chrome 系实现里超过 ~15 秒会截断。所以长回复要按句子或段落切块入队,逐段speak,段间留 50~150ms 间隙。队列上再挂两个动作:
cancel():用户点停止/发新消息时全清;- 新消息高优先级插队:用户追问时,先念完当前句再切新回复,比生硬中断体面得多。
🚀 低延迟的三个优化手段
| 手段 | 作用点 | 说明 |
|---|---|---|
| 流式送读 | TTS 触发时机 | 不等整条回复完成,LLM 每产出一个句子就切块入队,首音延迟从"整条生成完"降到"第一句生成完" |
| 预热 AudioContext / 识别引擎 | 启动阶段 | AudioContext在页面加载后即new并resume(),避免首句因懒初始化多等几百毫秒;识别引擎同理提前构造 |
| 识别语言与系统语言对齐 | ASR 配置 | rec.lang跟用户界面语言走,跨语言(中文界面识别英文)准确率会明显下降,必要时提供手动覆盖项 |
📌 收尾:能力边界与下一步
把能力边界想清楚,能省掉一半 bug:
- 权限:麦克风是系统级授权,被拒后只能引导用户去系统设置,应用内重试没意义;
- 隐私:语音数据出不出本机,决定了你选 Web Speech API、云端还是本地模型,这应当是产品决策而不是技术细节;
- 降级:任何一环失败(无识别引擎、TTS 无可用音色),都应有"静默转纯文本"的兜底路径,语音是增强,不是前置条件。
延伸阅读:Cherry Studio 的架构与消息生命周期文档在 docs/references/architecture/,provider 模型分类定义在 packages/provider-registry/src/,可以对照本文的分层去看源码落点。
【免费下载链接】cherry-studioAI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs项目地址: https://gitcode.com/GitHub_Trending/ch/cherry-studio
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考