← 全部文章

LLM API 生产加固清单:超时、重试、熔断器与优雅降级

一份可直接执行的生产加固清单,覆盖超时预算、带抖动的重试策略、熔断器模式、优雅降级链、速率限制吸收、流式可靠性和基于健康检查的路由。

· TheRouter

生产环境的 LLM API 加固需要五层防御机制配合运作。超时防止挂起连接阻塞应用;带抖动的重试在不引发重试风暴的前提下恢复瞬态错误;熔断器在级联故障拖垮整个系统之前切断流向故障 provider 的流量;优雅降级链在主模型不可用时路由到更便宜或缓存的替代方案;基于健康检查的路由把这一切从被动变为主动。这份清单汇总了我们在多 provider OpenAI 兼容路由运营中积累的经验。

OpenAI 兼容指供应商提供一个 chat-completions 接口,其请求与响应结构与 OpenAI API 契约足够接近——只需替换三个值(API key、base URL、模型名),原来的 OpenAI SDK 调用即可直接工作。最小实践面是POST /v1/chat/completions 带 messages、model, 并返回 OpenAI 形式的流式响应。

为什么需要统一的清单

单项可靠性话题已有充分的文档支持。OpenAI 发布了速率限制指南,Anthropic 记录了错误码和 SDK 重试行为,DeepSeek 列出了错误码及推荐操作。我们也分别讲过超时、重试与幂等性以及路由 fallback。

缺失的是一份把所有零件连成完整生产部署的可执行清单。团队做出来的系统往往重试和熔断器互相打架,超时设置使 fallback 逻辑失效,健康检查盯错了指标。本文把这些碎片拼到一起。

生产加固清单一览

先看全局,然后逐项展开。把这张表贴到部署 runbook 里,上线前逐条核对。

#类别检查项优先级
1超时连接超时已设置 (5-10s)关键
2超时读取超时按场景设置 (非流式 30-120s)关键
3超时总超时封顶端到端延迟关键
4超时流式逐 chunk 截止时间已配置高
5重试仅重试 429 和 5xx关键
6重试指数退避 + 完全抖动关键
7重试遵守 Retry-After 头关键
8重试上限 3-5 次,重试预算 <10% 流量高
9重试重试只发生在一个层高
10熔断器失败率阈值已配置 (60s 内 20-30%)高
11熔断器打开状态立即快速失败,不调用 provider高
12熔断器半开探针测试恢复中
13降级模型降级链已定义高
14降级全部宕机时有缓存/排队兜底中
15速率限制应用层同时追踪 RPM + TPM高
16速率限制请求前 token 预估中
17流式部分响应处理和重连中
18监控分 provider 追踪 p50/p95/p99 延迟高
19监控错误率与熔断器状态变更告警高
20监控每请求成本追踪和异常检测中

超时策略

超时是第一道防线。没有超时保护,一个响应缓慢或无响应的 provider 就会无限期地占住连接、线程和用户注意力。

LLM API 比典型 REST API 慢得多。一次生成请求通常需要 5-60 秒,取决于模型大小、输入长度和输出长度。标准 HTTP 客户端默认值(通常总共 30 秒)会对合法的长生成请求产生误超时。但完全不设超时意味着 provider 故障会悄悄阻塞你的应用。

连接超时保护 TCP/TLS 握手阶段,建议设为 5-10 秒。如果 provider 在这个窗口内无法接受连接,说明端点很可能已经宕机或不可达。再多等待也修复不了 DNS 解析失败或防火墙丢包。

读取超时(流式场景下即首字节超时)保护从发送请求到收到第一个响应字节的间隔。非流式请求 30-120 秒合理,具体取决于模型。流式请求的第一个 chunk 应该在 10-30 秒内到达,之后为每个 chunk 设置 15-30 秒的截止时间。

总超时封顶整个请求生命周期,用作 SLA 兜底。非流式请求通常 60-180 秒,如果请求在总预算内没有完成,无论部分进展如何都要终止它。

import httpx

# LLM API 调用的生产超时配置
client = httpx.Client(
    timeout=httpx.Timeout(
        connect=5.0,       # TCP/TLS 握手
        read=60.0,         # 等待首字节 / 下一个 chunk
        write=10.0,        # 发送请求体
        pool=10.0,         # 等待连接池分配连接
    )
)

分 provider 的推荐配置(基于各自文档行为)

