- 作者
-
-
- 姓名
- Amit Shekhar
- 发布于
-
语音 AI Agent 的延迟预算是一份计划,它把允许一次回复的总等待时间拆分成更小的部分,Agent 的每一步各占一份,从而确保 Agent 总是能足够快地开始回复,让人感觉自然。
在这篇博客中,我们将学习如何为语音 AI Agent 构建延迟预算。我们还会了解为什么语音 AI Agent 需要延迟预算,如何确定总目标并将其分配到每个阶段,如何测量真实数据并优先削减最大的延迟,流式处理和工具调用如何影响预算,以及我们必须接受怎样的权衡。
我们将涵盖以下内容:
- 什么是延迟预算?
- 为什么语音 AI Agent 需要延迟预算?
- 第 1 步:确定总目标
- 第 2 步:列出流水线的每个阶段
- 第 3 步:给每个阶段分配相应的时间份额
- 第 4 步:测量真实数据
- 第 5 步:优先削减最大的延迟
- 流式处理如何改变预算
- 工具调用如何融入预算
- 我们必须接受的权衡
我是 Amit Shekhar,Outcome School 的创始人。我曾指导和培养过许多开发者,他们的努力帮助他们获得了高薪的技术工作,帮助许多科技公司解决了独特的问题,并创建了许多被顶级公司使用的开源库。我热衷于通过开源、博客和视频分享知识。
我在 Outcome School 教授 AI 和机器学习。
让我们开始吧。
什么是延迟预算?
在深入预算之前,我们必须先了解延迟的含义。
延迟是指从发出请求到得到结果之间的等待时间。
对于语音 AI Agent 来说,最重要的延迟是从我们停止说话到听到 Agent 开始回复之间的时间。这通常被称为 语音到语音延迟。
现在,让我们来理解预算部分。
延迟预算是我们为一次回复允许的固定总时间,它被拆分成更小的时间份额,分配给产生回复的每一步。
假设我们一个月有 30,000 卢比可以花。我们不会随机花掉它,而是决定 12,000 用于房租,8,000 用于食物,3,000 用于出行,7,000 用于储蓄,加起来正好是 30,000。如果某一项超出了它的份额,我们就能准确知道问题出在哪里,并且知道必须削减其他某一项才能保持总额不变。
延迟预算的工作原理与此相同。只不过在这里,我们花费的是毫秒而不是卢比。
一毫秒是一秒的千分之一,我们将其简写为
ms
。
为什么语音 AI Agent 需要延迟预算?
当两个人交谈时,双方轮流说话。一个话轮就是一个人的发言机会。一个人说完话到另一个人开始说话之间的间隔非常短。一项针对十种语言对话的著名研究发现,这个间隔平均约为 200 毫秒。
因此,我们的大脑被训练成期待几乎即时的回应。当语音 AI Agent 沉默 2 到 3 秒时,就会让人感觉它坏了。人们开始说“喂?你还在吗?”,并且会抢在 Agent 之前说话。
语音 AI Agent 并不是一个单一的程序。它是一连串的步骤。它聆听、判断我们是否说完了、把我们的语音转成文本、思考答案,再把答案转回语音。每一步都需要一些时间。
如果我们不规划每一步的时间,每一步都会被单独建成“足够快”的样子。然后,这些“足够快”的部分加在一起,得到的回应却慢得多。
这时延迟预算就派上用场了。它为每一步设定一个明确的、不得超过的数值,从而使总时间保持在我们的目标之内。
现在,让我们一步一步来看看如何构建延迟预算。
第 1 步:确定总目标
首先,我们需要确定允许花费的总时间。对语音 AI Agent 来说,达到 200 毫秒非常困难,因为音频必须经过多个模型并通过网络传输。因此,在实践中,许多构建语音 AI Agent 的团队将语音到语音的延迟目标定在 800 毫秒左右或更低。超过一秒很多的回应就会开始让人感觉慢了。
举例来说,在这篇博客中,我们将使用 800 毫秒作为总目标。
这里还要注意一件事。我们必须决定计时从哪里开始、到哪里结束。
- 计时从用户停止说话时开始。
- 计时在 Agent 回应的第一个声音到达用户耳中时结束。
计时并不是在完整答案准备好时结束,而是在用户听到第一个声音时结束。我们将在流式传输部分看到这为什么重要。
第 2 步:列出流水线的每个阶段
现在,我们需要列出计时开始和结束之间的每一个步骤。
一个典型的语音 AI Agent 流水线如下所示:
+--------------------------------------+
| User stops speaking |
+--------------------------------------+
↓
+--------------------------------------+
| Network: user's audio to our server |
+--------------------------------------+
↓
+--------------------------------------+
| Endpointing: is the user done? |
+--------------------------------------+
↓
+--------------------------------------+
| STT: final text of what was said |
+--------------------------------------+
↓
+--------------------------------------+
| LLM: first token of the answer |
+--------------------------------------+
↓
+--------------------------------------+
| TTS: first chunk of audio |
+--------------------------------------+
↓
+--------------------------------------+
| Network: audio back to the user |
+--------------------------------------+
↓
+--------------------------------------+
| User hears the first sound |
+--------------------------------------+让我们来了解每个阶段。
- 网络(上行): 用户的声音从他们的手机或浏览器传输到我们的服务器。
- 端点检测: Agent 判断用户已经结束了话轮。它通常通过等待短暂的静默,或使用预测话轮结束的模型来实现。
- STT(语音转文本): 语音识别模型将音频转成文本。
- LLM(大语言模型): 模型读取文本并开始生成答案,一次一个 token。从开始到第一个 token 生成出来的时间称为首 token 时间。
- TTS(文本转语音): 语音模型将回答文本转换为音频。从开始到第一段音频就绪所需的时间称为首字节时间或首音频时间。
- 网络(下行): 音频从我们的服务器传回用户,设备开始播放。
在列举时很容易漏掉几个环节,尤其是网络和端点检测等待。我们必须把它们全部列出来,因为用户能感受到每一个环节。
这种由独立的 STT、LLM 和 TTS 模型组成的链条称为级联流水线。我们有一篇关于 级联 vs 语音到语音 的详细博客,讲解了如何在这条流水线和单一语音到语音模型之间做选择。
计划转型进入 AI 领域?
无论你今天在哪个技术领域工作,只要付出努力,你都可以转型到以下这些角色:
- AI 工程师
- 前沿部署工程师
- AI Agent 工程师
- Harness 工程师
- 语音 AI 工程师
- AI 架构师
- LLM 工程师
- LLM 推理工程师
- 生成式 AI 工程师
- MLOps 工程师
- 机器学习工程师
- 深度学习工程师
Outcome School 的 AI 与机器学习课程 助你达成目标。
第 3 步:给每个阶段分配相应的时间份额
有了这份清单,接下来就是把 800 毫秒分配到各个环节。
这些数字只是为了便于理解。实际数字取决于模型、硬件以及我们服务器的位置。下面来看一个示例预算:
| 环节 | 预算 |
|---|---|
| 网络(上行) | 50 毫秒 |
| 端点检测 | 200 毫秒 |
| STT 最终文本 | 100 毫秒 |
| LLM 首个 token 时间 | 300 毫秒 |
| TTS 首音频时间 | 100 毫秒 |
| 网络(下行) | 50 毫秒 |
| 总计 | 800 毫秒 |
在这里,我们可以注意到几个重要的点。
首先,LLM 占据最大份额,300 毫秒,因为思考答案是整个流水线中最繁重的工作。但这并不是写出完整回答所需的时间,而只是生成第一个 token 的时间。回答的其余部分会在用户已经开始听到开头的同时继续生成。
其次,端点检测环节占据第二大份额。Agent 会把预算的很大一部分花在等待上,以确保用户真的说完了。
第三,STT 只分到 100 毫秒。这是可能的,因为流式 STT 模型在用户还在说话时就已经在把语音转成文本了。当用户停下来时,它只需完成最后几个词。
分配预算的一个好方法是从我们无法大幅改变的环节(如网络)入手,然后把剩余的时间分配给我们可控的环节。
给你的小提示
无论你在哪个技术领域工作,都要熟悉这些主题:
- LLM
- RAG
- MCP
- Agent
- 微调
- 量化
我们把所有内容整合在一个视频中:
AI 工程详解:LLM、RAG、MCP、Agent、微调与量化
不必现在读完——先收藏起来,等有空时再看。未来的你会感谢现在的你。
现在,让我们回到正题。
第 4 步:测量真实数据
如果不测量实际发生的情况,纸面上的预算就没有用处。
因此,我们必须在每一轮对话的每个阶段边界处记录时间戳。这样,我们就能将每个阶段的实际耗时与其预算进行比较。
我们有一篇关于 AI Agent 可观测性 的详细博客,逐步讲解了如何用 trace、span 和指标来记录这类数据。
下面来看一个简单版本的代码:
import time
BUDGET_MS = {
"endpointing": 200,
"stt": 100,
"llm_first_token": 300,
"tts_first_audio": 100,
}
marks = {}
def mark(name):
marks[name] = time.monotonic() * 1000 # time in ms
def report():
stages = ["user_stopped", "endpointing", "stt", "llm_first_token", "tts_first_audio"]
for prev, curr in zip(stages, stages[1:]):
spent = marks[curr] - marks[prev]
status = "OK" if spent <= BUDGET_MS[curr] else "OVER BUDGET"
print(f"{curr}: {spent:.0f} ms (budget {BUDGET_MS[curr]} ms) {status}")在这里,我们做了以下几件事:
- BUDGET_MS 保存了在我们服务器上运行的每个阶段的预算。
- mark() 在某个阶段结束时保存当前时间。我们在音频中用户语音停止的时刻调用 mark("user_stopped"),在 Agent 判断该轮对话结束时调用 mark("endpointing"),在最终文本就绪时调用 mark("stt"),依此类推。
- report() 计算每对标记之间所花费的时间,并将其与预算进行比较。
它会输出类似以下的内容:
endpointing: 210 ms (budget 200 ms) OVER BUDGET stt: 85 ms (budget 100 ms) OK llm_first_token: 520 ms (budget 300 ms) OVER BUDGET tts_first_audio: 95 ms (budget 100 ms) OK
现在,我们可以清楚地看到哪个阶段超出了它应占的份额。在这个例子中,LLM 是最大的问题,超出了预算 220 毫秒。
注意: 仅从服务器端很难测量网络阶段。要捕获这些数据,我们还必须记录用户设备上的时间,或者通过一次同时录制两端的测试通话来测量完整的语音到语音时间。
关注慢回复,而不仅仅是正常回复
单次测量是不够的。每一轮对话所花的时间都不同。
因此,我们要看百分位数。
- p50 是一半回复所低于的时间。它反映的是正常情况。
- p95 是 100 个回复中有 95 个所低于的时间。它反映的是慢的情况。
假设我们的 p50 是 750 毫秒,但 p95 是 2000 毫秒。这意味着每 100 个回复中有 5 个需要 2 秒或更长时间。在多轮的长通话中,用户很可能会遇到其中一些慢回复,而每一次都像是一段漫长而尴尬的沉默。
因此,我们不仅要为 p50 设定预算,也必须为 p95 设定预算。
还有一点需要注意。把每个阶段的 p95 相加,并不能得到总体的准确 p95,因为不同阶段的慢时刻并不总是发生在同一轮对话中。所以,我们还必须直接测量总的语音到语音时间,并单独跟踪它的 p95。
要深入学习 LLM 推理性能指标、监控与日志,以及每个 AI 工程师都应该知道的数字,请查看 Outcome School 的 AI 与机器学习课程。
第 5 步:优先削减最大的延迟
现在我们知道了哪个阶段超出预算,接下来就修复它。
我们总是先解决最大的数字。当 LLM 已经超时 220 ms 时,在网络方面节省 20 ms 并没有多大帮助。
以下是缩短每个阶段的常用方法:
- 端点检测: 使用能够理解句子何时结束的轮次检测模型,而不是总是等待长时间的静默。这样,当用户明显已经说完时,Agent 就能更快地回复。
- STT: 使用流式 STT 模型,这样在用户停止说话之前,大部分文本就已经准备好了。
- LLM: 使用更小或更快的模型,保持提示词简短,对每一轮都重复的提示词部分复用已保存的工作(提示词缓存),并将模型部署在离用户较近的位置。
- TTS: 使用流式 TTS 模型,从第一个短语开始就生成音频,而不是等待完整的答案。
- 网络: 将服务器部署在靠近用户的区域,并保持连接打开,而不是每一轮都新建连接。
每次修复后,我们都会重新测量。然后,我们挑选下一个最大的数字并重复这一过程。这个循环会一直持续到总耗时在目标值以内,p95 也一样。
保持更新: 订阅我们的新闻通讯,将我们最新的 AI 和机器学习博客直接发送到您的收件箱。
流式处理如何改变预算
我们先来看看缓慢的方式。假设每个阶段都等待上一个阶段完全完成。举例来说,各阶段耗时如下:
- Agent 等待长时间的静默,以确保用户已经说完:800 ms
- STT 转录完整的录音:500 ms
- LLM 生成完整的答案:2000 ms
- TTS 将完整的答案转换为音频:1000 ms
在用户听到任何内容之前,总耗时已达 4300 ms,而这甚至还没有算上网络时间。
现在,让我们看看流式处理的方式。
- STT 在用户还在说话时就将语音转换为文本。
- LLM 一生成第一个短语,就立即送入 TTS。
- TTS 一生成第一块音频,就立即发送给用户。
- 当用户听到第一句话时,LLM 和 TTS 还在继续处理后面的句子。
这样,各个阶段就重叠起来了。它们像流水线一样工作,而不是像队列那样,每个人都等着前面的人完成。
这就是为什么我们的时间预算只计算 LLM 的第一个 token 和 TTS 的第一块音频。实际上,TTS 需要一个短语才能开始,所以前几个 token 也必须快速输出。其余的工作在用户已经开始收听时进行,因此不会增加等待时间。
我们有一篇详细的博客,探讨为什么首音频时间比总响应时间更重要,对此进行了端到端的讲解。
工具调用如何融入预算
许多语音 AI Agent 需要在回复过程中查询信息。例如,客服 Agent 需要查询订单状态。
在这里,LLM 推荐一个工具及其输入,比如“查询订单 1234 的状态”。然后,Agent(我们的代码)运行该工具,获取结果,并将其返回给 LLM。只有这样,LLM 才能写出真正的回答。
这为流水线增加了新的阶段:
- LLM 生成工具请求所需的时间
- 工具本身运行所需的时间,例如数据库查询或外部服务调用
- 第二次 LLM 调用,将结果转化为回答
这些阶段可能增加几百毫秒到几秒不等,这会使回复时间远远超出我们 800 毫秒的目标。
因此,我们必须为工具调用分配独立的预算。常见的做法有:
- 保持工具快速,并设置超时,确保缓慢的工具不会让对话卡死。
- 让 Agent 先说一句简短的话,比如“我来帮您查一下。”,同时工具在后台运行。用户能在预算内听到回应,对话也不会陷入沉默。
- 在通话开始时预加载经常需要的数据,这样在对话轮次中就不需要进行工具调用了。
要掌握 Agent 中的工具使用、Agent 架构,并设计实时语音 AI Agent,以及从零开始构建 AI 编程 Agent,请查看 Outcome School 的 AI 与机器学习课程。
我们必须接受的权衡
端点检测阶段存在一个艰难的权衡。
如果 Agent 等待较长时间才回复,很少会打断用户。但每次回复都显得很慢。
如果 Agent 只等待很短时间,每次回复都显得很快。但当用户在句子中间停下来思考时,它会开始打断别人。
因此,我们不能随意把端点检测的份额压得越小越好。我们必须找到既不会太频繁打断用户的最短等待时间,并且必须同时衡量延迟和打断次数。
同样的道理也适用于 LLM 阶段。更大的模型通常能给出更好的回答,但生成首个 token 的时间更长。更小的模型更快,但往往更容易出错。延迟预算让这个选择变得清晰,因为现在我们确切地知道可以花多少毫秒来换取质量。
现在,我们应该已经理解了如何为语音 AI Agent 构建延迟预算、如何在各个阶段之间分配预算,以及如何让整个回复保持在目标之内。
常见问题
什么是语音到语音延迟?
语音到语音延迟是指从我们停止说话到听到语音 AI Agent 开始回复之间的时间。计时从用户停止说话开始,到回复的第一个声音到达用户耳中为止。它不会等到完整回答准备好才停止。
在语音 AI Agent 的延迟预算中,哪个阶段占的份额最大?
LLM 通常占最大份额,因为思考答案是整个流程中最繁重的工作。在我们 800 毫秒的示例预算中,LLM 获得 300 毫秒,但这只是用于生成第一个 token,而不是完整答案。端点检测等待占第二大份额。
为什么我们要跟踪 p95 延迟,而不仅仅是 p50?
我们应该跟踪 p95,因为 p50 只反映正常情况,而 p95 能反映慢速回复。如果 p50 是 750 毫秒,但 p95 是 2000 毫秒,那么每 100 条回复中就有 5 条需要 2 秒或更长时间。在长时间的通话中,用户很可能会遇到这些尴尬的沉默。
我们可以把每个阶段的 p95 相加来得到总体的 p95 吗?
不行。把每个阶段的 p95 相加并不能得到总体的精确 p95,因为不同阶段的慢速时刻并不总是发生在同一轮对话中。因此,我们还必须直接测量总的语音到语音时间,并单独跟踪它的 p95。
我们如何测量延迟中的网络部分?
仅从服务器端很难测量网络阶段。为了捕捉它们,我们还必须记录用户设备上的时间,或者通过一次同时录制两端的测试通话来测量完整的语音到语音时间。
目前就到这里。
谢谢
Amit Shekhar Outcome School 创始人
你可以通过以下方式与我联系:
关注 Outcome School: