OpenRouter 的几种 router 分别按什么挑模型

2026-08-18

写接入代码时,model 那一栏迟早会碰到一个选择:写死一个具体 slug,还是写一个 router slug 让平台替你挑。写死可复现,但有新模型就得改代码;交给 router 不用改代码,代价是你得先搞清楚它按什么挑。

这篇沿着官方文档 openrouter.ai/docs/guides/routing/routers/ 这一节走一遍,把每个 router 的选择依据摘出来,再说清楚请求打回来之后怎么确认它到底挑了谁。这一节下面是六页:Auto Router、Pareto Router、Free Models Router、Latest Model Resolution、Fusion Router、Body Builder。行为差得相当远,后面会看到其中两个严格说根本不是”在候选里替你挑一个模型”。

Auto Router:按社区花钱的方向挑

openrouter/auto 的依据在 How It Works 一节里写得很直白:先由一个轻量分类器给 prompt 打一个细粒度任务类型标签(文档举的例子有 code:debuggingagent:multi_step_planningqa_knowledgemathcustomer_supportresearch_report);再查 OpenRouter 社区在过去 7 天滚动窗口里对这个任务类型实际把钱花在哪些模型上,也就是 rankings 页的 “Share of Spend” 视图;然后套用你的 cost tier;最后按市场份额顺序把存活候选排成首选加 fallback,排之前还要先过账户级的模型与 provider 限制、guardrails、ZDR 策略、allowed_models 限制和输出模态要求。文档自述这样做的效果是:有人把工作负载迁到新模型,路由几天内就跟上。

cost_tier 是这个 router 上你唯一能拧的旋钮,取值从便宜到能打是 lowmediumhighxhighmax 五档。这里有一句非常反直觉、值得单独抄出来:tier 是一条带(band),不是上限——比这一档更便宜的模型同样会被排除,不只是排除更贵的。选了 medium 不等于”medium 及以下”,而是”就在 medium 这条带里”,带内仍按市场消费份额排序。不设任何 cost 参数的请求,文档写明大致按 low 那一档路由。

想收窄候选就用通配符:

plugins: [
  {
    id: 'auto-router',
    allowed_models: ['anthropic/*', 'openai/*'],
    excluded_models: ['openai/gpt-4o'],
  },
]

excluded_modelsallowed_models 之后生效,被排除的模型即使匹配了允许模式也不会被选中。要是限制把候选全筛没了,请求以 404 失败,文案是 No models match your request and model restrictions。上面的具体 slug 只是官方文档当时的示例值,平台上有哪些模型随时在变,别当清单用。

两个坑得提前知道。一是这个 router 有两个 slug:openrouter/autoopenrouter/auto-beta,后者是新路由行为先落地的早期通道,文档明确标了它是 early-access track,插件 id 要用 auto-beta-router 而不是 auto-router。文档专门用 Warning 提醒:每个 slug 只读自己那个 plugin id 下的设置,发到另一个 id 下的设置会被接受但静默忽略allowed_modelsexcluded_modelscost_tier 一律不生效。不报错,只是没效果,这种最难查。二是 cost_quality_tradeoff 属于上一代 Auto Router,文档已标为 deprecated,只为向后兼容还收着;两个都传时以 cost_tier 为准。

Pareto Router:按编码百分位定下限,档内挑最便宜的

openrouter/pareto-code 的依据是一个数:min_coding_score,取值 0 到 1,1 最好。它映射到三档 tier,每档对应 Artificial Analysis 编码百分位的一条带:

min_coding_scoreTierAA 编码百分位带
>= 0.66highAA 编码榜的头部
>= 0.33< 0.66medium头部之下的现代旗舰
< 0.33low仍高于 AA 中位数的可用编码模型
省略high(默认)AA 编码榜的头部

关键在于档内怎么选:把该 tier 的候选名单过滤成当前在 OpenRouter 上架的,然后按价格升序取第一个当主选,接下来两个留作同档 fallback;请求 :nitro 变体时排序改成按 p50 吞吐降序。完整依据就是”能力下限由你定,线之上挑最便宜的(或最快的)“。fallback 的语义要看清楚:文档写明它只在 provider 的瞬时错误或限流时触发,不做流量负载均衡;只有整档在目录里都没模型了才跨到相邻档。

还有一条 Note 值得记住:因为轴是 AA 编码场里的百分位而非绝对分,新的强模型发布会把现有模型往下挤一个带,所以 min_coding_score=0.66 永远表示”当前场上的头部”,而不是”高于某个固定的能力分”。你的配置没变,落到的模型可能变。

