Claude Media
LLMゲートウェイのモデルディスカバリはどう動くか

LLMゲートウェイのモデルディスカバリはどう動くか

Claude CodeがゲートウェイのAPIを問い合わせてモデル一覧を取得する仕組みを、実行タイミング・リクエスト形式・ピッカーへの反映・キャッシュまで解説します。

モデルディスカバリは既定で無効になっている

モデルディスカバリは、Claude Codeが起動時にゲートウェイの/v1/modelsエンドポイントへ問い合わせ、返ってきたモデルを/modelピッカーに追加する機能です。共有のAPIキーを使うゲートウェイでは、鍵がアクセスできる全モデルを全員に見せたくない場面があるため、既定では無効です。

ゲートウェイが送受信するリクエスト全体の仕様はClaude Code LLM gatewayのプロトコルで扱い、本記事はモデルディスカバリ単体の挙動とデバッグに絞ります。

有効にするには、開発者自身の環境かmanaged settings経由でCLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1を設定します。

export CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1

対象はAnthropic Messages形式を話すゲートウェイだけに限られます。ANTHROPIC_BASE_URLがゲートウェイを指していても、形式が違えば動きません。

検証中の内部モデルや契約上まだ公開したくないモデルまでキーの権限に含まれていると、ディスカバリを無条件でオンにした瞬間に、その存在が全開発者の/modelピッカーに漏れます。オプトイン制になっているのは、この漏れを開発者・運用者どちらの判断でも止められる状態を既定にするためです。実際には開発者が自分の環境変数で有効化することもでき、組織として一律に許可するかどうかはmanaged settings側の判断に委ねられています。

ディスカバリが動かない3つの条件

ディスカバリはAnthropic Messages形式にしか対応しないため、次のいずれかに当てはまると実行されません。

条件理由
CLAUDE_CODE_USE_*系の変数が設定されている理由ANTHROPIC_BASE_URLと併用していても、別プロバイダー形式が優先される
ANTHROPIC_BASE_URLが未設定、またはapi.anthropic.comを指している理由問い合わせ先のゲートウェイが存在しない
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFICが有効理由組織ポリシーで非必須通信を止めている場合を含む

形式が違うと動かないのは、ディスカバリが問い合わせるGET /v1/modelsというエンドポイント自体が、Anthropic Messages形式のゲートウェイにしか存在しないためです。CLAUDE_CODE_USE_BEDROCK=1ANTHROPIC_BEDROCK_BASE_URLを指す構成では、Claude Codeは推論を/model/{model}/invokeという別形式のエンドポイントに送ります。CLAUDE_CODE_USE_VERTEX=1ANTHROPIC_VERTEX_BASE_URLを指すGoogle CloudのAgent Platform(旧Vertex AI)形式も、:rawPredictというサフィックス付きのエンドポイントを使う点は同じです。どちらの形式にも/v1/modelsという問い合わせ先自体が存在しないため、ディスカバリは失敗するのではなく、そもそも聞きに行く相手がありません。自社ゲートウェイをこれらの形式でAnthropic互換に見せている場合も、/v1/modelsを独自に実装しない限りディスカバリの対象にはなりません。

いずれかの条件に当てはまるかどうかは、claude --debugのログで直接確認できます。認証情報が解決できずスキップされた場合も、デバッグログの[gatewayDiscovery] skippedという行に記録されます。

ディスカバリの成否をデバッグログで確認する

claude --debugで起動すると、セッションごとのデバッグログ(~/.claude/debug/<セッションID>.txt)に[gatewayDiscovery]という接頭辞の行が残ります。Claude Codeは初回のディスカバリ成功時にキャッシュしたモデル数を記録し、その後はゲートウェイ側のモデル一覧に変化があったときだけ再度記録します。何も変わらない起動のたびに同じログが延々と増え続けることはありません。

失敗した場合も同じ接頭辞で記録されるため、404・タイムアウト・リダイレクトのどれで失敗したかをログから切り分けられます。ゲートウェイ側の設定ミスなのか、途中経路のネットワークの問題なのかを、運用チームに問い合わせる前に開発者の手元だけで判断できるのは実務上の助けになります。

リクエストは3秒でタイムアウトし、リダイレクトは失敗として扱う

