構造化 LLM 出力を OpenAI、Anthropic、Gemini 間で移植可能にする方法
正規スキーマ、プロバイダー固有のコンパイル、検証、修復制限、契約テストを使用して、OpenAI、Anthropic、Gemini 全体で移植可能な構造化 LLM 出力を設計するための実践的なガイドです。
チームがプロバイダーのサポートを交換可能なものとして扱う場合、構造化 LLM 出力は脆弱になります。 OpenAI、Anthropic、Gemini はすべて構造化データを生成できますが、同じコントラクトを公開するわけではありません。スキーマ構文、ツール呼び出しエンベロープ、厳密性の保証、拒否動作、ストリーミング動作、検証失敗モードは大きく異なるため、あるモデルで動作するパーサーが別のモデルでは黙って失敗する可能性があります。
実際的な解決策は、プロバイダー固有の構造化出力機能をビジネス契約として扱うのをやめることです。ワークフローごとに 1 つの正規出力コントラクトを定義し、それを各プロバイダーがサポートするリクエスト形式にコンパイルし、正規スキーマに対してすべての応答を検証し、運用トラフィックの提供を許可する前に各モデルをテストします。
ポータブルな構造化出力とは何ですか?
ポータブルな構造化出力とは、基になるモデル プロバイダーが変更された場合でも、アプリケーションが同じビジネス レベルの応答形状に依存できることを意味します。すべてのプロバイダーがまったく同じ JSON スキーマを受け取るという意味ではありません。また、プロバイダ側の検証だけで十分というわけでもありません。
ポータブル契約には 5 つの層があります:
- 正規スキーマ: ワークフローのプロバイダーに依存しないデータ形式。
- 移植性リンティング: スキーマが、サポートされていない、強制力が弱い、またはプロバイダ間で危険な構造を回避していることをチェックします。
- プロバイダーのコンパイル: 正規コントラクトから OpenAI、Anthropic、Gemini、または別のプロバイダーのネイティブ構造化出力サーフェスへの変換。
- 実行時検証: 返されたオブジェクトの正規スキーマに対するアプリケーション側の検証。
- 契約テスト: 運用ルーティングが変更される前に、各モデルが必須フィールド、列挙型、拒否、切り捨て、不正な形式のリクエスト、およびセマンティック エッジ ケースを処理することを証明する反復可能なテスト。
このパターンは、複数のチーム、テナント、またはパートナーのワークフローが予測可能な出力契約に依存する エンタープライズ LLM API にとって特に重要です。
事実: プロバイダーの構造化出力は 1 つの標準ではない
ポータビリティ レイヤを設計する前に、文書化されたプロバイダの動作を想定から切り離してください。
OpenAI
OpenAI は JSON モードとは異なる構造化出力を文書化します。 JSON モードは有効な JSON を保証することを目的としていますが、構造化出力は指定された JSON スキーマと一致することを目的としています。 OpenAI は、応答の書式設定およびスキーマに厳密に準拠した関数呼び出しを通じて構造化出力をサポートしますが、文書化された動作にはモデルとバージョンの制約とサポートされるスキーマのサブセットが含まれます。厳密モードでサポートされていないスキーマでは API エラーが発生する可能性があり、アプリケーションは引き続きエッジ ケースを処理する必要があります。
Anthropic
Anthropic ドキュメント Claude ツールは、input_schema が付いた開発者提供のツールに基づいて構造化されたツール呼び出しとして使用されます。モデルは tool_use ブロックを返し、アプリケーションは要求されたツールを実行します。これは関数呼び出しに似ていますが、OpenAI 応答形式の JSON スキーマ応答と同じコントラクトではありません。 Anthropic のエンジニアリング ガイダンスでは、JSON スキーマは構造、必須フィールド、および列挙型を定義できますが、オプションのパラメーターをいつ含める必要があるか、どのフィールドの組み合わせがビジネス上意味があるかなどの使用パターンを表現できないことにも言及しています。
Gemini
Google の Gemini API ドキュメントには、JSON 応答に対して構造化出力を構成できることと、Gemini 構造化出力モードが JSON スキーマ仕様のサブセットをサポートしていることが記載されています。これは重要です。なぜなら、有効なスキーマ JSON スキーマであっても、ポータブル プロバイダー リクエスト スキーマとしては不適切である可能性があるからです。
可観測性
OpenTelemetry GenAI のセマンティック規則には、プロバイダー、要求されたモデル、応答モデル、出力タイプ、終了理由、トークンの使用法、ツール定義、ツール呼び出し引数、関連する GenAI 要求および応答フィールドの属性が含まれます。これらのフィールドは、構造化出力の失敗がプロンプト、スキーマ、モデル、プロバイダーの問題と区別するのが難しい場合に、クロスプロバイダーのデバッグをサポートできます。
ステップ 1: 正規のビジネス スキーマを定義する
プロバイダー API ではなく、ビジネス ワークフローから始めます。サポートトリアージワークフロー、請求書抽出ワークフロー、承認の推奨、またはパートナー向けの自動化エンドポイントにはそれぞれ、アプリケーションがダウンストリームで必要とするものを表す正規スキーマが必要です。
たとえば、サポート優先順位付けワークフローでは、次の標準的な形式が使用される場合があります:
{
"type": "object",
"additionalProperties": false,
"required": ["category", "priority", "summary", "requires_human_review"],
"properties": {
"category": {
"type": "string",
"enum": ["billing", "technical", "security", "account", "other"]
},
"priority": {
"type": "string",
"enum": ["low", "normal", "high", "urgent"]
},
"summary": {
"type": "string",
"minLength": 1,
"maxLength": 500
},
"requires_human_review": {
"type": "boolean"
},
"customer_risk_signals": {
"type": "array",
"items": { "type": "string" },
"maxItems": 5
}
}
}
このスキーマは、アプリケーションの残りの部分が信頼するコントラクトです。プロバイダー固有のリクエスト形式はそこから生成されます。それらは真実の情報源ではありません。
バリデーターがサポートしている場合は、明示的な必須フィールド、境界付き配列、境界付き文字列、閉じた列挙型、および additionalProperties: false を使用します。ダウンストリーム システムが任意のキーを処理するように設計されていない限り、任意のネストされたオブジェクトを受け入れることは避けてください。
ステップ 2: スキーマの移植性 lint を追加するステップ
移植性リンターは、技術的には有効な JSON スキーマだが、マルチモデルで使用すると危険なスキーマを拒否する必要があります。 JSON スキーマの完全な表現力が目標ではありません。目標は、アプリケーションの保証を弱めることなく、複数のプロバイダー サーフェスにコンパイルできる安定した出力コントラクトです。
役立つ lint チェックには次のようなものがあります:
- コンパイラに明示的なプロバイダー マッピングがない限り、サポートされていない合成キーワードを拒否します。
- 任意のプロパティを許可する制約のない
objectフィールドを拒否します。 - ルーティング、請求、承認、またはパートナーに表示される状態を制御する分類フィールドには列挙型が必要です。
- オートメーションまたは UI 表示で使用される配列には
maxItemsが必要です。 - データベース、キュー、またはパートナー API に入るテキスト フィールドには
maxLengthが必要です。 - 存在しないとビジネス上の意味が変わるオプションのフィールドにフラグを立てます。
- ネストまたはサイズが機能マトリックスの既知のプロバイダ制限を超えるスキーマを拒否します。
リンターは CI 内でスキーマのデプロイ前に実行する必要があります。スキーマ キーワードがサポートされていないため、実稼働リクエストでプロバイダー エラーが発生するまで待たないでください。
ステップ 3: プロバイダー固有のリクエスト形式にコンパイルする
プロバイダーのコンパイルは、正規スキーマを各モデルが実際にサポートする API サーフェスに変換します。コンパイラは決定的であり、バージョン管理されている必要があります。また、どの正規スキーマ バージョンがどのプロバイダ リクエスト ペイロードを生成したかを示す監査証跡も残す必要があります。
OpenAI コンピレーション
OpenAI の場合、コンパイラは、抽出タスクや分類タスクの応答フォーマット、またはアクション指向のワークフローの厳密な関数呼び出しを通じて構造化出力をターゲットにする場合があります。コンパイルされたペイロードには、サポートされているスキーマのサブセットとプロバイダー固有の厳密性設定 (利用可能な場合) が含まれている必要があります。
Anthropic コンピレーション
Anthropic の場合、コンパイラは多くの場合、目的の出力を input_schema を持つツールとして表します。次に、アプリケーションはツール使用ブロックを予期し、正規スキーマに対してツール入力を検証します。これは、アダプターが JSON.
Gemini コンピレーション
Gemini の場合、コンパイラは、サポートされているスキーマ サブセットのみを使用して、構造化された JSON 応答構成をターゲットにする必要があります。 Gemini がネイティブに強制しない制約を正規スキーマが使用している場合、コンパイラーはそれらの制約をアプリケーション側の検証が必要であるとして記録する必要があります。
アダプターの出力は 1 つの内部結果タイプに正規化する必要があります:
type StructuredResult = {
provider: "openai" | "anthropic" | "gemini";
model: string;
schema_version: string;
status: "ok" | "refusal" | "truncated" | "provider_error" | "invalid";
raw_response: unknown;
parsed_output?: unknown;
finish_reason?: string;
};
この正規化により、ダウンストリームのワークフロー コードがプロバイダー エンベロープに依存することがなくなります。
ステップ 4: 正規スキーマに対してすべての応答を検証する
プロバイダー側の構造は防御の第一線であり、最終的な正当性チェックではありません。モデルが応答した後は、解析された出力を正規スキーマに対して常に検証してください。
実行時検証では次のような構造化エラーが生成されるはずです:
missing_required_fieldinvalid_enumunexpected_propertytype_mismatcharray_too_longstring_too_longsemantic_rule_failed
構造検証を意味論的検証とは別にしてください。 JSON スキーマは、形状、必須プロパティ、列挙型、および基本的な型制約をチェックできます。有効な状態遷移、承認しきい値、顧客固有のポリシー、相互に依存するフィールドの組み合わせなどのビジネス ルールを完全に表現することはできません。
たとえば、これはスキーマ ルールではなくセマンティック バリデータです:
if (output.priority === "urgent" && output.requires_human_review !== true) {
throw new SemanticValidationError(
"urgent tickets must require human review"
);
}
顧客の構成、ワークフローの状態、認可コンテキスト、ポリシーのバージョン管理を使用できるアプリケーション コードにこれらのチェックを組み込みます。
ステップ 5: 制限付き修復を使用してからフェールクローズ
修復パスは小さな出力欠陥から回復できますが、体系的なスキーマを隠し、問題を引き起こす可能性もあります。検証エラーを含めて、対象を絞った修復を 1 回試行します。修復された応答が依然として検証に失敗する場合は、フェールクローズするか、レビューにルーティングします。
実際の修復ポリシーは次のようになります:
- 必須フィールドの欠落、無効な列挙型、不正な形式の JSON など、対象となる障害タイプに対してのみ修復を試みます。
- プロバイダーの拒否、安全ブロック、認証の失敗、またはポリシーに依存する出力は修復しないでください。
- 修復プロンプトに正規スキーマのバージョンと検証エラーを含めます。
- 顧客に表示されるワークフローまたはアクションを引き起こすワークフローについては、最大 1 回の修復試行を許可します。
- 修復数、修復の成功、トークンの使用状況、および最終ステータスをログに記録します。
リスクの低い内部抽出ジョブの場合、待ち時間が重要でない場合は、2 回目の修復試行が許容される場合があります。支払い、アクセス変更、パートナー API の応答、またはコンプライアンス アクションをトリガーするワークフローの場合、通常はフェールクローズ ポリシーの方が安全です。
ステップ 6: 各実稼働モデルの契約テストを構築する
契約テストは、モデルがワークフローの対象となるかどうかを判断します。これらは、初回起動前、モデルのアップグレード前、ルーティング ポリシーの変更前に実行する必要があります。
少なくとも、各モデルとプロバイダー アダプターをテストしてください:
- 有効なハッピーパス出力。
- 必須フィールドが欠落しています。
- 列挙値が無効です。
- スキーマが閉じている場合の追加プロパティ。
- 配列項目が多すぎます。
- 長すぎる文字列。
- 拒否または遵守不能。
- 切り捨てられた出力。
- ツール呼び出しの不一致またはツール呼び出しの欠落。
- 構造的には有効ですが、意味的に間違った出力です。
モデルに契約に違反するよう圧力をかける敵対的なプロンプトを使用します。たとえば、追加の説明フィールドを追加したり、列挙型の外に優先度を返したり、必須のブール値を省略したりするように依頼します。テストは、プロバイダー、アダプター、バリデーターが期待されるアプリケーション ステータスを生成した場合にのみ合格します。
ワークフローごとに、単純な互換性記録を維持します:
{
"workflow": "support_triage",
"schema_version": "2026-09-24",
"model": "provider-model-name",
"supports_strict_schema": true,
"supports_tool_calling": true,
"supports_streaming_structured_output": false,
"refusal_signal_normalized": true,
"contract_tests_passed": true,
"known_limitations": ["max schema depth 4"]
}
この機能マトリックスは、散在するアプリケーション定数ではなく、コントロール プレーンに属します。
ステップ 7: 機器の構造化された出力の信頼性
テレメトリーがなければ、チームは障害の原因がプロバイダーの非互換性、モデルのドリフト、スキーマの欠陥、プロンプトの欠陥、切り捨て、またはダウンストリームのバリデーターにあるのかどうかを判断できません。
すべての構造化出力リクエストについてこれらのフィールドをログに記録します:
- ワークフロー名とスキーマのバージョン。
- プロバイダー、要求されたモデル、および応答モデル。
- 応答スキーマ、ツール呼び出し、または JSON モードなどの出力モード。
- 検証ステータスと失敗の種類。
- 修理回数と修理結果。
- 理由と拒否ステータスを終了します。
- プロンプト トークン、完了トークン、総コスト、レイテンシ。
- アダプターのバージョンとコンパイラーのバージョン。
ルーティングを変更する前に、このデータを使用してモデルを比較します。安価なモデルでも、修理の試行、手動レビュー、パートナー API の障害、または下流のインシデント処理
が増える場合は、安くはなりません。明示的に決定するトレードオフ
最小公倍数スキーマは移植性を向上させますが、制約を弱めます。 プロバイダー要求スキーマを簡素化する必要がある場合は、正規検証とセマンティック検証でより厳密なチェックを保持します。
プロバイダ固有のコンパイルでは、ネイティブの強みは維持されますが、メンテナンスが追加されます。 これは通常、運用ワークフローでは価値がありますが、コンパイラがバージョン管理され、契約テストでカバーされている場合に限ります。
厳密なスキーマはパーサーの失敗を減らしますが、より適切なタスク設計が必要です。 タスクが過少指定されている場合、厳密であるとすぐに曖昧さが露呈します。これは便利ですが、初期の開発が遅くなる可能性があります。
修復により完了率は向上しますが、コストと遅延が増加します。 制限付き修復は回復メカニズムであり、スキーマ設計の代替ではありません。
ツール呼び出しと応答形式 JSON は、さまざまなワークフローに対応します。 ツール呼び出しは、多くの場合、アクション指向のワークフローにより適しています。応答形式 JSON は、抽出、分類、要約が簡単です。マルチモデル プラットフォームは両方をサポートできますが、ワークフロー ポリシーは明示的である必要があります。
予測: 構造化出力の移植性はどこへ向かうのか
構造化出力に対するプロバイダーのサポートは今後も改善される可能性がありますが、モデル間の完全な JSON スキーマの移植性は想定されるべきではありません。プロバイダーには、さまざまなモデル アーキテクチャ、API の履歴、ツールの使用パターン、安全行動があります。スキーマのサポートが強化されても、ビジネスの正確性は JSON の形状以外にも依存するため、アプリケーションには依然として正規の検証が必要です。
チームは、構造化された出力ルーティングの決定がよりデータ主導になることを期待する必要があります。モデルの品質と並んで、信頼性、修理率、拒否処理、遅延、コストも重要になります。運用に関する質問は、「このモデルは JSON をサポートしていますか?」から変わります。 「このモデルは許容可能なコストと失敗率でこのワークフローの契約を満たしていますか?」
実装チェックリスト
- ワークフローごとに 1 つの正規スキーマを定義します。
- スキーマをバージョン化し、プロバイダー リクエストのペイロードを再現可能に保ちます。
- サポートされていない、または危険なスキーマ機能に対して CI lint を追加します。
- 正規スキーマをプロバイダー固有の形式にコンパイルします。
- プロバイダー エンベロープを 1 つの内部結果タイプに正規化します。
- すべての解析された応答を正規スキーマに照らして検証します。
- ビジネス ルールのセマンティック バリデータを追加します。
- 制限付きの修復試行のみを許可します。
- リスクの高いワークフローについては、フェイルクローズするかレビューにルーティングします。
- 実稼働モデルを追加またはアップグレードする前に、契約テストを実行します。
- コントロール プレーンでモデル機能マトリックスを維持します。
- すべてのリクエストの検証、修復、コスト、レイテンシ、プロバイダー、モデル フィールドをログに記録します。
実用的な結論
構造化出力の移植性は API 契約の問題であり、プロンプトのフォーマットの問題ではありません。信頼できるパターンは、正規のビジネス スキーマを定義し、移植性を確保するためにそれを lint し、各プロバイダーのネイティブ サーフェスにコンパイルし、プロバイダーが応答を返した後にすべての応答を検証し、コントラクト テストを使用してどのモデルが運用に適しているかを決定することです。
この設計では、事前のエンジニアリング作業が追加されますが、よりコストのかかる障害モード、つまりモデル ルーティング、プロバイダーの動作、またはスキーマの適用がその下で静かに変更される一方で、ダウンストリーム システムが安定したコントラクトを前提としているという事態を回避できます。