如何设计 AI 大模型 API 的超时、重试和降级策略?
调用大模型 API,和调用一个普通 HTTP 接口其实不是一回事。
普通接口大多是“请求进来—服务计算—结果返回”,耗时通常比较可预期。但大模型 API 就复杂得多:模型大小、上下文长度、GPU 排队、供应商限流、流式输出、工具调用链路,都会影响一次请求的耗时和成功率。也就是说,它的延迟不太稳定,失败方式也更多样。
所以,在设计大模型 API 的高可用方案时,不能简单写一句“超时后重试 3 次”就结束。更靠谱的做法是:先把错误类型分清楚,再分层设置超时;在时间和成本允许的范围内谨慎重试;如果仍然不稳定,就通过模型降级、能力降级、结果降级和交互降级,尽量把用户体验兜住。
下面就围绕“大模型 API、API 超时重试、接口降级策略”这几个问题,整理一套更适合落地的设计思路。
为什么大模型 API 比普通接口更容易超时?
大模型 API 的超时,通常不是某一个单点问题导致的,而是很多因素叠在一起造成的。
- 推理本身就慢:大模型需要一个 token 一个 token 地生成内容,回答越长,耗时自然越久。
- 上下文越长,首 token 越慢:prompt、历史对话、检索结果塞得越多,输入 token 就越多,模型开始输出第一个 token 的时间往往也会变长。
- GPU 资源可能要排队:高峰期请求不一定马上进入推理阶段,可能先在队列里等一会儿。
- 流式输出不代表请求已经完成:即使首 token 已经返回,后面的 token 仍然可能变慢、中断,甚至超时。
- 供应商限流很常见:429、并发限制、配额不足,这些都会直接影响接口稳定性。
- Agent 链路更长:用户一次提问,背后可能要经过检索、工具调用、函数执行、多轮推理,任何一步慢了都会拖累整体耗时。
- 客户端、网关、服务端的超时不一致:上游已经放弃了,下游还在继续生成,这不仅浪费成本,还可能产生脏状态。
因此,大模型 API 的超时、重试和降级策略,不能只盯着“请求能不能成功”。更重要的是在用户等待体验、系统稳定性和调用成本之间找到平衡。
先建立错误分类:哪些失败能重试,哪些不能?
重试不是万能药。对大模型 API 来说,盲目重试很容易带来三个问题:重复扣费、重复执行有副作用的操作,以及把供应商限流进一步打爆。
比较好的做法,是先按错误类型建立一张决策表。
| 错误类型 | 是否重试 | 处理建议 |
|---|---|---|
| 408 / 网络超时 | 可以重试 | 使用指数退避,并加上随机抖动 |
| 429 限流 | 谨慎重试 | 优先遵守 Retry-After,同时降低并发 |
| 500 / 502 / 503 / 504 | 可以重试 | 短暂退避,必要时切换备用模型或备用供应商 |
| 400 参数错误 | 不重试 | 检查请求参数、模型名、消息格式 |
| 401 / 403 鉴权错误 | 不重试 | 检查 API Key、权限和额度配置 |
| 413 请求过大 | 不重试原请求 | 压缩上下文、截断历史消息、减少检索片段 |
| 内容安全拦截 | 不重试原请求 | 改写提示词,或者返回合规说明 |
| 流式输出中断 | 通常不直接覆盖重试 | 保留部分结果,提示用户继续生成或重新生成 |
除了看错误码,还要看接口本身是否幂等。
- Embedding 一般可以重试。同样的输入重复请求,通常不会造成业务副作用。
- 普通问答可以重试,但结果可能不一样。大模型生成有随机性,两次回答不完全一致是正常的。
- Agent 工具调用要特别谨慎。如果涉及写数据库、发消息、下单、调用外部系统,就必须使用
request_id或idempotency_key。 - 已经开始流式返回后,不建议自动覆盖重试。用户已经看到一部分内容了,系统突然换一版新答案,体验会很割裂。
大模型 API 的超时应该分层设计
很多线上问题,最后追到根因,都是因为只设置了一个总 timeout。对普通接口来说,这可能勉强够用;但对大模型 API,特别是流式接口,就远远不够了。
更合理的超时设计,通常要拆成几层来看。
1. 连接超时
连接超时主要限制 TCP 连接、DNS 解析、TLS 握手这些阶段的等待时间。这个值一般不应该太长,否则网络一抖动,请求就会被卡住很久。
2. 请求发送超时
当 prompt 很长,或者上传了文件、图片、多模态内容时,请求本身发送到服务端也可能耗时。这个阶段也要单独限制,避免请求还没送到模型服务,用户耐心就已经被耗光了。
3. 首 token 超时
首 token 延迟,是大模型体验里非常关键的指标。
用户最敏感的,往往不是完整回答一共花了多少秒,而是“系统多久开始有反应”。在线聊天、智能客服、RAG 问答这类场景,都应该重点关注首 token 时间。
4. token 间隔超时
对于流式输出,只看总超时很容易误判。只要 token 持续往外吐,长回答可以适当允许更久;但如果很长时间没有新 token,就要认为流可能中断了,或者模型卡住了。
5. 总响应超时
总响应超时用来限制一次生成的最长时间,避免超长回答一直占着连接、线程和 token 成本不释放。
6. 业务 deadline
业务 deadline 应该从用户体验倒推,而不是从模型能力倒推。
比如用户最多愿意等 10 秒,那么检索、模型生成、后处理、网络传输、前端渲染加起来就不能超过 10 秒。不能给大模型 API 单独设置 30 秒,然后让用户在页面上干等。
7. 队列超时
如果请求进入了内部任务队列,也要设置排队超时。上游已经取消的请求,不应该继续排队等待昂贵的模型资源。
8. 链路级 deadline 传递
每个下游调用都应该知道自己还剩多少时间。
比如总预算是 12 秒,检索用了 2 秒,重排又用了 1 秒,那么 LLM 实际只剩下 9 秒。不能让下游在上游已经放弃后还继续生成,这样既浪费钱,也容易让系统状态变复杂。
不同接口可以参考下面这样的策略:
| 接口类型 | 建议关注指标 | 超时策略 |
|---|---|---|
| Chat / 对话 | 首 token、总生成时长 | 首 token 5–15s,总超时 30–120s,按模型和场景调整 |
| RAG 问答 | 检索耗时、首 token、总耗时 | 检索和 LLM 分别设置超时 |
| Embedding | 批量大小、并发量 | 使用较短超时,失败后一般可安全重试 |
| JSON 结构化生成 | 完整性、解析成功率 | 总超时可以稍长,失败后可降级为普通文本 |
| 图片/视频生成 | 排队时间、异步完成状态 | 不建议长连接同步等待,优先任务化 |
| Agent 工具调用 | 多步骤累计耗时 | 设置全局 deadline,每一步消耗预算 |
重试策略怎么设计:次数、间隔、抖动和预算
API 超时重试的重点,不是“多试几次”,而是“只在值得重试的时候,用可控成本再试一次”。
最大重试次数
在线请求一般建议最多重试 1–2 次,不建议默认重试 5 次甚至更多。
原因很简单:大模型 API 一次请求本身就可能比较慢,如果重试次数太多,延迟、成本和供应商压力都会被放大。用户也不一定愿意等这么久。
如果是后台任务、离线任务,可以适当增加重试次数。但这类任务最好放到任务队列里异步执行,而不是阻塞在线请求。
指数退避与随机抖动
比较常见的做法是:
- 第一次失败后,等待 300–800ms;
- 第二次失败后,等待 1–2s;
- 每次等待都加一点随机抖动;
- 总等待时间不能超过业务 deadline。
这里的随机抖动非常重要。没有 jitter 的话,大量客户端可能同一时间失败,又在同一时间重试,最后形成“重试风暴”。
429 要特殊处理
429 代表被限流了。这个时候最不应该做的,就是马上原地重试,更不能让所有请求一起重试。
更合理的处理方式是:
- 如果响应里有
Retry-After,优先遵守; - 降低这个模型或供应商的并发;
- 把低优先级任务排队,甚至临时暂停;
- 必要时切换到备用模型;
- 重试请求也要计入限流,不能绕过限流规则。
重试预算
重试一定要有预算,包括时间预算、次数预算和成本预算。
举个例子,一个在线聊天请求总预算是 15 秒,主模型第一次调用已经花了 10 秒。这时再完整重试一次,大概率只是让用户继续空等。更好的选择可能是切到轻量模型、缩短输出,或者直接返回“继续生成”的入口。
幂等 key 与重复扣费
只要调用链路里涉及副作用,就必须做幂等设计。
常见做法包括:
- 每次用户请求生成唯一的
request_id; - 工具调用、写库、发消息时使用
idempotency_key; - 重试前先检查任务状态;
- 避免出现“模型请求失败,但工具其实已经执行成功”的不一致状态。
接口降级策略:不要只返回错误,要保住用户体验
传统接口降级,经常是返回 null、mock 数据,或者一句“系统繁忙”。但在大模型应用里,这样往往不够。
更好的降级策略,应该围绕一个问题来设计:用户还能不能拿到一个可接受的结果?
1. 模型降级
| 原策略 | 降级策略 |
|---|---|
| 使用高阶模型 | 切换到轻量模型 |
| 使用长上下文模型 | 压缩上下文后改用短上下文模型 |
| 使用主供应商模型 | 切换备用供应商 |
| 实时生成 | 使用缓存答案、历史相似答案或模板回复 |
模型降级一定要提前验证,尤其是 JSON 输出、函数调用、多轮对话这些场景。不要等线上故障发生后,才发现备用模型的输出格式不兼容。
2. 能力降级
当系统压力变大,或者模型服务不太稳定时,可以先降低能力复杂度。
比如:
- 临时关闭联网搜索;
- 减少检索片段数量;
- 关闭复杂工具调用;
- 限制 Agent 的规划步数;
- 降低
max_tokens; - 关闭多候选答案生成;
- 从深度推理切换到快速回答;
- 多模态任务先降级为文本任务。
这类降级的好处是,用户仍然能得到答案,只是答案没那么“重”。
3. 结果降级
结果降级不等于失败,而是给用户一个更小但仍然可用的结果。
例如:
- 返回摘要,而不是生成一篇长文;
- 返回已经生成出的部分内容;
- 提示“已生成部分内容,可继续生成”;
- JSON 生成失败时,先降级为自然语言说明;
- 对低优先级请求排队处理;
- 给出清楚的失败原因,并提供重新生成按钮。
很多时候,一个“可继续”的半成品,比一个冷冰冰的错误提示要好得多。
4. 交互降级
图片生成、视频生成、长文生成、复杂 Agent 任务,不太适合一直同步等待。
更自然的交互方式是:
- 创建异步任务;
- 返回任务 ID;
- 前端通过轮询或 WebSocket 获取状态;
- 高峰期展示排队状态;
- 失败后允许用户一键重试。
这样用户不会被迫盯着一个一直转圈的页面,系统也能更从容地调度资源。
5. 业务分级
不同业务的价值不同,不应该使用同一套降级规则。
| 业务等级 | 示例 | 策略 |
|---|---|---|
| P0 核心链路 | 付费用户对话、客服回复 | 优先保障,必要时使用备用模型 |
| P1 重要功能 | 文档摘要、RAG 问答 | 缩短输出,但保留核心答案 |
| P2 辅助功能 | 标题润色、推荐语生成 | 可以排队,必要时暂时关闭 |
| P3 后台任务 | 离线批处理、低优先级生成 | 可以延迟、暂停或重跑 |
降级触发条件也要写清楚,不能只说“压力大时降级”。常见触发条件包括:错误率升高、P95/P99 延迟超过阈值、429 比例上升、tokens/s 明显下降、队列长度过高、单个供应商异常、单租户流量异常、成本预算接近上限等。
超时、重试、降级如何组合成一条决策链?
在真实工程里,超时、重试和降级最好不要散落在各个业务代码里,而是形成一条统一的调用链。
一个比较完整的流程可以是:
第一,请求进入后,先读取全局 deadline。
第二,判断当前用户、租户、接口和模型是否触发限流。
第三,根据业务等级选择主模型和调用参数。
第四,调用主模型,并记录连接耗时、首 token 时间和总耗时。
第五,如果出现 408、5xx 或网络异常,再判断这个错误是否值得重试。
第六,如果可以重试,就检查剩余时间和重试预算。
第七,在预算内使用指数退避和 jitter 进行重试。
第八,如果遇到 429,要尊重 Retry-After,并降低并发。
第九,如果重试仍然失败,再判断是否切换备用模型。
第十,如果模型不可用,就触发能力降级或结果降级。
第十一,如果降级后还是失败,再返回可解释的错误。
第十二,整个链路都要记录错误码、token 用量、成本、重试次数和降级原因。
这里的关键点是:不要等所有重试都失败了才想起降级。对在线请求来说,及时降级通常比长时间等待更符合用户体验。
推荐参数表:不同场景下怎么配置?
下面这些参数只能作为设计参考,真正上线时还要结合模型速度、供应商限制、上下文长度和业务 SLA 来调整。
| 场景 | 超时 | 重试 | 降级 |
|---|---|---|---|
| 在线聊天 | 首 token 设置较短,总超时中等 | 最多 1 次 | 切轻量模型、缩短输出 |
| RAG 问答 | 检索和生成分别设置超时 | 检索可重试,生成谨慎重试 | 无检索时基于已知信息回答 |
| Embedding | 使用短超时 | 可重试 1–2 次 | 拆小批次、降低并发 |
| JSON 结构化生成 | 总超时中等 | 可重试 1 次 | 降级为文本后再解析 |
| Agent 任务 | 使用全局 deadline | 单步骤谨慎重试 | 减少工具调用,或转为异步 |
| 图片/视频生成 | 不长时间同步等待 | 查询任务状态可重试 | 异步通知、排队、稍后查看 |
一个比较实用的原则是:在线链路优先控制等待时间,后台链路优先保证最终完成;高价值用户优先保障体验,低优先级任务优先让出资源。
代码示例:一个可落地的调用封装
下面是一段简化版 Python 伪代码,主要展示错误分类、重试预算、退避和模型降级的思路:
import random
import time
RETRYABLE_STATUS = {408, 500, 502, 503, 504}
def is_retryable(error):
if error.status_code in RETRYABLE_STATUS:
return True
if error.status_code == 429:
return True
return False
def backoff(attempt):
base = 0.5 * (2 ** attempt)
jitter = random.uniform(0, 0.3)
return min(base + jitter, 3)
def call_llm_with_policy(client, request, deadline_seconds=15):
start = time.time()
models = ["primary-large-model", "fallback-small-model"]
max_retries = 1
for model_index, model in enumerate(models):
attempt = 0
while attempt <= max_retries:
remaining = deadline_seconds - (time.time() - start)
if remaining <= 0:
raise TimeoutError("global deadline exceeded")
try:
return client.chat(
model=model,
messages=request["messages"],
timeout=remaining,
max_tokens=request.get("max_tokens", 800)
)
except Exception as error:
log_error(error, model=model, attempt=attempt)
if not is_retryable(error):
break
if attempt >= max_retries:
break
sleep_time = backoff(attempt)
if time.time() - start + sleep_time >= deadline_seconds:
break
time.sleep(sleep_time)
attempt += 1
# 主模型失败后,尝试切换到备用模型
if model_index < len(models) - 1:
request["max_tokens"] = min(request.get("max_tokens", 800), 400)
continue
return {
"type": "fallback",
"message": "当前模型服务繁忙,已返回简化结果或建议稍后重试。"
}
在真实生产环境里,还需要补上很多细节,比如:Retry-After 解析、流式首 token 超时、token 间隔超时、幂等 key、限流计数、熔断状态、指标上报和成本记录。
熔断、限流与隔离也要一起设计
虽然这里重点讨论的是 API 超时重试和接口降级策略,但熔断、限流、隔离其实是配套能力,不能分开看。
熔断主要是为了保护下游。当某个模型或供应商持续出现 5xx、超时,或者 429 明显飙升时,就应该短时间快速失败,或者直接切到备用模型,而不是让所有请求继续排队等超时。
限流最好分层做:
- 用户级限流;
- 租户级限流;
- 接口级限流;
- 模型级限流;
- 供应商级限流;
- 重试请求单独计数。
隔离则要按业务和模型拆开。不要让低优先级批处理任务占满在线问答资源,也不要让某一个租户的异常流量拖垮整个模型调用池。
上线后要监控哪些指标?
没有观测,就很难判断策略到底有没有效果。大模型 API 建议重点关注下面这些指标。
| 指标 | 作用 |
|---|---|
| 首 token 延迟 | 判断用户等待体验 |
| 总响应时长 | 判断超时配置是否合理 |
| tokens/s | 判断模型生成速度 |
| prompt tokens / completion tokens | 判断上下文和输出规模 |
| timeout rate | 判断超时是否频繁 |
| 429 rate | 判断是否触发供应商限流 |
| 5xx rate | 判断模型或供应商稳定性 |
| 重试次数分布 | 判断是否存在重试放大 |
| 重试成功率 | 判断重试是否值得 |
| 降级触发率 | 判断系统是否长期不健康 |
| fallback 成功率 | 判断降级方案是否有效 |
| 单请求成本 | 判断重试和长输出是否推高成本 |
| 用户取消率 | 判断等待时间是否超过用户耐心 |
尤其要关注“重试放大倍数”。比如原始请求只有 1000 次,但实际打到模型供应商的请求变成了 1800 次,这就说明重试已经明显放大了流量,需要重新评估策略。
常见错误做法
设计大模型 API 调用策略时,下面这些做法最好尽量避免:
- 所有错误都重试,包括 400、401、413。
- 超时时间设置得过长,让用户一直等。
- 流式接口只设置总超时,不监控首 token 和 token 间隔。
- 429 之后立刻重试,导致限流更严重。
- 降级时只返回“系统繁忙”,没有任何可用替代结果。
- Agent 工具调用没有幂等 key。
- 重试请求不计入限流。
- 多模型切换后,输出格式不一致。
- 不记录 token 用量和单次请求成本。
- 后台任务和在线请求共用同一个资源池。
总结:推荐的默认策略
设计大模型 API 的超时、重试和降级策略时,可以先遵循下面这些默认原则。
- 超时要分层设置:连接、首 token、token 间隔、总响应、业务 deadline 分开管理。
- 重试只针对临时错误:408、5xx、部分网络异常可以重试;400、401、413、内容安全失败不要重试原请求。
- 重试次数要克制:在线请求通常最多 1–2 次,并配合指数退避和随机抖动。
- 遇到 429 先降速:尊重
Retry-After,降低并发,而不是马上批量重试。 - 降级优先保体验:优先考虑模型降级、能力降级、输出降级和异步化,不要一上来就直接失败。
- 用熔断保护下游:供应商持续异常时,要快速切换或快速失败。
- 观测必须覆盖成本:延迟、错误、重试、降级、token 和单请求成本都要记录。
一句话来说,大模型 API 的高可用设计,不是简单把 timeout 调大、把 retry 次数加多,而是在有限时间、有限成本和不稳定外部依赖之间,尽量给用户一个稳定、可解释、能接受的结果。
更多推荐




所有评论(0)