このガイドの終わりまでに、ツールを定義し、それをOpenAIに送信し、モデルが返すツールコールを読み取り、構造化された引数で独自の関数を実行できるようになります。また、厳格モードと並列呼び出しをオンにし、Apidog を使用してツール側をアサートおよびモックすることで、本番環境にデプロイする前に出力が信頼できるものとなるようにします。OpenAIの関数呼び出しドキュメントを別のタブで開いて真実のソースとして参照し、より上位の概要についてはOpenAI Agents SDKでエージェントを構築するに関する入門記事をご覧ください。
始める前に必要なもの
関数呼び出し(ツール呼び出しとも呼ばれる)は、モデルがあなたのコードや外部システムに接続する方法です。アプリが公開する関数を記述すると、モデルはユーザーのリクエストを読み取り、関数が適切であると判断した場合、関数名と引数のJSONオブジェクトを返します。モデルはそれ自体で何も実行しません。構造化されたリクエストを渡し、あなたのコードがその処理を行います。
構築する際にこの役割分担を覚えておくことが重要です。モデルは意図を抽出し、パラメータを埋めます。実行の制御はあなたが持ちます。「パリの天気」というメッセージは、手動で解析する必要がある段落ではなく、get_weather({"location": "Paris, France"})というクリーンな呼び出しに変換されます。
このガイドを進めるには、OpenAI APIキーと、モデルにトリガーさせたい独自のコード内の関数が必要です。もう一つ事前に決めておくべきことがあります。それは、どのエンドポイントを使用するかです。この機能は2つの場所で動作します。古いChat Completions APIと、Chat CompletionsおよびAssistants APIに分かれていた機能を統合した新しいResponses APIの両方がサポートしています。形式はわずかに異なりますが、以下の手順では両方をカバーしています。
ステップ1:ツールを定義する
ツールは、モデルが読み取ることができる関数定義です。ツールには、名前、説明、そして引数用のJSONスキーマを与えます。ここで説明が実際に機能します。これは、モデルにいつその関数を使用すべきかを伝えるため、ラベルではなく指示のように記述してください。
これは、functionラッパーの下に機能が配置されるChat Completions形式のツール定義です。
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city. Use when the user asks about temperature or conditions.",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City and country, e.g. Bogotá, Colombia"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location"],
"additionalProperties": false
}
}
}
Responses APIでは、これがフラット化されます。name、description、parameters、strictフィールドは、ネストされたfunctionキーなしで、ツールオブジェクトのトップレベルに配置されます。同じ情報ですが、階層が少なくなります。
基礎となるサービスについて既にOpenAPI仕様を管理している場合、パラメータの形式はほぼ直接的に引き継がれます。OpenAPI仕様からテストコレクションを生成する方法に関する私たちのチュートリアルでは、そのスキーマ作業がいかに二重に報われるかを示しています。
ステップ2:最初のリクエストを作成する
ユーザーのメッセージとともにツールをモデルに送信します。これを行う完全なChat Completionsリクエストは次のようになります。
curl https://api.openai.com/v1/chat/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4.1",
"messages": [
{"role": "user", "content": "What is the weather in Paris right now?"}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string"}
},
"required": ["location"],
"additionalProperties": false
}
}
}
]
}'
```tools配列には、このターンで公開したいすべての関数が含まれます。モデルはユーザーメッセージを読み取り、どのツールが適切かを判断し、応答します。モデルがツールを選択すると、散文ではなくツールコールが返されます。これは次のステップで読み取ります。
ステップ3:モデルが返すツールコールを読み取る
モデルが関数を呼び出すことを決定した場合、テキストは返しません。代わりに、レスポンスから読み取るツールコールを返します。
Chat Completionsでは、アシスタントメッセージにtool_calls配列が含まれます。各エントリにはid、function型のtype、およびnameとarguments文字列を含むfunctionオブジェクトがあります。
{
"id": "call_12345xyz",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"location\":\"Paris, France\"}"
}
}
```Responses APIでは、コールはoutput配列にフラットな形式で表示されます。つまり、function_call型のtype、call_id、name、およびargumentsです。
{
"type": "function_call",
"call_id": "call_12345xyz",
"name": "get_weather",
"arguments": "{\"location\":\"Paris, France\"}"
}
```注意すべき点として、argumentsは解析されたオブジェクトではなく、JSONエンコードされた文字列です。自分で解析し、関数を実行してから、モデルが返信を完了できるように結果を返します。
ステップ4:結果をモデルに返す
関数を実行した後、モデルが最終的な回答を生成できるように結果を返します。Chat Completionsでは、コールidをキーとするtoolロールメッセージを追加します。Responses APIでは、call_idをキーとするfunction_call_output項目を送信します。どちらの方法でもループは同じです。モデルが尋ね、あなたが実行し、結果を返し、モデルが応答します。
ステップ5:並列呼び出しと厳格モードを追加する
これらの2つの設定は、信頼性と速度に影響を与え、基本的なループが機能するようになったら追加します。
並列ツール呼び出し。 デフォルトでは、モデルは1回のターンで複数のツール呼び出しを返すことができます。ユーザーが3つの都市の天気を尋ねた場合、一度に3つの呼び出しを受け取り、それらを同時に実行できるかもしれません。1ターンあたり最大1つの呼び出しを強制したい場合は、parallel_tool_callsをfalseに設定します。
厳格モード。 関数定義でstrict: trueを設定すると、モデルの引数はベストエフォートではなく、JSONスキーマに一致することが保証されます。OpenAIは常に有効にすることを推奨しています。厳格モードにはルールがあり、すべてのオブジェクトはadditionalProperties: falseを必要とし、properties内のすべてのフィールドはrequiredに含める必要があります。フィールドをオプションにするには、requiredから削除するのではなく、許可される型のリストにnullを追加します。
| 設定 | 制御するもの | デフォルト | 変更するタイミング |
|---|---|---|---|
parallel_tool_calls |
1回のターンで複数のツール呼び出しが返されるかどうか | true |
呼び出しが相互に依存する場合や順番に実行する必要がある場合はfalseに設定します |
strict |
引数がスキーマに一致するように強制されるかどうか | 設定されていない場合はベストエフォート。オンにすることを推奨 | 防御的なコードなしで解析するすべての呼び出しで有効にする |
tool_choice |
モデルがどの関数を呼び出すことができるか、また呼び出すかどうか | auto |
呼び出しを強制するにはrequired、無効にするにはnone、特定の関数を指名するには名前を設定 |
オプションフィールドのルールは、人々を驚かせることがあります。get_weatherでunitがオプションであるとします。厳格モードでは、それをrequiredにリストし、スキーマ内で"unit": {"type": ["string", "null"], "enum": ["celsius", "fahrenheit"]}のようにnull許容としてマークします。モデルは実際の単位または明示的なnullを渡すことができますが、キーを省略することはできません。この予測可能性こそが重要な点であり、あなたの解析コードは毎回どのキーを期待すべきかを正確に知っています。
厳格モードは不正な形式のJSONを減らしますが、値がビジネス的に意味があるかどうかはチェックしません。場所がスキーマ的には有効であっても、提供していない都市である可能性もあります。そこでテストが必要になります。
Apidogでテストする方法
モデルはツールコールを返します。それを実際の関数に接続する前に、2つの保証を求めたいはずです。それは、引数が期待する形式に一致すること、そして関数が呼び出すであろうダウンストリームAPIが想定通りに動作することです。Apidogはその両面をカバーしており、どの側面をカバーするのかを正確に理解しておく価値があります。

ApidogはAPI側を検証し、モックします。Apidogはあなたのアプリケーションの関数を実行しません。Apidogが優れているのは、それらに関する契約を扱うことです。
引数の構造をアサートする。 実際のツールコールからarguments文字列を取得し、それをリクエストボディとして扱い、Apidogでアサーションを実行します。locationが存在し文字列であること、enumフィールドが常に許可された値のみを保持すること、必須フィールドが存在することを確認します。ペイロードから特定のフィールドを抽出するのはJSONPath式で簡単であり、より深い構造チェックには、厳格モードでOpenAIに渡したスキーマと同じものを反映するJSONスキーマに対するバリデーションがあります。モデルの出力が関数が期待するスキーマと同じものに合格すれば、ループが閉じられたことになります。
関数が呼び出すダウンストリームAPIをモックする。 あなたのget_weather関数はおそらく天気プロバイダーを呼び出すでしょう。開発中、そのプロバイダーはレート制限がかかっていたり、有料だったり、まだ構築されていなかったりするかもしれません。Apidogで現実的な天気ペイロードを返すモックAPIを立ち上げ、そのモックにあなたの関数を向け、実際のサービスにリクエストを費やすことなくコールパス全体をテストします。ライブAPIがオンデマンドでめったに生成しないエラーケースを含め、応答を制御できるため、ユーザーが発見する前にコードがタイムアウトや429エラーを処理できることを確認できます。
まとめると、ワークフローは次のようになります。OpenAIからツールコールをキャプチャし、Apidogでその引数をスキーマに対してアサートし、その後、Apidogの実際のAPIのモックに対して関数を実行します。これにより、実際の呼び出しを消費したり、エッジケースを推測したりすることなく、両端の契約を確認できます。
よくある質問
関数呼び出しはChat CompletionsとResponses APIの両方で機能しますか? はい、両方のエンドポイントでサポートされています。Responses APIは、以前Chat CompletionsとAssistants APIの間で分割されていた機能を統合します。主な違いは形式です。Chat Completionsはfunctionキーの下に関数をネストしtool_callsを返しますが、Responses APIはフラットなツール定義を使用し、output配列にfunction_call項目を返します。
モデルは引数をオブジェクトではなく文字列として返すのはなぜですか? argumentsフィールドはJSONエンコードされたテキストです。使用する前にコードで解析します。これは両方のAPIで一貫しているため、盲目的に信頼するのではなく、常にJSONパーサーを通して結果を検証してください。それらの引数をJSONスキーマ検証にかけることで、不正な形式のペイロードが関数に到達する前に検出できます。
厳格モードは関数が成功することを保証しますか? いいえ。厳格モードは引数がJSONスキーマに一致することを保証するため、構造は信頼できます。しかし、値がビジネスロジックに対して正しいかどうかはチェックせず、関数を実行することもありません。値の検証とダウンストリーム呼び出しの失敗処理は、依然としてあなた自身が行う必要があります。
Apidogは私の実際の関数を実行できますか? いいえ、試みません。Apidogはモデルが生成した引数を検証し、あなたの関数が依存するAPIをモックします。あなたのアプリケーションは依然として自身の関数を実行します。Apidogは両端の契約をカバーするため、本番環境に移行する前に、入力と依存関係を信頼することができます。
まとめ
これで、完全なループができました。ツールを明確に定義し、リクエストとともに送信し、tool_callsまたはfunction_callの出力を読み取り、結果を返し、その後厳格モードをオンにして、並列呼び出しが役立つか害になるかを判断します。引数がスキーマと一致することをアサートし、ダウンストリームAPIをモックすることでテストを完了させ、本番環境にデプロイする前に自信を持てるようにします。
テスト側を試したいですか?Apidogをダウンロードして、ツールコールの引数をスキーマに対してアサートし、関数が依存するAPIをすべて一箇所でモックしましょう。
