OpenAI互換APIをエージェントに接続する前に検証する方法

チャット、構造化JSON、ツール呼び出しの往復、SSEの終了、入力エラーを個別に確認。Pythonスクリプトとローカル異常系テスト、実測結果を掲載します。

一つの穴には合うがすべてには合わない部品を示す検査ゲージ。

チャットへのリクエストが一度成功しても、そのOpenAI互換APIでエージェントが動くとは限りません。 構造化出力、ツール実行結果の返送、ストリーミングの正常終了、扱えるエラーなど、必要な処理を個別に試してください。エンドポイントと正確なモデルIDも記録します。互換性は事業者に永久に付く属性ではなく、特定の経路・リクエスト・レスポンスについて確認するものです。

この記事では、Chat Completions形式を調べる小さなPythonスクリプトを提供します。リクエストは最大6回で、自動再試行はしません。OpenCode、Copilot、n8nの設定手順とは別の検証です。クライアントが使う経路を先に確認し、その後クライアント自体でも試します。ここで通過しても、Responses API、負荷耐性、アプリが送る全パラメータの対応を認定するわけではありません。

検証スクリプトと対象範囲を確認する

check_api.pyをダウンロードしてください。ツールの往復には最初の呼び出しと結果を含む2回目が必要なため、5項目を最大6リクエストで調べます。

項目スクリプトが求める条件通過しても確認できないこと
基本チャット指定した短い回答を含む、解析可能なChat Completionsレスポンスツール、ストリーミング、画像理解
構造化出力strictスキーマの要求が受理され、JSONがスキーマと既知の正解値に一致全JSON Schemaキーワード、任意データからの安定した抽出
ツールの往復種類・関数名・引数・呼び出しIDが正しく、ローカル結果を返した後の回答が完全一致引数のストリーミング、並列ツール、リモートツールの安全性
ストリーミングSSEのContent-Type、解析可能なイベント、期待する文字列、stop、[DONE]Responsesイベントとの互換性、再接続動作
入力検証エラー型の不正なフィールドにHTTP 400/422とJSONエラーオブジェクト全エラー種別、再試行方針、課金規則

ローカル回帰テストはHTTP呼び出しを固定レスポンスに置き換えます。ソケットを開かず、APIキーも不要です。check_api.pyと同じ場所に置き、後述の依存パッケージを入れて実行します。

python test_check_api.py
python -O test_check_api.py

2026年11月7日、通常実行と最適化実行の両方で9ケースが期待どおりの結果になりました。正常な通信、途中で切れたストリーム、誤ったツール回答、誤ったツール種類、不正なスキーマ値、誤ったチャット回答、nullの内容、認証エラー、レート制限です。これは実際に行ったローカル検証スクリプトのテストであり、ホストされたモデルへの呼び出しではありません。ケースの合格とは観測した状態が期待状態と一致したことです。壊したレスポンスまですべてPASSになってはいけません。モックのPASSを事業者の実測として引用しないでください。実測には日付、経路、モデル、使用量の証拠が別途必要です。

エンドポイントと費用の範囲を決める

事業者のドキュメントからAPIルートを確認します。このスクリプトは、たとえば/v1に/chat/completionsを付けます。完全な/chat/completionsパスやモデル紹介ページをルートとして渡さないでください。認証情報を調べる前に、モデルがそのプロトコルに対応しているか確認します。

Ofoxでは現在のAPIドキュメントと対象モデルの説明を参照してください。OpenAI互換という説明は、すべてのOpenAIエンドポイントへの対応を意味しません。ツール利用にResponsesが必要なモデルでは、URLだけ変更するのではなくResponses移行手順を使います。

利用を許可された低コストのモデルを選び、現在の価格を確認し、可能ならアカウント単位の支出上限を設定してください。合成データと小さな出力トークン上限を使いますが、トークン指定は金額上限の保証ではありません。 フィールドが拒否されたり異なる扱いになったりする場合があり、推論トークンの扱いも異なります。短い例だからといって高価なモデルで実行しないでください。

