プロバイダー検出
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
ワンショット構造化抽出(スキーマアノテーション、DAG計画、計画分析)。モデルに特定の仮想関数を呼び出すことを強制し、構造化JSON出力を保証します。これはプロバイダー固有のエラーをトリガーするコールサイトです。structured_llm_callは3レベルの劣化チェーンを実装します:
重要な設計上の違い:structured_llm_callのフォールバックはランタイムです — 各レベルを動的に試行し、例外をキャッチしてフォールスルーします。ReActエンジンのモード選択はビルドタイムです — 開始時に_native_mode_activeを一度チェックし、ループ全体で1つのモードにコミットします。つまり、structured_llm_callはプロバイダー固有の400エラーから透過的に回復できますが、ReActは事前に正しくモードが選択されていることに依存しています。
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 フラグは、Level 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は渡されない → プリフィルなし → Bedrock が動作。デグラデーションチェーンはnative_fc → plain_textとなり、失敗する json_mode 呼び出しをスキップします。 - 品質低下なし: システムプロンプトが JSON を返すよう指示するため、モデルは引き続き有効な JSON を返します。plain_text レベルは
extract_json()を使用して自由形式のコンテンツから JSON を解析し、最新のモデルで確実に動作します。
思考モデル + 強制 tool_choice
一部のモデルには拡張思考(思考の連鎖)が永続的に有効になっています。これらの API は強制tool_choice を拒否します。特定の関数呼び出しを強制することは、モデルが最初に推論する自由度と矛盾するためです:
structured_llm_call は native_fc を呼び出す際に reasoning_effort=None を渡すことで自動的にこれを処理し、その特定の呼び出しの拡張思考を無効にします。構造化出力呼び出しにはスキーマ準拠が必要であり、深い推論は必要ありません。ここで思考を無効にすることは正しく、かつ有益です(レイテンシーが低く、コストが低い)。
ただし、一部のモデル(例:Kimi K2.5)は思考が永続的にオンになっており、外部から無効にする方法がありません。これらのモデルの場合、native_fc は常に 400 エラーで失敗し、構造化呼び出しごとに約 10 秒の無駄なレイテンシーが追加されてから、劣化チェーンが json_mode にフォールスルーします。
修正: 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 は一貫した動作を保証します。ユーザーアクションは不要です — 構造化出力はすべてのプロバイダーで同じように機能します。クイックリファレンス: どこで何が機能するか
モデルごとの推奨設定
tool_choice_enabled と json_mode_enabled は、Admin → Models → Advanced settings でモデルごとにトグル切り替えできます。デフォルト(両方 TRUE)はほとんどのプロバイダーで機能します。エラーや不要なレイテンシーが発生した場合のみ調整してください。
ENV レベルのオーバーライド は、環境変数経由で設定されたすべてのモデルに適用されます(admin UI ではなく):
推論努力と思考設定
FIM One は、拡張思考/推論を制御するための 2 つの環境変数を公開しています:reasoning_effort が設定され、モデルが anthropic/ として解決される場合、以下の 2 つの追加動作が適用されます:
- 温度は 1.0 に強制されます。 Bedrock は思考が有効な場合、
temperature != 1.0を拒否します。FIM One はこれを自動的に処理します — ユーザーアクションは不要です。 - GPT-5.x とツール:
toolsが存在する場合、reasoning_effortはサイレントにドロップされます。これは GPT-5 の/v1/chat/completionsエンドポイントがこの組み合わせを拒否するためです。これは ReAct ツールループにのみ影響します。toolsパラメータを持たないstructured_llm_call呼び出しは影響を受けません。
構造化出力の防御的パース
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 番目の呼び出しです — シングルショットクエリは利益を得ません。
推理重放策略(无模型正确性)
Extended thinking / reasoning blocks 在不同提供商之间的行为不同。统一的序列化策略会破坏协议契约和自动前缀缓存。fim_one.core.prompt.reasoning.reasoning_replay_policy(model_id) 返回三个值之一,并在 OpenAICompatibleLLM._build_request_kwargs() 中控制 ChatMessage.to_openai_dict(replay_policy=...) 的行为。
3つのポリシー
anthropic_thinking— Claude ファミリー(anthropic/、bedrock/anthropic、vertex_ai/claudeを含む)。思考ブロックはsignatureを付けて再生する必要があります。Anthropic は署名が欠落または変更されている場合、後続のターンを拒否します。informational_only— CoT を出力しますが再生を期待しないモデル:DeepSeek R1 / R1-Distill、Qwen QwQ、Gemini 2.x thinking、OpenAI o1 / o3 / o4。これらのドキュメントは「メッセージ履歴にreasoning_contentを送信しないでください」と明示しています。それでも送信する場合:- プロバイダー契約に違反します(将来のバージョンで拒否が開始される可能性があります)
- 自動プレフィックスキャッシュをサイレントに無効化します — メッセージバイトがターンごとに変更され、ハッシュが破損します
unsupported— 推理機能のないモデル(GPT-4o、GPT-4 Turbo、Gemini 1.5、Mistral、Llama)。再生する CoT がなく、フィールドは表示されません。このポリシーは、未知のモデル ID に対しても安全なデフォルトです。
実装
すべてのポリシー評価は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)の場合は、モデルの詳細設定で「Native Function Calling」を無効にするか、グローバルに LLM_TOOL_CHOICE_ENABLED=false を設定してください。劣化チェーンは native_fc をスキップし、代わりに json_mode または plain_text を使用して構造化出力を抽出します。
“DAG pipeline failed: LLM ‘steps’ is not an array”
LLM が steps フィールドを文字列または単一オブジェクトとして返しました。これは通常、構造化出力が 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 フォールバックを提供するため、このエラーは明示的にデフォルトを省略した呼び出しサイトからのみ伝播します。