プロバイダー検出
FIM Oneはユニバーサルアダプターとして LiteLLM を使用します。core/model/openai_compatible.py の _resolve_litellm_model() 関数は、ユーザーの LLM_BASE_URL + LLM_MODEL を、プロバイダープレフィックス付きの LiteLLM モデル識別子にマッピングします。プレフィックスは、LiteLLM がリクエストをどのようにルーティングするかを決定します — ネイティブ API プロトコル(Anthropic Messages API、Gemini など)またはジェネリック OpenAI 互換の /v1/chat/completions。
解決順序:
- 明示的なプロバイダー(DB の
ModelConfig.providerフィールドから)— 最優先。プロバイダーが URL 内の既知ドメインと一致する場合、api_baseは返されません(LiteLLM はネイティブでルーティング)。それ以外の場合、api_baseはリレー URL に設定されます。 KNOWN_DOMAINSに対するドメイン一致 — 公式 API エンドポイントはホスト名で認識されます。PATH_PROVIDER_HINTSに対する URL パスヒント — UniAPI のようなリレープラットフォームで一般的です。パスに/claudeまたは/anthropicが含まれている場合、アップストリームプロトコルを示します。- フォールバック —
openai/プレフィックス(ジェネリック OpenAI 互換)。
プロバイダープレフィックスがネイティブプロトコル(anthropic、gemini など)で、URL が公式エンドポイントでない場合、LiteLLM はネイティブプロトコルを使用しますが、リレーの
api_base にリクエストを送信します。これは、プロバイダー固有の動作(以下で説明する Bedrock プリフィル問題を含む)がリクエストが公式 API に送信されるか、リレー経由で送信されるかに関わらず適用されることを意味します。
tool_choice — 4つのモード
tool_choice パラメータは OpenAI 形式で標準化されています。LiteLLM はリクエストを送信する前に、各プロバイダーのネイティブプロトコルに変換します。
"auto" と強制モード({"type":"function",...})の区別は、FIM One のあらゆる互換性問題の核心です。これら 2 つのモードは、異なる要件を持つまったく異なるサブシステムで使用されています。
tool_choiceが使用される場所
2つのサブシステムがtool_choiceを使用しており、それらは根本的に異なる方法でそれを使用しています。
ReAct エンジン — tool_choice=“auto”
ReAct ループでは、モデルが各イテレーションで以下を決定する必要があります: ツールを呼び出すか、最終的な回答を提供するか。ここで意味があるのは"auto" だけです — モデルは tool_calls を生成するか、テキスト コンテンツを生成するかを自由に選択します。これはすべてのプロバイダー、すべてのモデル、拡張思考を含むすべてのモードと互換性があります。
ReAct エンジンは、abilities["tool_call"] = True の場合にネイティブ関数呼び出し (_run_native) を使用し、それ以外の場合は JSON-in-content モード (_run_json) にフォールバックします。両方のモードで "auto" を使用します — 違いは、ツールが tools パラメータを介して渡されるか、システム プロンプトで説明されるかです。詳細は ReAct エンジン — デュアルモード実行 を参照してください。
structured_llm_call — tool_choice=forced
One-shot の構造化抽出(スキーマアノテーション、DAG 計画、計画分析)。モデルに特定の仮想関数の呼び出しを強制し、構造化された JSON 出力を保証します。これはプロバイダー固有のエラーを引き起こす呼び出し箇所です。structured_llm_call は、3 段階の縮退チェーンを実装します。3 つの段階はコードにちなんで命名されており、以下のセクションで説明する 4 つの保証レベルとは番号が一致しません。
重要な設計上の違いは、structured_llm_call のフォールバックが ランタイム で行われる点です。各レベルを動的に試行し、例外を捕捉して次のレベルへ移行します。一方、ReAct エンジンのモード選択は ビルド時 に行われます。開始時に _native_mode_active を一度確認し、ループ全体を通じて 1 つのモードを使用します。つまり、structured_llm_call はプロバイダー固有の 400 エラーから透過的に復旧できますが、ReAct はあらかじめモードが正しく選択されていることを前提とします。
構造化出力:4つの保証レベル
「構造化出力」は、4つの異なる仕組みを包括する表現です。いずれも JSON を生成しますが、同じ保証を提供するわけではありません。違いを分けるのは制約が存在する場所です。デコーダー内にある場合は、スキーマで禁止された token を出力できません。一方、prompt 内にある場合は、要求することしかできません。
T1 と T2 の背後にある仕組みは、制約付きデコーディングです。プロバイダーはスキーマを文法にコンパイルし、各デコーディングステップで、その出力をスキーマに照らして解析不能にするすべての token をマスクします。必須フィールドを省略することはできません。そのフィールドが出力されるまで、オブジェクトを閉じる token は許可された集合に含まれないためです。これは「モデルに丁寧に頼んだら従った」という主張とは異なる種類のものです。そのため、prompt への従属性が期待できない temperature 1 や小規模モデルでも、T1/T2 は保証を維持できます。
T1とT2は互換性がありません
両者は異なるチャネルを通じて同じ保証を提供しますが、そのチャネルの違いは3つの点で重要です。 ターンの形状が異なります。 T1はツール呼び出しターンを返します。つまり、モデルは実行することを決定しています。T2は通常のアシスタントメッセージを返します。つまり、モデルはある形で回答しています。呼び出し元が必要としているのが1回の抽出であり、エージェントループではない場合、T2はモデルが「呼び出す」ための仮想関数を作り出すことなく、その意図を直接示します。 T1の強制は、思考と衝突します。 T1からスキーマで制約されたペイロードを取得するには、通常、required または名前付き関数のいずれかによって選択を強制する必要があります。拡張思考が有効な場合、強制されたツール選択を拒否するプロバイダーがいくつかあります。どのプロバイダーが該当するかは、下記の表Bに記載されています。T2にはこのような競合がありません。T2はツールの制約ではなくレスポンスの制約であるため、思考を有効にした状態でも利用できる唯一のスキーマ制約付きパスです。推論モデルを標準とするデプロイメントにとって、これはスタイル上の選択ではなく、T2を選ぶ実務的な理由になります。
T1では、T2にはないスキーマ連携のラウンドが必要です。 仮想関数には、名前、説明、そしてモデルがその関数の呼び出しを拒否できるようにするかどうかの判断が必要です。
T2 のコスト
制約付きデコーディングは無料ではなく、すでに用意しているスキーマが、そのまま受け入れられるスキーマであるとは限りません。- スキーマのサブセット。 OpenAI の strict mode では、すべてのオブジェクトに
additionalProperties: falseを指定し、すべてのプロパティをrequiredに列挙する必要があります。オプショナル性はrequiredから省略するのではなく、nullとのユニオンとして表現します。ルートはオブジェクトでなければなりません。プロパティの総数とネストの深さには上限があります。手書きのスキーマの多くは、利用可能にする前に編集が必要です。 - 文法のコンパイル。 新しいスキーマを含む最初のリクエストでは、プロバイダー側で一度だけコンパイルレイテンシが発生します。安定したスキーマを再利用すればそのコストを分散できますが、リクエストごとに新しいスキーマを生成すると分散できません。
- 新たな失敗パターン。 モデルが回答しない場合、スキーマに沿ったオブジェクトではなく拒否を返すため、呼び出し側でそのための分岐が必要になります。
どのティアでも保証されないこと
すべてのティアは形式を制約しますが、真実性を制約するものはありません。T2 のレスポンスはスキーマに完全に準拠していても、事実としては誤っている可能性があります。また、列挙型で制約されたフィールドは、正しい値がどれも存在しない場合でも、許可された値のいずれかを返します。これは、デコーダーの役割が答えを知ることではなく、出力を文法の範囲内に収めることだからです。スキーマによるゲーティングによって、解析エラーやフィールド形式の不具合は解消されます。しかし、値の内容を確認する必要がなくなるわけではありません。FIM Oneの位置付け
ここから、現在の設計における正直な制約として、5つの帰結が導かれます。
- チェーンには、スキーマによって制約されたフォールバック段階がない。 Level 1 が失敗した場合、次の段階は
json_objectであり、保証されるのはテキストをパースできることだけです。フィールドを引き続き強制する中間段階はありません。 - Level 1 の保証は、その名前から受ける印象よりも弱い。
strictなしのネイティブ function calling は、2024年以前の非 strict 形式です。モデルは通常スキーマに従いますが、必須フィールドの省略、存在しないキーの追加、数値として宣言された値の文字列での返却を防ぐことはできません。 anthropic/ルートでは、Level 2 は実質的には T3 ではない。 Anthropic Messages API にはresponse_formatがないため、LiteLLM は assistant prefill を注入して JSON mode をエミュレートします。これはプロンプト層の仕組みなので、実効的な保証は T4 と T3 の間に位置し、T3 と同等ではありません。以下で説明する Bedrock の prefill trap は、このエミュレーションが明示的に失敗するケースです。より見えにくい問題は、パラメーター名が示唆するような保証が、そもそも存在していなかったことです。- 結果が後からスキーマに対して検証されることはない。
jsonschemaは依存関係に含まれていません。行われる検証はすべてオプションのparse_fn内、呼び出し箇所ごとのものであり、その厳密さは呼び出し箇所によって異なります。 - 失敗は表面化せず、吸収される。 ほぼすべての呼び出し元が
default_valueを渡すため、チェーンを使い切っても例外を送出せず、一見もっともらしいオブジェクトを返します。StructuredCallResult.level_usedにはどの段階で値が生成されたかが記録されますが、これを読み取る呼び出し箇所はありません。また、default_valueの経路では、実際には何も生成されていないチェーンについてもplain_textと報告されます。
structured_llm_call は、完了した各呼び出しについて INFO レベルで1行をログに記録します。このログはデータが parse_fn を通過した後にのみ出力されるため、実際に成功した段階が示されます。
WARNING レベルで level=none outcome=default_value として記録されます。この2行を1日分のトラフィックに対して grep することだけが、デプロイ環境において T2 の欠落がコストを生んでいるかどうかを知る方法です。default_value の設計により、症状はエラーではなく、質の低い回答として現れるためです。
各ティアのプロバイダーサポート
この表が記載するのは、FIM One のコードパスではなくアップストリーム API です。 このページにある他の表では、すべて動作を実装する関数を示しています。この表でそれができないのは、FIM One がどのプロバイダーの T2 機能も使用せず、いずれにも
strict を設定していないためです。ここでは、T2 を実装する判断を、実際に利用可能な機能に基づいて開始できるようにしています。ベンダーの機能は急速に変化しており、あるファミリー内でも一部のモデルに先行してサポートが追加されることがあります。行の内容を前提にする前に、ベンダーの公式ドキュメントを再確認してください。
単に表を言い換えるのではなく、その構成を説明する重要なパターンが 3 つあります。
T3 は普遍的ですが、T2 はそうではありません。 FIM One がサポートするすべてのプロバイダーが
json_object を提供していますが、スキーマティアを提供するプロバイダーはおよそ半数です。したがって、移植可能な structured-output パスは T3 以下を基盤に構築する必要があります。これが FIM One のチェーンが現在の形になっている理由です。T2 を追加するには、グローバルスイッチではなく、モデルごとの機能フラグを追加する必要があります。
常時有効な thinking は、T1 の扉を閉ざす傾向があります。 GLM、Kimi の thinking モデル、deepseek-reasoner はいずれも強制された tool choice を制限または拒否し、Anthropic も thinking が有効な間はそれを拒否します。MiniMax はその反例です。扉が閉じている場合、残されたスキーマでゲートされた選択肢は T2 だけです。そのため、OpenAI のデプロイメントよりも中国系モデルのデプロイメントで、欠けている段がより重要になります。
セルフホストでは、通常の順序が逆転します。 制約付きデコーディングはサービングスタックの特性であるため、命令追従能力が最先端モデルよりはるかに弱いローカルチェックポイントでも利用できます。スキーマによるゲーティングを最も必要とするモデルこそ、それを最も容易に得られるモデルなのです。
Bedrock プリフィル トラップ
response_format={"type":"json_object"} が anthropic/ プレフィックスで解決されたモデルに渡される場合、LiteLLM は内部的にアシスタント プリフィル メッセージを挿入して JSON モードをシミュレートします。Anthropic Messages API には ネイティブな response_format パラメータがないため、LiteLLM は開き括弧をアシスタント コンテンツとして先頭に追加することで近似します:
role: "assistant" を持つ会話を拒否します。これを「アシスタント メッセージ プリフィル」と呼び、以下をスローします:
- モデルが
anthropic/プレフィックスで解決されている(ドメイン マッチまたは URL パス ヒント経由)。 response_format={"type":"json_object"}が渡されている(structured_llm_callの json_mode コード パス)。- 実際のバックエンドが AWS Bedrock である(プリフィルを拒否)。
json_mode_enabled フラグは、無駄な Level 2 呼び出しを排除します。
修正: json_mode_enabled
モデルごとのjson_mode_enabled フラグは、レベル 2(json_mode)を試行するかどうかを制御します。
- DB で構成されたモデル: Admin → Models → Advanced settings で切り替えます。このフラグは
ModelProviderModel.json_mode_enabledに保存されます(デフォルトはTRUE)。 - ENV で構成されたモデル: 環境変数に
LLM_JSON_MODE_ENABLED=falseを設定します。 - 効果: 無効にすると、
abilities["json_mode"]はFalseを返すため、response_formatが渡されることはなく、prefill も行われません。その結果、Bedrock が動作します。フォールバックチェーンはnative_fc → plain_textとなり、失敗する json_mode 呼び出しを完全にスキップします。 - スキップによるコスト: システムプロンプトが JSON を要求し、最新のモデルでは
extract_json()が自由形式のコンテンツを確実に解析できるため、実際にはモデルは引き続き有効な JSON を返します。失われるのは出力そのものではなく保証です。チェーンは T4 で終了し、結果を制約するのはプロンプトだけになります。anthropic/ルートでは、この損失は見た目ほど大きくありません。エミュレートされた JSON モードも、デコーダーレベルでの保証ではなかったためです。
思考モデル + 強制 tool_choice
複数のプロバイダーは、拡張思考がアクティブな状態で強制tool_choice を拒否します。特定の関数呼び出しをピン留めすることが、モデルが最初に推論する自由と矛盾するという理由からです:
structured_llm_call はネイティブ FC レベルで reasoning_effort=None を渡すことで競合を自動的に解決し、その 1 つの呼び出しで思考をオフにします(structured.py::_call_llm)。構造化出力にはスキーマ準拠が必要であり、深い推論は不要なため、ここで思考を無効にすることは正しく、かつより安価です。
API を通じて思考をオフにできない場合、native_fc はすべての構造化呼び出しで 400 で失敗し、チェーンが json_mode にフォールスルーする前に約 10 秒かかります。Kimi が一般的なケースです。思考がオンの場合、auto のみがサポートされ、強制ツール選択には思考をオフにする必要があります。Moonshot はこれをモデル ID を通じてのみ公開しています(kimi-k2 はオフ、kimi-k2.5 と kimi-k2-thinking はオン)。FIM One にはそれを切り替えるパラメータがないため、以下の tool_choice_enabled フラグが対策です。
修正: tool_choice_enabled
モデルごとのtool_choice_enabled フラグは、Level 1 (native_fc) が試行されるかどうかを制御します:
- DB設定モデル: Admin → Models → Advanced → “Native Function Calling” で切り替え。フラグは
ModelProviderModel.tool_choice_enabledに保存されます (デフォルトTRUE)。 - ENV設定モデル: 環境で
LLM_TOOL_CHOICE_ENABLED=falseを設定。 - 効果: 無効にすると、
abilities["tool_choice"]はFalseを返す → 劣化チェーンは Level 2 (json_mode) または Level 3 (plain_text) から開始され、native_fc は完全にスキップされます。これにより、互換性のないモデルの構造化呼び出しあたり約10秒のペナルティが排除されます。 - ReAct エージェントは影響を受けない:
tool_choice_enabledはstructured_llm_callでの強制ツール選択のみを制御します。ReAct エンジンはtool_choice="auto"(モデルが自由に決定) を使用し、この設定に関係なくすべてのモデルで動作します。
tool_choice_enabled と tool_call は別の能力フラグです。tool_call (OpenAICompatibleLLM では常に True) は、ツールがモデルに渡されるかどうかを制御します — これを無効にすると ReAct エージェントが破損します。tool_choice は、構造化出力抽出のための強制ツール選択が試行されるかどうかのみを制御します。tool_choice="auto" は思考モードの影響を受けません。ReAct エンジンは "auto" のみを使用するため、思考が有効な場合でもエージェント実行は機能します。
プロバイダー移行に関する注記: 一部のサードパーティリレーは
reasoning_effort などのサポートされていないパラメータを静かにドロップします (drop_params=True)。そのため、設定されていても思考は決してアクティブ化されません。思考を適切にサポートするプロバイダー (Bedrock、直接 Anthropic API) に移行する場合、native_fc の reasoning_effort=None は一貫した動作を保証します。ユーザーアクションは不要です — 構造化出力はすべてのプロバイダーで同じように機能します。プロバイダー機能マトリックス
このセクションは、各プロバイダーが何をサポートしており、FIM Oneがそれについて何をするかの信頼できる記録です。すべての行は動作を実装する関数に名前を付けているため、ここでの主張はコードに対して確認できます。他のページはデータを繰り返す代わりにここにリンクしています。コードが変わると、このセクションも変わります。 行は単一のモデルではなく、プロバイダーのプロトコルを説明しています。1つのファミリー内のモデルが異なる場合(DeepSeek chatと推論器、Kimiは思考をオンにするかオフにするか)、セルはそのことを示しています。表 A: プロトコルのルーティング
設定されたbase_url と model がどのように LiteLLM の呼び出しに変換されるか、また最初に選択したインターフェースが利用できない場合にどうなるかを示します。
GPT-5.x がプロトコルを選択する方法。
FIM_GPT5_RESPONSES_MODE で選択します。native(デフォルト)は litellm.aresponses 経由で /v1/responses を直接使用し、bridge は LiteLLM の chat-completions 変換を使用し、off は通常の chat completions を強制します。ブリッジは重要な箇所で情報を失うため、ネイティブパスが用意されています。ブリッジは推論アイテムを破棄するため、GPT-5.x のエージェントはツールを呼び出すたびに思考の連鎖を再導出します。プロトコルを直接使用すれば、これらのアイテムを再生できます。reasoning_effort=None を明示的に渡す呼び出しは、structured_llm_call や終了シグナルのプローブが行う呼び出しと同様に、chat completions を使用します。思考を必要としない呼び出しには、保持すべき推論状態がないためです。ただし、推論をオフにできないモデル(gpt-6.1-*、gpt-6-astra)は例外です。これらのモデルでは、reasoning_effort の値にかかわらず chat completions が関数ツールを拒否するため、そのような呼び出しは /v1/responses を使用し、最も低い推論強度である low で実行されます。同じ理由から、off を設定するか、/v1/responses のないエンドポイントを使うと、これらのモデルではツール呼び出しができなくなります。
このネイティブリクエストには、挙動を左右する、間違えやすい重要なプロパティが 2 つあります。
store=falseにすると、上流では会話がステートレスに保たれ、include=["reasoning.encrypted_content"]を指定すると、暗号化されたペイロードが返されるよう要求できます。includeがない場合、推論アイテムは空で返され、再生しても何も起きないままになります。- 再生する推論アイテムでは、サーバー側の
idを削除する必要があります。store=falseでは上流に何も保存されないため、id をそのまま返すとItem with id 'rs_...' not found. Items are not persisted when 'store' is set to falseというエラーになります。encrypted_contentのデータ自体に状態が含まれているため、id を削除しても問題ありません(sanitize_reasoning_item)。
anthropic/ にルーティングされるリレー経由でアクセスすると、Anthropic のプロトコル動作を引き継ぎます。これには、より新しい Bedrock バージョンで拒否される LiteLLM の json-mode assistant prefill も含まれます。OpenAI 互換のゲートウェイ経由でアクセスすると、openai/ として解決され、prefill は挿入されず、json_mode_enabled を有効のままにできます。
Table B: 競合と回避策
FIM Oneは4つのtool_choice状態のうち3つを発行します:ReACtループからのauto(react.py::_run_native)、構造化出力からの名前付き関数(structured.py::_call_llm)、および終了シグナル応答がツールペイロードを再生するときのnone。呼び出しサイトはrequiredを発行しません。その列はプロバイダーのauto以外のツール選択に対する制約を記録しており、requiredと名前付き関数の両方に適用されます。
Table C: 思考プロトコル
LLM_REASONING_EFFORTはlow、medium、highを受け入れます。その他の値は未設定として読み込まれます(deps.py::_reasoning_effort)。FIM Oneがワイヤに送信するのはプロバイダごとであり、このテーブルはそれを記録します。replayカラムはreasoning_replay_policyの戻り値で、プロバイダごとのリストではなく、4つの状態の小さなクローズドセットです。
unsupportedとinformational_onlyはワイヤ上で同じバイトを生成します。どちらも発信履歴からreasoning_contentとsignatureを削除します。意図が異なるため、明らかに推論するがランディングがunsupportedのモデルは、ライブバグではなくフラグメントテーブルのギャップです。
Relay/proxy gotchas
Third-party gateways fail in ways a direct provider does not, and most of those failures are silent. Each row below pairs the symptom with its mechanism and with what FIM One already does about it.Support boundary. FIM One guarantees the behaviour documented on this page for first-party endpoints: OpenAI’s own API, Anthropic, Google, and any vendor serving its own models directly. Third-party relays are supported on a best-effort basis and are not covered by that guarantee, because what a relay does to a request is outside our control and frequently outside its own documentation. A relay can drop a parameter, rewrite history, strip a cache breakpoint, or answer a protocol it only partially implements, and in most of those cases it returns a
200 rather than an error.This is a statement about what we promise, not a restriction on what runs. FIM One does not maintain an allowlist of approved hosts, and nothing here is gated on a domain. Capability is decided by what an endpoint actually does: a missing route answers 404 and is remembered, an ignored include yields empty reasoning items and the replay becomes a no-op, and a rejected request falls back for that call. Probing the endpoint is more accurate than inferring its capabilities from its hostname, and it is the only approach that keeps working for Azure OpenAI, enterprise gateways, and self-hosted proxies that implement the protocol correctly.If a relay misbehaves in a way the fallbacks do not catch, pin the protocol yourself with FIM_GPT5_RESPONSES_MODE (bridge or off) or the per-model tool_choice_enabled and json_mode_enabled toggles, and reproduce against the first-party endpoint before filing it as a FIM One bug.モデルごとの推奨設定
tool_choice_enabledとjson_mode_enabledは、Admin → Models → Advanced settingsでモデルごとにトグル切り替えできます。デフォルト値はどちらもTRUEで、ほとんどのプロバイダーに適切です。エラーやレイテンシーの無駄が見られる場合のみ調整してください。調整が必要なプロバイダーは上記の表Bに記載されており、オペレーターが入力するモデルごとのビューはModel Managementにあります。
ENV レベルのオーバーライド は、環境変数経由で設定されたすべてのモデルに適用されます(Admin UIではなく):
推理努力度と思考設定
FIM Oneは、拡張思考/推理を制御するための2つの環境変数を公開しています:
思考がオンになると、以下の2つの動作が自動的に実行され、ユーザー設定は不要です:
- 温度は自動的に処理されます。 思考がアクティブな
anthropic/ルートでは、_build_request_kwargsはtemperatureを1.0に固定します。これはBedrockが要求する値です。サンプリングパラメータを完全に拒否するモデル(Opus 4.7および4.8、Fable 5、Mythos 5)は、思考の有無にかかわらず、リクエストからtemperatureが削除されます。このためにLLM_TEMPERATURE=1を手動で設定しないでください。 - GPT-5.xは可能な限りツールと推理を一緒に保ちます。 FIM Oneは、GPT-5.xの場合、Responses ブリッジを最初にプローブします。これが2つが組み合わさる唯一のサーフェスだからです。使用可能な
/v1/responsesルートがないエンドポイントはチャット補完にフォールバックし、判定はエンドポイントごとにキャッシュされます。そのパスでtoolsを含むリクエストは、明示的なreasoning_effortとしてnoneを送信します。フィールドを省略することは同等ではありません。サーバーのデフォルトがnoneではないからです。
構造化出力の防御的パース
native_fcが正しく動作している場合でも、構造化出力パイプラインには、任意のプロバイダーまたは互換性レイヤーからのエッジケースを処理するための防御的パース層が含まれています。 DAGプランナーの_dict_to_stepsパーサーは、3つの一般的なエッジケースを処理します:
-
配列の代わりに単一オブジェクト。 一部のモデルは
{"steps": [{"id": "1", "task": "..."}]}(配列)の代わりに{"steps": {"id": "1", "task": "..."}}(単一ステップオブジェクト)を返します。パーサーはidまたはtaskキーをチェックしてこれを検出し、オブジェクトをリストでラップします。 -
ダブルエンコードされたJSON文字列。 構造化出力がスキーマ強制を欠くjson_modeにフォールバックする場合、一部のプロバイダーは
steps値をネイティブ配列ではなくJSON文字列として返します。例えば{"steps": "[{\"id\": \"1\", ...}]"}です。この文字列には、標準的なjson.loadsを破壊するモデルのフォーマットからのリテラル改行も含まれる場合があります。パーサーはextract_json_value()(_repair_json_stringsを含む)を使用して以下を処理します:- JSON文字列値内のリテラル改行
- 無効なエスケープシーケンス(LaTeXまたはコードコンテンツで一般的)
- 互換性レイヤーからの他のシリアライゼーション特性
-
stepsラッパーの欠落。 モデルはstepsラッパーキーなしでトップレベルオブジェクトとして単一ステップを返す場合があります。パーサーはルートレベルでidとtaskを検出し、それに応じてラップします。
通常の動作では、native_fcは適切に構造化されたツール呼び出し引数を返し、これらのエッジケースは発生しません。防御的パーサーは、カスタム
BaseLLMサブクラス、異常なプロバイダー動作、または構造化出力がjson_modeまたはplain_textに低下するフォールバックシナリオのための安全ネットとして存在します。プロンプトキャッシング(クロスプロバイダー)
FIM One は Anthropic の明示的なプロンプトキャッシング(cache_control ブレークポイント経由)を実装し、同時にプロンプトセクションレジストリを通じて他のすべてのプロバイダーの自動プレフィックスキャッシングの恩恵を受けます。目標は、呼び出しごとのプロンプト形状の相違なく、すべてのプロバイダーで機能する単一のプロンプト組立パスです。
アーキテクチャ
fim_one.core.prompt モジュールは3つのプリミティブを公開しています:
PromptSection— 静的なcontent: strまたは動的なcontent: Callableを持つ名前付きフラグメントPromptRegistry— メモ化されたストア(静的セクションは1回レンダリングされ、動的セクションはコール毎に再レンダリングされます)DYNAMIC_BOUNDARY— レジストリが最後の静的セクションと最初の動的セクションの間に挿入するセンチネルマーカー。呼び出し元がキャッシュ破断点でレンダリングされた提示词を分割できるようにします
- 静的プレフィックス(提示词の約95%)— アイデンティティ、コアガイドライン、ツール説明
- 動的サフィックス — 現在の日時、リクエスト毎の言語指示、ハンドオフコンテキスト
キャパビリティ検出
fim_one.core.prompt.caching.is_cache_capable(model_id) は、モデル ID に claude、anthropic、bedrock/anthropic、vertex_ai/claude のいずれかが含まれている場合に True を返します。これらのプロバイダーは、最初の(静的な)メッセージに cache_control: {"type": "ephemeral"} を付けた 2 つ の role="system" メッセージを受け取ります。
その他すべてのプロバイダーは、cache_control フィールドなしの 単一の 連結されたシステムメッセージを受け取ります。これは、Anthropic 以外のエンドポイントがこのフィールドを拒否するか暗黙的に削除するため、また一部のリレーを通じて送信すると 400 unknown parameter エラーが発生するため、必要です。
プロバイダー間の対応状況
PromptRegistry は、プレフィックスキャッシュを自動利用するすべてのプロバイダーで「そのまま」効果を発揮します。呼び出し間で静的な部分をバイト単位で同一に保つことで(現在日時はプレフィックスではなく動的なサフィックスに置かれます)、自動キャッシュを利用する各プロバイダーでハッシュが一致し、キャッシュヒットします。そのため、Anthropic固有の cache_control を考慮する前から、Registryはモデルに依存しない基盤的な利点となります。
可観測性
すべてのchat/* レスポンスの done_payload に以下が含まれるようになりました:
TurnProfiler はターンごとに構造化ログ行を出力します: turn_cache summary | model=claude-sonnet-4-6 | read_tokens=1067 | create_tokens=0 | saved_input_tokens=961 (~90%)。これはリレー正直性プローブとしても機能します — API リレーを経由してルーティングする場合、実際に請求された入力トークンと read_tokens を比較して、リレーが cache_control を削除しているか、0.10× の割引を保持しているかを検出できます。
LLM レイヤーではドル推定値は返されません — 価格設定とリレー マークアップはその上で適用されるため、LLM レイヤーは客観的なトークン数のみを返します。
マルチターンキャッシュ ROI
Claude 4 ReAct ターンでデフォルトエージェント提示词で測定:
10 ツール付きの 10 イテレーション ReAct 実行は、最初のターン後、ターンあたり ~8,640 入力トークンを節約します (9 キャッシュヒット × 1067 トークン × 90%)。Anthropic は最初の呼び出しでキャッシュ書き込みに 1.25 倍の料金を請求するため、損益分岐点は2 番目の呼び出しです — シングルショットクエリは利益を得ません。
推論リプレイポリシー(モデルレス正確性)
拡張思考/推論ブロックはプロバイダー間で異なる動作をします。統一されたシリアライゼーションポリシーはプロトコルコントラクトと自動プレフィックスキャッシュの両方を破壊します。fim_one.core.prompt.reasoning.reasoning_replay_policy(model_id) は4つの値のいずれかを返し、OpenAICompatibleLLM._build_request_kwargs() 内の ChatMessage.to_openai_dict(replay_policy=...) をゲートします。
4つのポリシー
anthropic_thinking— Claude ファミリー(anthropic/、bedrock/anthropic、vertex_ai/claudeを含む)。思考ブロックはsignatureを付けて必ずリプレイする必要があります。Anthropic は署名がない場合または変更された場合、後続のターンを拒否します。informational_only— CoT を出力しますが、リプレイを期待しないモデル:DeepSeek 推論モード(V3.2 のdeepseek-reasoner、および古いdeepseek-r1と R1-Distill ID はフラグメントテーブルが一致)、Qwen QwQ、Gemini flash-thinking、OpenAI o1 / o3 / o4。ドキュメントでは明確に「メッセージ履歴にreasoning_contentを送信しないでください」と述べられています。それでも送信した場合:- プロバイダー契約に違反します(将来のバージョンで拒否を開始する可能性があります)
- 自動プレフィックスキャッシュを静かに無効化します — メッセージバイトはターンごとに変更され、ハッシュが破損します
openai_responses— GPT-5.x、gpt-5フラグメントでマッチします。その推論状態はテキストではなく、暗号化されたペイロードを含む不透明なアイテムのシーケンスであり、/v1/responsesだけがそれらのスロットを持ちます。そのプロトコルでは、アイテムはそのままリプレイされ、これがツールラウンド全体でモデルの思考の連鎖を保つものです。読み取り可能なサマリーは依然として送信リクエストから削除されるため、chat-completions フォールバックではこれはinformational_onlyと全く同じように動作します。汎用のreasoningエントリがプロキシタグ付き GPT-5 ID を吸収する可能性がある情報フラグメントの前にチェックされます。unsupported— キャッチオール:推論機能のないモデル(GPT-4o、Gemini 1.5、Mistral、Llama)、および ID がフラグメントと一致しない推論モデル(GLM、MiniMax、Kimi、Doubao)。どちらの方法でもフィールドをリプレイしないため、このポリシーはinformational_onlyと同じバイト列をワイヤに配置します。また、不明なモデル ID の安全なデフォルトでもあります。
reasoning_content と不透明な reasoning_items は ChatMessage 上の独立したフィールドです。to_openai_dict() はアイテムを全くシリアライズしないため、ポリシーが何を言おうとも、構造的に chat-completions リクエストにリークすることは不可能です。
実装
すべてのポリシー評価は1つの場所(_build_request_kwargs)で行われます。ChatMessage.to_openai_dict(replay_policy=None)はA3の寛容なデフォルトを保持するため、調整されていない呼び出し元は回帰しません。クロスプロバイダーテストマトリックスはtests/test_reasoning_replay_policy.pyに存在し、逆アサーションによって非Anthropicリクエストがreasoning_contentをリークしないことを証明しています。
ユーザー向け
機能とバグの動作は自動的です — 何も設定する必要はありません。ワークフローへの影響:- 同じ会話内でClaudeとDeepSeekの間でエージェントを切り替える場合、履歴は思考ブロックをそのまま保存されます。次のターンで、送信メッセージの形状は現在のモデルに適応します。
- プロキシ / カスタム
BaseLLMサブクラスを使用する場合、そのモデルIDが認識可能であることを確認してください(フラグメントの1つを含む)。そうでない場合、デフォルトのunsupportedポリシーが適用されます — これは安全ですが、異常なプロキシの背後にあるClaudeが思考リプレイを失う可能性があります。モデルIDフラグメントを_CACHE_CAPABLE_MODEL_FRAGMENTS(core/prompt/caching.py内)および/または推理ポリシー検索に追加してください。
トラブルシューティング
“This model does not support assistant message prefill” Bedrock + json_mode。2つの修正方法があります:(1)LLM_JSON_MODE_ENABLED=false を設定するか、管理者モデル設定で JSON Mode を無効にする、または (2) Bedrock プロバイダーが OpenAI 互換の /v1/chat/completions エンドポイントを提供している場合は、それに切り替えてください。FIM One はそれを openai/ として解決し、prefill インジェクションは発生しません。
“Thinking may not be enabled when tool_choice forces tool use” / “tool_choice ‘specified’ is incompatible with thinking enabled”
Anthropic モデルの場合、structured_llm_call は native_fc 呼び出しの思考を自動的に無効にします。kimi-k2.5 や kimi-k2-thinking、deepseek-reasoner など API を通じて思考をオフにできない場合は、モデルの詳細設定で「Native Function Calling」を無効にするか、グローバルに LLM_TOOL_CHOICE_ENABLED=false を設定してください。劣化チェーンは native_fc をスキップし、代わりに json_mode または plain_text を使用して構造化出力を抽出します。思考モデルがこの問題を持つと仮定する前に、Provider Capability Matrix の表 B を確認してください。MiniMax はこの問題を持ちません。
“DAG pipeline failed: LLM ‘steps’ is not an array”
LLM が steps フィールドを文字列または単一オブジェクトとして返しました。これは通常、構造化出力が json_mode にフォールバックしたことを意味します(json_mode はスキーマ強制がありません)。ログで structured_llm_call: level=xxx を確認してください。native_fc ではなく json_mode が表示されている場合、native_fc は静かに失敗しています。カスタム BaseLLM サブクラスを使用している場合は、reasoning_effort kwarg を受け入れることを確認してください。
ReAct が予期せず JSON mode にフォールバックする
モデルの abilities["tool_call"] が True であることを確認してください。これは OpenAICompatibleLLM では常に True ですが、カスタム BaseLLM サブクラスはそれをオーバーライドする可能性があります。管理者 API のモデル詳細エンドポイントで確認してください。
structured_llm_call がすべてのレベルを使い尽くし、StructuredOutputError を発生させる
モデルはどのレベルでも解析可能な JSON を生成できませんでした。これは最新のモデルではまれです。確認してください:(1) スキーマが有効な JSON Schema である、(2) モデルが完全な応答を生成するのに十分な max_tokens を持っている、(3) システムプロンプトがスキーマ指示と矛盾していない。DAG プランナーとアナライザーの両方が default_value フォールバックを提供するため、このエラーは明示的にデフォルトを省略した呼び出しサイトからのみ伝播します。