生产环境中的 LLM API 韧性:速率限制、故障转移以及简单重试逻辑的隐藏成本
2025 年中,一个构建多智能体(multi-agent)财务助手的团队发现其 API 开支从每周 127 美元飙升至 4.7 万美元。一个智能体循环——智能体 A 向智能体 B 寻求澄清,智能体 B 反过来询问智能体 A,以此类推——已经递归运行了 11 天。没有熔断机制(circuit breaker)拦截它,也没有及时触发预算报警。重试逻辑尽职地在每次超时后不断重试,使每一环节的失控成本不断叠加。
这不是一个关于模型质量的故事。这是一个关于分布式系统工程的故事——特别是关于大多数 LLM 应用开发者跳过的那部分,因为他们假设供应商会处理好这些。
事实上,他们并不会。
LLM API 供应商的正常运行时间(uptime)大约在 99.0% 到 99.5% 之间。这听起来不错,直到你进行换算:99% 的运行时间意味着每年有 3.5 天的停机时间。相比之下,三大云服务商的平均运行时间为 99.97%——每年约 2.5 小时。这意味着在停机风险敞口上有 6 到 14 倍的差距,而且这种情况短期内并不会改善。随着需求增长超过基础设施扩展的速度,2024 年第一季度到 2025 年第一季度之间,LLM 行业的 API 运行时间从 99.66% 下降到 99.46%——停机时间同比增加了 60%。
如果你正在从生产系统调用 LLM API,且你的弹性策略只是“出错即重试”,那么你已经做出了几个昂贵的假设,而这些假设最终都会被证明是错误的。
为什么重试逻辑是美好愿望破灭的地方
LLM 应用中最常见的弹性错误不是没有重试,而是没有抖动(jitter)、没有分层约束且没有预算的重试。
当发生速率限制或超时时,天真实现的重试循环会立即再次触发。这会轰炸同一个本已过载的端点,在几毫秒内耗尽重试预算,且不留任何恢复窗口。更糟糕的是,在具有多个服务层的系统中,这种放大效应会不断叠加。在一个包含 5 个服务的调用链中,如果每层重试 3 次,那么对于每个原始用户请求,后端将产生 3^5 = 243 次调用。这是典型的重试风暴(retry storm):最初的问题很小,但重试行为使其变得致命。分布式系统中约 40% 的级联故障(cascading failures)可以追溯到重试逻辑。
解决方案不是“删除重试”。重试是必不可少的。解决方法分为三个部分:
使用完全抖动(full jitter),而不是无抖动。 没有抖动的纯指数退避会导致所有客户端在同一时刻重试,在每次尝试时重现“惊群效应”(thundering herd)。完全抖动可以分散重试压力:sleep = random_between(0, min(cap, base * 2^attempt))。有效的起始值建议:第 1 次尝试等待最多 1 秒,第 2 次最多 2-3 秒,第 3 次最多 4-6 秒,封顶在 32-60 秒,最大尝 试次数为 3-5 次。
仅在一个层级进行重试。 如果你的应用调用一个服务,而该服务又调用另一个服务,那么每一跳的重试都会成倍增加。选定一个层级——通常是最外层的应用层——并使其成为唯一进行重试的地方。内部层级应清晰地向上传递失败信息。
实施重试预算。 设置一个全局约束:在任何给定时间内,总重试次数不应超过总请求数的 10%。如果你的重试率超过了预算,请快速失败(fail fast)。这可以防止一个性能下降的端点拖垮其他所有部分。
还有一点:永远不要盲目重试 4xx 错误。无论你重试多少次,400 或 403 每次都会失败。唯一值得重试的 4xx 错误是 429(速率限制),即便如此,在选择等待时长之前,也要先读取 retry-after 响应头。如果供应商告诉你了确切的重置时间,请直接使用它而不是靠猜测。
TPM vs. RPM:你可能只处理了其中之一
LLM 速率限制同时在两个独立的轴上运行,而大多数团队只考虑了其中之一。
RPM(每分钟请求数) 限制 API 调用次数。它保护基础设施免受请求洪峰的影响。TPM(每分钟 Token 数) 限制计算消耗。它保护 GPU 容量免受带有长 Prompt 或复杂智能体链的工作负载影响。你可能会在保持 RPM 达标的同时突破 TPM,反之亦然。两者都会产生 429 错误,但其根本原因和正确处理方式各不相同。
对于智能体和 RAG 管道,TPM 几乎总是瓶颈所在。一个检索 20 个文档并将其填入 15,000 个 Token 的 Prompt 的管道,其 TPM 消耗量大约是简短查询的 15 倍,即使请求数量相同。
生产级的 Token 管理需要:
- 请求前预估:使用分词器(OpenAI 使用
tiktoken,其他供应商使用对应的等效工具)在请求超出预算之前将其拦截或排队。 - 始终设置
max_tokens:为输出设定上限。如果没有这个设置,一个决定写出超长回复的模型可能会在单次请求中默默耗尽你的 TPM 预算。 - 在应用层实施双重速率限制:不仅仅是在供应商边缘。在自己的代码中强制执行 RPM 和 TPM 限制,并使用 Redis 或 Kafka 队列来平滑突发流量,而不是直接丢弃请求。
Azure 部署还增加了另一个维度:单实例限制和共享区域上限是独立的。一个配置了 5 个 Azure 实例(每个实例在 GPT-4o 上配置了 450K TPM)的部署,仍可能触及区域范围的限制,导致所有实例总计被限制在 300K TPM。这在文档中并不显眼,通常是在高负载下才被发现。
熔断器:区分优雅降级与自我崩溃的机制
熔断器位于你的应用程序和 LLM 供应商之间。在正常运行(关闭状态)下,所有请求都会通过。当在一个滑动窗口内的失败率超过阈值时——例如,在过去 60 秒内超过 20% 的请求失败——电路就会跳闸开启。在开启状态下,请求会立即失败而不触及供应商,从而给供应商恢复的时间。经过 一段冷却期后,电路进入半开状态,允许一小部分测试流量通过,以探测是否已经恢复。
具体的生产环境影响非常显著。对于一个在 5 分钟故障期间每分钟发出 100 个请求的应用程序:
- 没有熔断器:500–1,000 个请求每个都会悬挂 30 秒以等待超时。用户在整个故障期间都会经历响应迟缓。
- 有熔断器:在大约 10–15 个失败请求达到阈值后,剩余的约 485 个请求会在 10 ms 内快速失败。回退逻辑立即介入。用户看到的是来自备用供应商的 200 ms 响应,而不是 30 秒的超时。
通过熔断器遥测,平均检测时间从 30 分钟下降到 2 分钟,因为电路状态是一个明确的信号,表明出了问题。
对于 LLM 应用,标准的 HTTP 熔断触发器——错误率、连续失败、P95 延迟——是必要的,但还不够。需要增加:
- 单次请求成本超过阈值:开头提到的每周 4.7 万美元的失控 Agent,如果配置了当单次对话成本超过 X 美元时开启的熔断器,本可以被截获。
- 对话轮数:在 Agent 式对话中,当轮数达到 20+ 时断开电路。合法的推理链很少需要更多轮次;失控的循环几乎总是需要更多。
- 输出质量评分低于阈值:这需要在输出到达用户之前,运行一个轻量级的 LLM-as-judge。
多供应商故障转移已不再是可选项
到 2025 年中期,40% 的生产级 LLM 团队已经部署了多供应商路由,而仅仅 10 个月前这一比例仅为 23%。主要推动因素是一系列知名的供应商故障——包括两大主流基础模型供应商发生的持续数小时的事故——这导致单供应商应用彻底瘫痪,而多供应商应用则在几秒钟内完成了故障转移。
故障模式是可预测的:某个供应商的频率限制风暴、另一个供应商长达 10 小时的推理基础设施故障、以及 HTTP 成功率无法检测到的静默质量降级。这些故障不会同时对所有供应商产生统一影响。跨供应商路由将单供应商故障转化为短暂的波动。
有两种值得了解的故障转移架构:
顺序故障转移:主供应商 → 次要供应商 → 第三供应商。实现简单。成本是每跳约 1–3 秒的额外延迟,这对于非交互式工作负载通常是可以接受的。
并行对冲:同时向主供应商和次要供应商发起请求;使用最先响应的一个;取消另一个。消除了顺序故障转移的延迟损失,但 Token 成本大约翻倍。这适用于首个 Token 延迟是主要 SLO 的交互式用例。
多供应商路由的工程挑战往往被低估:
- 每个供应商都有不同的错误格式、频率限制标头和响应架构。像 LiteLLM 这样的库可以实现归一化,但在约 2,000 RPS 时,LiteLLM 的内存占用会攀升超过 8 GB。更高吞吐量的环境需要专门构建的网关(如 Portkey、Go 语言编写的 Bifrost 或 AWS 原生技术栈的 Bedrock)。
- 回退模型可能会产生结构不同的输出。如果模型格式化响应的方式不同,故障期间从一个模型切换到另一个模型可能会破坏下游的 JSON 解析器。
- 故障转移期间成本可能会大幅飙升。如果你首选的供应商是最便宜的选择,而故障转移路由到了更昂贵的供应商,那么高峰时段长达 10 小时的故障可能会 产生巨大的意外成本。
静默降级:你未在监控的失效模式
2025 年 8 月,一家 LLM 供应商发布了一份复盘报告,记录了三个同时存在的 Bug,这些 Bug 已经导致响应质量下降了数周。它们都不是硬性错误,HTTP 成功率全程看起来都很正常。这些失效包括:
- 负载均衡的更改导致请求被路由到为不同上下文窗口大小配置的服务器。在高峰期,某个模型的 16% 请求受到了影响。
- TPU 配置错误导致高概率权重被分配给稀有 Token,导致断断续续地产生错误语言的响应。
- 编译器算术不匹配导致最高概率的 Token “有时会完全从考虑中消失”——产生技术上合理但事实错误的输出。
标准的运行时间监控没有捕捉到任何这些问题。它们是通过用户投诉和手动调查才被发现的。
这是最难防范且对输出正确性至关重要的应用影响最大的失效类别。监控需求与大多数团队现有的方案不同:
- 输出 Schema 验证:如果你的应用期望结构化的 JSON,请验证每一个响应的 Schema。Schema 失败是模型退化的领先指标。
- 对样本进行 LLM-as-judge:对一小部分响应进行轻量级质量评估。质量评分的下降早于 HTTP 成功率的下降,是一个宝贵的早期预警信号。
- 输出的 Embedding 偏移:跟踪响应随时间变化的语义分布。输出 Embedding 的突发偏移——即使输出在语法上是有效的——也表明上游发生了变化。
这些实现的成本都不高。如果实施了这些措施,原本可以比等待用户投诉更快地发现 2025 年 8 月的那些 Bug。
总结:最小可行韧性栈
全景图如下:生产系统中的一次 LLM 调用应当首先通过一个强制执行 TPM/RPM 双重限制的请求队列,然后通过一个带有错误率和成本阈值触发器的熔断器,接着到达一个处理带有全抖动(full jitter)的指数退避(exponential backoff)的网关。该网关可以在遇到 429、5xx 错误或延迟突破阈值时路由到备用供应商。在返回给应用程序之前,输出应经过 Schema 验证,并将一定比例的样本发送至质量监控器。
这并非什么奇特的架构。它就是让 HTTP 微服务变得可靠的同一种分布式系统工程——只是被应用到了一类全新的外部依赖上。与工程师以往处理过的大多数服务相比,这种依赖往往更慢、单次调用更昂贵,且更容易发生静默降级。
建立了这套体系的团队,其应用程序在 2025 年和 2026 年的每一次重大供应商故障期间都能持续为用户服务。而没有建立这套体系的团队,则在撰写事故复盘,解释为什么当供应商宕机 10 小时时,他们的应用也跟着宕机了 10 小时。
供应商故障是不可避免的,而熔断器是可选的。
- https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
- https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/
- https://portkey.ai/blog/failover-routing-strategies-for-llms-in-production/
- https://portkey.ai/blog/rate-limiting-for-llm-applications/
- https://portkey.ai/blog/retries-fallbacks-and-circuit-breakers-in-llm-apps/
- https://www.sitepoint.com/claude-api-circuit-breaker-pattern/
- https://www.zenml.io/blog/what-1200-production-deployments-reveal-about-llmops-in-2025
- https://www.anthropic.com/engineering/a-postmortem-of-three-recent-issues
- https://medium.com/google-cloud/building-bulletproof-llm-applications-a-guide-to-applying-sre-best-practices-1564b72fd22e
- https://learn.microsoft.com/en-us/azure/architecture/antipatterns/retry-storm/
- https://cookbook.openai.com/examples/how_to_handle_rate_limits
- https://www.runtime.news/as-ai-adoption-surges-ai-uptime-remains-a-big-problem/