不想每次请求都带 plugin,官方文档写明可以在 Settings > Plugins 页面配默认值:找到 Pareto Router 那一行点配置(齿轮)图标,选 HighMediumLowCustom score 填一个 0 到 1 的值,点 Save,再把插件开关打开。文档同时写明单次请求传 pareto-router plugin 仍可覆盖默认值,除非你打开了 “Prevent overrides”。

curl https://openrouter.ai/api/v1/chat/completions \
  -H "Authorization: Bearer $OPENROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openrouter/pareto-code",
    "plugins": [
      {
        "id": "pareto-router",
        "min_coding_score": 0.8
      }
    ],
    "messages": [
      {"role": "user", "content": "Write a Python function that merges two sorted lists."}
    ]
  }'

局限文档自己列了:它只调编码任务;min_coding_score 是唯一的 router 参数,没法在这个 router 上直接给单次请求设成本或延迟上限。Auto 和 Pareto 都有 session stickiness——多轮会话尽量留在上次选中的模型和 provider 上,靠显式 session_id 或消息指纹识别会话,被记住的模型只在它仍属当前候选时才复用。

Free Models Router:能力过滤之后,随机

openrouter/free 的依据最简单也最容易被误解。文档写的流程是:分析请求需要什么能力(比如视觉、工具调用、结构化输出)→ 把免费模型池过滤成支持这些能力的 → 随机选一个 → 转发。

“随机”是原文的 Random Selection,不是我的概括。同样的请求两次可能落到不同模型上,Limitations 里也明说了你无法控制具体选中哪一个。要定住某个免费模型,得走另一条路:给具体模型加 :free 后缀。

{
  "model": "meta-llama/llama-3.2-3b-instruct:free"
}

这里的 slug 是官方文档当时的示例值,免费模型有哪些随时在变,别当清单用——文档自己也挂了 Warning 说免费模型的可用性变动频繁,要看当前有哪些得去 models 页面查。

文档还自述了几条限制:免费模型的速率限制可能比付费模型低,可用性会波动,高峰时延迟可能更高。这些是官方文档的口径,我们只是转述。

Latest Model Resolution:不看任务不看价,只看谁最新

~author/family-latest 这类 slug 的依据里没有任何”挑选”成分——认 ~ 前缀,识别出别名指向哪个模型家族,取该家族里最新的可见模型,然后照常跨 provider 路由,就像你直接写了那个具体 slug。

两条边界得记住。排除项:解析结果永远不会是另一个别名 slug,也不会是被隐藏的模型;家族里没有可用模型时直接报错,而不是回落到不相干的东西。没有回退档:文档写明没有内建方式钉到”第二新”或往回滚,要降级只能改成具体 slug。

这一页最容易被忽略的是兼容契约那一节。当别名开始指向一个强制 reasoning 的模型时,reasoning: { effort: "none" } 会被上调成该模型支持的最低档,reasoning: { enabled: false } 会被翻成 enabled 同样落在最低档,reasoning: { max_tokens: 0 } 同样处理且零预算被丢弃。而这套重映射只对 ~latest slug 生效:具体模型 slug 保持严格校验,给一个强制 reasoning 的模型发 effort: "none" 返回 400。这两条口径放在一起读才不至于踩空——你在 ~latest 上跑通的参数,换成具体 slug 可能直接被拒。

Fusion Router:它挑的不是模型,是要不要开会

openrouter/fusion 跟前面几个不是一类东西。文档的说法是”multi-model deliberation as a model slug”:router 把这个 alias 解析成一个真实模型,并给它挂上 openrouter:fusion 这个工具。是否调用由模型自己决定,觉得任务不值得开会就直接答;想每次都开会,加 tool_choice: "required"

真调用了之后,文档描述的路径是:panel(一组模型)并行回答你的 prompt,每个都开着 openrouter:web_searchopenrouter:web_fetch;analyst 拿到全部 panel 回答后做比较而不是合并(原文 it doesn’t merge them),产出结构化 JSON——共识、分歧点、只有部分模型覆盖到的内容、个别模型的独有洞见、以及所有模型都没碰的盲区;最后你的模型拿这份分析写终稿。

能配的字段在 fusion plugin 里:analysis_models 决定 panel 由谁组成(文档写明允许 1 到 8 个),model 指定 analyst(默认就是处理你请求的那个模型),max_tool_calls 限制 panel 与 analyst 在搜索/抓取循环里最多走几步就必须返回文本,max_completion_tokens 限制每次内层调用的输出预算,reasoningtemperature 转发给 panel——analyst 恒定跑在 temperature 0。默认值与取值区间以官方文档为准。

