打字指挥 AI 已经很好用了,但总有腾不出手的时候。于是我给 DSH 装上「耳朵」和「嘴」:说一句「你好小智」,AI 就开始听;听完指令,把结果用语音播报给你。整套识别和合成全部跑在本地,免费、离线、不依赖任何云端语音服务。
方案选型:能本地就本地
| 环节 | 选择 | 为什么 |
|---|---|---|
| 语音识别 | sherpa-onnx(本地流式) | 免费、离线、流式,唤醒即说,不用等整段录完 |
| 语音合成 | vits-zh-ll(本地) | 免费、中文效果好,进程内单例加载后秒出 |
| 备用识别 | 豆包 STT(云端) | 本地识别不稳时的兜底 |
| 播放 | MCI 播放器脚本 | 比 ffplay 启动快、更稳定 |
整体架构
语音助手拆成两个部分协作:一个常驻的识别守护进程,和一个负责「听懂后干活」的注入插件。
麦克风 ──► sherpa 流式识别(守护进程)
│ 识别出文本
▼
commands.txt ──► voice-core 插件轮询
│ 发现新指令
▼
创建独立新会话执行指令
│ 拿到回复
▼
vits 合成 + 播放语音播报
三个关键设计
1. 唤醒词容错
「你好小智」四个字,识别引擎经常听成「你好小志」「你好小只」。我没有死磕识别精度,而是做了谐音容错:智/志/只/之/知 全部算数。这套思路后来被我用在很多地方——不要追求完美,容错才是工程。
2. 防自播自识别回声
AI 播报时,自己的喇叭声音会被麦克风再录进去,触发「自问自答」的幽灵循环。解法:播放提示音时进入 muteUntil 静默期,期间跳过识别帧;播放完成后再加一小段缓冲才重新开放。就这么一个细节,治好了整个系统的精神分裂。
3. 每条指令开独立新会话
语音指令不污染主对话,而是创建独立的语音会话去执行,执行完播报结果。这样语音操作再多,主聊天区永远干净清爽。
踩过的坑(血泪精选)
Windows 的 wscript 不支持 UTF-8 编码的 .vbs——带 BOM 直接弹编译错误,整行不执行。vbs 必须纯 ASCII,路径用环境变量展开。
✅ 教训:脚本能不用 vbs 就不用;用了就记得 ASCII + 环境变量。
录音工具独占麦克风,起两个识别进程,后启动的那个直接崩溃。排查发现是开机自启 + 手动启动双份。
✅ 教训:任何常驻服务都要有「只允许一个实例」的约定,排查用进程命令行过滤确认数量。
排查很久,发现是监听事件时机错了:任务结束时根本没有「用户消息」在尾部,永远匹配不上。改成按会话前缀 + 最后一条助手文本判断,立刻正常。
✅ 教训:不要假设事件流的形状,去打印真实事件再写逻辑。
发布成开源插件
这套东西我整理成了 dsh-voice-assistant 开源插件:一条 node install.cjs 命令,自动安装依赖、下载模型、注册服务、启动守护进程。发布前我专门写了个敏感扫描脚本,把硬编码的 API Key、个人路径全部脱敏——开源代码里绝不能有密钥,这是底线。
想看完整实现?去 GitHub 看 dsh-voice-assistant,或者读本博客的插件页。