OpenRouterの落とし穴 — 同じモデルでもプロバイダ次第でツール呼び出し精度が23ポイント変わる

はじめに

2026年9月7日、iMessage 上で動く AI アシスタント「Olly」を運営する Mo Moustafa 氏が「So you want to use OpenRouter?」という記事を公開した。Hacker News では580ポイント、160件超のコメントが付いている。Olly はこれまでに1,800万件以上のメッセージを処理しており、その約3分の1を OpenRouter 経由のオープンウェイトモデルが返している。

記事の主張を一言でまとめると「モデル名を指定しても品質は決まらない」になる。同じ重みのモデルでも、配信するプロバイダによってベンチマークが15〜23ポイント変わる。画像を黙って無視するプロバイダがあり、HTTP 200 なのに中身が空の応答も返ってくる。この記事では報告された落とし穴を整理し、差が生まれる理由、OpenRouter 側の対策(Exacto)、利用者がコードで取るべき対策を解説する。

なお当サイトは8月20日の Stripe による OpenRouter 買収の記事で、quantizations(量子化精度での絞り込み)を「品質のブレを抑える」手段として紹介した。今回の実測はこれを否定しているため、訂正も兼ねて扱う。

同じ重みなのにスコアが23ポイント違う

OpenRouter は同じモデルについて、プロバイダごとのベンチマークを公開している。使われているのは GPQA Diamond(大学院レベルの科学問題)と、TAU-Bench Airline(航空会社の窓口業務を模したツール呼び出しタスク)の2つだ。Moustafa 氏が引用した数字は次のとおり。

モデル指標最高最低
DeepSeek V4 FlashGPQA90.2%(DeepSeek 公式)75.3%(DigitalOcean)
DeepSeek V4 FlashTAU81.3%(DeepSeek 公式)58.4%(DigitalOcean)
GLM 5.3 FlashGPQA90.2%(Wafer)50.7%(Sail Research)
GLM 5.3 FlashTAU80.0%70.3%

For an agent TAU is the score that matters and a 20 point swing is not noise. (エージェントにとって重要なのは TAU のスコアで、20ポイントの振れ幅はノイズではない)

ほかにも、次のような現象が報告されている。

  • 画像を正しく読めない: DeepInfra の Qwen 3.5 は、同じ画像の K を R と読み、赤を「青」と答えた。Venice と Together の MiniMax M3 は、画像を送っているのに「画像がない」と返した
  • reasoning.effort の無視: 大半のプロバイダは low / high / max で思考トークンの量が変わる。しかし DigitalOcean・GMI Cloud・Mancer・Venice は、設定に関係なく同じ量だった
  • ツール呼び出しがテキストのまま返る: プロバイダ側のパースが失敗し、<use_skills><parameters>{"skills":["search"]}</parameters></use_skills> のような生のマークアップが本文として返ってくる
  • 200 OK で答えがない: 推論モデルが HTTP 200・finish_reason: "stop" のまま content: null を返す。content も reasoning も null で、usage すら欠けた応答もある
  • 会話履歴のルールが違う: DeepSeek の思考モードで空の reasoning_content を含む履歴を送ると、SiliconFlow はエラー 20015 で拒否する。一方、Baidu・Alibaba・Cloudflare は同じ履歴を受け付ける
  • 本番でだけ起きるレート制限: Venice と Novita は本番インフラの IP にだけレート制限をかけ、手元の PC からは再現しなかった

The contract isn’t per model, it’s per provider. (契約はモデル単位ではなく、プロバイダ単位で結ばれている)

なぜ同じモデルで差が出るのか

OpenRouter は推論を自前では行っていない。リクエストを受けたプロバイダが自社の GPU で、自社で選んだ精度と最適化、自社のツールパーサーを使ってモデルを動かす。入口は OpenAI 互換 API で共通でも、その裏の実装は事業者ごとに異なる。

差の原因は、モデル開発元自身も調べている。Moonshot AI は Kimi K2 の API を提供する事業者の品質を比較する「Kimi Vendor Verifier」を公開している。ツール呼び出しについては、4,000件のリクエストに対する各社の応答を公式 API と比べ、呼ぶべきときに呼んだか(F1)と、JSON スキーマの正確さを測る。Moonshot が挙げた原因は次の5つだ。

  1. デコードパラメータの誤用(Temperature や TopP を推奨値どおりに適用していない)。差の相当部分はこれだった
  2. KV キャッシュのバグ
  3. 量子化による劣化
  4. 画像入力の前処理の違い
  5. ツール呼び出しの扱いの不一致

HN でも同じ論点が挙がった。vLLM や SGLang など推論エンジンごとのバグ、チャットテンプレートの実装差、KV キャッシュの精度などだ。あるコメントは「推論を間違った設定で動かしても動いてしまう。最適でないだけだ」とまとめている。エラーで落ちるのではなく、気づかないうちに少しずつ劣化するのが厄介なところだ。

では量子化精度で絞れば防げるのか。Moustafa 氏の答えは No だった。fp4 のプロバイダは中位で fp8 と重なり、GLM 5.3 Flash の GPQA でトップだった Wafer は、精度を「unknown」と申告していた。成績の悪いプロバイダはどの精度帯にもいる。量子化は劣化の原因の一つだが、申告された精度ラベルからは品質を予測できない。しかも精度で強く絞るほど、障害時のフォールバック先が減る。

Filter on the board, not the bits. (ビット数ではなく、ボード(プロバイダ別の実測値)で絞れ)

OpenRouter 側の対策 — Exacto

OpenRouter もこの問題を把握している。2025年10月には、ツール呼び出しの精度が高いプロバイダだけにリクエストを送る :exacto サフィックスを導入した。2026年3月には「Auto Exacto」として、ツールを含むリクエストでこれをデフォルトで有効にしている。