既定の上限フィールドはmax_completion_tokensです。対象モデルの仕様がmax_tokensを要求する場合は対応するオプションを使います。上限フィールドの拒否はリクエストを仕様に合わせる必要があるという意味であり、テキスト生成ができない証拠ではありません。

分離した環境で一度実行する

サポートされるTLSライブラリを備えた現在のPython 3環境を使います。アプリの依存関係を変えないよう仮想環境を作成してください。

python3 -m venv .venv
source .venv/bin/activate
python -m pip install requests jsonschema

普段使うローカルの秘密情報管理方法でLLM_API_KEYを設定します。キーをコマンドライン引数、公開ノートブック、スクリーンショット、配布スクリプトに書かないでください。提供されていれば一時的な制限付き認証情報を使います。

python check_api.py \
  --base-url https://api.example.com/v1 \
  --model YOUR_EXACT_MODEL_ID \
  --token-field max_completion_tokens \
  --max-output 256 \
  --out first-run.json

api.example.comとYOUR_EXACT_MODEL_IDは例示用です。文書化されたエンドポイントと実際のモデルIDに置き換えてください。明示したローカルループバックの試験を除きHTTPSが必要です。リダイレクトは無効で、誤った宛先からBearer認証情報が別の場所へ黙って転送されないようにしています。既存レポートの上書きも拒否するため、再実行時は新しいファイル名にします。

接続・読み取りタイムアウトと、完結したSSE行の間で確認する期限を持ちますが、実時間での厳密な終了期限ではありません。 サーバーが未完成の行を少しずつ送ると読み取りが続く可能性があります。この入門用スクリプトは対話的に実行し、応答が進まないリクエストは中断してください。無人の本番監視に使うなら、プロセス単位の強制終了期限とレスポンスサイズ制限を実装する必要があります。

スコアではなく証拠として結果を読む

PASSはその検査条件を満たしたこと、FAILはリクエスト拒否や期待する仕様からの逸脱、UNKNOWNは通信障害や予期しない形式により確かな結論を出せないことを示します。詳細欄と実際のHTTPステータスを読んでから対処してください。

ネットワーク障害はツール非対応の証拠ではありません。401はJSON Schemaの失敗ではありません。意図的に不正にしたリクエストの429も、型検証の成功ではありません。検証に到達する前に制限された可能性があるからです。そのため認証・レート制限エラーを入力検証テストの成功として扱いません。

5項目を平均して「互換性80%」としないでください。ツール必須のエージェントは、無関係な4項目が通ってもツールを使えない経路では動きません。アプリの機能を必須と任意に分けた要件表で判断します。

Ofoxで実際に6リクエストを実行した結果

2026年11月7日、https://api.ofox.ai/v1に対し、アカウントに表示されたopenai/gpt-6-lunaをmax_completion_tokens: 256で試しました。不正な要求を含む6リクエストで再試行はありません。実測JSONに応答時間と返された使用量があります。この日本語版は同じ実験を説明しており、追加の実行を意味しません。

項目観測結果
基本チャットPASS:指定した答えと正常終了
構造化出力PASS:スキーマ、SKU、数量が正解と一致
ツールの往復PASS:2回目の回答が正確に17
ストリーミングPASS:文字列、終了理由、[DONE]を確認
不正なパラメータPASS:JSONエラーオブジェクト付きHTTP 400

保存した実際のAPI結果JSONを表示する英語のローカルレポート画面。

これは保存済みJSONをローカルで表示した画面です。Ofoxの管理画面でも追加のライブテストでもありません。

結果はその時点のモデル・経路・入力に限られます。全モデル、サーバー内部のstrictスキーマ強制、Responses、ツール引数ストリーミング、fallback、クライアントの追加パラメータを認定しません。不正な要求の400は期待した検査成功であり、サービス停止ではありません。

ツールの結果をモデルへ返す理由

tool_callsがあるだけではやり取りの前半しか確認できません。アプリは呼び出しを検証し、許可されたツールを実行し、対応するIDとともに結果を返します。2回目のモデル回答がその結果を正しく使うことまで確認します。