リクエストはGET /v1/models?limit=1000で、タイムアウトは3秒です。リダイレクトは無条件で失敗扱いになります。認証ヘッダーがリダイレクト先のホストに漏れる経路を作らないための設計です。応答が遅いゲートウェイや、httpからhttpsへのリダイレクトを挟むだけのゲートウェイでも、この失敗は静かに起きます。/v1/modelsは設定したベースURLで直接応答するように実装しておく必要があります。

認証ヘッダーは次の2種類を状況に応じて送ります。両方を送るにはClaude Code v2.1.248以降が必要で、それより前のバージョンはANTHROPIC_AUTH_TOKENがあればAuthorizationのみ、なければx-api-keyのみを送っていました。

  • Authorization: ANTHROPIC_AUTH_TOKENをベアラートークンとして、無ければapiKeyHelperの戻り値をベアラートークンとして使う
  • x-api-key: ANTHROPIC_API_KEYなど解決済みのAPIキー。ヘルパーの値しか無い場合はここにも同じ値が入る

ANTHROPIC_CUSTOM_HEADERSで追加のヘッダーを設定している環境では、そのヘッダーもディスカバリのリクエストに乗ります。プロキシ認証やルーティング用のヘッダーを通常のAPIリクエストに足しているゲートウェイでも、ディスカバリだけ素通しにする必要はありません。

apiKeyHelperしか資格情報が無い場合、Claude Codeはヘルパーの戻り値を待ってからリクエストを送ります。ヘルパーがVaultや社内APIを呼びに行くタイプで応答が遅いと、リクエスト送信そのものが遅れます。ヘルパーの応答時間はCLAUDE_CODE_API_KEY_HELPER_TTL_MSのキャッシュ期間とは別物なので、ヘルパー自体の速さは別途確保しておく必要があります。

レスポンスのidをどうフィルターするか

ゲートウェイはdata配列でidと任意のdisplay_nameを返します。

{
  "data": [
    { "id": "claude-sonnet-4-6", "display_name": "Claude Sonnet 4.6" },
    { "id": "claude-opus-4-8" }
  ]
}

Claude Codeはidclaudeまたはanthropicという文字列を含むエントリだけを残し、大文字小文字は区別しません。vertex_ai/claude-sonnet-4-6bedrock/anthropic.claude-sonnet-4-5のようなプロバイダー接頭辞付きのIDもこのフィルターを通ります。v2.1.223より前はidclaudeまたはanthropicで始まる場合しか残さなかったため、プロバイダー接頭辞付きのIDが軒並み隠れていました。

具体的に4つのIDで考えると分かりやすくなります。claude-opus-4-8bedrock/anthropic.claude-sonnet-4-5はどちらも文字列のどこかにclaudeまたはanthropicを含むため残ります。一方gpt-4oは含まないため除外され、internal-model-7のような社内命名のIDも同じ理由で除外されます。フィルターは先頭一致ではなく部分一致なので、接頭辞の付け方に厳密なルールを設ける必要はなく、us-west.claude-sonnet-4-6のようなリージョン接頭辞やclaude-sonnet-4-6-previewのようなバージョン接尾辞を足してもclaudeが残っていればそのまま通ります。運用中のゲートウェイで既に配っている独自命名のIDであっても、末尾に-claudeを後付けするだけでディスカバリの対象に切り替えられ、命名規則を作り直す必要はありません。

ピッカーでの重複統合とキャッシュの挙動

/modelを実行したときのピッカーで、発見されたモデルは「From gateway」というラベル付きの行になり、display_nameがあればそれを表示名に使います。

状況ピッカー上の扱い
発見IDが既存の行と完全一致ピッカー上の扱いスキップ(重複させない)
発見IDと既存IDが同じモデル世代(Fable)の別表記ピッカー上の扱いスキップ
発見した明示IDが、エイリアスが指す先と同じモデルピッカー上の扱いエイリアス行に統合される(例: claude-sonnet-5sonnet行に畳まれる)
発見した明示IDが、エイリアスの指す先と違うモデルピッカー上の扱いエイリアス行とは別に「From gateway」行を追加(例: claude-sonnet-4-6)

エイリアス行への統合はv2.1.197から入った挙動で、それより前はclaude-sonnet-5のような発見IDもsonnet行とは別に単独の行を作っていました。管理者がreplaceBuiltInOptionsmodelPickerの設定に入れている場合、発見されたモデルもピッカーから隠れます。ただし例外が1つあり、replaceBuiltInOptionsが有効でも、そのセッションが現在使っているモデルの行だけはピッカーから消えません。切り替え中に自分がいま何を使っているか見失わないための配慮です。

