OpenRouter 路由失败怎么排查?五层剥洋葱式定位法

2026-07-28

数据截至 2026-07,价格与限额以各官网为准。

大多数被叫做”OpenRouter 路由失败”的问题,跟路由本身没关系:要么请求压根没出你的机器,要么模型名少写了命名空间前缀,要么免费档的每日次数已经用完。真正属于”上游提供商挂了”的情况占比很小,而且平台本身就有自动切换机制在兜。所以排查的第一原则是分层,从最外层的网络链路往里剥,别一上来就怀疑平台。

一个常见误解是:既然 OpenRouter 官方文档写了会自动处理 fallback,那调用就不该失败——只要失败就是平台的锅。这个理解偏了一半。官方对 fallback 的描述是,某个提供商返回错误时会自动切到下一个提供商,这个过程对调用方是透明的。但这句话的作用域仅限于”同一个模型有多个可用提供商,其中一个报错”。你把模型名写错、余额扣光、请求根本没连上端点,这些都不在 fallback 的射程内,切一百次也没用。分清这条边界,排查效率能提高一大截。

先把”失败”拆成三类现象

对着一行英文报错干瞪眼是最浪费时间的。动手前先花十秒钟判断你遇到的是哪一类,后面的路径完全不同:

  • 第一类:连不上。 表现是超时、连接被重置、DNS 解析不出来,客户端抛的是网络层异常,而不是一个带 HTTP 状态码的响应体。这类问题跟 API 参数一点关系都没有。
  • 第二类:连上了但被拒绝。 你拿到了明确的 HTTP 状态码和 JSON 错误体,比如鉴权失败、模型不存在、超出限速、余额不足。这类问题信息量最大,错误体里通常直接写了原因。
  • 第三类:调用成功但结果不对。 返回了 200,但内容为空、被截断、或者你以为在用 A 模型实际却走了别的路由。这类最隐蔽,因为监控看着一切正常。

三类现象对应三套查法。下面按由外到内的顺序过一遍。

第一层:链路,也就是请求有没有出得去

OpenRouter 是境外托管的海外服务,官网和 API 端点在中国大陆网络环境下普遍需要经过网络代理才能稳定访问。这不是它一家的特殊情况,它底层依赖的那些海外模型提供方同样如此。所以在大陆环境下,超时、握手失败这类现象大概率是链路问题,不是代码问题。

判断方法很朴素:先绕开 SDK,用最原始的方式打一次端点,看能不能拿到任何 HTTP 响应。能拿到响应(哪怕是 401)说明链路通了,问题在里面;拿不到任何响应,说明卡在外面,改代码没有意义。

还有一个高频细节:你的程序未必用了你以为的代理。终端里设了代理环境变量,不代表 Python 进程一定读到了;有些 HTTP 客户端默认不走系统代理,容器里的进程更是常常跟宿主机的网络配置脱节。排查时直接在代码里打印一下实际生效的代理配置,比反复猜要快得多。

关于大陆访问,有一点要说清楚:这里不提供也不背书任何具体的第三方中转、镜像或代理渠道。这类服务的合规性、稳定性和数据安全无法从官方信息核实,变动也快,风险要由使用者自己承担。官方并未针对中国大陆访问发布正式说明,一切以官网当前条款为准。

第二层:鉴权与 base_url 有没有指对

链路通了之后,第二个最容易翻车的地方是 base_url。OpenRouter 走的是 OpenAI 兼容协议,正确的地址是:

https://openrouter.ai/api/v1

Chat Completions 端点则是 https://openrouter.ai/api/v1/chat/completions

从别的服务迁移过来的项目,最典型的事故是 key 换了、base_url 忘了改,结果拿着 OpenRouter 的 key 去撞另一家的端点,对方当然认不出来,回一个鉴权失败。反过来也一样:base_url 改了、key 还是旧的。看到鉴权类报错,第一反应应该是把这两个值一起打印出来核对,而不是去怀疑密钥本身失效。

