跳到主要内容

Agentic System 中的重试风暴问题:为什么简单的重试逻辑会消耗 200 倍的 Token

· 阅读需 13 分钟
Tian Pan
Software Engineer

你的智能体(Agent)调用了一个工具。工具超时了。智能体进行重试。每一次重试都会将完整的对话上下文发送回 LLM,在注定无法成功的请求上白白浪费 token。与此同时,重试触发了依赖于第一个工具的第二个工具调用,而它同样失败并重试。短短几秒钟内,一个不稳定的 API 就被放大成了数十个冗余请求,每一个都在消耗算力、token 和时间 —— 并且每一个都让潜在的问题变得更加糟糕。

这就是重试风暴(retry storm)。这并不是一个新概念 —— 分布式系统工程师几十年来一直在与重试放大(retry amplification)作斗争。但 Agent 智能体系统使这一问题急剧恶化,其程度是微服务时代的模式无法完全解决的。

为什么智能体重试在本质上更昂贵

在传统的微服务中,一次重试只需消耗一次 HTTP 往返。而在智能体系统中,每一次重试都会重新将完整的对话上下文提交给 LLM。一个触发 10 次请求的重试循环不仅仅是浪费了 10 次 HTTP 调用 —— 它还消耗了 10 倍的 token,每次都要支付完整上下文窗口的成本。

来看一个具体的例子。一个拥有 8,000 个 token 对话上下文的智能体调用了一个返回 429 频率限制(rate-limit)错误的工具。采用简单的 3 次重试策略:

  • 无控制状态下:总共 4 次 LLM 调用(原始调用 + 3 次重试),每次都发送完整的 8,000 token 上下文。这意味着为了完成一项注定失败的任务,消耗了 32,000 个输入 token。
  • 带有熔断器(circuit breaker)的状态下:1 次 LLM 调用,随后立即返回失败响应。成本:约 8,000 token。

这仅仅是单个工具调用带来的 4 倍放大。在每一个步骤都会触发工具调用的多步智能体工作流中,这种放大效应会呈复合式增长。支付处理失败会触发订单智能体的重试,进而导致库存智能体重试分配检查,从而压垮库存服务 —— 在几秒钟内将负载增加 10 倍或更多。

成本计算彻底改变了权衡逻辑。当一个生产团队在一次 API 故障期间测量差异时,他们发现无控制的重试在单次对话的 30 秒窗口内消耗了约 2 美元的 token,而熔断机制将其降低到了约 0.01 美元 —— 实现了 200 倍的成本削减。

三种放大模式

智能体系统中的重试风暴遵循三种截然不同的模式,每种模式都需要不同的防御措施。

1. 垂直放大(Vertical Amplification):单个工具调用内的重试

这是最简单的情况。智能体调用 API,遇到瞬时错误,并以指数级退避(exponential backoff)进行重试。这在传统分布式系统中已经得到了很好的解决。修复方法很简单:带有抖动(jitter)的指数级退避、限制重试次数(通常为 3 次),并遵守 Retry-After 响应头。

但即使是这种基础模式在智能体系统中也有其独特之处。在微服务中,重试逻辑位于基础设施层。在智能体系统中,LLM 本身可能会决定重试 —— 它在上下文中看到了失败响应,并决定再次调用同一个工具。这创造了一个影子重试循环,它独立于你在工具层构建的任何重试逻辑。智能体的上下文现在包含了原始的失败尝试,模型可能会不断尝试相同调用的变体,每次都在发送新的请求时附带累积的失败历史。

2. 水平放大(Horizontal Amplification):跨工具调用的故障级联

当智能体工作流涉及顺序工具调用,且步骤 N 依赖于步骤 N-1 时,早期步骤的失败会产生级联重试。后续的每个工具都会看到退化或缺失的输入,从而失败并触发自己的重试链。

这种乘数效应非常严重。如果每个步骤都有 3 次重试策略,而你有 5 个依赖步骤,那么步骤 1 的一次失败理论上可以产生 3^5 = 243 次重试尝试。在实践中,超时通常会阻止全面的爆炸,但你经常会看到 10-50 倍的放大。