const completion = await openRouter.chat.send({
  model: 'openrouter/fusion',
  plugins: [
    {
      id: 'fusion',
      analysis_models: [
        '~anthropic/claude-opus-latest',
        '~openai/gpt-latest',
        '~google/gemini-pro-latest',
      ],
      model: '~openai/gpt-latest',
    },
  ],
  messages: [
    { role: 'user', content: 'Compare ridge, lasso, and elastic-net regression. Where does each shine?' },
  ],
});

以上为按官方文档中的字段语义组合的示例,未经实测,以官方文档与 API 的实际响应为准。示例里的 slug 是文档当时的示例值,不构成模型清单。另外两点:openrouter/fusion-flash 是单独上架的模型,等价于预先钉了 general-fast preset,你显式传的配置一律优先;递归被挡住了——内层调用带 x-openrouter-fusion-depth 头,panel 和 analyst 不能再套一层 fusion。

Body Builder:它一个模型都不挑

openrouter/bodybuilder 干的事是把自然语言变成一组可以直接执行的 OpenRouter 请求体,返回形如 {"requests": [...]} 的 JSON,然后你自己去并发跑。它不替你发请求,也不替你选择由谁作答——选择在生成出来的那组 body 里,执行权在你手上。文档写明生成请求体本身不收费,执行时按标准模型价计;输入需要 messages 格式,生成的请求默认只带最小必需字段,你输入里的 system message 会被保留并转发。这一页文档自己挂了 Warning,说页上那些 slug 示例只是某个日期的快照,新版本发布后就会变。

回读:这次到底走了哪条路

除 Body Builder 之外的五个 router 有一个共同的验证点:响应的 model 字段报告的是真正服务这次请求的具体模型,不是你发过去的那个 alias。这是 Auto、Pareto、Free、Latest、Fusion 五页各自都写明的一条。Body Builder 不适用这条——它返回的是一组待你执行的请求体,谁作答写在生成出来的每个 body 的 model 里,得等你自己发出去之后才有响应可读。

想看更细的,加一个头 X-OpenRouter-Metadata: enabled,响应里就会多出 openrouter_metadata 对象,其中 strategy 字段直接写明这次用的路由策略,枚举是 directautofreelatestaliasfallbackparetobodybuilderfusion 九个。把这九个和上面那六页对一下就很清楚:autofreelatestparetobodybuilderfusion 正好一一对上六个 router 页,剩下的 directaliasfallback 不属于 routers 这一节——direct 就是你写死具体 slug 的那条常规路径。

Auto Router 还多一层:openrouter_metadata.pipeline 里 router 那一段会带上 data.task_type,值形如 code:debugging,分类不可用时字段直接缺席。Fusion 则可以查 generation 元数据,那里的 router 字段会报 openrouter/fusion

几个会让你白查半天的例外文档写得很清楚:缓存命中的响应从不带 openrouter_metadata(流式与非流式都剥掉);500 响应也会被剥掉,其它 5xx(502503504529)在 opt in 时仍然带;认证失败、限流这类在路由器拿到可用状态之前就返回的错误同样不带,得靠 X-Generation-Id 响应头去捞 generation 记录。旧头名 X-OpenRouter-Experimental-Metadata 仍被接受,官方建议迁到新名字。

Windows 上跑文档里的示例

官方文档的 cURL 示例用的是 Unix shell 写法:反斜杠续行、单引号包住整段 JSON。这两样在 Windows 的 PowerShell 里都不成立——PowerShell 的续行符是反引号,单引号字符串也不做你期望的转义。最省事的是把示例贴进 Git Bash 或 WSL 跑;不想装就别用 cURL 示例,改用文档里同时给出的 TypeScript 或 Python 版本。这一段是通用的 shell 常识,不是 OpenRouter 官方文档的内容,文档本身没有区分平台。

一句话对照

六个 router 的依据是:Auto 看社区在同类任务上的消费份额再套 cost tier 带;Pareto 看编码百分位定下限、档内取最便宜(或 :nitro 下最快);Free 看能力过滤后随机;Latest 只看家族里谁最新;Fusion 不挑模型而是决定要不要开一场多模型评议;Body Builder 干脆不挑,只把你的话译成一组请求体交回你手上。

需要可复现就钉具体 slug,其余各自绑着一条明确的偏好轴——先想清楚你要哪条轴,再决定 model 那一栏写什么。这些路由行为与字段随平台迭代变动,落地前请以官方文档最新内容为准。


本文依据 OpenRouter 官方文档(openrouter.ai/docs)于 2026-08-18 的公开内容整理。 该平台闭源,本文只复述官方文档写明的机制,不推断其内部实现我们没有对文中涉及的功能做过实测,因此不涉及界面外观与运行表现的任何描述。 该平台的供应商、模型与路由策略随时变动,文中不列具体供应商名单与模型清单; 价格、额度与限流的具体数值请以官方定价页与用量说明为准。 安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。