揭示生产环境中Agent失败的根本原因是容量瓶颈而非推理问题,分享了实测数据和容量工程最佳实践。
当我的 agent 开始在生产环境中失败时,我做了每个人最先做的事:我开始寻找幻觉。更好的 prompt、更严格的输出 schema、更多的护栏。这些都没有改变现状,因为我调试的是错误的层级。Agent 的推理没有问题。崩溃的是底层架构——而罪魁祸首是最无聊的东西:速率限制。
这不仅是我的问题。这是现在 LLM 应用最主要的生产失败模式,几乎没有人讨论它,因为它不会成为好的演示。
TL;DR —— 在生产环境中,导致 agent 失败的通常不是推理不当——而是容量。供应商的速率限制现在是真实跟踪中 LLM 调用错误的最大来源之一。演示一次只发出一个请求;生产中的 agent 会扩展成几十个链式、重试、并发调用,并碰到演示从未触及的限制。解决方案不是更聪明的模型,而是容量工程:预算、反压、带抖动的重试、降级模型,以及缓存。
这个数字改变了我对 agent 可靠性的思考方式。根据 Datadog 对真实 LLM 可观测性跟踪的分析,速率限制错误占了所有 LLM 调用失败的一个巨大比例——在 2026 年 3 月,大约三分之一的所有 LLM span 错误都是速率限制,数量级在百万个单独错误。他们的结论很直白:当你的 LLM 应用的主要失败模式是容量时,你需要加倍投入容量工程,而不是 prompt 工程。
想想这一点。失败模式不是模型很笨。而是模型供应商说"请求过多"——而你的 agent 对这个回答没有任何计划。
这几乎完美地映射到所有人都在写的"agent 在生产中失败"的故事上。演示会说谎不是出于恶意;而是结构性的。演示运行一个干净的请求、一个用户、一个最佳路径。生产是并发、重试、扇出、和负载——这些正是制造速率限制错误的确切条件。"在笔记本里有效"和"在凌晨 3 点负载下有效"之间的差距,比人们承认的要频繁得多,实际上是一个容量差距穿着可靠性的外衣。
普通的聊天机器人每个用户轮次发出一次 API 调用。Agent 是另一种野兽。单个"任务"会扩展为:
N 个工具选择调用,当它循环时。
每个工具结果一次调用来决定下一步。
在这些中任何一个出现问题时的重试。
通常还有一个或两个子 agent,每个都有自己的循环。
所以一个用户行动变成 10-40 个模型调用,频繁地并发,频繁地重试。这个倍数是 agent 的全部意义——也正好是让你走进速率限制的东西。更糟的是,天真的失败响应使其灾难性:一个调用收到 429,框架立即重试,那个重试也收到 429,现在你已经把一个速率限制错误变成了一个重试风暴,导致整个任务崩溃。
一旦写出来,算术是无情的。假设你的供应商给你 500 个请求/分钟。如果每个 agent 任务扇出到约 20 个模型调用,那么仅仅 25 个并发任务就会饱和你的整个配额——这还没考虑单个重试。在得到的 429s 上添加天真的立即重试,你不会优雅地降级,而是直接尖峰穿过天花板。我看过这个模式多次出现,每次房间里的第一个本能都是"模型坏了"——而模型从未运行过。
这也是无服务器特别不利的地方。在 Cloud Run 上,流量尖峰会高兴地旋转新实例——计算扩展得很好。但你的 LLM 供应商配额不会随着你的容器数量扩展。所以自动扩展做了最坏的可能的事情:它让更多的并发 agent 启动,每个都发出其调用扇出,所有都从同一个固定的供应商配额中提取,所有都同时碰到天花板。本应吸收负载的平台变成了将其放大成速率限制器的东西。这是一个真正违反直觉的失败:你的自动扩展在计算仪表板上看起来越健康,你就越狠劲地敲打一个无法随之扩展的配额。
没有任何的修复是奇特的。它们是分布式系统人员几十年来使用过的相同的模式——它们只是还没有迁移到大多数 agent 代码库中,因为该领域是在 prompt 工艺而不是运维上成长的。这是真正改变了我的可靠性数字的东西。
本能是更用力地重试。修复是发出更少。在所有出站模型调用之前放一个并发限制器(信号量 / token bucket),这样你的应用永远不会超过你已知的供应商配额。当预算已满时,排队——不要 fire-and-retry。这个单一的改变比任何重试调整做得都多,因为它防止了风暴而不是从中恢复。
import asyncio
# Cap concurrent in-flight calls below your provider's actual limit.
# Leave headroom — you are NOT the only caller against this quota.
sem = asyncio.Semaphore(8)
async def call_model(client, **kwargs):
async with sem:
return await client.messages.create(**kwargs)
当你重试时,永远不要立即重试,也不要同步重试。来自许多工作线程的同步重试创建一个雷鸣羊群,重新触发限制。带有随机抖动的指数退避会分散它们。
import asyncio, random
async def with_backoff(fn, max_retries=5, base=0.5):
for attempt in range(max_retries):
try:
return await fn()
except RateLimitError:
if attempt == max_retries - 1:
raise
# exponential + full jitter
delay = random.uniform(0, base * (2 ** attempt))
await asyncio.sleep(delay)
如果供应商发送 Retry-After 标头,请尊重它——它告诉你确切的等待时间,这比猜测要好。
将这个联系回蒸馏思维:你不需要对每个调用都用你的前沿模型。当主模型被限制速率时,路由到更便宜/次要的模型(不同的供应商,或者独立配额上的较小模型)。降级的答案比任务死亡要好,而且你已经把负载分散到两个配额池中,而不是一直敲打一个。这是与为简单的 90% 保留廉价学生模型并回退到昂贵教师模型相同的混合模式——只是应用于可用性而不是能力。
Agent 调用的一个令人惊讶的部分是近似重复:相同的工具描述、相同的系统上下文、跨运行的相同子查询。Prompt/response 缓存和重用供应商端 prompt 缓存减少了到达限制器的调用量。最便宜的速率限制错误是你从未发送的请求。
你无法工程化你看不到的东西。速率限制让团队措手不及的原因是它们显示为通用的"agent 失败"错误,而不是标记的容量问题。记录错误类别(429 vs 超时 vs 工具错误),追踪你的在途并发和你的 429 速率作为一等指标,并对它们设置警报。对我来说最重要的转变是简单地在遥测中分离"模型是错的"和"供应商说不"——在你做到之前,每个失败看起来都像是推理错误,你不断地修复错误的层级。
我会告诉我的过去的自己的东西:把你的 LLM 供应商配额视为一个共享的、有限的、不可扩展的资源——像数据库连接池一样,而不像 CPU。计算可以弹性扩展。你的每个 token 分钟和每个请求分钟配额不会。一旦你内化这一点,agent 可靠性就不再看起来像 AI 问题,而是看起来像一个经典的分布式系统容量问题——这是好消息,因为我们已经知道如何解决这些。
更聪明的模型在这里救不了你。一个推理完美的 GPT-6 仍然在你超过配额时返回 429。2026 年 agent 的可靠性边界不是智能——而是容量工程。
如果你在生产环境中运行 agent,我很想知道当你分离错误类别时你的实际主要失败模式是什么——推理、容量或工具集成?我越来越相信是容量。在评论中告诉我我错了。
Datadog,"AI 工程状况"(2026) —— 速率限制错误作为生产跟踪中 LLM 调用失败的主要部分。
"为什么 AI Agent 在生产中失败以及工程团队如何修复它",C# Corner (2026)。
"2026 年 AI Agent 可靠性差距",DEV Community。
"为什么 88% 的 AI Agent 永远无法进入生产",Digital Applied (2026)。