OmniRoute:1つの API で多数の AI モデルを扱う
コーディングエージェントのモデルを替えるだけの作業のはずが、退屈で済まない。URL 一つ、モデル名一つ、必要ならクレデンシャルを一枚。実際には、クライアントごとに形式の解釈が違い、プロバイダーごとに独自のフィールドが乗ってきて、挙句の果てに私は、もう技術的な判断をするべきではない時間帯に環境変数を編集している。
そういう不快さにちょうど刺さるから OmniRoute を使っている。ローカルゲートウェイがポート 20128 で動き、ダッシュボードも付いてきて、http://localhost:20128/v1 に OpenAI 互換のエンドポイントを出す。反対側は商用プロバイダーでも、自分のマシンで回している Ollama でもいい。先に断っておくと、私は作り手ではないし、プロジェクトとの関係もない。ただの、向こう側でくたびれているユーザーだ。
アイデアは一文で収まる。1つの API、多数のモデル。面白くなるのは、このレイヤーが本当になくしてくれるものと、場所を移しただけで終わる問題を区別してからだ。
プロキシ、ただし判断付き
使ったことがなければ、特化型のプロキシと考えるのが早い。エージェントは OpenAI や Anthropic、Google を直接は呼ばない。OmniRoute を呼ぶ。OmniRoute がリクエストを受け取り、宛先を決め、必要ならプロトコルを合わせて、正規化した応答を返す。
コーディングエージェント
↓ OpenAI 互換
http://localhost:20128/v1
↓ 選択と変換
OpenAI | Anthropic | Google | Ollama | その他もともと至る所に散らばっていた判断が、一か所に集まる。複数のクライアントに鍵と URL とフォールバックの規則をばら撒く代わりに、その論理の全部が、開いて読めるローカルのプロセスの中に住むことになる。
ドキュメントどおりのインストールは短い。
npm install -g omniroute
omnirouteダッシュボードは http://localhost:20128。その日動いていたバージョンは 3.8.48。それから、夜 11 時のエラーではなくドキュメントで見つけておきたかった細部がひとつ。パッケージは Node >=22.22.2 <23 || >=24 <27 を要求し、Node 23 は意図的に範囲外だ。環境がたまたまそれに当てはまるなら、インストールの前に確認したい。
プロジェクトは MIT で、セルフホスト可能。diegosouzapw/OmniRoute から出ていて、npm パッケージ omniroute としても公開されている。Docker とソースからのインストールもある。試すだけなら npm のほうが手間が少ない。もっと筋の良い運用をしたいなら、コンテナのほうがプロセスと設定とシークレットの境界を明確にしてくれる。
その間で起きていること
プロキシという言葉では少し小さい。プロバイダーに触れる前に、障害耐性のレイヤーがある。不調な経路を切り離す circuit breaker と、上流が 429 を返したときに Retry-After を守る、鍵ごとの cooldown だ。エージェント相手にはこれが効く。リクエストを大量に撃つし、普通のプロキシはエラーをそのまま投げ返して「あとは頑張って」で終わる。こちらは、プロバイダーが指定しただけ待って、別の経路を試す。魔法ではない。判断付きのリトライだ。とはいえ、運用する部品がひとつ増えるのも事実で、ゲートウェイが落ちれば、プロバイダーが健全でも全部がアクセスを失う。
クライアント側に必要なのは、ベース URL とローカルの鍵だけだ。
export OPENAI_BASE_URL=http://localhost:20128/v1
export OPENAI_API_KEY=omniroute-local-keyあとはモデルを明示するか、自動ルートを頼むか。auto 系のルートは意図を表す。auto/coding、auto/cheap、auto/offline。この別名の良いところは、設定の側からプロバイダー名が消えることだ。auto/offline を頼めば、可能なかぎり仕事は自分のマシンに留まる。明日プロバイダーを替えても、7つの設定ではなく、別名を 1 回直せば済む。
条件がひとつ。auto/coding の 2 回の実行は、違うモデルに着地することがある。作業を終えるぶんには構わない。評価を再現するぶんには困る。結果を比べるときや回帰を追っているとき、私はプロバイダーとモデルを手で固定する。フォールバックにも同じだけの疑いをかけるべきで、流れの途中でモデルが変われば、tool calling もコストも変わり得る。サーキットは、閉まった扉を叩き続けずに済むようにするだけ。隣の扉が同じ用途に使えるかまでは面倒を見てくれない。
揺れないインターフェイスの下で揺れるプロトコル
これが一番価値を置いている効用だ。クライアントは 1 つか 2 つの方言しかしゃべらない。OpenAI 互換のインターフェイスが結合を弱めて、それぞれの上流のために設計されたことなど一度もないツール同士をつないでくれる。反対側では、OpenAI Chat Completions、Responses、Anthropic Messages、Gemini をしゃべる。多く聞こえるし、大部分は本当だ。それでも「互換」を「同一」とは読まない。正規化された応答は、共通のモデルが表現できるものしか残せないし、元の形式はストリーミング、ツール、見える reasoning のあたりで互いに違う。
だから私は、文字が返ってくる curl ではなく、経路全体を試す。イベントが途切れないストリーミングと、壊れていないツール引数。「チャットが応答した」では、ファイルを編集しコマンドを実行する相手への検証として薄すぎる。
プロバイダー側には、API キー、PKCE 付きの OAuth 2.0、キー不要のサービスに対応する。Ollama は一等市民として扱われ、LM Studio と vLLM もドキュメントにいる。一番使うのはローカルのケースで、同じベース URL のまま、いくつかのタスクを Ollama に流す。これで auto/offline が建前ではなく実際の方針になる。ドキュメントがクライアントとして挙げているのは、Claude Code、Codex CLI、Cursor、OpenCode、Cline、Continue、Aider。ほぼそのまま、私の毎日のメニューだ。
ただし、と前置きしておく。この全部がデフォルトでプライベートになるわけではない。プライバシーは、ポリシーがどの経路を選ぶかと、ログとクレデンシャルの設定次第で、ルーターをセルフホストしたところでそれ自体は何も閉じ込めない。お金も同じで、OmniRoute 自体は $0、カード登録も不要だが、コストも制限も規約も各上流の持ち物だ。自動ルートがキー不要のプロバイダーから課金される先へ飛んだら、明細を見て知るのではなく、その前に知りたい。
カタログの数字が合わない。もう笑えてくる
公式の情報源が 3つ。同じ日に見て、在庫が 3つ違う。サイトは 339 プロバイダー、56 の free-forever、95 の MCP 統合、25k tests。README は 351 プロバイダー、56 の free-forever、110 の MCP 統合、35 の CLI エージェント、39k tests。npm パッケージは 352。
もう合わせようとすらしていない。カタログは速く動くし、数字のいくつかは、別々の時点で更新された宣伝文に見える。ただ、これ自体はプロジェクトについてのデータだ。自分が何プロバイダー抱えているかで自分の内側が合意できないなら、私はその数字をどこにも引用しない。
判断材料としては、もっと控えめな 3つの問いで足りる。必要なプロバイダーはあるか。エージェントが使う機能は動くか。このバージョンを、シークレットを晒さずに運用できるか。実際の経路に対する 1 回のテストのほうが、正しい在庫が 339 か 351 か 352 かの議論より重い。大きな数字の老け方は荒い。
今のところの結論。退屈であるべき仕事の一部を、退屈に戻してくれるから使い続けている。透明な抽象ではない。私のシークレットに触れるインフラなので、バージョンを固定し、実際に使う tool call を試してから、機微なものを任せた。「1つの API、多数のモデル」は、何を隠しているかを忘れなければ良い頭の中のインターフェイスだ。契約の違い、他人のクォータ、ゲートウェイが並べ替えることはできても等価にはできない振る舞い。今のところは役に立っている。エコシステムが落ち着けば不要になるかもしれないし、分裂し続ければもっと必要になる。どちらかを急いで宣言する気はない。