Auto Exacto は約5分ごとに、スループット、ツール呼び出しのテレメトリ(JSON が正しいか、スキーマに合うか、ツール名が正しいか)、ベンチマーク(TAU-Bench Airline と GPQA Diamond)の3つでプロバイダを評価する。そのうえで「検証済み」「データ不足」「外れ値として降格」の3段階に分ける。OpenRouter の発表によると、GLM-5 のツール呼び出しエラー率は88%、GLM-4.7 は80%下がり、gpt-oss-120b は5.6%から3.5%になった。

HN では OpenRouter の共同創業者(numlocked)が、ライブのエンドポイントで継続的にベンチマークを取り、中央値を監視して悪いプロバイダを外していると説明した。ツール呼び出しを誤ってパースしがちなプロバイダは避けてルーティングしている、とも述べている。一方で「人手がまったく足りていない」とも書いている。

ただし、Auto Exacto の評価項目はツール呼び出しとベンチマークが中心だ。画像入力の扱い、reasoning.effort の反映、空の応答は、評価項目に含まれていない。これらは利用者側で検知するしかない。

エンジニアへの影響 — コードで何をするか

1. 精度で絞らず、実測で除外する。 当サイトの8月20日の記事で紹介した quantizations は、品質の保証にはならない。品質の低いプロバイダを実測で見つけ、ignore で外すほうが確実だ。

{
  "model": "<model-slug>",
  "messages": [{ "role": "user", "content": "..." }],
  "provider": {
    "ignore": ["<実測で品質が低かったプロバイダ>"],
    "allow_fallbacks": true,
    "require_parameters": true
  }
}

require_parameters を true にすると、リクエストに含めたパラメータ(tools など)をすべてサポートするプロバイダだけに送られる。ただし、reasoning.effort を「サポートしている」と申告しながら実際には反映しないプロバイダがあることは、先に見たとおりだ。

2. 1社に固定しすぎない。 Moustafa 氏は provider.order: [cloudflare, baidu, alibaba] に allow_fallbacks: false を組み合わせて固定した。すると2週間後には Baidu が429を返し続け、Cloudflare はそのモデルの提供をやめていた。全トラフィックが Alibaba に集中し、その Alibaba も429を返し始めた。一方で、評価や研究の用途では、プロバイダを固定しないと結果が再現しない。LessWrong には2026年7月に「OpenRouter のプロバイダを固定しないと研究結果が無効になりうる」という記事も出ている。評価では固定して再現性を確保し、本番では固定しすぎずに悪いプロバイダだけを外す、というように目的で使い分けたい。

3. 200 を成功とみなさない。 content も tool_calls もない応答は失敗として扱い、リトライする。本文にツール呼び出しのマークアップが混ざっていたら、クライアント側でパースするフォールバックも用意しておく。

def extract_message(resp: dict) -> dict:
    msg = resp["choices"][0]["message"]
    if not msg.get("content") and not msg.get("tool_calls"):
        # HTTP 200 でも答えがなければ失敗として扱い、リトライさせる
        raise EmptyCompletionError(resp["id"])
    return msg

4. どのプロバイダが答えたかを記録する。 応答の id を使って GET /api/v1/generation?id=... を呼ぶと、provider_name(実際に応答したプロバイダ)と native_tokens_reasoning(プロバイダが報告した思考トークン数)が取れる。これをプロバイダ別に集計すれば、空応答の多い事業者や、effort を無視している事業者を見つけられる。HN には、自前のベンチマークは5ドル未満で回せるので、毎日でも毎時でも実行しているという声もあった。

5. 本番環境から試す。 IP 単位のレート制限は、手元の PC からでは再現しない。

まとめ

  • 同じ重みでも、プロバイダ次第で GPQA は15ポイント、TAU-Bench は23ポイント変わる(DeepSeek V4 Flash の例)
  • 原因はデコードパラメータ、KV キャッシュ、量子化、ツールパーサー、チャットテンプレートといった実装の差。申告された量子化精度からは品質を予測できない
  • OpenRouter の Auto Exacto はツール呼び出しの品質でプロバイダを選ぶが、画像入力・effort・空応答は評価項目に入っていない
  • 対策は「精度でなく実測で除外する」「200 を信用しない」「provider_name を記録する」「1社に固定しすぎない」の4つ

Moonshot AI は「重みが開かれ、配信経路が多様になるほど、品質は制御しにくくなる」と書いている。オープンウェイトモデルを本番で使うなら、選ぶ単位は「モデル」ではなく「モデル × プロバイダ」だと考えたほうがよい。


今日のその他のニュース

トークン削減ツール RTK で、コストはむしろ増えた(Quesma)— コマンド出力を圧縮してエージェントに渡す RTK を、Terminal-Bench 2.1 で2モデル・1,740回試行して検証した。rtk gain は89%削減と表示するが、1タスクあたりのコストは Fable で +1%、DeepSeek で +17% になった。削られた出力を補おうとターンが増え、入力トークンが膨らんだため。

Google Play が「Zero-Tap Sign-In Restoration」を義務化へ(Play Console ヘルプ)— ログイン機能のあるアプリは、ユーザーが新しい端末に移行したとき、ログイン状態を操作なしで復元することが2027年4月から必須になる。推奨 API は Restore Credentials API。2026年9月30日までに Block Store で復元を実装したアプリも準拠とみなされる。

terraform plan の -minimal-refresh で plan が10倍以上速く(Qiita)— Terraform v1.17 のベータ機能。tf ファイルと差分のあるリソースだけをリフレッシュする。Route53 リソース約200個の環境で、plan が45〜50秒から3〜4秒になった。代わりに Terraform の外で行われた変更(ドリフト)は検出されない。

ソース