大模型 API 故障切换:模型出错时怎样保持应用可用
大模型 API 故障切换方案决定了模型调用失败时应用怎么做:等一下再试,把同一个请求发到别处,还是换一款模型来回答。没有方案,模型服务出问题的那一个小时,也就成了应用的故障时间。方案考虑不周,重试循环可能让账单翻倍,或者让用户收到两段不完整的回答。这篇讲实际会出现的失败、对应的处理模式、一段简短的 Python 实现,以及多线路 API 已经替你承担了哪些部分。
生产环境里实际会出什么错
大模型调用的失败有几种明显不同的类型,每种要不同的处理。
- 限速(429)。 某个账户、Key 或模型每分钟的请求数或 token 数超了。通常是暂时的,响应里往往会说要等多久。
- 服务端错误(500、502、503)。 模型服务过载或出了故障。通常是暂时的,有时会持续几个小时。
- 超时(504 或客户端超时)。 在允许的时间内没有回答。超时不一定是故障:2026 年 10 月 16 日,我们把一个较长的编程提示词以非流式方式发给两款推理模型,两款都没能在非流式请求 180 秒的上限内完成,都返回了
provider_timeout。 - 流中断。 回答传到一半,连接断了。
- 请求错误(400)。 请求本身有问题:模型不存在、模型不接受某个参数、输入超过了上下文窗口。原样重试只会得到同样的错误。
- 账户错误(401、402、403)。 Key 不对、余额不足、Key 被停用。不先处理账户问题,重试没有用。
重试、故障转移和换模型回退是三件事
这三个词常被混着用,分开来想,设计会清楚很多。
- 重试:隔一会儿,把同一个请求再发到同一个地方。用来应对短暂的过载和限速。
- 故障转移:把同一个模型的同一个请求,发到另一个提供这款模型的地方,比如另一个区域、另一套部署或另一家服务商。模型相同,回答应该是等价的。
- 换模型回退:把请求交给另一款模型。某款模型在哪里都用不了时,应用还能继续回答,代价是回答会不一样:擅长的方面不同,格式不同,有时连调用工具的习惯也不同。
重试和故障转移可以放心自动化。换到另一款模型是产品决策,因为用户可能会察觉到变化。
大模型 API 故障切换的常用模式
1. 先给错误分类,再决定怎么做。 在代码里放一张小表,能避开两个常见错误:对永远不会成功的请求反复重试,以及在几秒钟就会恢复的错误上直接放弃。
| 错误 | 重试 | 故障转移 | 换另一款模型 |
|---|---|---|---|
| 429 限速 | 要,等到响应要求的时间之后 | 要 | 持续出现时 |
| 500、502、503 | 要,按退避间隔 | 要 | 持续出现时 |
| 504 或客户端超时 | 改用流式,或放宽时限 | 要 | 持续出现时 |
| 400 请求错误 | 不要,先改请求 | 不要 | 只在另一款模型接受这个请求时 |
| 401、402、403 | 不要,先处理账户 | 不要 | 不要 |
2. 指数退避,加随机抖动,并遵守 retry-after。 每次重试的等待时间翻倍,再加一点随机偏移,可以避免大量客户端在同一时刻一起重试。响应里带了 retry-after 时,至少等这么久。
3. 长文本生成用流式,超时分层设置。 连接超时用来发现网络不通,首个 token 超时用来发现卡住的请求,总超时用来截住失控的回答。如果提示词会让模型思考好几分钟,流式能保持连接、让客户端看到进度,而非流式请求会先撞上时间上限。
4. 回退链要短,而且要测过。 事先选好两三款模型,每一款都用真实提示词测过。没人用你的提示词试过的模型链,只是猜测,算不上方案。
5. 对一直失败的目标暂停发送。 连续失败几次后,短时间内不再向那款模型或那个接口发请求,而不是让每个请求都去重新撞一次同一个故障。这就是熔断器模式,在故障期间既省等待时间也省钱。
6. 用户已经在看的流,不要悄悄重试。 如果文字已经显示给用户而流断了,重新发请求得到的是另一段回答,不是接着往下写。把收到的部分显示出来,标明不完整,再提供重试按钮。
7. 记下请求 ID。 每次失败的调用都记录模型、错误码和请求 ID。这样「今天上午 AI 用不了」就变成了客服能追查的具体记录。
一段简短的 Python 实现
OpenAI 的 Python SDK 本身就会对连接错误、408、409、429 和 5xx 响应按指数退避重试,默认两次,它的 README 里有说明。下面的代码补上它不做的部分:某款模型持续不可用时,换到一条短链里的下一款模型;请求错误和账户错误则直接抛出。
import openai
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.boostrail.com/v1",
max_retries=2,
timeout=120,
)
FALLBACK_CHAIN = ["claude-sonnet-5", "kimi-k3", "deepseek-v4.1-flash"]
RETRYABLE = (
openai.RateLimitError,
openai.APITimeoutError,
openai.APIConnectionError,
openai.InternalServerError,
)
def complete(messages):
last_error = None
for model in FALLBACK_CHAIN:
try:
return client.chat.completions.create(model=model, messages=messages)
except RETRYABLE as error:
last_error = error
raise last_error
reply = complete([{"role": "user", "content": "Summarise this incident report in three bullet points."}])
print(reply.model, reply.choices[0].message.content)
所有模型都在同一个接口、同一个 Key 后面,所以回退链就是一组字符串。reply.model 告诉你实际是哪款模型回答的,值得记进日志,有些产品里也值得展示给用户。
多线路 API 替你处理了什么
在 BoostRail,同一款模型的故障转移在响应到达你之前就完成了。规则公开在错误与故障切换页面:
- 承接请求的线路失败时,BoostRail 会在向你发送任何内容之前,把同一款模型换到另一条线路上再试。它不会换成另一款模型。
- 服务端错误、限速,以及线路拒绝 BoostRail 自身的访问,会换线路重试。请求错误和非流式超时不会,因为换一条线路也会以同样的方式失败。
- 流式请求如果 45 秒内没有收到第一个事件,会换到下一条线路。
- 一旦已经向你发送了数据,请求就不再换线路。
- 所有线路都失败时,你收到的是最后一次尝试的错误,这个请求不收费。
这改变了你自己的代码要做的事。同一款模型的故障转移已经由 BoostRail 处理,你的重试主要处理剩下的情况:带 retry-after 的 429,以及 BoostRail 已经试过各条线路之后的 502 或 504。换成另一款模型仍由你决定,BoostRail 不会替你换。无论哪条线路承接请求,价格都是模型页上这款模型公布的价格。
围绕故障切换的其他集成问题,比如数据结构差异、Key 太多难管,见多接口 AI 集成的隐性复杂度。
测一测回退路径
从没跑过的回退路径,在真正需要的时候往往会出问题。三种低成本的演练方法:
- 制造超时。 给链上排在首位的模型设一个远短于正常回答时间的客户端超时,确认第二款模型接上了,日志也记下了原因。
- 断开网络。 把客户端指向一个会拒绝连接的地址,确认这个错误被归为可重试,而不是请求错误。
- 检查账户错误。 在测试环境里用一个已停用的 Key,确认应用会报告问题,而不是在链上一款款地试下去。
每次改动回退链都跑一遍,并留意演练的费用:每一次成功的尝试都会计费。
常见问题
应用应该自动换到另一款模型吗?
只换到用真实提示词测过的模型,并且只在用户能接受回答风格变化时这样做。内部工具和批处理任务通常可以自动回退。面向客户的功能,要记录或展示是哪款模型回答的。
大模型 API 的超时应该设多长?
够最慢的正常回答用就行,不要更长。凡是可能推理很久或写很长内容的请求,都用流式:在 BoostRail,非流式请求的上限是 180 秒,而流式请求只要求每条线路在 45 秒内收到第一个事件。
失败的尝试收费吗?
在 BoostRail,所有线路都失败的请求不收费。你的代码发出的每一次重试或回退都是新请求,成功了就正常计费。
流传到一半断了会怎样?
数据一旦开始发送,BoostRail 就不再换线路,因为重新尝试会得到另一段回答。客户端应保留已收到的内容,标明不完整,并提供重试。中断的流式请求按价格页的规则计费。
重试 400 错误有用吗?
只有在改了请求之后才有用:把输入裁到上下文窗口以内,去掉模型不接受的参数,或者换一款接受这个请求的模型。原样再发一次,只会得到同样的错误。