OpenRouter 的 provider 路由怎么配:把请求钉在你想要的那家
一、你会在什么时候需要它
用 OpenRouter 最容易踩的一个认知差是:你在请求体里写的 model 是一个模型 slug,不是一台机器。同一个模型 slug 背后可能挂着多家供应商的多个端点,你不指定的时候,是路由器替你挑的。
官方文档《Provider Routing》页写得很直接:默认行为是在多个供应商之间做负载均衡,优先考虑价格。文档把这套默认策略拆成了三步:先把近期没有出现明显故障的供应商排在前面;在这些稳定供应商里,从成本较低的候选中按价格的平方倒数加权随机选一个;剩下的作为 fallback。
这套默认策略在「我只想跑通」的阶段是舒服的。它开始咬人,通常是这几种处境:
- 同一段 prompt,昨天和今天的输出风格对不上,你怀疑落到了不同供应商的不同端点上;
- 你的请求带了
tools或者结构化输出,但某些端点对参数的支持不一致; - 合规上要求 prompt 不被留存,你需要按请求维度把这件事钉死;
- 你要做 A/B 对比,必须保证两组请求落在同一个端点上,否则对比没有意义。
这些都不是靠换模型解决的,是靠 provider 这个对象解决的。它放在 Chat Completions 的请求体里,和 model、messages 平级。
二、前置条件
动手前有四件事要先确认,官方文档里都能核到:
第一,改的是请求体,不是别的地方。 文档写明这些字段放在 Chat Completions 请求体的 provider 对象里。也就是说,这是一次请求一次生效的东西,不是全局开关。
第二,provider slug 你得拿准。 文档给的办法是:在模型详情页上,供应商名字旁边有复制按钮,复制出来的就是精确的 provider slug,包含形如 /turbo 这样的变体后缀。这里我们不列任何具体供应商——OpenRouter 的供应商名单是这一页打开时才拉取的动态数据,文档正文里并没有一份静态清单,我们没有依据去写它,你也应该以页面上当时显示的为准。
第三,账户级设置会兜住你。 文档提到 only、ignore、数据策略过滤、ZDR 这几项在隐私设置里都有账户级的对应配置,并且账户级和请求级是叠加的(下面第四节会说清叠加规则)。所以你在请求里怎么写,不等于最终生效的是什么。
第四,区域驻留是企业功能。 文档在这一页专门标注:EU 与 US 的区域内路由面向企业客户(Enterprise),启用后 prompt 与 completion 会完全在所选区域内处理。这不是随手能开的开关。
三、字段一个个过
provider 对象里的字段,按作用可以分成三类:选谁(筛选)、按什么顺序试(排序)、允不允许退而求其次(回退)。
筛选类
| 字段 | 类型 | 默认值 | 语义 |
|---|---|---|---|
order | string[] | - | 按顺序尝试的 provider slug 列表 |
only | string[] | - | 本次请求只允许这些 provider |
ignore | string[] | - | 本次请求跳过这些 provider |
require_parameters | boolean | false | 只用支持请求中全部参数的 provider |
data_collection | ”allow” | “deny" | "allow” | 是否允许可能存储数据的 provider |
zdr | boolean | - | 只路由到 ZDR(零数据留存)端点 |
enforce_distillable_text | boolean | - | 只路由到作者允许文本蒸馏的模型 |
quantizations | string[] | - | 按量化等级过滤 |
max_price | object | - | 本次请求可接受的最高价 |
order 和 only 的区别值得单独说一句:order 是「优先按这个顺序试」,文档明说如果这些都不可用,路由器会继续尝试其它供应商——除非你同时把 allow_fallbacks 关掉。only 才是「本次请求只准用这些」。
require_parameters 有个容易被忽略的默认行为:文档写明,在默认策略下,不支持你请求中某些参数的供应商照样可能收到这个请求,只是会忽略它不认识的参数。设成 true 之后,请求压根不会被路由过去。
紧接着还有一段更细的:即使 require_parameters 是 false,也有一小组参数会被当作软偏好参与选择,文档列出的是 tools、response_format(含结构化输出)和 verbosity 三个。规则是——如果这个模型的部分供应商支持而另一部分不支持,请求只会走支持的那些;如果这个模型所有供应商都不支持,请求仍然会路由到这个模型,参数被忽略。文档特别强调,这个软偏好不会把某个模型从候选列表里剔除。
quantizations 接受的取值文档列全了:int4、int8、fp4(含 mxfp4 或 nvfp4)、mxfp4、nvfp4、fp6、fp8(含 mxfp8)、mxfp8、fp16、bf16、fp32、unknown。同页有一条警告:量化模型在某些 prompt 上可能表现下降,取决于所用方法。
max_price 是个对象,文档提到的属性有 prompt、completion、request(部分供应商支持按请求计价)和 image。具体填什么数由你的成本口径决定,本文不列任何价格数值。
排序类
sort 只有三个取值,文档原文列的是:"price"(最低价优先)、"throughput"(最高吞吐优先)、"latency"(最低延迟优先)。
这里有一条必须记住的连带效应:文档写明,只要你设了 sort 或 order,负载均衡就被关掉了,路由器改成按顺序逐个尝试。也就是说,你为了稳定性去指定 order,代价是放弃默认那套「加权随机 + 避开近期故障」的分散。文档在「默认负载均衡」和「Provider Sorting」两节都写了这条连带效应,不是随口一提。
fetch('https://openrouter.ai/api/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': 'Bearer <OPENROUTER_API_KEY>',
'HTTP-Referer': '<YOUR_SITE_URL>',
'X-OpenRouter-Title': '<YOUR_SITE_NAME>',
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'meta-llama/llama-3.3-70b-instruct',
messages: [{ role: 'user', content: 'Hello' }],
provider: {
sort: 'throughput',
},
}),
});
模型 slug 后面还有两个后缀是 sort 的快捷写法,文档说得非常明确是「完全等价」:追加 :nitro 等价于 provider.sort 设成 "throughput",追加 :floor 等价于设成 "price"。注意 :floor 只是把路由目标改成价格最低的那一档,它不告诉你那一档多少钱。
sort 还可以写成对象,用来配合多模型 fallback:
| 字段 | 类型 | 默认值 | 语义 |
|---|---|---|---|
sort.by | string | - | 排序策略:"price" / "throughput" / "latency" |
sort.partition | string | "model" | 端点分组方式:"model"(默认)或 "none" |
partition 是这一页里最容易被漏掉的字段。文档写明:当你用 models 指定多个模型做 fallback 时,默认会先按模型给端点分组再排序,结果就是主模型的端点永远先被尝试,不管它当下的性能表现如何。把 partition 设成 "none",这层分组被去掉,端点在所有模型之间全局排序。想显式使用默认行为就写 partition: "model"。
回退与性能偏好
allow_fallbacks 默认是 true。设成 false 的效果是:只由你圈定的范围来服务这次请求,圈不到就失败。它通常和 order 一起用。
fetch('https://openrouter.ai/api/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': 'Bearer <OPENROUTER_API_KEY>',
'HTTP-Referer': '<YOUR_SITE_URL>',
'X-OpenRouter-Title': '<YOUR_SITE_NAME>',
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [{ role: 'user', content: 'Hello' }],
provider: {
allow_fallbacks: false,
},
}),
});
以上两段为官方文档中的原始示例。若你把 order、sort、zdr 等多个字段组合起来用,那是按官方文档中的参数语义组合的示例,未经实测,请以官方文档的最新内容为准。
preferred_min_throughput 与 preferred_max_latency 这两个字段名字里带 preferred 不是修辞。文档明确说:不满足阈值的端点是被降优先级(挪到列表末尾),而不是被排除。两者都可以写成一个数(作用于 p50),也可以写成带百分位切点的对象,可用的百分位文档列了四档:p50、p75、p90、p99。指定多个切点时,必须全部满足才算进入「优先组」。
因为涉及具体的吞吐与延迟数值,本文不抄这两个字段的示例值——那些数字取决于你的业务容忍度,官方文档里的示例值也只是示例。
四、边界:这些地方文档专门警告过
preferred_* 不是保证。 文档原话的意思是:这两个偏好不保证你能拿到达到该性能水平的供应商或模型,只是让达标的被优先;因此它们永远不会阻止你的请求被执行。而 max_price 不一样——文档在同一段里做了对照:如果没有满足价格条件的选项,max_price 会让你的请求跑不起来。这两者的性质完全不同,别混用同一套心智模型。
base slug 匹配是「连坐」的。 文档给了一张匹配表,说明在 order、only、ignore 里写基础 slug 时会匹配该供应商的所有端点,包括变体和区域:
| 请求里写的 slug | 匹配到什么 |
|---|---|
"google-vertex" | 该供应商的全部端点(每个区域) |
"google-vertex/us-east5" | 只匹配 us-east5 区域的端点 |
"deepinfra" | 该供应商的全部端点(默认 + turbo) |
"deepinfra/turbo" | 只匹配 turbo 端点 |
(上表是官方文档用来讲解匹配语义的示例 slug,不代表当前可用的供应商清单,实际 slug 请用模型页上的复制按钮取。)
这张表有个例外文档单独标了出来:service tier 端点不被 base slug 匹配,它们需要通过 service_tier 参数或带 tier 后缀的 slug 显式选入。你以为写了 base slug 就把一家全包了,实际上并没有。
账户级设置的叠加规则,三个字段三种。 这是本页最反直觉的一块,文档分别写在三处:
only:账户级的允许列表是外层上限,请求级的only在其中再收窄。如果没有任何供应商同时满足两边,请求以 404 失败。ignore:请求级列表与账户级列表合并。zdr:请求级参数与账户级、guardrails 设置之间是 OR 关系——任意一处开启就会执行 ZDR 强制。文档还明说,请求级参数只能确保 ZDR 被打开,不能覆盖账户级或 guardrail 的强制。
同样是「账户级 + 请求级」,一个是取交集、一个是取并集、一个是取或,这三条最好抄在你的内部文档里。
only 和 ignore 各自带一条警告。 文档在这两处都放了 Warning:只允许部分供应商 / 忽略多个供应商,可能显著减少 fallback 选项,限制请求的恢复能力。
供应商专属 beta header 是实验性的。 这一页末尾列了可以透传给 Anthropic 模型的 x-anthropic-beta header,文档写明的取值有两个:interleaved-thinking-2025-05-14(让思考内容与常规输出交错,而不是集中成一整块)和 structured-outputs-2025-11-13(为支持的 Claude 模型启用严格工具调用,按 schema 校验工具参数)。多个特性用逗号分隔。文档在这一节挂了 Warning:beta 特性是实验性的,可能被 Anthropic 变更或废弃,以其官方文档为准。
同一段还有一条容易漏的:如果你在 tools 上用了 strict: true,必须显式带上 structured-outputs-2025-11-13 这个 header;不带的话,OpenRouter 会把 strict 字段剥掉,然后按正常流程路由——不会报错,只是你的约束静悄悄没了。
数据策略标签不是权威来源。 关于 data_collection,文档只说 allow 是允许可能非临时存储并可能用于训练的供应商,deny 是只用不收集用户数据的供应商;同时说明模型页上的 Data Policy 标签「不是第三方数据策略的权威来源,只代表我们已知的最佳信息」。这句限定要一起转述给你的合规同事。
响应里怎么读出实际落到了哪一家? 这一页没有说明这一点。我们不替它补,你需要按官方 API 参考页去核。
五、怎么验证你配对了
这一页没有给「验证路由结果」的专门章节,但它写明的几处失败行为本身就是可用的信号:
- 先验一次「必然失败」。 用
only写一个与你账户级允许列表相冲突的范围,按文档描述应当以 404 失败。能稳定复现 404,说明账户级的外层限制确实生效了,你后面所有的收窄才有意义。 - 再验
allow_fallbacks: false。 配合order只圈一个端点。按文档语义,此时要么落在这个端点上,要么这次请求失败——不会静默漂到别家。所以这一步能验的是「圈不到会不会失败」:slug 故意写错一个字符,请求应当失败;写对了却仍然失败,回头看第四节 base slug 与 service tier 那条。 max_price与preferred_*分开测。 前者按文档会阻止请求执行,后者按文档永远不会。如果你设了性能阈值之后请求直接跑不起来,那问题多半不在阈值上。- 验
require_parameters时留意软偏好。 请求里同时带tools或response_format的话,即使require_parameters是false,路由也已经在做偏好了。测「开与不开的差异」时,用不在软偏好那三项里的参数更干净。
关于命令行:官方文档给的 cURL 示例用的是 \ 续行,那是 Linux/macOS shell 的写法。在 Windows 上,cmd 的续行符是 ^,PowerShell 的是反引号,而 PowerShell 里的 curl 默认是 Invoke-WebRequest 的别名,要跑文档里的示例得显式写 curl.exe,或者干脆把请求体存成一个 .json 文件再用 -d "@body.json" 引用,省掉引号转义的麻烦。这一段是通用的命令行做法,不是 OpenRouter 官方文档的内容,写在这里只是因为直接照抄示例在 Windows 上大概率不会跑通。
最后提醒一句口径:provider 对象的字段集合与默认值都可能随平台迭代变化,本文只反映我们整理时那一版文档。记住「硬过滤还是软偏好」这一层,具体字段与默认值每次以官方文档最新内容为准。
本文依据 OpenRouter 官方文档(openrouter.ai/docs)于 2026-08-18 的公开内容整理。
该平台闭源,本文只复述官方文档写明的机制,不推断其内部实现;
我们没有对文中涉及的功能做过实测,因此不涉及界面外观与运行表现的任何描述。
该平台的供应商、模型与路由策略随时变动,文中不列具体供应商名单与模型清单;
价格、额度与限流的具体数值请以官方定价页与用量说明为准。
安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。 合规与许可条款请以官方原文与你所在组织的要求为准,本文不构成法律意见。