名称:语音系统专家 描述:当处理Quetrex的语音界面、OpenAI实时API、WebRTC或回声消除时使用。了解Quetrex特定的语音架构决策和模式。关键 - 防止破坏正常工作的语音系统。 允许工具:读取
Quetrex语音系统专家
关键:首先阅读此内容
Quetrex的语音系统架构经过充分文档记录和实战测试。在对语音相关代码进行任何更改之前,您必须阅读:
- ADR-001-VOICE-ECHO-CANCELLATION.md(权威架构决策)
- docs/architecture/VOICE-SYSTEM.md(技术实现)
- docs/features/voice-interface.md(面向用户的功能)
位置: src/lib/openai-realtime.ts
核心架构决策
始终开启的麦克风 + 浏览器AEC
这是VOICE-SYSTEM.md中的决策4,也是ADR-001中记录的权威方法。
// ✅ 正确:始终开启的麦克风
const mediaStream = await navigator.mediaDevices.getUserMedia({
audio: {
echoCancellation: true, // 关键 - 浏览器处理回声消除
noiseSuppression: true,
autoGainControl: true,
},
})
// 麦克风轨道在整个对话期间保持启用状态
// 浏览器的原生AEC防止反馈循环
// 服务器端VAD(语音活动检测)处理轮次检测
工作原理
音频流水线
用户说话
↓
麦克风(始终启用,echoCancellation: true)
↓
WebRTC → OpenAI实时API
↓
服务器端VAD检测语音
↓
OpenAI处理并响应
↓
通过WebRTC的音频响应
↓
HTMLAudioElement播放(保持在浏览器流水线中)
↓
浏览器AEC比较麦克风输入 + 扬声器输出
↓
回声自动消除(无反馈循环)
为什么这有效
浏览器回声消除要求:
- 麦克风输入和扬声器输出都必须在浏览器的音频图中
- 音频必须流经浏览器的WebRTC堆栈
- 无需手动干预(浏览器处理)
这是行业标准:
- ChatGPT语音模式
- Google Meet
- Zoom
- Discord
- Microsoft Teams
它们都使用始终开启的麦克风 + 浏览器AEC。
不要做这些事情
❌ 不要:切换麦克风轨道
// ❌ 错误 - 这会破坏回声消除
async function pauseRecording() {
microphone.enabled = false // 不要这样做
}
async function resumeRecording() {
microphone.enabled = true // 不要这样做
}
为什么失败:
- 不是行业标准
- 导致音频故障
- 可能破坏回声消除
- 增加人为延迟
- 更复杂的状态管理
- 没有实际好处
❌ 不要:将音频路由到浏览器外部
// ❌ 错误 - AudioWorklet旁路到原生音频
const workletNode = new AudioWorkletNode(audioContext, 'bypass-processor')
workletNode.port.postMessage({ cmd: 'route-to-native' })
为什么失败:
- 破坏浏览器AEC(音频离开浏览器流水线)
- 导致回声/反馈循环
- 需要复杂的原生音频处理
- 平台特定的实现
- 已经尝试并失败(参见abandoned-approaches/)
❌ 不要:实现自定义回声消除
// ❌ 错误 - 自定义AEC实现
class CustomEchoCanceller {
cancelEcho(input: AudioBuffer, output: AudioBuffer) {
// 复杂的DSP代码...
}
}
为什么失败:
- 重复造轮子
- 浏览器AEC经过数十亿用户实战测试
- 正确实现极其复杂
- 需要深厚的DSP知识
- 性能问题
- 需要平台特定的调优
❌ 不要:添加人为延迟
// ❌ 错误 - 用于回声预防的延迟
await new Promise(resolve => setTimeout(resolve, 500))
await playAudio()
await new Promise(resolve => setTimeout(resolve, 500))
resumeRecording()
为什么失败:
- 不必要(浏览器AEC处理)
- 降低用户体验
- 增加延迟
- 如果实现错误,仍然无法防止回声
您可以更改什么
✅ 可以:调整音频设置
// ✅ 可以调整这些设置
const constraints = {
audio: {
echoCancellation: true, // 必须为true
noiseSuppression: true, // 可以调整
autoGainControl: true, // 可以调整
sampleRate: 24000, // 可以更改以提高质量
channelCount: 1, // 单声道适合语音
},
}
✅ 可以:处理连接状态
// ✅ 可以管理连接生命周期
async function connectVoice() {
// 设置WebRTC连接
// 开始流式传输
// 处理连接事件
}
async function disconnectVoice() {
// 清理WebRTC连接
// 停止流式传输
// 释放麦克风
}
✅ 可以:添加UI反馈
// ✅ 可以添加视觉指示器
function onVoiceActivity(active: boolean) {
if (active) {
// 显示“正在聆听”指示器
// 动画麦克风图标
} else {
// 显示“空闲”指示器
}
}
✅ 可以:处理错误
// ✅ 可以改进错误处理
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
} catch (error) {
if (error.name === 'NotAllowedError') {
// 显示权限请求UI
} else if (error.name === 'NotFoundError') {
// 显示“未找到麦克风”错误
}
}
实现位置
主要文件: src/lib/openai-realtime.ts
关键函数:
setupMediaStream()- 使用正确的约束初始化麦克风connectToOpenAI()- 建立WebRTC连接handleAudioResponse()- 通过HTMLAudioElement播放AI响应
不要修改:
- 麦克风启用/禁用逻辑(应保持始终开启)
- 回声消除设置(必须为true)
- 音频路由(必须保持在浏览器流水线中)
可以修改:
- UI反馈和视觉指示器
- 错误处理和用户消息
- 连接重试逻辑
- 音频质量设置(采样率等)
为什么选择此架构
来自ADR-001:
选项A:信任行业模式(已选择)
- ✅ ChatGPT、Google Meet、Zoom、Discord使用
- ✅ 浏览器供应商为此模式优化
- ✅ 在所有平台上工作(macOS、Windows、Linux、移动设备)
- ✅ 无需自定义实现
- ✅ 经过验证的可靠性
已拒绝的选项:
- ❌ 手动麦克风切换(不是行业标准)
- ❌ AudioWorklet原生旁路(破坏AEC)
- ❌ 自定义回声消除(重复造轮子)
- ❌ 原生Rust WebRTC(2-4周工作,无好处)
平台上下文:Web应用程序
重要: Quetrex现在是一个纯Web应用程序,不是Tauri桌面应用程序。
这对语音的重要性:
- 浏览器回声消除在所有浏览器中完美工作
- 没有WKWebView限制(那是Tauri问题)
- 通用兼容性(Chrome、Safari、Firefox、Edge)
- 无需平台特定的音频处理
- 直接工作™️
WKWebView问题(历史): Quetrex最初是Tauri桌面应用程序。在macOS上,Tauri使用WKWebView,它有一个错误:WebRTC音频播放不起作用。这迫使我们尝试变通方法(AudioWorklet旁路、原生音频路由),这些方法都破坏了回声消除。
解决方案: 转换为Web应用程序。现在回声消除在任何地方都完美工作。
测试语音更改
如果您必须修改语音代码:
- 在真实浏览器上测试(不仅仅是开发工具)
- 测试回声场景:
- 调高扬声器音量
- 开始语音对话
- 验证无反馈循环/回声
- 跨平台测试:
- Chrome(最常见)
- Safari(macOS、iOS)
- Firefox
- Edge
- 测试边缘情况:
- 网络连接不良
- 麦克风权限被拒绝
- 对话中断开连接
何时查阅文档
在进行任何语音更改之前,请阅读:
docs/decisions/ADR-001-VOICE-ECHO-CANCELLATION.mddocs/architecture/VOICE-SYSTEM.mddocs/development/abandoned-approaches/
如果您看到提及:
- Tauri → 忽略(Quetrex现在是Web应用程序)
- WKWebView → 忽略(不再相关)
- AudioWorklet旁路 → 不要实现(已经尝试,失败)
- 手动麦克风切换 → 不要实现(不是行业标准)
总结
黄金法则: 信任浏览器回声消除。始终开启的麦克风 + echoCancellation: true + 音频在浏览器流水线中 = 完美的回声消除。
应该做:
- 在整个对话期间保持麦克风启用
- 使用
echoCancellation: true - 保持音频在浏览器中(HTMLAudioElement)
- 让OpenAI实时API处理VAD
- 信任行业模式
不应该做:
- 切换麦克风轨道
- 将音频路由到浏览器外部
- 实现自定义回声消除
- 添加人为延迟
- 尝试“改进”已经有效的东西
如果有疑问: 阅读ADR-001和VOICE-SYSTEM.md。架构经过充分文档记录是有原因的。