managed settingsのavailableModelsでモデルを制限している場合は、この統合より手前で効きます。availableModelsに無いモデルは、ゲートウェイがディスカバリで返してもピッカーには追加されません。ディスカバリの公開範囲は、あくまでavailableModelsが許可した範囲の内側に収まります。

結果は~/.claude/cache/gateway-models.json(Windowsは%USERPROFILE%\.claude\cache\gateway-models.json)にキャッシュされ、起動のたびに更新されます。CLAUDE_CONFIG_DIRを設定している環境では、キャッシュもそのディレクトリ配下に置かれるため、複数の設定ディレクトリを使い分けているマシンでもキャッシュが混ざりません。リクエストが失敗した場合や、ゲートウェイが/v1/modelsを実装していない場合は、前回起動時のキャッシュか組み込みのモデル一覧にフォールバックします。

Claude apps gatewayでは/v1/modelsを誰が作るか

Claude apps gatewayを使っている場合、この/v1/modelsgateway.yamlmodelsブロックから作られます。運用者がモデルIDをキュレーションしてupstream_modelごとにAnthropic API・Bedrock・Foundryの実際のIDへ変換する仕組みで、設定リファレンスで詳しく扱っています。

auto_include_builtin_modelsが既定のtrueのままなら、キュレーションしたモデルに加えて組み込みモデルもディスカバリの対象になります。falseに切り替えると、modelsブロックに書いた分だけが公開される仕組みです。米国外リージョンのAmazon Bedrock、プロビジョンドスループットのARN、Microsoft Foundryのデプロイ名を使っている場合は、実際のIDと表向きのIDが一致しないため、このブロックでの変換が必須になります。ディスカバリが返すのは、あくまでこの変換後の表向きのIDであって、Bedrockの実際のARNやFoundryの内部デプロイ名がそのままクライアントに見えることはありません。

手動登録との使い分け

モデルをピッカーに追加する方法は、ディスカバリだけではありません。モデル設定の環境変数で個別に手動登録する方法と、どちらを選ぶかは運用の性質で決まります。

観点環境変数での手動登録モデルディスカバリ
反映のタイミング環境変数での手動登録設定を配布し直すまで変わらないモデルディスカバリゲートウェイの/v1/modelsが変わるたびに起動時反映
公開範囲の性質環境変数での手動登録登録した分だけが見える(明示的)モデルディスカバリ共有キーが到達できる範囲がフィルター後にまとめて見える
独自エイリアス名環境変数での手動登録そのまま登録できるモデルディスカバリclaude/anthropicを含まないIDは拾えない
向いている運用環境変数での手動登録モデルの入れ替えが少ない、公開範囲を厳密に絞りたい組織モデルディスカバリ複数モデルの増減が頻繁で、都度の配布作業を省きたい組織

自社ゲートウェイがClaudeモデルを独自のエイリアス名で配信していて、そのエイリアスがフィルターの条件に当てはまらない場合は、ディスカバリを有効にするだけでは解決しません。手動登録に切り替えるほうが確実です。

組み込みのモデル一覧だけで運用がすでに回っているなら、そもそもディスカバリを有効にする理由がありません。管理者がmanaged settings経由ですでに有効化しているケースを除けば、既定の無効のままにしておいても支障は出ません。

まとめ

モデルディスカバリは、Anthropic Messages形式のゲートウェイに限定した、明示的なオプトイン機能です。3秒のタイムアウトとリダイレクト即失敗という厳しめの実装は、認証ヘッダーの漏洩経路を作らないための設計です。フィルターとキャッシュの挙動はバージョンごとに変わってきたため、古いClaude Codeのままだとプロバイダー接頭辞付きのIDが拾えない、といった見え方の違いが起こります。

導入するかどうかを決める基準はシンプルです。組み込みモデルだけで足りるなら既定のオフのままにし、Claude apps gatewayのmodelsブロックや自社ゲートウェイの/v1/modelsでモデルを頻繁に入れ替えるなら、フィルターの制約を踏まえたうえでオンにする価値があります。ゲートウェイ全体の選び方はLLM gatewayとは、ロールアウトの手順は管理者向けロールアウト手順で確認できます。

この記事を共有:XはてブLinkedIn