Provider连接读取 (非流式)读取 (流式首 chunk)备注
OpenAI5-10s60-120s15-30s长上下文模型 (GPT-5.5) 可能需要更高的读取超时
Anthropic5-10s60-120s15-30sextended thinking 请求可能更久;服务端超时返回 504
DeepSeek5-10s60-90s10-20sV4 Flash 通常很快;V4 Pro 推理可能需要更长时间
DashScope5-10s60-90s10-20sOpenAI 兼容端点,超时逻辑一样

带指数退避和抖动的重试策略

重试用来恢复瞬态故障。天真的重试比原始故障造成更大的伤害。LLM API 的标准模式如下。

值得重试的状态码

  • 429(速率限制),遵守 Retry-After 头
  • 500(内部服务器错误),加退避
  • 503(服务过载),加退避
  • 连接重置、DNS 超时、TLS 握手失败

绝不重试的状态码

  • 400(请求格式错误)每次都会以相同方式失败
  • 401(认证错误)需要修复 API key
  • 402(余额/计费)需要充值
  • 403(权限错误)需要改配置
  • 413(请求过大)需要缩小请求

指数退避 + 完全抖动防止重试风暴。纯指数退避不加抖动会把所有客户端同步到同一时刻重试,每次尝试都再现惊群效应。

import random
import time

def retry_with_jitter(func, max_attempts=3, base_delay=1.0, max_delay=32.0):
    for attempt in range(max_attempts):
        try:
            return func()
        except RetryableError as e:
            if attempt == max_attempts - 1:
                raise

            # 如果 provider 返回了 Retry-After 头,遵守它
            retry_after = getattr(e, 'retry_after', None)
            if retry_after:
                delay = float(retry_after)
            else:
                # 完全抖动:在 0 到指数上限之间取随机值
                exp_delay = min(max_delay, base_delay * (2 ** attempt))
                delay = random.uniform(0, exp_delay)

            time.sleep(delay)

重试预算防止一个降级端点吞掉所有容量。设一个全局约束,任何时刻的总重试不超过总请求量的 10%。超出预算就直接 fail fast 并路由到 fallback,不再继续敲打降级的 provider。

单层重试防止乘法爆炸。如果你的应用调用一个服务、那个服务又调用另一个服务,每一跳的重试会相乘。五层服务链每层 3 次重试产生 3^5 = 243 次后端调用。选一个层做重试,通常是最外层的应用层或路由网关。

使用 TheRouter 这样的路由层时,在路由器层面配置重试,不要在应用代码中重复配置。路由器能看到所有 provider 端点,可以更智能地决定是重试还是 failover 到另一条路由。这样避免了应用和网关同时重试同一个失败请求的双重重试问题。

LLM provider 的熔断器模式

重试处理瞬态故障,熔断器处理系统性故障。两者的区别很重要。

熔断器在滚动窗口内监控失败率,有三个状态。

关闭(正常运行)。所有请求正常通过,熔断器在滚动窗口内统计成功/失败数。

打开(已触发)。失败率超过阈值,所有请求立即快速失败,不再触碰 provider。这给 provider 恢复时间,也防止应用在注定失败的请求上浪费资源。

半开(探测中)。冷却期过后,熔断器放行少量测试请求。成功则关闭熔断器、恢复正常流量,失败则重新打开并进入下一个冷却周期。

LLM API 的推荐阈值

参数推荐值理由
滚动窗口60 秒足以检测持续问题,又足够短以快速反应
失败率阈值请求的 20-30%比典型微服务高,因为 LLM API 有基线错误率
连续失败触发5-10 次低流量端点的替代触发器
冷却期30-60 秒给 provider 足够的恢复时间
半开探针数1-3 个请求用最少流量测试恢复
成功后重置连续 2-3 次半开成功确认恢复稳定,不是碰巧一次成功

标准 HTTP 错误率之外,LLM 场景下还应加入以下熔断触发条件

  • 延迟劣化。p95 延迟超过基线 3 倍时触发。一个返回 200 OK 但每次请求耗时 90 秒的 provider 实际上已经降级。
  • 成本异常。每请求成本超过预设阈值时触发,能捕获失控的 agent 循环和意外的计费飙升。
  • 流式中断率。超过 30% 的流式响应在最终 chunk 前断开时触发。

优雅降级链

