把检索塞进语音轮次:你已经建好的那条流水线装不下。
在一通电话里,一个有依据的回答必须在来电者停止说话后大约 800 毫秒内开始从扬声器里出来,而传统的"检索—改写—重排"链条,在模型看到第一份文档之前就已经把这段时间花掉了大半——这就是为什么在聊天原型里挺准的语音智能体,一上电话就开始瞎猜。解法不是换一个更快的向量数据库,而是在这一轮说完之前就开始检索、把问题分布的头部预先算好,并删掉那些别人告诉你必不可少的流水线阶段。
先算账:检索能分到的是约 200 毫秒,不是 2 秒。
一个自然的轮次所需的延迟预算,在检索被请上桌之前就已经分配完了。端点检测要判断来电者说完了,模型要产出第一个 token,合成的语音还要经过一条你控制不了的链路送达。
- 把你自己的预算逐行写下来,找出富余在哪。在典型的级联架构里,语义端点检测要几百毫秒,模型的首 token 时间再要几百毫秒,TTS 的首段音频时间要一百多毫秒——这还没算网络。在亚秒的目标下,真正空闲的时间大约只剩 150 到 300 毫秒,而且前提是检索不与任何别的事重叠。
- 再给你现有的流水线标个价。查询改写是一次模型调用——光这一项就是你的全部预算。一次嵌入调用是几十毫秒。一次向量检索又是几十毫秒。在五十个候选上跑一次交叉编码器重排又是一百多毫秒。多跳的智能体式检索——在研究型智能体里是正解——要好几秒,因此在这里根本不是候选项。
- 超支不会表现为"慢",而是先表现为空气死寂,再表现为答错。被延迟逼急了的团队会悄悄把检索超时调低,而一次超时的检索通常意味着模型改用参数化记忆来作答——语气笃定、声音悦耳、底下什么依据都没有。你造出来的不是一个慢的语音智能体,而是一个在负载下开始胡编的语音智能体。
让后文变得可解的那个框架是:在语音里,检索延迟不是性能指标,而是准确率指标。每一毫秒的超支,都会通过你自己配置的那个超时,转化成一个没有依据的回答。要么把这两者一起量,要么你就会把其中一个优化成另一个。
在来电者把这句话说完之前就开始检索。
能拿到的最大一笔收益不是把检索做得更快,而是把它挪得更早。流式 STT 早在端点检测触发前几百毫秒就已经在吐部分转写了,而那个窗口是你目前白白扔掉的死时间。
- 对着部分转写发一次投机查询。只要部分转写里的实词多到能构成一个说得通的问题,就把检索发出去。等最终转写到达时做个比对:如果意图没变,文档已经在手,检索花掉的感知时间是零。如果变了,丢掉重查——你损失的不过是一点算力。
- 为浪费留预算,因为浪费正是这套机制本身。预期会扔掉相当一部分投机查询;这不是需要调掉的 bug。相对于一整个轮次,打一个热索引是很便宜的,而用"被丢弃的查询"换来几百毫秒的感知延迟,是整个技术栈里最划算的一笔交易。给每通电话的并发设上限,免得一个絮絮叨叨的来电者把扇出撑爆。
- 按内容去抖,而不是按时间。每来一个部分 token 就重发一次,是在白烧索引。等部分转写多出一个新实体或一个新实词时再重发——用的正是查询理解所用的那些信号,只是增量地用。
- 别让投机去"动手"。投机检索可以随便错,因为还什么都没发生。投机工具调用不行:任何会写入、扣款或发通知的查找,都必须等到轮次被确认。这条读/写分界线与语音中的工具调用与状态画的是同一条。
- 打断必须取消在途的查询。如果来电者插话了,那条正在跑的投机检索属于一个已经不存在的轮次。把取消接进打断的同一条路径里,否则你偶尔会去回答一个没人问完的问题。
分布的头部是一份缓存,不是一次检索。
语音流量比聊天流量集中得多。来电者问的是营业时间、订单状态、某笔费用、某条政策、怎么退货——在多数部署里,几十个意图就覆盖了绝大多数轮次。每次都去搜这些答案,是一份你可以离线做一次的工作。
- 预先算好答案,而不只是文档。对你的头部意图,按意图为键存下写完的、口播长度的答案文本——经人工审过、按耳朵的习惯措辞。轮次于是变成"分类并朗读",延迟只是原来的一小部分,而且比在新鲜切片上现场生成可靠得多。
- 把稳定的那些预先合成成音频。任何不随来电者变化的内容——营业时间、退货政策、你必须朗读的某段告知——都可以缓存成音频,在个位数毫秒内开始播放。这是语音栈里最便宜的一笔质量收益,而几乎没人做。
- 在建立通话时就把来电者的上下文预热,而不是等第一个问题。你通常在对方开口之前就知道是谁打来的。趁问候语在播的时候把账户、上一笔订单、未结工单取回来;等第一个问题落地时,那次昂贵的查找早已在你反正要播的那段音频里完成了。
- 用语义缓存兜住近似命中。"你们几点关门?"和"今天营业到很晚吗?"应该命中同一条。机制与失效陷阱见语义缓存——语音特有的告诫是:一条过期的缓存答案会被以十足的笃定读出来,而且永远不会被人一眼扫过去发现不对。
- 把长尾留在实时路径上。覆盖头部的意义就在于:你因此负担得起在那些罕见、复杂的问题上更慢、更谨慎——而那恰恰也是"答得慢一点"在社交上说得过去的地方。
砍掉阶段,而不是压缩阶段。
人的本能是保留整条流水线、把每个阶段都往下调。这会失败,因为各阶段有固定成本,而且一个被赶工的阶段会掉质量却换不回多少时间。改成删掉它们,并且清楚地知道你在放弃什么。
- 删掉查询改写那次调用。它是一次完整的模型往返,用来修一个语音基本没有的问题:来电者的问题是以一个口语句子到达的,通常本来就成形,而对话历史可以作为原始文本直接带进检索查询里。如果你的领域里歧义确实常见,就用一句简短的澄清问句去解决——按毫秒算更便宜,对来电者也比一次看不见的改写更好。
- 删掉交叉编码器重排,或把它挪出关键路径。重排买来的是列表顶部的精度,而在一个语音轮次里你读的是两三段,不是二十段。用一个好的混合检索作为第一阶段取回一个小候选集,接受它的排序。如果你确实想要重排,就趁来电者还在说话时,在投机结果上跑。
- 取更少、更短的段落。一个语音回答是两三句话;一万 token 的上下文并不能让它更好,却会实打实地拖慢首 token 时间。按你真正会念出来的答案长度去切分语料。
- 只留一个索引,并且保持它是热的。冷缓存、冷连接和惰性加载的索引,会把一次 30 毫秒的检索,恰好在安静一阵后的第一通电话上变成 400 毫秒。把它钉住、发布时预热,并用一次真实查询而不是一次 ping 去做健康检查。
- 边落地依据边流式说出来。模型可以在后面的段落还在陆续到达时,就先把答案的铺垫部分说出口。这与流式生成是同一个形状,而在语音里,它把真实延迟变成了那半句不承载事实的话上的"零感知延迟"。
快不了的时候就诚实——并把填充语做成一件设计过的乐器。
有些查找就是慢,而且预热不了:合作方 API、大型机、一个需要人工审批的步骤。沉默是最糟的回应,而标准解法——一句填充语——只有在被设计过而不是临场发挥时才安全。
- 具体地说出你正在做什么。"我这就把那笔订单调出来"建立了预期,并诚实地买到两秒钟。"嗯,稍等一下"只买到大约一半,而且听着像在拖延。把等待变成进展的,正是具体性。
- 用计时器触发填充语,而不是每次查找都触发。如果检索 200 毫秒就回来了,一句前导语只会让智能体更慢也更聒噪。只有当查找越过一个阈值——几百毫秒——才开口,好让快路径保持利落。
- 绝不承诺一个你可能拿不到的结果。说了"我正在帮您查"然后失败,比直接说"这个我手上没有——我可以帮您转给有的人"要糟糕得多。填充语里的那个承诺,就是一个承诺。
- 给等待设上限,并预先定好出口。设一个硬性上限——几秒钟——超过就停止等待并走一条既定路径:提供回拨、转人工,或先回答它确实知道的那部分。没有上限,你就会得到语音失败模式里编录的那种延迟死亡螺旋,每一次道歉都再加一轮。
- 让措辞有变化,并保持可打断。每次查找都是同样那八个字,三轮之内就会被听成一台机器。而在填充语播放期间说"算了,不用了"的来电者必须被听见——填充音频不是停止倾听的借口。
- "我不知道"需要一份排练过的话术,且用来电者的语言。检索返回不了相关内容是一种正常结果,而模型的默认反应是把这个缺口填上。给它一条显式的低置信路径和一个指名道姓的去处,并把"没有转接目标"当作 bug——它落进的那个队列见客服智能体。
依据在语音里的失效方式不同,所以度量方式也要不同。
聊天用户看得见引用、能扫一眼原文、能抓住错误。而来电者听到的是一句笃定的话,没有脚注、没有回滚、没有任何核对的办法。文本 RAG 中承担了大部分安全性的那个"可核验"的设计余量,在这里干脆不存在,于是只能由度量来补上这个差额。
- 给实体级正确性打分,而不是段落相关性。recall@k 这类检索指标只告诉你文档在那儿;它不告诉你智能体有没有从里面读对那个数字。答案里说出口的数字、人名、日期和金额,才是要紧的单位——这与评估语音智能体关于"量实体错误率而非词错误率"的论点是同一个。
- 用录下来的音频、而不是打字的问题来搭评测集。真实来电者会含糊、会自我更正、会说错产品名、会盖过你的提示音。一套在干净文本查询上调好的检索栈会看起来非常漂亮,然后在一句"那个、呃、蓝色那个、四百的"上失手——而这个转写错误进的是索引,不只是模型。
- 音频里给不了引用,那就在 trace 里做归因。按轮次为键,记录取回了哪些段落、哪些进了上下文、哪些是投机取回但被丢弃的。这是投诉发生后你回答"它从哪儿看来的"的方式,也是通往可回放评测的唯一桥梁。trace 一旦存在,标准的 RAG 评估就可以照用。
- 把超时率当作一等公民的质量信号来跟踪。"检索错过预算而模型照样作答"的轮次占比,是你手上最接近"无依据回答率"的东西,而且只要语料、流量或供应商一变它就会动。
- 把凭据放到另一个渠道去给。"我已经把链接发到您手机上了"是语音原生的引用替代品。它只花一条短信,却把一句无法核验的口头声明,变成了来电者挂断后可以自己去查的东西。
按这个顺序做,做到轮次听起来自然就停:把上个月对话记录里最高频的四十个问题拉出来,为它们预先算好口播答案;在问候语期间预热来电者的账户上下文;对着部分转写发投机查询,并在打断时取消它;然后把查询改写调用和重排从实时路径上删掉。四件事都做完之后,"换个更快的向量数据库"才值得讨论。你删掉的那些阶段,买回来的延迟比你能优化出来的任何一个阶段都多;而在一个语音轮次里,延迟正是"有依据"这件事的原材料。
相关:STT、TTS 与语音到语音讲你拿来做检索的那份转写从哪来,实时智能体架构讲这个循环住在哪里,多语言语音智能体讲跨语言时实体检索会怎样,以及 RAG 详解讲那条正在被砍的流水线。