最小可用的接法就是把官方 OpenAI SDK 直接指过来:

import os
from openai import OpenAI

client = OpenAI(
    base_url="https://openrouter.ai/api/v1",
    api_key=os.getenv("OPENROUTER_API_KEY"),
)

另外提醒一句环境变量的坑:所谓”密钥无效”里有相当一部分其实是变量名拼错、或者根本没被当前进程读到。先 print(os.getenv("OPENROUTER_API_KEY")) 确认它不是 None,这一步花不了五秒钟,却能挡掉一大批误判。

顺带说一下两个可选请求头:HTTP-RefererX-Title,分别用于填站点 URL 和站点名称,用途是平台的站点排行榜统计。官方文档把它们标为可选,不传不影响调用——所以如果有人告诉你”没传这两个头所以路由失败”,那是误传。

第三层:模型名,命名空间前缀最容易漏

这是 OpenRouter 独有的一个高频坑。它的模型标识符带命名空间前缀,形如 openai/xxxanthropic/xxxgoogle/xxx。比如调用 Claude Opus 4.7 时写的是:

"model": "anthropic/claude-opus-4.7"

而不是直接抄某个模型官方文档里的裸名字。很多人从原厂教程复制模型名过来,忘了加前缀,于是收到”模型不存在”类的错误,然后开始怀疑是不是自己账号没权限调这个模型——方向就跑偏了。

需要注意,模型清单和可用性会变动,各家提供商也会调整型号。这篇不逐一罗列具体型号,你要确认某个名字当前是否可用,去 openrouter.ai/models 这个页面看实时清单最稳妥。这不是敷衍,而是模型名这种东西写死在文章里,过几个月就是误导。

第三类现象(调用成功但不是你要的模型)也常出在这一层。如果你用的是免费模型路由 openrouter/free,它会在可用的免费模型中做智能筛选和分配,官方页面显示的免费变体数量约在二十几个这个量级,主要来自 Qwen、DeepSeek、Llama、Gemma 等开源模型的免费额度。也就是说,用这个路由时你本来就不该假定每次都命中同一个模型。想要确定性,就指定具体模型名。

第四层:额度、余额与限速

OpenRouter 采用预充值信用金模式,没有订阅制:先充值,调用时按实际消耗从余额扣。所以”昨天还好好的今天全挂”这种现象,先去看余额是不是见底了。

还有一条容易被忽略的规则:官方在文档里保留了在购买满一年后清零未使用信用金的权利。对于充了一笔然后长期低频使用的账号,这是个真实存在的隐患。促销赠送的免费额度到期规则可能更短,具体以你账户内的实时提示为准。

免费模型档有明确的限速,这是新手最常撞的一堵墙:

  • 从未购买过信用金的账户:每天 50 次、每分钟 20 次
  • 账户历史充值达到 10 美元及以上:每天 1000 次,每分钟 20 次这条限制依然在。

看清楚,两档共享同一个每分钟 20 次的上限。所以就算你充过钱、日额度提到了一千次,只要瞬时并发压上去,照样会被限流。写批量任务时务必自己做节流,别指望客户端重试能扛过去——盲目重试反而会把限速窗口拖得更久。

另外,平台明确声明不对模型调用加价,模型目录里展示的价格就是你实际支付的价格,与提供商官网一致。它的收入来自充值环节的手续费:信用卡/借记卡渠道收 5.5%,最低 0.80 美元;加密货币渠道收 5%,无最低限制。这跟排查失败没有直接关系,但如果你在核对账单时发现充值到账金额对不上,原因通常就在这里,不是路由问题。

至于 BYOK(自带上游 key)的平台费规则,官方的常见问题页和定价页存在两套口径:一套按请求次数描述(首月一百万次 BYOK 请求免费,超出后按该模型正常价格的 5% 收平台费),一套按金额描述(按月给出一定额度的免手续费推理额度,超出部分收 5%)。两套口径之间的关系没能进一步核实一致性,如果你要走 BYOK 且费用敏感,请以官网当前页面的说明为准,别照抄任何二手转述。

