这件事的难点不在流程编排,而在把声音从一个不可编程的物理世界,接到一个必须完全相信它的第三方平台上。
autobot/,Node + Playwright 驱动真实 Edge):登录、点击、设备路由、桥接配音、全量数据归档。定位是「提线木偶师」。.251:8001,团队自部署):ASR 转写 → 知识库检索 → LLM 生成答案 → 语音合成。定位是「大脑」。三者关系可类比为:考场(平台)、大脑(助手)、提线木偶师(自动化层)。改不了题目和评分标准,只能保证木偶师把大脑的话稳稳送进考场的耳朵,并把考场的声音稳稳接回大脑。
朴素的三步方案(录音 → 转写 → 播放)在第一步就卡住:页面下拉已选虚拟声卡录音端、且有回读校验,但面试房间仍用系统默认麦克风(摄像头麦)。
根因:网页在设置里让你选麦克风,只是这个网页的界面设计;房间真正开麦走的是浏览器底层接口 getUserMedia,它读的是系统默认设备。两者可以完全对不上——页面上的选择是建议,不是命令。
修法:在浏览器加载页面之前注入脚本,拦下后续所有麦克风请求并强制改写为指定 deviceId,同时关闭 echoCancellation / noiseSuppression / autoGainControl。
为什么三项必须全关:它们是正常语音通话功能,目的是在开扬声器说话时把自身声音从麦克风里减掉。但本场景扬声器里放的是注入的答案,保留该功能等于系统会尽职地把我们刚放进去的答案消掉。
两根虚拟声卡名字含共同词。未锚定的字符串匹配会让两个角色的设备指向一起偏,而程序不报错——只是安静地把声音发给错误设备。规范确立为:宁可匹配不上,也不能匹配错。已写入 config.DEV_PREFIX。
不用空气的原因:声音一旦离开软件进入扬声器,就变成不可编程的空气,且会被重新拾取、被回声消除、被环境噪声干扰。
虚拟声卡原理一句话:播放进一头,另一头就变成麦克风,全程纯数字信号,零比特经过空气。
| 线 | 方向 | 路径 |
|---|---|---|
| 面试官线 | 听 | 平台页面播放 → Hi-Fi Cable Input ═→ Hi-Fi Cable Output → 助手 iv 通道采集 |
| 答案线 | 说 | 桥接 TTS → CABLE Input ═→ CABLE Output → 平台麦克风(gUM 覆写目标) |
关键:这是两块不同的虚拟声卡。 分开后助手在物理上听不到自己,问题从根上消失,无需任何压制手段(单线期的痛点留待 EP03)。
四个端点必须统一 24bit / 48000Hz,差一个比特或采样率不一致,输出即为全静音。
更隐蔽的坑:装完/重装虚拟声卡驱动后必须重启 Windows。不重启时设备处于「存在、能枚举、设置界面可见,但不传信号」的状态。当时误判为两根线冲突,查了整整一下午。
助手原本逐句合成答案(每句 3–10 秒),播出去是碎片。平台做整轮语音检测,拿到碎片判定「这个人没在说话」,随即触发那句经典台词「好像没有听到您的声音,您还在吗」。
修法:桥接 TTS。助手只下发文本 → 本机句切分 → edge-tts 并行合成 → 连续注入。三句合成压到 2.0 秒内。
切到另一知识库版本后全程空答案,但音频在动、浏览器在录、WebSocket 在发、助手不报错。根因:助手网关管理会话时不区分应用版本,切换后拿旧会话编号调新版本,返回 404 Conversation Not Exists。
这类故障最可怕之处在于完全静默——不崩、不红屏、日志无异常,只是安静地什么都不说。
防线(朴素但有效):开场连问两次。首问空 → 应用坏了,立即中止;首问正常续问失败 → 会话被拒,同样中止。20 秒拦下一个坏场次。
首场完整跑通:12.3 分钟 / 10 轮深度追问 / 1413 字答案;后续场次达 14 轮。
第 4 条最值钱:转写中发现面试官声音句尾重复,核对时间戳发现该现象出现在本项目注入任何声音之前,换机复现。定性后整理证据包反馈平台,而不在自己这边打补丁盖住——否则问题永远消失,下一人接手会以为本来如此。
自动化系统有天然倾向:把所有不正常归到自己头上,然后想办法继续跑。这在需要长期可信的系统里是毒药。
README.md §1 三方关系 · §4 演进史 · §5 定型架构;CODEBUDDY.md 双线段与 gUM 覆写;autobot/docs/REQUIREMENTS-DEV.md(EP04–06 的素材主源)。