- 修复心跳 Bug:应用层 ping 不更新 lastPong,60 秒后连接会被错误断开 - 03-接口文档:audio/mpeg→audio/mp3、STTConfig/TTSConfig 补充 Model 字段、 APP_ENV 环境变量名修正、配置搜索路径补充、.env 加载说明、Vite proxy 说明修正 - 02-系统架构:补充 ConfigPanel/Toast 组件、Model Router/Rate Limiter 标注规划中、 补充 Gin 框架、MVP 存储改为 Memory、AI 服务 provider 更新、Orchestrator 伪代码对齐 - 04-技术选型:新增 AI 服务栈选型章节(STT/LLM/TTS)、PostgreSQL 标注规划中 - 06-语音交互:VAD 参数名修正、STT 改为一次性识别描述、音频编码格式补充 - 07-视觉理解:关键帧检测代码改为 TypeScript、分辨率修正、阈值逻辑统一 - 08-成本控制:变量名修正、未实现功能标注规划中、对话历史裁剪策略补充 - CLAUDE.md:同步更新技术栈、模块结构、存储策略描述
2.8 KiB
2.8 KiB
语音交互
概述
语音交互全链路:VAD(语音活动检测) → STT(语音转文字) → LLM 推理 → TTS(文字转语音)。
用户感知延迟 = VAD 响应 + STT 耗时 + LLM 首 token + TTS 首包。人类对话中停顿超过 300ms 就会感到"对方在想"。
延迟目标:端到端 1.5~2 秒(用户说完话到听到 AI 回应);流式 TTS 下,LLM 开始生成后 0.5 秒听到第一个词。
全链路
麦克风 → VAD → STT → LLM → TTS → 扬声器
环节一:VAD(语音活动检测)
从持续音频流中检测"人什么时候在说话",避免将环境噪音当作有效输入。浏览器端完成,节省 ~70% 带宽。
import { MicVAD } from "@ricky0123/vad-web";
const vad = await MicVAD.new({
onSpeechStart: () => console.log("用户开始说话"),
onSpeechEnd: (audio) => {
// audio: Float32Array,送入 STT
sendToSTT(audio);
},
positiveSpeechThreshold: 0.5, // 检测灵敏度
negativeSpeechThreshold: 0.35, // 结束灵敏度
minSpeechMs: 250, // 最短语音时长 ms
redemptionMs: 300, // 语音结束确认时间 ms
preSpeechPadMs: 300, // 语音前填充 ms
});
vad.start();
环节二:STT(语音转文字)
| 方案 | 延迟 | 成本 | 特点 |
|---|---|---|---|
| Whisper API | 1-3s | 按分钟计费 | 准确率高,支持多语言 |
| Deepgram(默认) | <500ms | 按分钟计费 | 流式识别,延迟极低 |
| MiMo ASR(小米) | ~1s | 按量计费 | 国产替代,兼容 OpenAI 格式,HTTP 非流式 |
| 浏览器原生 | ~1s | 免费 | 中文效果一般 |
当前实现为一次性语音识别(非流式):前端 VAD 检测到用户说完后,将完整音频片段发送到后端,后端调用 stt.Recognize() 一次性返回识别结果。流式 STT 为未来优化方向。
音频编码格式:前端 audio.ts 将 Float32Array 转为 Int16 PCM(16kHz, pcm_s16le)再编码为 Base64。
环节三:TTS(文字转语音)
流式 TTS:检测 LLM 输出中的句子边界,每检测到一句就立即送入 TTS 合成并播放,不必等全部生成完。
句子切分规则:按中文标点(。!?)、英文标点(. ! ?)和换行符切分。
当前实现参数:Voice "alloy"、Speed 1.0、OutputFmt "mp3"、SampleRate 24000。
方案选择:
- OpenAI TTS(默认):音质好,延迟中等,按字符计费,模型 tts-1
- MiMo TTS(小米):国产替代,通过配置切换
- Edge TTS(规划中):微软免费方案,音质不错,延迟略高
延迟优化要点
- VAD 浏览器端处理(减少无效传输)
- 流式 STT(边说边识别)
- LLM 流式输出
- TTS 句子级流式合成
- STT 与上下文准备并行处理