第五层:provider 层,以及自动 fallback 到底管什么

走到这一层,才轮到”真的是上游出问题了”。官方对这个机制的描述是:某个提供商返回错误时,会自动切换到下一个可用提供商,整个过程对用户透明,让生产应用更有韧性。同时官方称对失败请求不计费,只按成功的请求收费。

这里有两个实用推论:

推论一:失败重试的成本焦虑可以放下一些。 既然失败请求不计费,你在客户端做有限次数的重试,不至于因为几次失败把余额烧掉。但请注意”有限次数”——限速类错误重试得越猛越糟,指数退避是基本礼貌。

推论二:fallback 是透明的,意味着它也可能悄悄改变你的实际执行路径。 同一个模型可能有多个提供商承载,切换后延迟特性、并发表现未必完全一致。如果你观察到”结果对但慢了很多”或者响应风格出现波动,值得往这个方向想一想,而不是只盯着自己的 prompt 改。

需要诚实说明的是:具体某次请求最终落到了哪个提供商、fallback 的判定细则和排序逻辑,这些属于平台内部行为,本文不做推测。你要查证,看官方文档中关于 provider 路由的说明和控制台里的请求记录,那才是一手依据。

上下文超限:一类会伪装成别的问题的失败

还有一类失败值得单拎出来:输入太长导致超出模型上下文窗口。它的报错措辞五花八门,有时候看着像参数错误,有时候干脆返回空内容,所以容易被误判成”路由抽风”。

主流大窗口模型的上下文规格已经相当宽裕,比如平台模型页上,GPT-5.5 标注的是 100 万级别的窗口(其中输入约 92.2 万、输出 12.8 万 token),Claude Opus 4.7 和 Gemini 2.5 Pro 也都标注为 100 万级别。但请注意两点:一是输入和输出往往是分开算的,别以为一百万全给你塞输入;二是不同型号差别很大,别拿旗舰模型的规格去套开源小模型。你要用哪个型号,就查那个型号的页面。

排查方法:在发请求前自己粗算一下消息体长度,超过阈值的直接截断或分块,别指望服务端给你温柔的提示。

一份能让下次排查快十倍的日志清单

线上问题最怕的是”复现不了”。建议每次调用至少记这几个字段,出问题时基本能直接定位到层:

  • 请求侧:模型名(完整带前缀)、base_url、消息体总长度、是否流式。
  • 响应侧:HTTP 状态码、错误体原文(别只记一句”调用失败”)、耗时、返回的 usage 字段。
  • 上下文:请求发起时间、重试次数、当前账户是免费档还是已充值档。

尤其是错误体原文。绝大多数排查僵局的成因,都是有人在代码里把异常捕获后只打了一句自定义的中文提示,把服务端辛辛苦苦返回的原因给吞了。

这篇讲不了的部分

有必要说清楚局限。价格、限速、额度政策都会变动,本文写下的数字是 2026-07 这个时间点的口径,正式引用前请回官网核对当前页面。免费档”不建议用于生产”这个说法流传很广,但这句表述本身出自第三方转述,未在官方原文中逐字核实,你可以把它当作一个合理的工程判断,而不是官方承诺。此外,模型清单、各型号价格、provider 排序规则都属于高频变动内容,以 openrouter.ai/models 与官方文档的实时页面为准。

小结

第一,排查按层剥:链路、鉴权与 base_url、模型名、额度限速、provider,顺序别乱,越靠外的层出错概率越高。第二,模型名的命名空间前缀是 OpenRouter 特有的高频坑,从原厂文档抄名字必然要加前缀。第三,免费档每分钟 20 次这条限制两档共享,充值提额也解不开,批量任务必须自己节流。第四,自动 fallback 只覆盖”提供商报错”这一种情况,别指望它兜住你的参数错误。第五,把错误体原文完整记进日志,这是所有排查手段里性价比最高的一条。

接下来看什么

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