熔断器触发后,应用需要有地方可去。优雅降级链定义了从主模型到越来越便宜或简单的替代方案的 fallback 路径,直到缓存响应或用户可见的降级模式。

一个编码助手的降级链实例

级别模型触发条件用户影响
L0(主)DeepSeek V4 Pro正常运行完整能力
L1(fallback)DeepSeek V4 FlashL0 熔断或延迟 >45s精度略低,速度快得多
L2(预算)Qwen3.8 Flash via DashScopeL0 + L1 均熔断不同模型,类似能力层
L3(缓存)常见查询的缓存响应所有在线 provider 降级可能过时但可用
L4(降级)带预计恢复时间的错误消息所有 fallback 耗尽诚实地告知不可用

几个关键设计决策

跨 provider 的 fallback 比同 provider fallback 更有韧性。OpenAI 宕机时,切换到 OpenAI 的另一个模型可能没用。切换到 Anthropic 或 DeepSeek,通过 TheRouter 的模型 fallback 路由绕过 provider 级别的故障。

模型降级通常比 provider 宕机更好。用户在 2 秒内从小模型拿到响应,比等 30 秒大模型超时的体验好。根据延迟而非仅错误来配置降级阈值。

缓存响应需要新鲜度策略。FAQ 类查询用 1 小时缓存通常可接受,实时数据问题不适合缓存。给查询标注可缓存性元数据。

诚实的降级建立信任。所有 fallback 失败时告诉用户发生了什么、预计何时恢复。一条清晰的“我们的 AI 服务暂时降级,预计 15 分钟内恢复”远好过一个永远转不停的加载动画。

速率限制处理与吸收

速率限制本质上属于流量控制机制。生产系统应该优雅地吸收速率限制,而非把它们当故障处理。

双轴追踪。LLM provider 同时在 RPM(每分钟请求数)和 TPM(每分钟 token 数)两个维度限速。长上下文请求尤其容易在 RPM 之内就突破 TPM。在应用层同时追踪两者,不要只依赖 provider 侧。

请求前的 token 预估防止 TPM 突然超限。用 tokenizer(OpenAI 兼容 API 用 tiktoken,其他 provider 用各自的库)在发送前估算 token 数。如果估算的 token 会超出剩余 TPM 预算,排队或丢弃请求,而非发出去再收 429。

始终设置 max_tokens 封顶输出长度。不设的话,模型生成一段异常长的回答可能在单个请求上悄悄耗尽你的 TPM 预算。

用队列吸收峰值。不要丢弃超出速率限制的请求,把它们放入有界等待时间的队列。一个基于 Redis 或内存的优先队列,配合 5-10 秒的最大等待时间,可以平滑峰值流量而不丢失请求。

流式可靠性

流式(SSE)响应带来了批量请求没有的可靠性挑战。

部分响应处理。流式响应可能中途断开。应用应追踪已接收多少输出、是否包含完成标记(OpenAI 兼容 API 中的 [DONE] 事件,或 Anthropic 格式中的 message_stop)。没有停止标记的部分响应需要标记为不完整。

重连策略。不要盲目重连并重发同一 prompt。provider 可能已经处理了部分请求并计费。把部分响应标记为不完整,然后带警告展示给用户,或者从头开始一个新请求并调整上下文。

逐 chunk 超时。为每个 SSE chunk 设截止时间,不只是为首字节。一个 2 秒发出第一个 chunk 但之后每个 chunk 间隔 60 秒的 provider 实际上已经降级。15-30 秒的逐 chunk 截止时间能捕获这种情况。

流中的错误事件。OpenAI 和 Anthropic 都可能在初始 200 响应之后返回错误事件。SSE 解析器必须处理流中到达的 error 事件类型,不能只处理 HTTP 级别的错误响应。

健康检查与主动路由

被动韧性(失败后重试、出错后熔断)是基线。主动韧性(在 provider 降级影响用户之前就路由走)是更高层次。

合成健康探针。按计划(每 30-60 秒)向每个 provider 发送轻量测试请求。用一个小的、快速的模型和短 prompt。追踪响应时间和成功率。如果某 provider 的探针延迟超过基线的 2 倍或探针开始失败,在用户流量受影响前降低它的路由权重。

Provider 状态页。程序化监控官方状态页(status.openai.com、status.claude.com、status.deepseek.com)。当 provider 报告降级或重大故障时,提前把流量路由走。

