AI API ゲートウェイでの応答 API 互換性レイヤーの構築
Responses API ゲートウェイは、新しいルートを持つ単なるチャット完了プロキシではありません。応答項目、状態、ツール呼び出し、ストリーム、推論の継続性、使用状況の帰属、およびダウングレード動作をファーストクラスの互換性レイヤーで保持します。
すべてのリクエストを /v1/chat/completions に変換し、形状が十分に近いことを期待して /v1/responses を実装しないでください。そのアダプターはテキストを返す可能性がありますが、応答項目、サーバー側の状態、ツール呼び出し、推論の継続性、ストリームのライフサイクル イベント、キャンセル セマンティクス、項目レベルの使用状況の属性など、開発者が気にする部分が暗黙のうちに失われる可能性があります。
実際的な目標は、応答 API をよりリッチなプロトコルとして扱う互換性レイヤーです。既存のクライアントに対するチャット完了のサポートは維持しますが、独自の状態モデル、ストリーム ノーマライザー、ツール呼び出し台帳、機能マトリックス、およびフォールバック ルールを備えた独自のゲートウェイ サーフェスとして応答を構築します。
事実とは何ですか、ポリシーとは何ですか?
事実: OpenAI では、レスポンス API について、ウェブ検索、ファイル検索、コンピュータの使用などのツールのサポートを含め、チャット補完とアシスタントに分割されていた統合機能として説明しています。 API は、previous_response_id、ストリーミング、ツール選択、組み込みツールなどのフィールドを公開します。 SDK のドキュメントでは、previous_response_id が会話の継続性を提供できることを示していますが、以前の指示は自動的には引き継がれず、適用する必要がある場合には再送信する必要があります。 OpenAI のストリーミング リファレンスには、トークン デルタだけではなく、個別の応答ライフサイクルと出力イベントが含まれています。
推奨事項: ゲートウェイは、デフォルトでこれらのセマンティクスをフラット化するのではなく、保持する必要があります。ターゲットプロバイダーが必要な動作をサポートできない場合、リクエストを拒否するか、明示的にダウングレードする必要があります。
予測: 応答項目構造、ツール実行トレース、ステートフル推論コンテキストに依存するエージェントのワークロードが増加します。これらの概念をモデル化したゲートウェイは、応答を表面的なエンドポイントとして扱うゲートウェイよりも拡張しやすくなります。
レスポンス用に別の互換性コントラクトを定義する
最初の実装上の間違いは、OpenAI 互換とは 1 つの汎用リクエストおよび応答スキーマを意味すると想定していることです。実際には、/v1/chat/completions と /v1/responses は別個の互換性契約である必要があります。
共有の認証、請求、割り当て、およびルーティング層を維持しますが、プロトコル層は分離します:
- チャット完了画面: メッセージ、選択肢、デルタ、チャット形式のツール呼び出し、従来のクライアントの動作。
- 応答の表面: 入力項目、出力項目、応答 ID、以前の応答参照、より豊富なツール イベント、ライフサイクル ストリーム イベント、推論関連フィールド、および最終応答状態。
この分割は適合性テストにとって重要です。チャット テストに合格したプロバイダー アダプターでも、previous_response_id、項目の順序、拒否構造、ホストされたツールのメタデータ、またはストリーミング イベント名を保持できないため、応答テストに失敗する可能性があります。
最小限の互換性契約は次のことに答える必要があります:
- どのリクエスト フィールドが受け入れられ、拒否され、変換され、または無視されますか?
- どの応答項目タイプが保持されますか?
- プロバイダーおよびモデルごとにどのツールの種類がサポートされていますか?
- プロバイダーは会話状態を維持できますか、それともゲートウェイが会話状態を維持する必要がありますか?
store=falseがリクエストされるとどうなりますか?- どのようなストリーム イベントが保証されていますか?
- キャンセル、タイムアウト、部分的な使用状況はどのように記録されますか?
すでに AI API ゲートウェイ を持っている場合は、応答のサポートをルート エイリアスではなくプロトコル拡張として扱います。
正規の応答項目モデルを使用する
応答 API は複数のアシスタント メッセージを返します。さまざまな出力項目やイベントを表すことができます。ゲートウェイはプロバイダにマッピングする前に内部正規モデルが必要です。
実際の内部項目スキーマは次のように始めることができます:
{
"gateway_response_id": "gw_resp_...",
"provider_response_id": "resp_...",
"tenant_id": "ten_123",
"key_id": "key_456",
"model_alias": "agent-default",
"provider": "openai",
"items": [
{
"item_id": "item_1",
"type": "text",
"role": "assistant",
"content": [{ "type": "output_text", "text": "..." }],
"status": "completed"
},
{
"item_id": "item_2",
"type": "function_call",
"call_id": "call_abc",
"name": "lookup_order",
"arguments_json": "{\"order_id\":\"123\"}",
"status": "completed"
}
],
"usage": {
"input_tokens": 0,
"output_tokens": 0,
"reasoning_tokens": null,
"tool_units": []
},
"status": "completed"
}
すべてのプロバイダーがアイテム タイプを生成する前であっても、アイテム タイプを含めます。役立つカテゴリは次のとおりです:
- テキスト出力
- 拒否
- 関数呼び出し
- アプリケーションによって送信された関数の出力
- 推論の概要または推論関連のメタデータ(利用可能な場合)
- ファイル参照
- ウェブ検索、ファイル検索、コンピューターの使用、またはその他のホストされたツール イベント
- 最終使用量と請求メタデータ
重要なのは、独自のスキーマをユーザーに公開しないことです。重要なのは、ゲートウェイが情報を監査、請求、ストリーミング、再生、または変換する前に情報を破棄しないようにすることです。
ゲートウェイ所有の州台帳を構築する
previous_response_id は、ステートレス チャット プロキシと応答の互換性の違いが最も明らかなフィールドです。クライアントが以前の応答を参照する場合、ゲートウェイはその ID の意味、テナントがその ID の使用を許可されているかどうか、プロバイダーがその ID から続行できるかどうかを認識している必要があります。
テナントと応答 ID をキーとした状態台帳を作成します:
{
"gateway_response_id": "gw_resp_789",
"provider_response_id": "resp_provider_789",
"previous_gateway_response_id": "gw_resp_456",
"tenant_id": "ten_123",
"user_id": "user_999",
"key_id": "key_456",
"model": "gpt-...",
"provider": "openai",
"store_mode": "provider|gateway|none",
"retention_policy": "standard|zero_retention|custom_30d",
"instructions_hash": "sha256:...",
"tool_policy_id": "tools_readonly_v3",
"created_at": "...",
"expires_at": "...",
"deleted_at": null
}
重要なルール: テナントが保持とコストの動作を明示的に許可していない限り、完全なチャット履歴を再生して previous_response_id を自動的にエミュレートしないでください。リプレイにより、トークンのコストが増加し、プライバシーの姿勢が変化し、モデルの動作が変更される可能性があります。アプリケーションが保持または再利用を期待していない保存された会話コンテンツを黙って送信するよりも、明確な機能エラーを返す方が安全です。
状態処理モード
- プロバイダーの状態: 上流プロバイダーは十分なコンテキストを保存し、ゲートウェイはゲートウェイ応答 ID をプロバイダー応答 ID にマッピングします。
- ゲートウェイの状態: ゲートウェイは必要な事前項目を保存し、許可されている場合はコンテキストを再構築します。
- 状態なし: リクエストでは
store=falseが使用されているか、テナント ポリシーで保持が禁止されています。previous_response_idは、プロバイダーがゲートウェイを保持せずにリクエストを受け入れることができ、ポリシーで許可されていない限り、拒否される必要があります。
また、以前の指示を引き続き適用する必要がある場合は、クライアントによる再送信が必要になる場合があることにも注意してください。ゲートウェイは、その動作が明示的なテナント ポリシーの一部でない限り、それを補うための隠された命令を作成すべきではありません。
発送前にツールを検証する
Responses により、ツールの使用がより中心的なものになります。互換性レイヤーは、次の 2 つの広いカテゴリを処理する必要があります:
- アプリケーション ツール: クライアントによって提供される関数定義。モデル プロバイダーの外部で実行され、出力は API. に送信されます。
- ホストされているプロバイダ ツール: Web 検索、ファイル検索、コンピュータの使用、コード実行、グラウンディング、またはプロバイダまたはゲートウェイ制御のインフラストラクチャによって実行される同様のツール。
入力時に、ルーティング前にツール スキーマを検証します:
- 無効な JSON スキーマを早期に拒否します。
- 最大スキーマ サイズとネストの深さを強制します。
- プロバイダーの互換性についてはツール名を確認してください。
- テナント、キー、ユーザー、環境のスコープを適用します。
- データの書き込み、金銭の支出、機密システムへのアクセス、または外部コネクタの呼び出しを行うツールには承認ゲートが必要です。
アプリケーション関数の呼び出しには、安定した呼び出し ID が必要です。モデルは call_id を使用して関数呼び出しを発行します。アプリケーションは、その ID を参照するツール出力を送信します。ゲートウェイは両方を同じトレースに記録します。この結合キーがないと、監査ログと再試行があいまいになります。
ホストされているツールの場合は、発送前に予算を予約し、後で費用を決済します。ホストされたツールでは通常のトークン アカウンティング以外の料金が追加される場合があるため、これらのコストを一般的なモデル呼び出しの合計内に隠すのではなく、ツール台帳を 統合 AI API 請求に接続します。
ストリーミングをトークン テキストではなくイベントとして正規化する
チャット プロキシは、多くの場合、トークン デルタの転送を回避できます。応答ゲートウェイではできません。ストリームにはライフサイクルの意味があります。つまり、応答が開始され、出力項目が開始および完了し、テキストがデルタで到着し、ツール呼び出しが増分的に組み立てられ、使用状況がストリームの最後または途中で到着し、応答が失敗またはキャンセルされる可能性があります。
ゲートウェイ イベント スキーマを定義し、各プロバイダー ストリームをそれにマッピングします:
event: response_started
data: { "response_id": "gw_resp_123", "status": "in_progress" }
event: output_item_started
data: { "item_id": "item_1", "type": "text" }
event: text_delta
data: { "item_id": "item_1", "delta": "Hello" }
event: tool_call_delta
data: { "item_id": "item_2", "call_id": "call_abc", "arguments_delta": "{\"order" }
event: usage_delta
data: { "output_tokens": 12 }
event: completed
data: { "response_id": "gw_resp_123", "usage": { ... } }
推奨される正規化イベント:
response_startedoutput_item_startedoutput_item_completedtext_deltarefusal_deltatool_call_deltatool_result_receivedusage_deltacompletedcancelledfailed
クライアントが切断すると、プロバイダーがサポートしている場合はキャンセルを上流に伝播します。いずれかの方法で部分応答状態を記録します。プロバイダーが後で遅延コールバックまたは最終チャンクを通じて最終使用量を返した場合は、台帳を調整します。ストリーミングの互換性は、レイテンシと同じくらいアカウンティングとライフサイクルにも関係します。
プロバイダー機能マトリックスを作成する
マルチモデル ルーティングは、何が安全にルーティングできるかをゲートウェイが理解している場合にのみ役立ちます。 Responses 固有の機能をモデル カタログに追加します:
{
"model_alias": "agent-default",
"routes": [
{
"provider": "openai",
"model": "...",
"supports_responses": true,
"supports_previous_response_id": true,
"supports_store_false": true,
"supports_builtin_web_search": true,
"supports_function_calling": true,
"supports_stream_lifecycle_events": true,
"supports_reasoning_context_continuity": true,
"max_tool_schema_bytes": 65536
},
{
"provider": "provider_b",
"model": "...",
"supports_responses": false,
"chat_adapter_available": true,
"loss_profile": ["no_previous_response_id", "no_hosted_tools", "flattened_stream"]
}
]
}
フォールバックは損失を認識する必要があります。リクエストに組み込みの Web 検索が必要で、フォールバック プロバイダーがそれを実行できない場合は、検索せずに黙って応答しないでください。リクエストが保持された推論コンテキストに依存しており、フォールバック ルートがそれを保持できない場合は、クライアントが明示的にオプトインした機能エラーまたはダウングレード応答を返します。
便利なリクエスト オプションは次のとおりです:
{
"model": "agent-default",
"input": "...",
"fallback_policy": {
"allow_lossy": false,
"allowed_losses": []
}
}
機密性の低いユースケースの場合、テナントは特定の損失を伴うダウングレードを許可できます:
{
"fallback_policy": {
"allow_lossy": true,
"allowed_losses": ["flattened_stream", "no_reasoning_summary"]
}
}
ゲートウェイはいずれかの方法でフォールバックの決定をログに記録する必要があります。これにより、プロバイダーの停止またはモデルの再ルーティング後にエージェントの動作が異なる場合に、後でデバッグが可能になります。
応答および項目レベルでの属性の使用
応答呼び出しには、ツールの実行、長いコンテキスト、推論トークン、ファイル検索、Web 検索、または繰り返しの指示が含まれる可能性があるため、同等のチャット完了よりもコストがかかる可能性があります。 AI API 使用状況分析ダッシュボード.
には、単一の集約トークン数では十分ではありません。2 つのレベルで使用状況を記録します:
- 応答レベル: テナント、キー、ユーザー、モデル、プロバイダー、レイテンシー、最終ステータス、入力トークン、出力トークン、報告された推論トークン、総コスト、フォールバック ルート。
- アイテム/ツール レベル: ツール名、コール ID、ホストされているツール ユニット、ファイル ID、検索クエリ数 (利用可能な場合)、ツールの遅延、ツールのコスト、および承認ポリシーの結果。
これにより、開発者は具体的な質問に答えることができます:
- 長い状態、推論作業、ツール呼び出し、またはフォールバックによりコストは増加しましたか?
- ホストされたツールの料金を生成しているのはどのテナントまたは API キーですか?
- ツール呼び出し後、最終テキストの前に失敗した応答はどれですか?
- キャンセルされたストリームのうち、依然としてアップストリームの使用量が発生しているものはどれですか?
ゼロ保持と削除を第一級の動作として処理します
サーバー側の状態は役に立ちますが、ゲートウェイの保持義務が変わります。ポリシーをログ設定として扱うのではなく、プロトコル層にポリシーを組み込みます。
すべての応答リクエストについて、次を解決します:
- テナント保持ポリシー
- リクエストレベル
store設定 - プロバイダー保持の互換性
- ゲートウェイのリプレイが許可されるかどうか
- ツールの入力と出力を保存できるかどうか
- 応答状態の有効期限と削除の動作
保持が無効になっている場合でも、ゲートウェイはタイムスタンプ、ID、ステータス、トークン数、コスト、ポリシー決定などの最小限の運用メタデータを保持する可能性があります。ポリシーで許可されていない限り、生のプロンプト、完全なツール出力、または再構築された履歴を保存しないでください。
発売前に追加する適合フィクスチャ
ハッピーパスの手動テストに依存しないでください。直接 OpenAI ルート、プロバイダーに適応したルート、およびフォールバック シナリオにわたるプロトコルの動作を検証するフィクスチャを追加します。
最小テストセット
- 基本的な応答: テキスト項目が安定した応答 ID と使用法とともに返されます。
- マルチターン状態: 2 番目のリクエスト参照
previous_response_id;ゲートウェイはテナントの所有権と状態モードを検証します。 - 繰り返される命令: 省略された命令がゲートウェイによって暗黙的に作成されないことを確認します。
- 関数呼び出しラウンドトリップ: モデルは呼び出し ID を発行します。アプリケーションは出力を送信します。最終応答は両方のレコードを結合します。
- ホストされたツール ポリシー: 承認されていない組み込みツールはディスパッチ前にブロックされます。
- ストリーミング順序: 応答の開始、項目の開始、デルタ、項目の完了、使用状況、完了は有効な順序で出力されます。
- ストリーム キャンセル: クライアントの切断により、サポートされている場合はアップストリーム キャンセルがトリガーされ、部分的な使用状況が記録されます。
- フォールバック拒否: 必要な応答セマンティクスのないプロバイダーは機能エラーを返します。
- 非可逆フォールバック オプトイン: 損失が許可されたリクエストは、明示的なダウングレード マーカーを受け取ります。
- ゼロ保持モード: 状態の再生とゲートウェイ側のプロンプト保持はブロックされます。
推奨されるロールアウト順序
- ベータルートを公開します。 既存のチャット動作を変更せずに
/v1/responsesを追加します。 - ネイティブ レスポンス サポートを備えたプロバイダに対しては、最初にパススルーを実装します。 ID、アイテム、ストリーム、使用状況、およびエラーを保持します。
- 状態台帳を追加します。 ゲートウェイ ID をプロバイダ ID にマッピングし、テナントの所有権を強制します。
- 正規アイテムを追加します。 監査、請求、ストリームの再構築に必要なアイテムのメタデータを保存します。
- ツール ガバナンスを追加します。 スキーマを検証し、スコープを適用し、ツール呼び出し結合を記録します。
- ストリーミング正規化を追加します。 プロバイダー固有のストリームをゲートウェイ ライフサイクル イベントに変換します。
- 機能を認識したルーティングを追加します。 デフォルトでは安全なフォールバックのみを許可します。
- 分析と請求決済を追加します。 属性トークン、推論、ツールの使用状況を個別に表示します。
- 互換性に関するメモを公開します。 どのフィールドがネイティブ、エミュレートされているか、サポートされていない、または損失があるかを開発者に伝えます。
実用的な結論
A Responses API 互換性レイヤーは、単にもっともらしいテキストを返すだけでなく、プロトコルの意味を保持する必要があります。正規応答項目モデル、会話状態台帳、ツール呼び出し台帳、ストリーミング イベント ノーマライザー、プロバイダー機能マトリックスの 5 つの耐久性のあるオブジェクトを中心に構築します。
最も安全なデフォルトは厳密な互換性です。ルートが必要な状態、ツール、推論コンテキスト、ストリーム イベント、または保持動作を保持できない場合は、明確な機能エラーを返します。開発者が何がドロップされるかを理解している場合にのみ、オプトイン非可逆フォールバックを追加します。このアプローチは、自動フラット化よりも利便性が劣るように感じられるかもしれませんが、最悪の障害モード、つまり互換性があるように見えながら、最初に応答 API を使用するためのセマンティクスが失われるアプリケーションを防ぐことができます。