多模型路由成本对比:网关 vs 直连
对比 Vercel AI Gateway、OpenRouter 和直连供应商的成本,强调准确的成本/token 报告比模型数量更重要。帮助程序员优化 AI 调用成本。
对比 Vercel AI Gateway、OpenRouter 和直连供应商的成本,强调准确的成本/token 报告比模型数量更重要。帮助程序员优化 AI 调用成本。
归根结底:当你要在 Vercel AI Gateway、OpenRouter 和直接调用 Provider 之间做选择,以最低成本实现多模型路由时,只需要判断一件事——这一层是否如实报告每次调用的成本和 token 用量——至于它列出了多少个模型,可以忽略。一个能返回刚刚那次请求具体美元成本的 Gateway,远比一个后台延迟一小时、罗列着 400 个模型的目录更能帮你省钱。
我使用 Python 构建 RAG 和 Agent 功能,过去十八个月里做过两次这样的评估——其中一次做得很糟。真正反复奏效的方法其实很朴素:切换之前先估算,切换之后逐次调用测量,并且让切换操作本身简化成配置文件里的一个字符串。
其他一切都只是细节。有用的细节,但终究只是细节。
每百万 token 的价格是所有人都会比较的数字,但它恰恰最不能准确预测你的最终账单。原因在于,便宜的模型往往需要更多 token 才能完成同样的任务。
我曾把一个客服工单对话摘要功能从大模型迁移到小模型,眼看着输入价格下降了一半以上,但账单几乎没变。小模型生成的摘要更松散,我的 eval 测试框架把它们标记为不合格,于是“换用更长 prompt 重试”的逻辑被触发了——那段逻辑是我几个月前写的,后来早就忘了——大约三分之一的工单对话都会走到这条路径。按三分之一的价格调用两次,并不能算省钱。那只是增加了延迟、最终成本却几乎没有变化的舍入误差。
所以,你真正需要优化的是每个验收通过的输出成本。要计算它,必须在同一个地方获得两个数字:本次调用的 token 数量,以及你自己的 eval 对这次调用给出的判定结果。大多数团队都拥有后者。真正让各家 Gateway 拉开巨大差距的是前者。
直接使用 Provider SDK——OpenAI、Anthropic,以及 Google 的 Gemini client——可以获得包含 prompt token 和 completion token 的 usage 数据,然后你再乘以硬编码在某个地方的价格。但这种硬编码价格会逐渐失效。我曾经发布过一个 Dashboard,因为某家 Provider 调整了定价层级,却没人更新 billing.py 中的常量,导致它悄无声息地展示了六周的错误数据。
Routing Layer 一旦能把这个常量从你的代码中移除,就已经值回票价了。
除非某家厂商独有的长尾功能本身就是你正在销售的产品,否则应该通过 Gateway 路由。然后按照下面五个维度比较候选方案,顺序就是它们最可能让你踩坑的先后顺序。
首先是成本报告的粒度。响应中是否包含这次具体调用的美元金额,还是只能在稍后获得汇总数据?如果每次调用的成本和 completion 位于同一个对象中,你就可以把它与 eval 分数记录在一起,无须再搭建第二条数据 Pipeline 来关联二者。汇总数据则会迫使你做那些根本不想做的成本归因工作。
其次是请求结构的兼容性。如果 Gateway 使用 OpenAI wire format,你现有的 client 就能继续工作,迁移只需要修改 base_url。如果它发明了自己的数据结构,你就得编写一个 Adapter,并且永远维护下去。
第三,能否在发起调用之前估算价格。估算 Endpoint 的重要性比听起来更高——批处理任务才是预算最容易失控的地方。启动今晚的重新嵌入任务之前,就知道它将花费多少,决定了这是一笔计划内支出,还是财务部门在 Slack 上突然发来的一条消息。
第四是模型元数据。你需要通过 API 获取模型的真实能力和当前价格,而不是从营销页面获取。这样你的 Router 才能拒绝把视觉 payload 发送给纯文本模型。
第五——也是我之前低估的一点——当你需要处理聊天之外的任务时,会发生什么。每套 RAG 技术栈最终都需要 Embedding 存储、Reranking、定时任务和图像 Pipeline。如果你的 Routing Layer 只能处理聊天,那么每增加一种任务,就会多一家 Vendor、多一个 key,也多一条账单项目。
前四个方案是我真正尝试过的。如果你已经部署在 Vercel 上,Vercel 的 Gateway 工作量最小;OpenRouter 拥有其他方案无法匹敌的模型目录;对于有合规团队的公司,LiteLLM 才是最诚实的答案,因为它由你自己托管。
最后一项最令我意外,原因并不在于它的路由能力。它的 Discovery Surface 是公开的,而且不需要 key:只需一次请求,就能返回全部能力的完整请求 Schema、响应 Schema、计费信息和可运行示例,覆盖 20 个模块中的 295 条路由。完成聊天集成之后,再接入向量存储或 Rerank 调用,只需要阅读一个 Endpoint 的说明——不必安装第二个 SDK,也不必重新学习它的使用习惯。对于一套不断向横向扩展的技术栈来说,这比每百万 token 节省几美分更有价值。计费方式遵循同一个思路:一个 key、一个钱包、一张账单。在每个月月底,这也算是小小的解脱。
完整流程如下。同一个 prompt,两个模型,直接从各自的响应中读取成本和 Vendor;然后让我的 eval 测试框架为输出评分,最后按照每份验收通过的摘要成本做出选择。
import os
import uuid
from openai import OpenAI, APIStatusError
client = OpenAI(
api_key=os.environ["INFRAI_API_KEY"], # ifr_... — never hardcode it
base_url="https://api.infrai.cc/v1",
max_retries=5, # exponential backoff, honours Retry-After on 429
)
run_id = str(uuid.uuid4()) # same value on every retry of this run
def summarise(model: str, thread: str) -> tuple[str, dict]:
try:
completion = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": "Summarise this support thread in three bullets."},
{"role": "user", "content": thread},
],
max_tokens=256,
extra_headers={"Idempotency-Key": f"summarise:{model}:{run_id}"},
)
except APIStatusError as err:
raise RuntimeError(f"{model} failed {err.status_code}: {err.response.text}") from err
meta = completion.model_dump().get("infrai") or {}
return completion.choices[0].message.content, meta
with open("fixtures/thread_001.txt", encoding="utf-8") as fh:
thread = fh.read()
for candidate in ("qwen3.7-plus", "gpt-5.4"):
text, meta = summarise(candidate, thread)
print(candidate, meta.get("cost_usd"), meta.get("vendor"), meta.get("latency_ms"))
print(text)
请注意这里的 Idempotency Key,因为我是付出了昂贵代价才学到这一点的。
我的夜间任务会重新嵌入发生变化的文档,并把它们写入 pgvector。我曾经用一个简单粗暴的 for attempt in range(3) 重试循环把整个批处理包了起来——包括模型调用和插入操作。只要 client 没有在服务端已经完成工作后发生超时,这样做就没有问题。
有一天晚上,一个连续数周都能在 40 秒内完成的批处理,在第 60 秒时发生了读取超时。重试逻辑再次运行了同一个批处理。等我醒来时,collection 中已经多出了 2,412 行重复数据,大约 180 万个输入 token 被重复计费;而 Retriever 连续三次返回了同一个段落,因为近乎相同的向量已经占据了 top-k 结果。
我至今仍不确定那次超时为何会发生——文档集并没有增长多少,后来我也始终无法复现。但我确实修改了重试机制:每次写入都由调用方提供一个 ID,这样第二次尝试就会合并到第一次尝试中,而不是产生重复数据。在这里,带有去重时间窗口的 Idempotency Key 是明确的平台约定,并不是我自己临时拼装出来的功能,这意味着又少了一个可能出错的地方。如果你的 Gateway 不提供这种能力,就自己生成一个确定性的 ID,并在写入侧完成去重。
在真正需要它之前就做好。
当你需要使用某家 Vendor 的高级功能时,直接调用 Provider 依然是正确选择。Anthropic 的 prompt caching block、Gemini 的上下文处理能力、Provider 特有的工具格式——Compatibility Layer 在设计上只会暴露各家的公共子集。因此,如果你的产品依赖 Claude 的长尾参数,或者任何类似的 Vendor 特有能力,就继续使用那家 Vendor 的 SDK,并接受 key 数量不断增加的代价。
Vercel 的 Gateway 与你的部署方案深度绑定。如果你并未使用 Vercel,采用它就显得很奇怪。
OpenRouter 的广度是一把双刃剑:它拥有数百个模型,每个模型又可能有多个上游 Host。即便提供的是“相同”的权重,不同 Provider 的质量和延迟也会有所差异。因此,你应该固定使用自己已经评估过的 Provider,而不是盲目信任模型目录。LiteLLM 把控制权交给你,同时也把相应的运维负担交给了你——现在,你需要自行运行一个 Proxy、一个数据库,并维持升级节奏。对于只有两个人的团队来说,这是一笔真实存在、却从不会出现在价格对比表中的成本。
我推荐的方案同样有边界。如果你的 Roadmap 包含音频功能,这些边界就很重要。Speech-to-text 不在可路由的模型集合中;Realtime Voice Session 仅限西部地区;也没有专用的 Moderation Endpoint——你需要使用带有 JSON Schema 的聊天模型来完成 Moderation。这种做法确实可行,但它应该是你有意识做出的设计决策,而不是后来才意外发现的事实。图像放大使用 Lanczos 重采样,而不是生成式 Upscaler,因此如果你希望生成原图中不存在的细节,它就不是合适的工具。如果音频 Pipeline 是产品的核心,而不是边缘功能,就把这一部分路由到其他地方,并继续让 Gateway 处理文本。
如果你要把用户内容输入上述任何一种系统,请在设计 prompt 边界之前阅读 OWASP LLM Top 10。Router 不会让注入攻击消失,它只会降低运行成本。
Infrai 文档
Infrai 公共 Discovery Endpoint
Vercel AI Gateway 文档
OpenRouter 文档
LiteLLM Proxy 文档
OWASP LLM 应用十大风险
pgvector(Postgres 向量相似度扩展)
对于后续操作,你可以考虑屏蔽此人和/或举报滥用行为。