这种模式中更隐蔽的情况是,当智能体没有干净利落地失败 —— 相反,它从退化的服务中收到了部分或错误的结果,将其传递给下一个工具,而错误直到两三步之后才暴露出来。现在的调试需要追踪整个调用链才能找到最初的故障点。

3. 递归放大(Recursive Amplification):多智能体委托

在多智能体架构中,智能体之间存在委托关系,重试放大遵循委托树进行。智能体 A 调用智能体 B,智能体 B 调用智能体 C。如果智能体 C 的工具调用失败,且智能体 B 重试其整个工作流(包括对 C 的委托),同时智能体 A 也重试其工作流(包括对 B 的委托),你就会得到指数级增长。

这是传统熔断器处理得最差的模式,因为重试决策发生的抽象层级与故障发生的层级不同。智能体 A 并不知道它的重试会触发下游数十次的重试 —— 它只看到了“子任务失败”并决定再次尝试。

为什么微服务熔断器不能直接套用

熔断器模式——跟踪失败率、在达到阈值后跳闸、在冷却期间快速失败——是分布式系统中处理级联故障的标准答案。但将其直接应用于 Agent 系统会产生三个问题。

粒度不匹配。 微服务熔断器作用于服务间的调用。Agent 系统需要在多个级别设置熔断器:单个工具调用、每个步骤的工作流阶段、每个对话的 Token 预算以及每个 Agent 的委派链。仅监控工具调用层的熔断器会错过更高级别的放大模式。

非确定性问题。 当微服务熔断器开启时,调用服务会收到一个确定的错误响应。当 Agent 的熔断器开启时,LLM 会收到一个工具失败的消息并决定下一步做什么。它可能会优雅地降级,也可能会产生幻觉,或者尝试以创新但错误的方式绕过失败。熔断器控制的是基础设施,而不是模型对基础设施故障的反应。

上下文累积问题。 在微服务中,每个请求都是独立的——熔断器开启没有内存成本。在 Agent 系统中,每次失败的尝试都会增加对话上下文。等到熔断器跳闸时,上下文中已经充斥着失败日志,这增加了该对话中所有后续调用的 Token 成本,并可能将重要上下文挤出有效的注意力窗口。

分层防御架构

生产环境中的 Agent 系统需要在堆栈的每一层都建立防御,因为重试放大是跨层运作的。

第一层:工具级韧性

每个工具调用都需要自己的韧性封装,包含五个协同工作的组件:

  • 速率限制器 (Rate limiter):防止并发工具调用压垮下游 API。
  • 单次尝试超时 (Per-attempt timeout) (10 秒):单次重试有其自身的预算。
  • 总请求超时 (Total request timeout) (30 秒):整个重试序列有一个外部界限。
  • 带退避和抖动的重试 (Retry with backoff and jitter) (最多 3 次尝试):指数退避配合随机抖动可防止惊群效应。AWS 的研究表明,这可以将重试风暴减少 60-80%。
  • 熔断器 (Circuit breaker) (在 30 秒窗口内失败率达到 10% 后开启):防止在失效的服务上浪费 Token。

当工具失败时,返回结构化的错误响应——例如清晰的 JSON:{"error": "Activity data temporarily unavailable"}——而不是抛出异常。这可以防止 Agent 进入混乱的重试状态,同时避免将基础设施细节泄露到 LLM 上下文中。

第二层:对话级预算

对单个对话的消耗设置硬性限制:

  • 每轮最大工具调用数:每轮 Agent 对话限制在 5-10 次调用。正常的 Agent 行为使用 1-3 次工具调用。超过 15 次几乎可以肯定出现了重试螺旋或死循环。
  • 每会话 Token 预算:如果消耗的总 Token 超过阈值,则带有诚实的降级提示地优雅终止。
  • 工具调用去重:检测 Agent 是否正在使用相同的参数调用相同的工具,并熔断该循环。

第三层:编排级控制