响应头监控。OpenAI 在每个响应中返回 x-ratelimit-remaining-requests 和 x-ratelimit-remaining-tokens 头。用这些来预测何时会命中限制,在收到 429 之前主动节流或重路由。

使用 TheRouter 作为路由层时,健康检查路由内建于模型 fallback 配置中。路由器跨所有经过它的流量监控 provider 健康状况,并绕过降级端点路由,无需应用层编写健康检查代码。

监控与 SLO 定义

你无法加固你不衡量的东西。为 LLM API 集成定义 SLO 并在违反时告警。

需要追踪的关键指标

指标度量方式SLO 示例
请求成功率1 - (error_count / total_count)5 分钟窗口 >99.5%
延迟 p50端到端请求时间中位数Flash 模型 <5s,旗舰模型 <15s
延迟 p95第 95 百分位请求时间Flash <15s,旗舰 <45s
延迟 p99第 99 百分位请求时间Flash <30s,旗舰 <90s
熔断器打开时间每小时熔断器打开的总秒数每小时 <300s
每请求成本总花费 / 总请求数低于预算阈值
重试率retry_count / total_count持续 <5%,峰值 <10%
流式完成率complete_streams / started_streams>99%

告警条件

  • 成功率连续 2 个窗口低于 SLO
  • 任何熔断器转为打开状态
  • 每请求成本超过 7 天滚动平均的 2 倍
  • 重试率超过 10% 持续超过 5 分钟

整体协作

防御层之间相互配合。以下是它们的组合方式。

  1. 请求到达应用
  2. 请求前检查。token 预估、速率限制预算检查。超出预算则排队或丢弃。
  3. 路由选择。基于健康检查的路由选出最佳可用 provider/模型
  4. 超时执行。连接 + 读取 + 总超时包裹 API 调用
  5. 故障处理。如果请求失败,分类错误
  6. 重试决策。可重试错误进入指数退避加抖动,在重试预算内进行
  7. 熔断器检查。如果 provider 的熔断器已打开,直接跳到 fallback
  8. Fallback 路由。如果重试耗尽或熔断器打开,路由到降级链的下一个模型
  9. 降级模式。如果所有 provider 宕机,提供缓存响应或返回诚实的错误
  10. 遥测。记录每个决策点,用于监控和 SLO 追踪

没必要从头造这一切。LLM 路由层在基础设施层面处理第 3、4、6、7、8 步,让应用代码专注于第 1、2、5、9、10 步。

FAQ

extended thinking / 推理模型应该设多大的超时?

DeepSeek R1 或 Claude extended thinking 这样的推理模型可能需要 60-180 秒才能产出结果。为这类模型设至少 120 秒的读取超时。用流式来获取进度可见性。总超时设为该模型和 prompt 长度下预期生成时间的 3 倍。

工具调用和函数调用应该重试吗?

只有当工具执行的目标是幂等的时候才行。如果工具调用发了邮件、创建了工单或写了数据库,重试可能会复制副作用。重试前先检查上一次调用是否已经执行。provider 支持幂等性 key 时务必使用。

如何处理多个 API key 的速率限制?

按 key 追踪 RPM 和 TPM,不要按应用追踪。如果你用多个 key 分配流量来获取更高的聚合吞吐,每个 key 有自己的限额。速率限制器需要独立追踪每个 key,并路由到还有剩余容量的 key。

应用层重试和网关层重试有什么区别?

应用层重试发生在你的代码中。网关层重试发生在路由层(比如 TheRouter 或反向代理)。选一个用,不要两个都用。两边都重试会造成乘法式的重试风暴。网关层重试通常更好,因为网关看到所有流量,可以对 provider 健康状况做出更明智的判断。

熔断器阈值多久调整一次?

每月检查一次。Provider 的可靠性特征会随时间变化。当 provider 99.5% 可用时合适的阈值,在 provider 提升到 99.9% 后可能过于敏感,在降级时可能过于宽松。用监控数据调优。熔断器每天误触发超过一次,就提高阈值;用户在熔断器触发前就反馈体验降级,就降低阈值。

什么时候该用路由层,什么时候自己写重试和 fallback 逻辑?

如果你只调一个 provider 的一个模型,应用层重试逻辑就够了。如果你调多个 provider、用多个模型、或需要熔断器和健康检查路由,专用路由层能从应用代码中移除大量复杂性,把韧性逻辑集中到可以独立监控和调优的地方。


本文引用的来源

帮助与联系