使用 OpenRouter 避坑指南:揭秘模型托管差异
表面上看似乎很简单,但实际上坑会一路埋到底。
我运营着 Olly,一个活在 iMessage 里的 AI 助手,底层通过 OpenRouter 调用开源模型。到目前为止,Olly 已经处理了超过 1800 万条消息,其中大约三分之一走的是 OpenRouter 上的开源模型。这个量级足以让你把每个坑都踩一遍。下面就是一些我希望自己一开始就知道的事情。
先解释几个术语:模型(model)指的是权重本身。供应商(provider)指的是 OpenRouter 把你路由到的那家公司——他们用自己的 GPU 托管模型,自己选择量化精度,加上各自的"专有"优化,用自己写的 XML/工具调用解析器,所以每家也各有一份"专有"的 bug 清单。当你请求 deepseek/deepseek-v4-flash 时,实际接单的可能是约 20 家你基本没听过的公司之一。纸面上是同一个模型,实际体验却天差地别。
好,下面是一些你需要警惕的坑。
1. 同一个模型,各家跑分差异巨大
OpenRouter 会针对同一个模型发布各供应商的跑分:GPQA Diamond 和 TAU-Bench Airline(一项工具调用任务)。这是 DeepSeek V4 Flash 0731 今天的排行榜,所有供应商跑的都是一模一样的权重:
DeepSeek 官方:GPQA 90%,TAU 81%。DigitalOcean,同样的权重:75% 和 58%。大多数托管方的工具调用成绩比官方低 5 到 7 个百分点,还有四家在知识类任务上直接崩了。对 Agent 来说 TAU 才是关键指标,20 个百分点的波动绝不是噪声。(七月时更糟:Fireworks 的 TAU 只有 46%,差距 30 个点)
在信任某个供应商之前,先查一下和你工作负载最相关的那个基准的排行榜。换了模型也要重新查——在 GLM-5.3 上,这些供应商的排名完全是另一番景象。
2. 视觉模型可能遇上"失明"的供应商
我发现图像任务出现一些奇怪的不确定行为,于是用同样三张小图(一封字母、一块纯色、背景上的一个词)跑遍了两个开源视觉模型的所有托管方:
DeepInfra 的 Qwen 端点将字母 K 识别为 R,把红色说成蓝色,甚至形容“umbrella”这个词很“搞笑”,而承载相同权重的另外四家托管商则全部回答正确。Venice 和 Together 甚至未能识别出 MiniMax 的图片。模型页面声称支持图像输入,但其中两家提供商并不支持,更糟糕的是,它们还会假装一切正常,返回 200 OK。
3. 部分提供商可选开启“努力程度”旋钮
reasoning.effort 参数在所有提供商处均被接受。但具体是否生效,取决于模型和提供商。我固定了所有提供 DeepSeek V4 Flash 0731 服务的提供商,分别从生产环境机器发送同一提示词,测试低、高、最大三档努力程度,每档各三次,以下是输出中的推理 Token 数量:
请按提供商追踪不同努力程度设置下的推理 Token 数量。
4. 量化过滤器买不到质量
OpenRouter 允许按声明的精度过滤提供商,例如 quantizations: ["fp8"](而非 fp4)。直觉上,比特数越少,模型越“笨”。我对 DeepSeek 应用了该过滤器长达一个月。随后,我将每家提供商的基准测试排行榜与其声明的精度放在一起对比:
使用 fp4 的托管商表现位列 fp8 阵营的中游。DeepSeek 上 GPQA 分数最低的三家提供商分别来自:一家 fp4 托管商、一家 fp8 托管商,以及一家未声明任何精度的提供商。GLM 在两项基准测试中得分最高的提供商 Wafer,也未声明任何精度。精度是衡量质量的糟糕代理指标,且硬性过滤会缩小 OpenRouter 在某家提供商宕机时可回退的池子大小。请依据排行榜而非比特数进行过滤。
5. 工具调用混在文本里
理想情况是:模型以某种标记格式发出调用,提供商的解析器将其转化为结构化工具调用,然后我的代码执行它。但有时解析器会漏掉,导致回复中出现以下内容:
<use_skills><parameters>{"skills":["search"]}</parameters></use_skills>
且这种复现率在各提供商之间差异巨大。
这种情况会频繁出现,且足以顽固到你需要自己写解析逻辑。这里有两类情况需要截然相反的处理方式:被包裹的工具调用,以及被包裹或半包裹的响应。如果你想看看针对 DeepSeek/GLM 的一些解析示例,可以看看我的仓库 github.com/0xmmo/190proof。
6. 200 OK,但没有答案
推理模型有时会把所有内容塞进 reasoning 字段,然后返回 content: null 和 finish_reason: "stop"。345 个 completion tokens,HTTP 200,但用户什么也看不到。
200 状态码只说明请求被处理了,不代表里面有答案。既没有 content 也没有 tool call 就是失败,应当抛出异常并重试。
7. 空心补全
这跟前一种情况相关但不同。有些端点返回 200,但 content 为 null,reasoning 为 null,甚至完全没有 usage 对象。7 月份时,StreamLake 在 DeepSeek 上就是这样:占我流量约 20%,却贡献了 92% 的空补全。一个月后,Together 在 DeepSeek 0731 检查点上犯了同样的错误。
8. 相同模型,不同的历史规则
DeepSeek 在思考模式下会输出 reasoning_content 块。在 Agent 循环中,模型经常携带空的 reasoning 发起工具调用。如果你把这个空的 reasoning 历史传回 OpenRouter,且请求路由到例如 SiliconFlow,它会返回 400 错误,代码 20015:“思考模式下的 reasoning_content 必须回传至 API”。而百度、阿里云和 Cloudflare 对完全相同的历史记录毫无异议。
所以契约不是基于模型,而是基于提供商。另外别想着跳过工具历史,否则模型会不断重试任务。这只是又多了一件事要处理。
9. 从生产环境测试,别用你的笔记本
为了速度、延迟,也作为示例:从我的 Mac 测试 DeepSeek V4 Flash 时,Venice 和 Novita 表现完美,但从我的基础设施发起的几乎每个探测请求都返回 429。同一个 Key,同一分钟。我的判断是他们按 IP 限速。
在生产环境运行的地方做基准测试,每次跑几个,样本数要比你觉得必要的更多。
10. 为什么不直接锁定单个提供商?
有一段时间我配置了 provider.order: [cloudflare, baidu, alibaba],并设置 allow_fallbacks: false,一次性锁定了 3 个看起来可靠的供应商。结果两周后,Baidu 全面限流(大量 429),Cloudflare 根本不再提供那个模型,100% 的流量全压到 Alibaba 身上,然后 Alibaba 也开始返回 429。就这样,OpenRouter 上的头号模型(DeepSeek V4 Flash)——明明锁定了 3 个最可靠的供应商——直接挂了,Olly 也跟着挂了。
祝你好运。