Agent 编排层是最后一道防线:

  • 级联检测:监控在同一轮对话中多个工具失败的模式。如果连续 3 个以上的工具调用失败,停止重试并升级为优雅降级。
  • 截止时间传播 (Deadline propagation):在 Agent 链中传递剩余时间预算。子 Agent 从父级继承缩减的截止时间,防止任何单一委派耗尽整个超时预算。
  • 背压信号 (Backpressure signaling):当下游服务降级时,将该信号向上传播,以便父级 Agent 能够调整其策略,而不是盲目重试。

第四层:作为容错机制的缓存

缓存不仅仅是性能优化,它也是一种韧性机制。当 API 宕机但最近的数据已有缓存时,Agent 可以从缓存中获取答案,而不是进入重试循环。

根据数据新鲜度要求使用自适应 TTL:当前数据缓存 5 分钟(可能仍在变化),历史数据缓存 1 小时(稳定)。这能将级联故障转化为优雅降级。

区分可恢复故障与终端故障

重试设计中最难的问题是知道何时停止。并非所有故障都是瞬时的,重试永久性故障纯属浪费。

构建能实时分类故障的监测机制:

  • 瞬时故障 (重试):429 速率限制、503 服务不可用、网络超时、TLS 握手失败、无服务器冷启动。
  • 持续故障 (快速失败):401 身份验证错误、404 未找到、架构验证失败、权限被拒绝。
  • 降级故障 (优雅降级):部分响应、延迟升高(正常基准的 3 倍)、间歇性可用。

对于降级类别,实施自适应熔断:不仅跟踪失败率,还要跟踪延迟分位数。如果 P95 延迟超过正常基准的 3 倍,就在硬性故障开始前开始卸载负荷——这能在级联发生前捕捉到降级。

对领先指标而非滞后指标进行报警:

  • 5 分钟窗口内工具调用错误率超过 50%
  • 每轮对话包含 15 次以上工具调用的会话(正常为 1-3 次)
  • P95 工具延迟超过基准的 5 倍
  • 每会话 Token 消耗超过滚动平均值的 3 倍

诚实降级模式

当重试耗尽且断路器开启时,Agent 需要诚实地进行沟通,而不是产生幻觉捏造答案或无限期挂起。

这种模式非常直接:返回一条结构化消息,告诉用户发生了什么,Agent 还能做什么,以及什么时候可以重试。“我目前无法获取你的活动数据。我可以回答关于你账户设置的问题,这些问题不需要该服务。数据服务通常会在几分钟内恢复。”

这比任何其他替代方案都好。好过一直重试直到超时(浪费 Token,让用户沮丧)。好过幻觉出一个答案(摧毁信任)。好过一个通用的错误提示(没有提供解决路径)。

核心设计原则:一个 Agent 系统在尝试从故障中恢复时所消耗的资源,绝不应超过其在第一次尝试成功时所消耗的资源。如果你的重试逻辑可能消耗掉比成功请求多 200 倍的 Token,那么重试逻辑本身就是 Bug —— 而不是它所重试的那个不稳定的 API。

构建级联抵御能力

就模式的存在而言,重试风暴是一个已经解决的问题。指数退避、断路器、截止日期传播和结构化降级都是众所周知的技术。但就 Agent 框架很少实现它们而言,这又是一个尚未解决的问题,而且 Token 成本的放大使得其利害关系比传统的分布式系统要高得多。

根本性的转变在于将你的 Agent 视为一个分布式系统,而不是一个提示-响应循环。每一次工具调用都是一个网络边界。每一次重试都是一个成本倍增器。每一次授权都是一个可能会产生放大效应的信任边界。

从单一最高价值的干预措施开始:在你的工具调用上设置断路器,并配以会话级的 Token 预算。仅这两项控制措施就能防止最严重的重试风暴 —— 即单个不稳定的 API 演变成浪费 50 美元 Token 的事故。在此基础上,随着 Agent 系统复杂性的增加,再逐步加入截止日期传播、级联检测和自适应 TTL 缓存。

目标不是零故障 —— 而是比例失效成本。当某些环节出现故障时,系统在处理故障时所付出的努力应该比成功运行时更少。

References:Let's stay in touch and Follow me for more thoughts and updates