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