浏览器语音代理如何识别通话状态
该开源项目用 Selenium 操控 Google Voice,通过页面信号和早期通话音频区分接通、结束及语音信箱。接通后串联语音活动检测、转写、Groq 推理和语音合成,并记录通话数据。
AI 深度解读
用 AI 打电话,难点可能还没轮到模型开口。作者做了一个运行在 Windows 上的 Google Voice 外呼系统:Selenium 操作真实 Chrome,Groq 负责语音识别和回复,再把合成语音送回电话。最费工夫的环节,是确认对方到底接没接。
这套实现值得看,是因为它把演示里常被跳过的问题摊开了:页面状态会误导程序,接通后的空白会赶走用户,音频线路接错还会让 AI 转录自己。它提供了一份工程拆解,但没有给出准确率和延迟数据。
为什么先判断接通,再考虑怎么回答
使用电话 API,通常能拿到通话事件、webhook 和音频流。作者的系统没有这些接口,只能观察 Google Voice 页面,再推断电话处于振铃、接通、结束、失败还是语音信箱状态。
问题在于,DOM 出现了某个元素,不代表对应事件刚刚发生。通话控制按钮可能提前出现,上一次的结束横幅可能还没消失,类似语音信箱的提示也可能在振铃时冒出来。直接按元素是否存在做判断,机器人就可能对着铃声讲话,或提前结束一次正常呼叫。
作者在 src/google_voice.py 里设置了一道保守的状态门槛。min_ring_seconds 没过去之前,不接受从振铃切换到接通、结束或语音信箱;过早出现的控制按钮会记录下来,但状态继续保持振铃。残留的结束提示和振铃期的语音信箱线索,也不能立即触发转换。
跨过时间门槛后,还需要接听控件、通话计时器等稳定证据,才确认接通。超过 max_ring_seconds,才按无人接听结束尝试。
页面出现线索,只是候选证据,不能直接当成业务事实。
这里还有个实用细节:开场白和语音信箱留言在拨号前就准备好。否则对方说完「喂」,系统才开始生成问候,等待期间听到的只有空白。预生成不能消除整条链路的延迟,但能减少接通后的第一段等待。
对话循环能拆模块,音频回路必须接清楚
确认接通后,conversation_loop.py 才启动实时对话。audio_capture.py 采集对方音频,通过语音活动检测(VAD)判断讲话的开始和结束;音频送到 Groq STT 做语音转文字,再由 ai_groq.py 生成回复。realtime_tts.py 使用 Edge TTS 合成并播放,支持缓存,以 pyttsx3 作为后备。
这条流水线有几个直接影响体验的配置。下面这段原始配置把振铃门槛、重试、静音判断和预热放到了一起:
{
"min_ring_seconds": 2,
"max_ring_seconds": 45,
"voicemail_detect_seconds": 15,
"silence_does_not_end_call": true,
"call_cooldown_seconds": 10,
"tts_warmup": true,
"stt_retry_count": 2,
"vad_silence_frames": 12,
"vad_speech_frames": 2
}
重点看 silence_does_not_end_call:对方暂时不说话,不应该被当成挂断。另一个容易误读的地方是 VAD 参数,它们的单位是帧;原文没给帧长,不能直接换算成毫秒,也不能据此判断响应速度。
语音信箱检测则结合 DOM 线索和通话早期的音频分类器,识别问候、提示音或真人通话信号。确认后,系统切换到预录留言。这里的 voicemail_detect_seconds: 15 是配置窗口,原文没有提供分类准确率或误判比例。
音频能生成,还得送进正确的位置。最小 VB-CABLE 接法是:Agent 播放设备选 CABLE Input,Chrome 麦克风选 CABLE Output,Chrome 扬声器则输出到 Agent 能采集的设备。
名字有点绕,可以沿声音的方向理解:合成语音进入虚拟线路,再作为浏览器的麦克风输入;对方声音则走另一条采集路径。
无人值守时,作者建议分开 TTS 播放和来电采集。若采集设备同时收到了 Agent 自己的声音,系统就可能识别自己的回复。CAPTURE_DEVICE=default 使用 Windows 扬声器回环,适合测试,作者认为它不够适合持续外呼。
项目提供了三个命令,分别用于检查设备、测试路由和执行启动前检查:
python -m src.main --list-audio-devices
python -m src.main --audio-route-test
python -m src.main --preflight
其中 --audio-route-test 应重点关注,它检查的是声音能否沿预期路径传递;设备列表里能看到某个名字,并不足以证明整条回路正常。
多开一路电话,需要隔离的不只是浏览器
系统从 CSV/XLSX 或 CRM 获取联系人,使用持久化 Chrome profile 拨号;每通电话结束后保存转录、录音、结果、CRM 更新和归档,冷却后再处理下一位。FastAPI 提供设置、预检、运行监控和日志等控制台功能,PyInstaller 则用于打包 Windows EXE。
并行外呼时,每个 worker 使用独立 Chrome profile,从共享联系人队列领取任务。但浏览器隔离只是其中一层,音频也必须分开。
项目默认拒绝共享音频路由,因为同一条 AI 回复可能被送进多通电话。各个槽位需要独立的回环与采集设备对,例如 CABLE-A、CABLE-B。对这种系统来说,多开进程很容易,保证每句话只送到对应通话才是关键。
账号管理也存在自动化边界。保存的密码由 Windows DPAPI 保护,web API 不返回密码;遇到 MFA 或 CAPTCHA,仍需人在可见 Chrome 窗口里完成。
作者还记录了每次状态判断的依据,包括振铃时长、DOM 线索、接听控件、通话活动状态、音频分类结果和超时原因。排查时,「已经接通」帮助有限;「振铃后 6.2 秒出现计时器,因此判定接通」才能让人判断规则是否合理。
我的判断
我认可作者把状态检测放在模型调优前面。这个项目的执行入口是真实网页,输入信号含糊,动作却会立刻影响真人。先约束何时允许开口,再优化说什么,优先级合理。
但原文没有接通检测准确率、语音信箱误判率、端到端延迟,也没有并发压力测试。最小振铃时间设为 2 秒、检测窗口设为 15 秒,只能当作这份配置的起点。网络、页面更新、设备和被叫行为改变后,效果是否成立,需要重新验证。
作者说多数失败运行来自配置问题,这是他的项目经验,没有故障分布数据支撑。它足以解释为什么要做预检,还不足以证明其他团队也会遇到同样比例的问题。
与原文开头提到的电话 API 路线相比,这种浏览器方案需要自己承担页面状态推断和 Windows 音频路由维护。原文没有给出两条路线的成本或稳定性对照,也没说明选择 Google Voice 的完整约束。因此,不能据此判断浏览器自动化更划算。
合规同样属于运行条件。原文明确提醒,向真人拨号需要考虑同意、拒接名单(Do Not Call)要求,以及适用情况下的录音告知;具体怎么落实,文章没有展开。
能拿来做什么
- 做浏览器自动化时,把 DOM 线索转换成有时间约束的状态机,并记录转换原因;可以借鉴这里处理残留横幅和提前出现按钮的方式。
- 做语音助手时,把开场音频预生成、TTS 预热和设备路由检查放进启动流程,先验证首次开口是否及时、声音是否送对。
- 调整 VAD 或语音信箱参数前,先确认帧长、采集路径和误判记录,避免只改数字却不知道改变了什么。
- 增加并行通话前,逐路验证 Chrome profile、播放设备和采集设备的隔离,再接入共享任务队列。