Tail Control: The Counterintuitive Engineering of Reliable Agentic Workflows
TL;DR · AI 摘要
在客户API后运行LLM工作流时,确保结果的可靠性和及时性比答案正确性更重要,需应对时间、成本和令牌预算的挑战。
核心要点
- LLM工作流在客户API后需应对时间窗口、成本和令牌预算三重限制。
- 失败类型包括无效答案、硬错误、无答案和超时,超时是客户视角的失败。
- 质量是硬性门槛,错误答案即使及时也视为失败。
结构提纲
按章节快速跳转。
思维导图
用一张图看清主题之间的关系。
查看大纲文本(无障碍 / 无 JS 友好)
- 可靠代理工作流的工程挑战
- 失败模式
- 无效答案
- 硬错误
- 无答案
- 超时
- 资源约束
- 时间窗口
- 成本利润
- TPM预算
- 核心原则
- 质量优先
金句 / Highlights
值得收藏与分享的关键句。
时间窗口关闭后客户会重试整个流程,导致资源浪费和体验下降。
成本从内部的资源池变为客户的利润计算,直接影响运行频率。
TPM预算在负载高峰时达到上限,导致延迟最严重时资源最紧张。
错误答案即使满足时效性要求,仍会被客户视为彻底失败。
尾部控制:可靠代理工作流的反直觉工程方法 | Towards Data Science
代理人工智能
尾部控制:可靠代理工作流的反直觉工程方法
在客户API的背后,高质量的答案并不足够。它必须具备可用性,这意味着要准时交付。持续稳定地实现这一目标是一个关于方差而非速度的问题,其解决方案往往违反直觉。
Frank Wittkampf
2026年6月28日
27分钟阅读
分享
作者使用Google – Nano Banana生成的图片
在公司内部运行LLM工作流时,几乎所有失败的成本都很低:你可以重试、回退,甚至可能直接忽略。但将相同的工作流部署到客户API或MCP服务器后,这种容错能力就消失了。此时唯一重要的事情是:客户是否获得了正确且可用的结果?他们的业务流程依赖于你提供这样的结果。现在,评判结果是否成功的是客户而非你。在Databook,我们为全球最大的企业提供数十亿token的处理服务;本文基于大规模生产流程的真实数据撰写。希望它能为你提供一些有价值的见解。
实现这一目标比看起来更困难,因为LLM以不可靠著称。它们经常失败,失败形式有四种:无效答案(空、无法解析或直接错误)、硬错误、完全无答案,或超时无答案。整个流程的成功取决于每个步骤都成功,因此串联的步骤越多,失败的可能性就越大。即使每个步骤单独来看都很优秀,整个流程的成功概率也可能只有50%。
图1 – LLM调用失败的四种方式。其中三种是显性失败:无效答案、硬错误、完全无答案,这些都容易被发现和处理。第四种是隐性失败:一个正确但延迟到达的答案,在你这边看起来像成功,在客户这边却像失败。
在公司内部,你可以吸收所有这些失败,因为你在各个维度都有缓冲空间:重试失败步骤、等待慢步骤完成、多花一点成本、必要时放宽标准。但将相同的工作流部署到客户API后,这些缓冲空间就消失了,因为现在运行必须同时满足三个资源预算限制,而这些预算限制并非你设定:
- 时间 – 一个无论是否完成都会关闭的窗口:硬性网关超时(1到3分钟,有时长达5分钟),会在运行中途切断连接;或更温和的限制:SLA(服务等级协议)、调用方因等待结果而被阻塞、只能等待有限时间的进程。窗口关闭后无法恢复:当窗口关闭时,客户只能重新尝试,整个流程将从零开始重新执行。
- 成本 – 现在是一个利润边际而非成本池。每次运行都带着客户已经支付的价格,因此必须实现盈利而非仅仅可负担。运行频率的决定权在客户而非你。
- Token和速率 – 每分钟的token预算(TPM),你必须同时分配给所有客户,而客户往往在同一时间爆发式调用。你恰好在负载最重时达到上限,而这时延迟最严重。
这三个维度之下还有一个不可逾越的硬性底线:质量。答案必须正确才能被算作成功。即使答案快速、廉价且准时,但内容错误仍然是失败。质量不是一个可以消耗殆尽的预算。
图2 – 面向客户的运行同时消耗三个资源预算 – 时间、成本和token/速率 – 建立在固定的最低质量标准之上。每个预算都来自外部约束;质量底线是所有约束中唯一不可逾越的红线。
你完全可以单独处理其中任何一个问题。但问题在于它们会同时生效并相互制约,因此对其中一个问题的明显修复方案会加剧另一个问题。等待一个缓慢的步骤会导致时间窗口被浪费。为了抢在截止时间前完成而启动第二个副本,会消耗成本和配额。为了提升质量而选择更强的模型,反而会让处理速度变慢。你无法随意调整任何预算,因此唯一的选择是:一次性在所有限制之间进行有意识的权衡——同时确保始终不低于最低标准。
这正是构建面向客户的工作流所面临的独特挑战,有时这种挑战会迫使你采取一种从内部视角看完全违背直觉的策略:
- 终止一个尚未失败的调用
- 为一个你已经在付费的调用启动副本
- 故意降级到性能更弱的模型
在你自己的系统内部,你永远不会考虑这些操作。你会让缓慢的步骤自然完成。而对你最安静地施加惩罚的预算其实是时间:错过时间窗口时,你的系统侧不会显示任何异常。一个完美但晚几秒返回的答案,在你的仪表板上仍会被视为成功,但在客户看来却是失败。而这个时间限制是整个系统堆栈中唯一不会为你强制执行的约束。
先明确核心观点,因为后续所有内容都围绕它展开:一旦质量达标,可靠交付的关键在于稳定性而非速度。可预测的完成时间胜过有长尾的快速完成,因为你的客户无法依赖你系统最佳情况运行其基础设施;他们必须为你的最坏情况做准备。
本文所指与未指:工作流,而非免费推理代理
先明确一个关键区别,这将改变你对整个问题的理解。本文讨论的是代理式工作流:一个已知的流程,其中包含由LLM驱动的步骤,由确定性调度器执行。它不是一个在运行时自行决定下一步行动的推理代理。对于相同任务,工作流的效率要高得多:它已经知道计划,跳过推理过程,将所有独立步骤并行执行,因此能在推理代理所需时间与成本的几分之一内得出相同答案。两者各有适用场景(推理代理更具灵活性),但它们的失败方式不同,修复方式也不同。推理代理的问题在于决定该做什么;工作流的问题(客户能感知到的问题)在于:在保证质量和按时交付的前提下,完成它已经知道如何执行的任务。本文讨论的是后者。
我们的系统构建方式
以下发现源于我们的架构设计,这些结论具有普适性。这些是普通的直接API调用。不过了解我们的系统配置仍有助于你与自身系统进行对比。
我们通过自定义调度器调用第三方托管API(此数据集不包含自托管模型),并且我们通过两种方式运行旗舰模型:一是直接通过其提供商(如OpenAI、Anthropic等),二是通过托管平台(如Bedrock、Databricks等)。这意味着顶级模型通常拥有多个提供商,这使我们能够比较不同的服务路径并在它们之间迁移工作负载。
我们的工作负载类型多样:包括简单代理调用、深度推理、信息提取、JSON和自由文本输出。对于大量调用,我们需要将大规模事实库综合成答案,因此输入规模大,输出规模为小到中等。本文的分析在输入和输出大小的分组内保持恒定(详见附录)。
我们遇到的缓慢尾部大多是暂时性的。请注意,如果你的架构是自托管或使用专用容量,尾部行为可能会有所不同,需要采用其他方法。其次,运行多个提供商使得将路由作为独立预算的对冲策略变得可行。使用单一提供商时,这类操作的选择会更少。
声明与证据
这里有一个看似矛盾的做法:即使我们知道它可能在稍后完美完成,我们仍会在20-30秒时主动终止流程——这反而使系统更加可靠,而非降低可靠性。
这不是猜测。理论上成立——重尾重试的数学计算是明确的——在数据层面也成立:我们对超过一百万次近期企业工作负载的LLM调用进行了扫描(真实客户流量)。这些流量数据首先告诉你单次调用的时序有多么异常。典型的长输出调用大约需要十几秒返回,但每百次调用中就有一次需要30秒,有时甚至长达一分钟或更久——这与它执行的工作量完全无关。
FIGURE 3 – 实际生产数据(100万+调用,前100名企业工作负载,已匿名化);1秒分桶,上限90秒。模型名称故意隐去。这不是排行榜,也不是公平的直接对比:我们的系统中不同模型运行不同的工作负载,因此每条曲线背后的调用任务并不相同——图表未说明哪个模型“更快”。它明确显示:每个模型都有显著的尾部(注意模型C——典型的最快时间,却有长尾),服务路径与模型本身同样重要——模型F通过托管API与直接调用的两种不同尾部。模型A仅显示自由形式回答调用;同一模型上一个独立的、严格限定的结构预填充工作负载被排除在外(参见数据说明),以避免将曲线分割成两个人为的峰值。
典型调用与缓慢调用之间的差距构成了本文的核心。文章其余部分将讨论如何应对这一问题。
为什么时间不容忍任何延迟
工作流程的评判标准不是平均值,而是截止时间。平均而言我们的流程能顺利完成;但长尾中的异常运行却无法达标。这些尾部运行并非故障,它们稍后会返回完美答案,在内部测试中会被视为成功。但从客户角度看,每一个都算作失败。无论你的延迟分布多么合理,整个尾部都会成为失败率的累加。
因此,这里真正重要的不是平均延迟,而是方差。如果尾部很长,即使中位数再快也没有意义。
第二个瓶颈是沉没成本。你投入工作流越深,已经花费的代价就越多:时间、金钱和TPM配额。第九步的失败比第二步的失败代价高得多。你必须丢弃工作流构建的所有成果,而且留给切换策略的时间更少了。我们从不自己重启整个工作流,但客户会这么做。如果失败,客户几乎肯定会重试,从头开始重新执行完整流程。这会加剧我们这边的问题。消耗更多成本、更多令牌预算和SLA的错误预算。而且由于导致运行失败的条件通常没有变化,重试失败的可能性与之前几乎相同。更糟糕的是,这种情况往往发生在高TPM时段。这是最糟糕的时间——在系统本已紧张的情况下增加额外负载,同时也是再次失败概率最高的时刻。
还有一个容易被忽视的倍增因素。第一个倍增因素如前所述:可靠性具有累积效应,因此即使每个步骤都很优秀,整个流程仍然可能像抛硬币一样随机失败。但这种失败总是被描述为正确性问题:得出错误答案。
你几乎从未听说过另一个同样严重的累积效应:它发生在时间维度上。每个步骤都会增加自己进入缓慢尾部的微小概率,这些概率会叠加。因此,串联的步骤越多,即使每个步骤本身都很快,至少有一个步骤超时的可能性也会显著增加。这就是本文要讨论的倍增因素,也是文献中常常忽略的部分。现在让我们来看具体数据。
LLM回答时间的真实情况
上图中显示的典型时间分布在相当狭窄的区间内:每个模型完成一次典型调用所需时间都在8到20秒之间。但尾部分布却非常分散。一个模型的99百分位调用耗时约30秒,另一个模型的99百分位调用耗时超过80秒。中位数相近,但最坏情况差异巨大。如果你向客户承诺的是中位数表现,实际上你是在欺骗那些处于尾部的1/20和1/100的调用。多步骤工作流会频繁遭遇这些极端情况。快速的典型时间并不意味着可预测的性能。
显而易见的反驳是:缓慢调用只是做了更多工作:更大的提示词、更长的回答。事实并非如此。固定提示词大小和响应长度后,尾部分布几乎没有任何变化:在相同大小的调用组(工作量固定)中,p99仍然比中位数高出2到7倍(图4)。延迟与调用需要处理的工作量无关——在我们的流量中,延迟主要来自瞬时因素(排队、调度、流中竞争、供应商故障),这正是值得中断处理的原因。
图4 – “尾部与工作量无关”。每行固定提示词大小和响应大小;随着工作量增加,中位数上升,但每行中p50→p99的差距始终保持在3.8-6.7倍。这是一个刻意设计的哑铃图,而非分布曲线——相同大小的调用,完成时间差异巨大。
一个缓慢步骤足以拖垮整个流程
你可能会认为流程错过截止时间是因为多个步骤都略微缓慢。但这种情况几乎从不发生。当流程超出预算时,通常是某个步骤进入了尾部,而其他所有步骤都表现正常。数学上,流程超时主要由最差的单个步骤决定,而非多个轻微缓慢步骤的累积。整体表现更接近最大值,而非总和。
这是一则好消息。你不需要每一步都很快,你只需要阻止任何单一步骤偏离轨道。这就是所谓的“截断”。
边栏 —— 简要说明数学原理
(除非你喜欢数学,否则可以跳过)
这一论点背后有三个核心结论:
- 复利效应。仅仅是独立步骤的算术关系:n个步骤每个成功的概率为p,则整体成功概率为pⁿ。当p=0.95时,10个步骤≈60%,20个步骤≈36%——这是单纯的乘法关系,无需建模。同样的复利效应也作用于延迟:每个新增步骤都会在延迟尾部(我们每次调用测得的2-7倍p99/p50)产生独立的抽样,因此至少有一个步骤超出预算的概率会随步骤数增加而上升。独立性是简化假设(实际步骤可能共享资源而存在相关性),但这是保守且具有说明性的案例。
- 单一大跳跃。大语言模型的延迟具有重尾分布(近似对数正态分布),而对数正态分布属于次指数分布。对于独立的次指数步骤,总和的尾部概率等于各步骤尾部概率之和——当t增大时,
P(ΣX_i > t) ≈ Σ P(X_i > t) ≈ P(maxᵢ X_i > t)。换句话说:链条超时是因为某个步骤触发了尾部,而不是多个步骤轻微延迟共同导致。2
- 对冲策略,为何适用于所有故障类型。发起n次独立尝试并取第一个成功结果:如果单次尝试失败概率为q,那么n次全部失败的概率为qⁿ。这种算术关系不关心“失败”的具体定义——超时、硬错误或错误答案都会以相同方式降低概率,这也是为何重试/竞速/回退策略适用于所有故障类型。对于时间相关的故障类型,这种策略还能缩小延迟分布范围:由于独立步骤的方差相加,
Var(ΣX_i) = Σ Var(X_i),限制每个步骤的尾部会压缩整个链条的方差。所有这些都依赖于尝试的独立性(独立抽样、独立队列)——这也是为何并行重抽能消除瞬时尾部(或不幸的错误答案),但对确定性故障无效。3
策略:尽早截断,然后竞速
如果某一步骤进入了尾部,等待是最糟糕的选择——你正在用最稀缺的资源换取最不可能的回报。因此,你应该尽早放弃并并行重试:发起新的尝试并取最先返回的结果。新的尝试很少会陷入同样的困境,因此两次尝试所需的时间几乎等于一个卡住的调用原本消耗的时间——且两次都慢的概率极低(如果单次慢的概率为q,两次都慢的概率为q²)。3
图5 – 相同的长步骤,等待执行与竞速执行的对比。每个点代表该步骤的一次生产环境运行(前100名企业流量,已匿名化);红色标记慢速尾部。竞速执行第二次尝试并取最先返回的结果显著压缩了分布范围(标准差从6秒降至3秒,p99大致减半),代价是额外的token消耗——主体部分几乎不变,因此你能以更小的方差获得几乎相同的典型速度。在这种情况下,对总时间进行顺序重抽不会有任何帮助:你将需要支付生成基础时间两次。
中位数几乎不变:约10秒而非12秒。尾部却呈现相反趋势:99百分位数从约60秒降至25秒,运行间差异超过一半。你只需支付额外token的代价,就能获得可预测性。
那个价格是真实的,而且会产生反作用力。在该步骤上进行竞速操作会使代币费用翻倍,而代币是一个共享且有上限的预算。因此成本会真正限制你自由重试和竞速的幅度。但进行数学计算后你会发现这种影响是不对称的。将某一步骤的费用翻倍只会消耗该步骤的代币一次。而错过截止时间则会浪费你已经支付的所有费用,客户几乎总会重试,重新运行整个N步工作流至少一次,有时甚至更多次。你所处的流程越深入,这种权衡就越失衡:对第九步进行冗余尝试的成本,与丢弃前九步并重新运行它们的成本相比几乎可以忽略不计。因此你仍然会进行对冲操作,但不会盲目对冲,因为当最需要消耗预算时,这个共享的代币预算反而会最强烈地反噬你(稍后会详细说明这种压力)。
决定选择哪种备用方案的一个微妙之处在于:方向必须与步骤失败的原因相匹配。
- 因临时性原因变慢 → 重新绘制,理想情况下并行执行。新的尝试可以摆脱停滞状态。(对于较长的步骤,普通的串行重试效果更差——你会支付两次漫长的生成时间。)
- 因工作本身确实复杂而变慢 → 不要重复执行相同的调用。降级到更快的模型,或切换到能更低成本达成相同结果的替代路径。
- 结果错误而非变慢 → 升级到能力更强的模型。速度无法修正错误答案;能力可能可以。(这就是之前提到的质量底线,在运行时强制执行。)
在正确的信号上进行截断
一个响应时间实际上包含两个阶段。4 第一个代币生成的等待时间主要来自排队和调度;随后逐个生成的代币则构成其余部分。哪个阶段产生尾部决定了你应设置的截断条件。这取决于该步骤的输出量。
对于本文讨论的较长步骤(那些紧贴截止时间的步骤),尾部出现在生成阶段,而非第一个代币的等待阶段。缓慢的队列只是四十二秒调用中的一个小片段;真正消耗预算的是生成的代币数量。因此应根据总耗时或已生成代币与剩余时间的比值进行截断,而不是根据第一个代币的等待时间。(对于短步骤则相反:生成内容较少时,第一个代币的等待时间占了大部分,此时以第一个代币等待时间作为截断条件更合理。通过测量自己的步骤可以判断属于哪种情况。)
无论哪种情况,这两个信号都值得接入:
- 截断时间后仍未收到任何代币?这表示完全卡住,而非变慢。应放弃并进行对冲。新的并行尝试将被重新调度,几乎总是能成功。
- 代币正在生成但会超出预算?不要重复执行。你只是以相同速度生成相同长度的内容。降级到更快的模型。
还有一个时钟无法捕捉的失败类型:某个步骤按时返回但返回了无效内容(例如内容为空、被截断或无法解析)。延迟截断会直接忽略这种情况,只有下游的质量检查才能发现。对于任何需要返回特定结构的步骤,最经济的检查方式是在调用后立即进行严格验证。将结果与预期的模式或对象进行解析,将验证失败视为与其他错误相同的情况:截断并切换策略(重新绘制或升级到更强大的模型)。这种检查能在错误答案到达下一步前拦截大量无效响应。早期截断带来的是可预测性,而非正确性。请始终将这两个任务分开处理。
关键点:对冲操作会消耗你最紧张的预算
并发请求存在一个令人尴尬的特性。当系统繁忙时,尾部延迟最严重,而“繁忙”恰恰是每分钟令牌预算最紧张的时刻。想要解决尾部延迟问题的唯一方法,却偏偏要在预算最紧张的时刻消耗令牌。盲目执行会导致连锁反应:缓慢的请求触发对冲,对冲增加负载,负载使整体变慢,更多请求超过截止时间。延迟问题最终演变为速率限制问题。
有两个事实让情况比表面看起来更严峻。一旦发出第二次请求,成本立即被确认。取消失败请求虽然释放了连接,但服务商仍会继续生成并计费被放弃的尝试。没有退款机制,因此所有控制必须在决定是否对冲时完成,而非之后。而且你通常无法看到剩余预算。估算虽然可行但复杂,因此任何“配额填满时自动减缓”的方案在实践中都难以实施。
实际可行的方案更粗暴且更具结构性:
- 将对冲请求路由到拥有独立预算的模型或服务商。令牌限制是按模型和服务商划分的,而我们通常运行多个模型(如《我们的系统构建方式》中所述)。将重试请求导向不同模型或服务商,可以获取独立配额并避免重复消耗稀缺预算。这一操作既摆脱了停滞,也避免了双重消耗。
- 通过设计让对冲请求本身罕见。预计算的截止阈值已实现这一点:将阈值设为每一步的p95测量值,对冲仅在少数缓慢请求时触发,因此额外消耗微乎其微且无需运行时会计。与下一章节使用相同截止阈值,无需新增机制。
- 根据实际接收到的信号作出反应。你可能无法读取剩余配额,但可以监测429错误和持续上升的延迟。将这些视为减少对冲、延迟触发的信号,而非增加对冲。
- 在实际饱和时停止对冲。当服务商已开始返回速率限制错误时,更多尝试只会加深困境。此时应降级到更小更便宜的模型,或直接放弃部分工作。
我们尚未实现但作为方向提出的一个杠杆:一个显式的全局上限,将对冲请求限制为总流量的一小部分,且独立于每一步的决策。这是大规模尾部处理工作建议的理论保障;3我们目前设置保守的截止阈值已足够,但若对冲率更高时,这将是下一步方向。
边栏 — 你最先采取的低成本措施
截止阈值和对冲是保险措施。如果工作流本身设计良好,就不需要购买太多保险。在任何反应性策略之前,默认会触发的措施:
- 通过设计实现并行性。将流程构建为依赖图,并在输入存在时立即执行每一步。更进一步 — 设计出依赖关系。更少的依赖意味着更多步骤成为叶子节点,叶子节点可以廉价失败而不会拖垮整个图。
- 在不需要时完全不调用模型。最可靠的调用是根本不调用 — 在不需要模型的地方,使用代码、查询和验证器替代。
- 按步骤混合模型,而非按工作流。在足够时使用快速廉价模型,在必要时使用强大模型。
- 对确定性部分进行缓存。不要为无法改变的答案支付LLM两次费用。
关键点在于:先将可靠性预算投入结构设计,让底层机制有更少的修复工作要做。
你实际触发对冲的时机是什么时候?
为什么几乎没有人这样做
这并不难。它很微妙,而大多数团队没有处理它的引擎。
流行的流程工具,如Airflow和Temporal,是为使管道具有弹性而构建的:重试、恢复、不丢失状态,它们在这方面做得非常好。它们的超时建议源于这一目标:将每个步骤的超时时间设置为比最慢运行时间更长,并重试直到成功。5 当任务是确保持久完成时,这种直觉是正确的;但当任务是按时完成时,这正是完全错误的建议。你的工作流引擎会很乐意多次重试一个步骤;它对步骤的测量典型时间和下游影响没有任何概念,因此无法提前截断并切换模型。这不是一个缺陷。这是有意为之的设计。
分布式系统的基础知识已经在我们这边:从截止时间预算中工作,将每个超时与测量的延迟相匹配。6 我们并不反对这一点。我们将其应用于这些工具未假设的情况:一个短且不可恢复的预算,其中在截止点采取的正确行动是更快的替代方案,而不是再次进行相同的调用。同样的原则,但方向相反。
总结
如果你什么都记不住,记住这一点:可预测的完成时间胜过有长尾的快速完成时间。低方差胜过低延迟。你无法向客户承诺中位数,只能承诺一个上限。这里的一切都服务于这个上限。提前截断、对冲、竞速、消除依赖:每种方法都牺牲了一点平均速度,以换取更少的方差。你放弃右尾,以换取左尾。
在面向客户的智能代理工作流中,可靠性就是产品。这项工作的核心不是拥有重试和回退的工具包,这些只是基本要求。它是在每一步决定是否对冲以及何时放弃,基于你系统的约束和测量行为。
附录
作者简介
Frank Wittkampf 是 Databook 的应用人工智能工程主管。他的团队设计、构建并运营一个完全定制的人工智能堆栈,包括深度推理、智能代理工作流引擎、人工智能资产生成、智能代理工具、知识库和上下文图、人工智能预处理、多租户人工智能配置管理等。这个人工智能基础设施为微软、Salesforce、亚马逊、Databricks 等顶级企业公司的 GTM 团队提供支持。
关于数据
(注:此处原文未提供具体内容,因此未进行翻译)
这些延迟数据来自近期(2026年6月)匿名化的企业客户工作负载生产流量——在30天窗口内约120万次LLM调用,而非合成基准测试或公开追踪数据。如《我们的系统构建方式》中所述,这些是直接调用托管第三方API的请求,这也是为什么缓慢尾部主要为临时性现象的原因之一。文中数据描述的是较长调用(输出≥600个token),因为这些调用才会真正对截止期限产生压力;较短调用更快且更稳定。全文中,“尾部比例”(p99/p50)在未特别说明时,会将调用规模固定在桶内。模型仅按家族和部署路径标注;可预测性取决于部署路径(例如托管API与直接调用),而非仅模型本身,因此这些数据刻意不构成模型排名。持续时间按每秒为单位分桶;硬性90秒上限仅截断最长调用的约0.2%,因此你看到的尾部是真实存在的,而非上限导致的伪像。
尾部只是大调用吗?
对图4的合理质疑是:每行对应的是token桶而非固定token数量,因此可能单元格内的慢调用只是规模更大的调用——需要更多预填充和生成——尾部只是规模问题,而非临时性因素。
事实并非如此,数据本身的形态已说明原因。如果规模导致单元格内尾部,将出现两种情况:尾部比例会随工作量增加而增长,且最严格约束的单元格几乎不会有尾部。但两者均不成立。
图A1 — 按输出规模分桶的单元格内p99/p50尾部比例。每个点代表一个模型×单元格,且token数量均固定在桶内;颜色=输入规模,点面积∝调用量;红色条=每列的体积加权平均值。可从中读取两点信息。首先,每个输出规模列的尾部比例在约2-4倍之间保持平稳——它不会随工作量增加而上升,因此尾部不会随工作量扩展。其次且决定性的是,观察最左侧列:这些调用最多输出50个token,因此生成时间物理上最多只能相差约1秒——但那里的尾部仍达到约3.5倍。没有足够大的规模变量能产生这种差异。残余波动是临时性因素(排队、调度、短暂的提供方故障),这正是新尝试能摆脱的。为什么这些数字比之前提到的2-7倍更小:此处列数据是多个单元格的体积加权平均值,平滑了波动;而正文中的2-7倍是单次调用的范围——各单元格实际覆盖的区间。相同数据,两种不同切面:平均值显示尾部不随工作量扩展;范围显示每次调用可能达到的波动幅度。
注释与脚注
注:所有图片均由作者创建。
1:每个步骤95%成功率,10步后整体成功率约60%;20步后约36%(假设独立)。
2:对数正态分布属于次指数类,其中独立项和的尾部渐进等于各单项尾部之和:P(S_n > t) ∼ Σ_i P(X_i > t) ∼ P(max_i X_i > t) 当t→∞时成立——即“单一大跳跃”原理(Foss, Korshunov & Zachary, 《重尾与次指数分布导论》,Springer,第2版2013年,公式1.3与1.6)。这是一个渐进性陈述,假设独立性,因此应将其视为理解为何一个慢步骤占主导地位的直觉,而非直接套用的公式。
3 : 如果每次独立尝试以概率 q 变慢,那么两次并行尝试同时变慢的概率为 q²;n 次尝试则为 qⁿ。经典的研究结果(Dean & Barroso, “The Tail at Scale,” CACM 2013)表明,在代理场景中,Winston 等人(arXiv:2605.21470, ICML 2026)通过测量延迟曲线,在串行、并行和混合执行模式之间进行选择。在我们的生产数据中,对长步骤进行两次尝试竞争执行,使 p99 延迟降低超过一半(≈60s→25s),而总时间上的顺序重试并未产生显著效果。
4 : 在推理任务中,这种划分是标准做法:“首个 token 生成时间”(队列 + 预填充)与“每个 token 生成时间”的对比。例如 Agrawal 等人(Taming the Throughput-Latency Tradeoff in LLM Inference with Sarathi-Serve, arXiv:2403.02310, 2024)的研究。在我们的生产流量中,长调用的尾部延迟主要出现在生成阶段而非首个 token 等待阶段——这也是为什么我们选择基于总耗时而非首个 token 时间来裁剪长步骤。
5 : Temporal 的活动超时机制设计为最终会完成(包括重试),因此 Start-To-Close 时限设置在慢尾部延迟之上。
6 : Google SRE、gRPC 截止时间机制和 Spanner 都建议传递总预算并丢弃无法帮助调用方的工作。我们将这一原则扩展到同步且不可恢复的客户预算场景。
Written By
查看 Frank Wittkampf 的所有文章
Ai Engineering
,
Artificial Intelligence
Editor’s Picks
LLM Agents
Llm Applications
分享本文
- Facebook 分享
- LinkedIn 分享
- X 分享
Towards Data Science 是社区出版物。提交您的见解以触达全球受众,并通过 TDS 作者支付计划获得收益。
更新为实际投稿链接
为 TDS 撰写文章
✦ end CTA ✦