给 OpenRouter 模型配回退链:主模型挂了自动换下一个
本文所有事实均来自 OpenRouter 官方文档
openrouter.ai/docs/guides/routing/model-fallbacks于 2026-08-18 的公开内容。该平台迭代频繁,字段与限制以官方文档最新内容为准。
一、这个东西是给谁准备的
你把一个模型 ID 写死在服务里,跑了几周都好好的,某天凌晨接口开始批量 500,日志里全是同一个模型返回的错误。这时候能做的事非常有限:要么手动改配置换模型重新发布,要么在自己的代码里写一层 try/catch 再发一次请求——后者听起来简单,真写起来要处理重试计数、要区分哪些错误值得重试、要保证消息体不被改坏,还得考虑第二次请求算谁的账。
OpenRouter 官方文档的《Model Fallbacks》页给的是把这层挪到平台侧的做法:请求体里除了主模型,再带一个候选列表,主模型出错时由 OpenRouter 顺着列表往下试。文档对这个能力的一句话描述是:models 参数让你在主模型的 provider 宕机、被限流,或因内容审核拒绝回复时,自动尝试其它模型。
注意这句话的措辞——它说的是「主模型的 provider」出问题。同一个模型在 OpenRouter 上可能由多个 provider 提供,provider 之间怎么挑是另一页文档(openrouter.ai/docs/guides/routing/provider-selection)的事,本文只讲模型之间的回退,两者不是一回事,别混着调。
二、前置条件
这个字段不是所有调用姿势下都叫同一个名字,动手前先确认你走的是哪条路。文档里给出的组合是这几种:
- OpenRouter 官方 TypeScript SDK(
@openrouter/sdk):在chat.send的参数里直接写models数组。 - 直接发 HTTP 请求:
POST https://openrouter.ai/api/v1/chat/completions,请求体 JSON 里放models。文档同时给了 TypeScript 的fetch版本和 Python 的requests版本。 - 用 OpenAI 官方 SDK 指到 OpenRouter:
base_url设成https://openrouter.ai/api/v1,models要放进extra_body,因为它不是 OpenAI SDK 自己认识的字段。 - 用 Anthropic 官方 SDK 指到 OpenRouter:这条路走的是
/api/v1/messages端点,参数名不叫models,叫fallbacks,形状也不一样。文档的示例调用的是anthropic.beta.messages.create——这是 SDK 上的beta命名空间,照实标出来,不要当成稳定接口对待。
鉴权方面,文档示例里是 Authorization: Bearer 加密钥。密钥本文一律写成 <OPENROUTER_API_KEY>,你自己的代码里也别硬编码进源文件——这是通用工程做法,不是官方文档里的要求。在 Windows 上,PowerShell 里临时设环境变量用 $env:OPENROUTER_API_KEY="<YOUR_API_KEY>",cmd 里用 set OPENROUTER_API_KEY=<YOUR_API_KEY>;Linux/macOS 的 shell 里用 export OPENROUTER_API_KEY=<YOUR_API_KEY>。这一段属于各平台通用做法,官方文档没有讲环境变量怎么设。
三、字段怎么写,每一步在改什么
models:一个按优先级排的数组
文档对工作方式的描述是:按优先级顺序提供一个模型 ID 数组,如果第一个模型返回错误,OpenRouter 会自动尝试列表里的下一个。
直接发 HTTP 请求时的写法(原样抄自官方文档的 Python 示例):
import requests
import json
response = requests.post(
url="https://openrouter.ai/api/v1/chat/completions",
headers={
"Authorization": "Bearer <OPENROUTER_API_KEY>",
"Content-Type": "application/json",
},
data=json.dumps({
"models": ["~anthropic/claude-sonnet-latest", "gryphe/mythomax-l2-13b"],
"messages": [
{
"role": "user",
"content": "What is the meaning of life?"
}
]
})
)
data = response.json()
print(data['choices'][0]['message']['content'])
代码里那两个模型 ID 是官方文档当时写的示例值,平台上有哪些模型、哪些 ID 还有效随时在变,别把它当成可用模型清单抄进生产配置,用之前去官方的模型页确认当前的 ID。
model 和 models 同时出现时谁先跑
用 OpenAI SDK 的那段示例里两个字段都在:model 写的是 ~openai/gpt-latest,extra_body 里的 models 是另外两个。文档对这个组合的说明很明确:~openai/gpt-latest 会被先尝试,然后 models 数组按顺序作为 fallback 依次尝试。
from openai import OpenAI
openai_client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=<OPENROUTER_API_KEY>,
)
completion = openai_client.chat.completions.create(
model="~openai/gpt-latest",
extra_body={
"models": ["~anthropic/claude-sonnet-latest", "gryphe/mythomax-l2-13b"],
},
messages=[
{
"role": "user",
"content": "What is the meaning of life?"
}
]
)
print(completion.choices[0].message.content)
TypeScript 那版官方示例里,models 是和 model 平级直接写在参数对象里的,上面还挂了一行 // @ts-expect-error——因为 OpenAI 的类型定义里没有这个字段,编译期会报错。这行注释是文档原文里就有的,不是我们加的。
fallbacks:Anthropic Messages 端点上的另一套形状
走 /api/v1/messages 时,参数叫 fallbacks,是一个对象数组,每个对象点名一个要按顺序尝试的 fallback 模型:
import Anthropic from '@anthropic-ai/sdk';
const anthropic = new Anthropic({
baseURL: 'https://openrouter.ai/api',
apiKey: '<OPENROUTER_API_KEY>',
});
const message = await anthropic.beta.messages.create({
model: 'anthropic/claude-sonnet-4.5',
max_tokens: 1024,
fallbacks: [{ model: 'anthropic/claude-opus-4.1' }],
messages: [{ role: 'user', content: 'What is the meaning of life?' }],
});
这段里出现的两个模型 ID 同样是官方文档当时写的示例值,不是推荐搭配,也不是可用清单——平台上有哪些模型、ID 怎么写随时在变,照抄前去官方的模型页对一遍。baseURL 写到 https://openrouter.ai/api 而不是带 /v1,这也是文档示例里的原样写法,别自己给它补一截。
这里有个容易误会的点,文档专门用一个 Note 说清楚了:这套 fallback 路由是 OpenRouter 自己处理的,fallbacks 参数不走 Anthropic 的服务端 fallback 功能。文档同时写明,这个列表映射到的就是 OpenRouter 的 models 路由,所以触发条件和下面那份错误清单一致——不是只在模型拒答时才触发。这一条值得记:参数名和 Anthropic SDK 的形状对齐了,但语义归 OpenRouter 管。
四、边界:什么会触发、什么不支持
默认触发条件是「任何错误」
文档的原话是:默认情况下,任何错误都可以触发使用 fallback 模型,并列了四类(这四类是我们照着文档那份列表数的):
| 文档列出的触发场景 | 说明 |
|---|---|
| Context length validation errors | 上下文长度校验失败 |
| Moderation flags for filtered models | 被过滤模型的内容审核标记 |
| Rate-limiting | 被限流 |
| Downtime | 宕机 |
文档用的是「默认情况下」这个措辞,措辞本身留了余地,但这一页文档没有说明这个默认能不能改、怎么改,也没有列出可配置的触发条件白名单。想只在限流时回退、不在审核拒答时回退,本文给不出依据,也不替文档推断有没有这样的开关。
回退只兜一层
文档对失败链路的描述是:如果你选的模型返回错误,OpenRouter 会尝试使用 fallback 模型;如果 fallback 模型也宕机或返回错误,OpenRouter 就会把那个错误返回给你。
按字面读,这句话说的是「回退之后再失败就返回错误」。数组里排到第三个、第四个的模型会不会继续接力,这一页没有展开说,我们不替它推断。你的调用方还是得有兜底路径,不能假设配了数组就一定拿得到回复。
fallbacks 的三条硬限制
文档的 Limitations 小节列了三条,每条踩中都是 400:
- 每个
fallbacks条目只接受model一个字段。按次覆盖的参数——文档点名的有max_tokens、thinking、speed、output_config——会被拒绝并返回 400 错误。也就是说你不能给回退模型单独设一套参数。 fallbacks不能和models参数一起用,两个都发会返回 400 错误。fallbacks的条目数有上限,超过上限的列表会返回 400 错误。具体上限是接口校验里的一个阈值,这类数值随版本调整的成本很低,本文不写死,动手前去官方文档确认当前值。
注意第三条只约束 fallbacks。models 数组有没有长度上限,这一页官方文档没有说明。
这三条合起来划出的边界是:fallbacks 是一个只能点名模型、不能带参数、长度受限、且和 models 互斥的轻量结构。想给回退模型单独调参数,或者想排一条更长的候选链,这条路走不通——要么改走 chat/completions 加 models,要么把这层逻辑留在自己的代码里。
计价按最终跑的那个模型算
文档写明:请求按最终实际使用的模型计价,而这个模型会在响应体的 model 属性里返回。具体价格数值本文不写,去官方定价页看当前值。
这条对做成本预算的人有实际影响:回退链里排在后面的模型,价格未必和主模型一个量级,触发一次回退,这次请求的成本口径就换了。所以候选列表不能只按「能顶上」来排,得同时看你能接受的成本区间。
五、怎么确认它真的配对了
最直接的验证点就是上面那个 model 属性——文档说明最终使用的模型会在响应体里返回,那么把它打出来就能知道这次请求到底落在了谁身上:
data = response.json()
print(data['model'])
print(data['choices'][0]['message']['content'])
以上为按官方文档中的参数语义组合的示例,未经实测,以官方文档与 --help 的实际输出为准。
几个建议的检查动作,按可核实程度排一下:
- 正常路径先跑通:主模型可用时发一次请求,读响应体的
model字段,确认它等于你写的主模型。这一步只是证明字段名没写错、extra_body放对了位置。 - 确认字段被接受:用 OpenAI SDK 时,
models如果没进extra_body,行为不受官方文档保证;TypeScript 那边则要留意// @ts-expect-error的位置。走/api/v1/messages时,把fallbacks和models同时发一次,按文档应该拿到 400——这是文档写明的行为,可以拿来当「参数确实被平台解析了」的反向验证。 - 不要用制造真实故障的方式去测:文档没有提供模拟 provider 宕机的开关,也没有提供强制触发回退的参数。我们没有对这个能力做过实测,所以不给「怎么造一次故障」的操作建议。
最后提醒一句口径问题:这一页讲的全是模型级回退,和同一模型下 provider 之间怎么挑、order / only 之类的 provider 路由字段不在一个层面。线上排查时先分清你这次失败是「模型不可用」还是「某个 provider 不可用」,两边的配置项不通用,改错地方会白忙一晚上。
本文依据 OpenRouter 官方文档(openrouter.ai/docs)于 2026-08-18 的公开内容整理。
该平台闭源,本文只复述官方文档写明的机制,不推断其内部实现;
我们没有对文中涉及的功能做过实测,因此不涉及界面外观与运行表现的任何描述。
该平台的供应商、模型与路由策略随时变动,文中不列具体供应商名单与模型清单;
价格、额度与限流的具体数值请以官方定价页与用量说明为准。
安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。
相关阅读
- 可观测方案对照:OpenRouter Broadcast 与 Claude Agent SDK observability 各自吐什么
- BYOK 自带密钥怎么接:用自己的供应商账号走 OpenRouter
- 流式与单次两种模式怎么选:Claude Agent SDK 把差异划在哪
- 让 Claude Code 走 Google Vertex AI 与 Microsoft Foundry:两条云上接入路径的配置差异
- Claude Agent SDK 与 OpenRouter Agent SDK 对照:循环放在哪一层、谁管工具谁管路由
- 用 API 驱动 Cursor Cloud Agent:endpoints 页的调用链