検証スクリプトは読み取り専用ローカル関数lookup_stockを一つ公開します。合成SKU 000102の在庫は固定値17です。種類、関数名、JSON引数、呼び出しIDを調べてから返送し、最終回答が完全に17と一致することを要求します。モデルが指定する任意の関数やシェルコマンドは実行しません。

課題を絞ると失敗を調べやすくなります。最初の呼び出しが正しく2回目だけ失敗するなら、モデル変更より先に結果メッセージの形式とIDを調べます。170は「17」を含んでいても不合格です。明示的な検証例外を使うため、Pythonの最適化を有効にしても検査は残ります。

実際のアプリではスキーマで引数を検証し、許可する操作を制限し、実行前に権限を確認してください。有効なツール呼び出しはメール送信、支払い、データベース更新の許可ではありません。

正しいJSONと完了した作業を区別する

構造化出力の正解は{"sku":"000102","quantity":3}です。SKUは文字列、数量は整数、両方必須、追加プロパティは禁止です。スキーマの後に既知の値を照合します。一度正しい答えが出ても、サーバーがstrictを強制した証拠にはなりません。ゲートウェイがフィールドを無視してもモデルが同じJSONを返すことはあります。与えた入力での観測として扱ってください。

JSONが解析できてもSKUの先頭ゼロが落ちる場合があります。型が正しくても値は誤り得ます。通常のJSONモードは使えてもstrictスキーマは拒否されることがあります。別の能力なので、緑の結果を得るためにstrictを黙って削除しないでください。

OpenAIの構造化出力ドキュメント(英語)は構造化レスポンスと関数呼び出しを区別しています。対象エンドポイントの仕様を使い、異なるAPI形式からフィールドだけコピーしないでください。実際の目的が抽出なら、正解付き文書とテキストからJSONへの検証手順(英語)へ進みます。小さな固定スキーマは初期確認にすぎません。

ストリームの終了まで確認する

文字が出始めてもレスポンスは未完成かもしれません。検証スクリプトはSSEイベントを読み、テキスト差分を結合し、終了理由とChat Completionsの[DONE]を要求します。途中の文が自然でも切断されたストリームは失敗です。

これは選択したプロファイルの条件で、全APIの共通規則ではありません。Responsesのイベント形式は異なり、終端での使用量の返し方も事業者ごとに違います。この検証スクリプトはstream_optionsを要求せず、ストリーム使用量がないだけでは失敗としません。課金や監視に必要なら、提供元が文書化した使用量オプションを別に試してください。

切断後は、外部への作用が既に起きたかもしれないワークフローを無条件に再実行しないでください。ツールや後続処理が実行済みか先に確認します。ストリーム切断のトラブルシューティングは特定クライアントの問題を扱います。一般的な通信再試行だけで安全な再開は保証されません。

原因のある層で修正する

観測最初に調べる点
全要求が401/403認証情報、アカウントアクセス、エンドポイント権限
404またはmodel-not-foundURL、モデルID、アカウントでの提供状況。モデル未検出の対処(英語)
チャット成功、strict失敗応答形式の対応と許可されるスキーマ範囲
最初のツール要求が失敗プロトコル、モデル能力、要求フィールド
ツール結果の返送が失敗呼び出しID、履歴、結果メッセージ形式
文字は出るが終わらないSSEフレーミング、プロキシのバッファ、接続終了、プロファイル不一致
n8n認証は成功、実行失敗実際の操作エンドポイント。n8nの経路診断

必要項目が通ったらOpenCodeのカスタムプロバイダー設定(英語)などを行い、クライアント経由で小さな課題を再試験します。クライアントはこの検証スクリプトにないフィールドを送ることがあります。直接APIの成功がアプリを保証すると考えず、両方の記録を残してください。

共有前には非公開エンドポイント名、リクエストID、アカウント固有情報があれば除去します。配布例は合成出力、モデル、時間、状態、使用量だけを記録し、キーを書きません。モデル版や経路を変える際も日付付きの証拠を残す方が、包括的な「互換」表示より役立ちます。