OmniRoute:一个 API,多个模型
给编码智能体换模型,本来应该是个无聊的操作。一个 URL、一个模型名,顶多再换一个凭据。实际呢,每个客户端对格式的理解都不一样,每个 provider 又要塞自己的字段,最后是我在一个早就不该做技术决定的时间点改环境变量。
我用 OmniRoute,就是因为它正面解决这个麻烦。它在本地 20128 端口起一个网关,自带 dashboard,对外暴露一个 OpenAI 兼容的端点 http://localhost:20128/v1。另一头既可以接商业 provider,也可以接我自己机器上跑的 Ollama。先把话说在前面:这不是我做的,我和项目没有任何关系。我只是对面那个用累了的用户。
核心想法一句话就能装下:一个 API,多个模型。有意思的部分从下一个问题开始:这层到底简化了什么,又有哪些问题只是换了个地方待着。
一个有判断力的代理
如果你没用过这类东西,就把它想成一个特化的代理。你的智能体不直接调 OpenAI、Anthropic 或 Google,它调 OmniRoute。OmniRoute 收下请求,决定去向,必要时改写协议,再把归一化后的响应还回来。
编码智能体
↓ OpenAI 兼容
http://localhost:20128/v1
↓ 选择与适配
OpenAI | Anthropic | Google | Ollama | 其他这等于把一个原来撒得到处都是的决策收拢到一个进程里。与其把密钥、URL 和回退规则摊给好几个客户端,不如让这套逻辑住在一个我打得开、读得懂的本地进程里。
文档给出的安装很短:
npm install -g omniroute
omniroutedashboard 在 http://localhost:20128。那天跑的版本是 3.8.48。还有一个我宁愿在文档里、而不是在晚上十一点的报错里发现的细节:包要求 Node >=22.22.2 <23 || >=24 <27,Node 23 是被故意排除在外的。如果你的环境正好卡在不兼容的那一档,装之前先看这里。
项目是 MIT 的,可以自托管,仓库在 diegosouzapw/OmniRoute,同时以 npm 包 omniroute 发布。也有 Docker 和源码安装。只是试水,npm 步骤最少;要更有纪律地跑,容器能给进程、配置和密钥画一条更清楚的边界。
中间发生了什么
「代理」这个词其实说小了。请求碰到 provider 之前,先过几层容错:circuit breaker 把病态的线路摘出去,按 key 的 cooldown 在上游回 429 时尊重 Retry-After。对智能体这很关键:它们请求打得很密,普通代理把错误原样丢回来就完事,这个会按 provider 要求的时间等一等,再换一条路试。不是魔法,是带着判断的重试。不过这也是多出来一个要运维的部件:网关一倒,哪怕所有 provider 都健康,全部客户端一起断粮。
客户端这边只需要一个 base URL 和一个本地 key:
export OPENAI_BASE_URL=http://localhost:20128/v1
export OPENAI_API_KEY=omniroute-local-key然后要么点名模型,要么用自动路由。auto 系列表达的是意图:auto/coding、auto/cheap、auto/offline。这个别名最大的好处,是我的配置从此不用再写死 provider:点一个 auto/offline,能留在本机的活就留在本机。明天换 provider,改一处别名,不用改七份配置。
但有个条件:两次 auto/coding 可能落到两个不同的模型上。把活干完,没问题;复现一次评测,不行。我在对比结果或者排查回归时会手动钉死 provider 和模型。对 fallback 也得留同样的戒心:流程中途换模型,tool calling 和成本都可能变。熔断机制保证你不至于一直敲一扇关着的门,但它判断不了旁边那扇门是不是干同一件事的。
稳定的接口,架在不稳定的协议上
这是我最看重的一点。客户端只会说一两种方言,一个 OpenAI 兼容的接口降低了耦合,还能把从来没为某个上游设计过的工具接起来。另一头它会说 OpenAI Chat Completions、Responses、Anthropic Messages 和 Gemini。听着很多,大部分也是真的。但我不会把「兼容」读成「等同」:归一化的响应只能保留公共模型能表达的东西,而这些格式在流式、工具调用、可见推理上彼此都不一样。
所以我要试整条链路,而不是 curl 一下看到出字就算数:流式事件不能断,工具参数必须合法。「聊天回复了」对一个要去改文件、执行命令的东西来说,是个太廉价的验收。
朝 provider 那一侧,支持 API key、带 PKCE 的 OAuth 2.0,还有免密钥的服务。Ollama 是一等公民,LM Studio 和 vLLM 也写在文档里。我用得最多的就是本地这条路:同一个 base URL,一部分任务分流给 Ollama,auto/offline 这才从口号变成真的。文档列出的客户端有 Claude Code、Codex CLI、Cursor、OpenCode、Cline、Continue 和 Aider,差不多正好是我每天的那张菜单。
丑话说在前:这些都不会默认把一切变私密。隐私取决于策略选了哪条路、日志和凭据怎么配;把路由器自托管不等于把任何东西关进笼子。钱也一样:OmniRoute 本身 $0、不用绑卡,但成本、额度和条款都是各家上游的。要是自动路由从免密钥的 provider 跳到一个要计费的,我想提前知道,而不是对着账单才发现。
目录里的数字对不上,到这个份上我已经想笑了
三个官方口径,同一天,三份不一样的库存。站点说 339 个 provider、56 个永久免费、95 个 MCP 集成、2.5 万个测试;README 说 351 个 provider、56 个永久免费、110 个 MCP 集成、35 个 CLI agent、3.9 万个测试;npm 包说 352。
我已经懒得对账了。目录变得快,有些数字看着就像在不同时间点各自更新过的宣传文案。但这确实是个信号:一个项目要是连自己有几个 provider 都对不齐,我是不会拿这个数字去引用的。
真要做决定,我留三个更朴素的问题:我要的 provider 在不在?我的智能体用的功能灵不灵?这个版本我能不能在不暴露密钥的前提下运维?拿我真实链路跑一次,比争论库存到底是 339、351 还是 352 值钱。大数字过期得都很难看。
我目前的结论:继续用,因为它把一件本来就该无聊的工作变回了无聊。它不是一层透明的抽象:它是一个握着我密钥的基础设施,所以我钉住了版本,把自己真正会用的 tool call 都试过一遍,才敢把敏感的东西交给它。「一个 API,多个模型」是个好用的心智接口,前提是你记得它盖住了什么:不一样的合约、别人家的额度,还有那些网关只能排序、没法拉平的行为。眼下它对我有用。生态要是收敛,它可能变得没必要;生态要是继续碎下去,它会越来越必要。两个方向我都不急着下结论。