跳到主要内容

LLM Agent 的重试预算:为什么 20% 的单步失败率会让你的 Token 账单翻倍

· 阅读需 10 分钟
Tian Pan
Software Engineer

大多数团队只有在账单出现时才会发现重试问题。智能体(Agent)“运行正常”;延迟仪表盘保持绿色;错误率看起来也没问题。然后财务部门询问为什么本月的推理支出翻了一番,这时才有人终于去翻看日志。结果发现,一个 3 步操作的智能体中,20% 的工具调用在静默重试,每次重试都重放了完整的提示词(prompt)历史记录,而账单已经连续几周在攀升。

这背后的数学逻辑并不神秘,但极其反直觉。20% 的单步重试率听起来还可以接受 —— 大多数工程师看一眼就会忽略它。但一旦考虑到现代智能体框架的重试方式,实际的 Token 成本会更接近 2 倍而非 1.2 倍。而且,这种失败模式对于团队通常关注的每一项指标都是不可见的。

重试预算(Retry budgets)—— 这是一个源自 Google SRE 工作的旧概念 —— 是最简洁的解决方案。但该模式的 LLM 版本需要调整,因为 Token 的行为方式与 RPC 不同。

复利效应比看起来更糟

天真的模型:一个 3 步智能体,每步 20% 的失败率,失败后重试一次。每步的预期调用次数是 1/(1-0.2) = 1.25。三步意味着 3 × 1.25 = 3.75 次调用,而基准值为 3 次 —— 也就是 25% 的开销。这虽然令人烦恼,但并不算警报。

这种模型对几乎所有生产环境中的智能体来说都是错误的。原因如下。

大多数智能体框架 —— LangChain、LlamaIndex、OpenAI 和 Anthropic 的工具使用 SDK —— 都是通过重放对话历史来进行重试的,而不是孤立地重试单个失败的步骤。当第 3 步失败并重试时,你会重新发送系统提示词,加上第 1 步和第 2 步的输出,以及失败尝试的错误信息。该重试的 Token 成本不是单步的 Token 量;而是截止目前累计的上下文窗口(context window)。

在上下文重放的情况下重新计算。在一个重试率为 20% 的 3 步智能体上,预期 Token 消耗会攀升至约 基准值的 1.7–1.9 倍。在同样重试率的 5 步智能体上,Token 开销会攀升至 2.2–2.5 倍。这种复利效应在上下文大小上是平方级的(quadratic),而不是在步骤数上线性增长的,而这正是从业者所忽略的部分。

再加上多层重试 —— 工具内部的 SDK 重试、包装工具的中间件重试、包装整个循环的智能体级重试 —— 你就会遇到 Google SRE 手册在十年前就警告过的乘法链条。跨五个服务的每层三次重试是典型的“重试风暴(retry storm)”案例:在最坏的情况下,单个用户请求会产生 3^5 = 243 次后端调用。LLM 流水线也不能幸免;而且情况更糟,因为每次调用的成本都更高。

来自真实事件的真实账单

重试风暴并非理论。Anthropic 自家的 claude-code 仓库在 2025 年 7 月记录了一个问题,描述了单个用户会话在 5 小时内消耗了 16.7 亿个 Token,峰值达到每秒 224 次请求,估计账单在 16,000 美元到 50,000 美元之间。事后分析确定了四个重叠的根本原因,其中一个是明确的:253 个 "usage limit" 错误未能停止循环。在供应商提示停止后,重试逻辑仍在继续运行。

同一仓库中 2025 年 10 月的另一个问题记录了单日消耗 1.088 亿个 Token —— 按 Sonnet 价格计算约为 64 美元 —— 原因是自动压缩(autocompaction)漏洞导致同一个文件被循环重复读取。根本原因不同,但形式相同:没有预算限制的重试。

在个人开发者层面,一篇流传甚广的 Substack 帖子记录了 8 个月的 Claude Code 使用量累计达到 100 亿个 Token,按目前的 Sonnet 价格计算约为 15,000 美元,这主要源于用户从未直接观察到的链式调用模式。

更小、更隐蔽的失败更为常见。最近一项针对 ReAct 风格智能体的基准测试发现,在 200 个任务中,90.8% 的重试 —— 513 次尝试中的 466 次 —— 被浪费在了幻觉产生的或不存在的工具名称上。智能体虚构了一个不存在的工具,工具调用失败,智能体带着稍微不同的幻觉进行重试,以此类推。重试并不是解决方案;它们是披着“乐于助人”外皮的失败模式。

适配于 LLM 的 Google SRE 重试预算

SRE 手册针对重试风暴的解决方案优雅而简单。首先在单个请求级别限制重试(尝试次数绝不超过 N 次)。然后,在客户端级别,在一个滚动窗口内跟踪 重试次数 / 总请求数,一旦该比例超过 10%,就拒绝进一步重试。如果没有第二个限制,在部分停机期间,负载会增长到基准值的 3 倍。有了它,负载只会增长到 1.1 倍。该预算将重试视为一种有限资源,系统通过成功的基准请求来“赚取”重试机会。

将此方案转化为 LLM 智能体需要进行两项调整。

第一,将单位从“请求次数”改为“Token 数量”。 LLM 领域的重试成本不只是一个容量单位;其成本与提示词大小成正比。预算应以每个用户每个滚动窗口内的“重试 Token 数”为单位,而不是“重试次数”。一个在 2,000 Token 上下文中重试两次的会话,不等同于一个在 50,000 Token 上下文中重试两次的会话。

第二,在循环内部强制执行预算,而不是在外部。 大多数重试中间件位于 HTTP 层,远低于智能体逻辑所在的层级。智能体循环本身 —— 工具调用重试、JSON 解析重试、验证重试 —— 才是需要检查预算的地方,因为那是发生重放的地方。通用的 HTTP 重试策略看不到带有历史重放的 5 步重试所产生的累积上下文成本。

加载中…
References:Let's stay in touch and Follow me for more thoughts and updates