このガイドでは、ツールを定義し、OpenAIに送信し、モデルが返すツールコールを読み取り、構造化された引数で独自の関数を実行する手順を実装ベースで確認します。さらに、厳格モードと並列呼び出しを設定し、Apidog を使ってツール側の入力をアサートし、ダウンストリームAPIをモックすることで、本番投入前に出力の信頼性を確認します。仕様の詳細はOpenAIの関数呼び出しドキュメントを参照し、より上位の概要はOpenAI Agents SDKでエージェントを構築するの記事を確認してください。
始める前に必要なもの
関数呼び出し(ツール呼び出し)は、モデルをあなたのコードや外部システムに接続する仕組みです。
基本の役割分担は次のとおりです。
- モデル: ユーザーの意図を読み取り、呼び出す関数名と引数を生成する
- あなたのアプリ: 引数を検証し、実際の関数やAPIを実行する
- モデル: 実行結果を受け取り、最終回答を生成する
たとえば、ユーザーが「パリの天気は?」と聞いた場合、モデルは自然文を直接処理する代わりに、次のような構造化された呼び出しを返します。
get_weather({ location: "Paris, France" })
このガイドを試すには、次のものが必要です。
- OpenAI APIキー
- モデルに呼び出させたいアプリケーション側の関数
- Chat Completions APIまたはResponses APIのどちらを使うかの方針
関数呼び出しは、Chat Completions APIとResponses APIの両方で利用できます。形式は少し異なりますが、実装ループは同じです。
ステップ1:ツールを定義する
ツールは、モデルに公開する関数定義です。主に次の情報を含めます。
-
name: 関数名 -
description: いつ使うべきかの説明 -
parameters: 引数のJSONスキーマ -
strict: スキーマ準拠を強制するかどうか
description は単なるラベルではなく、モデルへの指示として書きます。
Chat Completions APIのツール定義
{
"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のツール定義
Responses APIでは、name、description、parameters、strict をツールオブジェクトのトップレベルに置きます。
{
"type": "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
}
}
すでにOpenAPI仕様を管理している場合、パラメータ定義は関数呼び出しのスキーマにも流用しやすくなります。OpenAPI仕様からテストコレクションを生成する方法を使うと、API定義をテストにも再利用できます。
ステップ2:最初のリクエストを作成する
ユーザーのメッセージとツール定義を一緒にモデルへ送信します。
Chat Completions APIでは次のようになります。
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 APIの場合
Chat Completions APIでは、アシスタントメッセージの tool_calls 配列にツールコールが入ります。
{
"id": "call_12345xyz",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"location\":\"Paris, France\"}"
}
}
Responses APIの場合
Responses APIでは、output 配列に function_call として返されます。
{
"type": "function_call",
"call_id": "call_12345xyz",
"name": "get_weather",
"arguments": "{\"location\":\"Paris, France\"}"
}
重要なのは、arguments がオブジェクトではなくJSONエンコードされた文字列で返る点です。必ずアプリケーション側でパースしてから使います。
const args = JSON.parse(toolCall.function.arguments)
const result = await getWeather({
location: args.location,
unit: args.unit
})
ステップ4:関数を実行して結果をモデルに返す
関数を実行したら、その結果をモデルに返します。これにより、モデルはユーザー向けの最終回答を生成できます。
実装ループは次の流れです。
- ユーザーの入力をモデルへ送る
- モデルがツールコールを返す
- アプリ側で
argumentsをパースする - 対応する関数を実行する
- 実行結果をモデルに返す
- モデルが最終回答を返す
Chat Completions APIでは、tool_call_id に対応する tool ロールのメッセージを追加します。
{
"role": "tool",
"tool_call_id": "call_12345xyz",
"content": "{\"temperature\": 18, \"unit\": \"celsius\", \"condition\": \"Cloudy\"}"
}
Responses APIでは、call_id に対応する function_call_output を送信します。
{
"type": "function_call_output",
"call_id": "call_12345xyz",
"output": "{\"temperature\": 18, \"unit\": \"celsius\", \"condition\": \"Cloudy\"}"
}
ステップ5:並列呼び出しと厳格モードを設定する
基本ループが動いたら、信頼性と実行制御のために次の設定を確認します。
| 設定 | 制御するもの | デフォルト | 変更するタイミング |
|---|---|---|---|
parallel_tool_calls |
1回のターンで複数のツール呼び出しを返せるか | true |
呼び出し順序が重要な場合や、依存関係がある場合は false
|
strict |
引数がJSONスキーマに一致することを強制するか | 未設定時はベストエフォート | 本番相当の処理では有効化を推奨 |
tool_choice |
モデルがツールを呼ぶか、どのツールを呼ぶか | auto |
強制するなら required、無効化するなら none、特定関数を指定するなら関数名 |
並列ツール呼び出し
デフォルトでは、モデルは1回のターンで複数のツールコールを返すことがあります。
たとえば、ユーザーが「東京、パリ、ニューヨークの天気を教えて」と聞いた場合、3つの get_weather 呼び出しが返る可能性があります。
並列実行できる処理なら高速化できますが、順序が重要な処理では無効化します。
{
"parallel_tool_calls": false
}
厳格モード
strict: true を設定すると、モデルが返す引数がJSONスキーマに一致するようになります。
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city.",
"strict": true,
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string"
},
"unit": {
"type": ["string", "null"],
"enum": ["celsius", "fahrenheit", null]
}
},
"required": ["location", "unit"],
"additionalProperties": false
}
}
}
厳格モードでは、次の点に注意します。
- すべてのオブジェクトに
additionalProperties: falseを設定する -
properties内のフィールドはrequiredに含める - オプション扱いにしたいフィールドは、省略ではなく
nullを許可する
たとえば unit を任意にしたい場合でも、キー自体は必須にし、値として null を許可します。これにより、アプリ側のパース処理は毎回同じキー構造を期待できます。
ただし、厳格モードは「構造」を保証するだけです。location が実在する都市か、サービス対象エリアか、APIが成功するかまでは保証しません。そこはアプリ側の検証とテストが必要です。
Apidogでテストする方法
モデルはツールコールを返しますが、それを実際の関数に接続する前に確認すべきことが2つあります。
- モデルが返した引数が期待するスキーマに一致しているか
- 関数が呼び出すダウンストリームAPIが想定どおりに動作するか
Apidog は、このAPI契約の検証とモックに使えます。
Apidogは、あなたのアプリケーション内の関数そのものを実行するわけではありません。主に次の2つを担当します。
1. ツールコールの引数をアサートする
OpenAIから返された arguments 文字列を取得し、リクエストボディとして扱います。
確認する項目の例です。
-
locationが存在する -
locationが文字列である -
unitが許可されたenumのいずれかである - 必須フィールドが欠けていない
- 余分なプロパティが含まれていない
特定フィールドの抽出にはJSONPath式を使えます。より厳密にチェックする場合は、OpenAIに渡したものと同じスキーマを使ってJSONスキーマに対するバリデーションを行います。
2. ダウンストリームAPIをモックする
get_weather 関数は、おそらく外部の天気APIを呼び出します。
しかし開発中は、次のような問題が起こりがちです。
- 外部APIにレート制限がある
- APIが有料である
- まだ本物のAPIが完成していない
- エラーケースを再現しにくい
この場合、Apidogで現実的な天気レスポンスを返すモックAPIを作成し、get_weather 関数の接続先をそのモックに向けます。
これにより、次のケースを本番APIなしで検証できます。
- 正常レスポンス
- タイムアウト
429 Too Many Requests- 不正なレスポンス形式
- 空の結果
- サービス対象外の都市
おすすめのテストフローは次のとおりです。
- OpenAIからツールコールを取得する
-
argumentsをJSONとしてパースする - Apidogでスキーマに対してアサートする
- アプリ側の関数を実行する
- 関数からApidogのモックAPIを呼び出す
- 正常系と異常系の両方を確認する
これにより、モデル出力の契約と外部APIの契約を、本番呼び出しを消費せずに検証できます。
よくある質問
関数呼び出しはChat Completions APIとResponses APIの両方で使えますか?
はい。両方でサポートされています。
主な違いは形式です。
- Chat Completions API:
functionキーの下に関数定義をネストし、tool_callsを返す - Responses API: フラットなツール定義を使い、
output配列にfunction_callを返す
なぜ arguments はオブジェクトではなく文字列で返るのですか?
arguments はJSONエンコードされた文字列として返ります。使う前に必ずアプリ側でパースしてください。
const args = JSON.parse(toolCall.function.arguments)
そのまま信頼せず、JSONスキーマ検証に通してから関数に渡すのが安全です。
厳格モードを使えば関数の成功は保証されますか?
いいえ。
厳格モードが保証するのは、引数の構造がJSONスキーマに一致することです。値がビジネスロジック上正しいか、外部APIが成功するか、関数が例外を出さないかは保証しません。
そのため、次の処理は引き続きアプリ側で必要です。
- 値の検証
- 権限チェック
- サービス対象範囲の確認
- タイムアウト処理
- リトライ処理
- エラーハンドリング
Apidogは実際の関数を実行できますか?
いいえ。Apidogはアプリケーション内の関数そのものを実行するツールではありません。
Apidogは主に次を担当します。
- モデルが生成した引数の検証
- 関数が依存するAPIのモック
- API契約の確認
- 正常系と異常系レスポンスのテスト
実際の関数実行は、あなたのアプリケーション側で行います。
まとめ
OpenAIの関数呼び出しは、次のループで実装します。
- ツールをJSONスキーマ付きで定義する
- ユーザーメッセージと一緒にモデルへ送る
-
tool_callsまたはfunction_callを読み取る -
argumentsをパースして検証する - アプリ側の関数を実行する
- 結果をモデルに返す
- モデルに最終回答を生成させる
本番向けには、strict: true を有効化し、parallel_tool_calls と tool_choice を用途に合わせて設定します。
さらに、Apidogでツールコールの引数をスキーマに対してアサートし、関数が依存するAPIをモックすれば、実際の外部APIを消費せずに入力と依存関係の契約を検証できます。
テスト側を試す場合は、Apidogをダウンロードして、ツールコールの引数検証とAPIモックを一箇所で管理できます。

Top comments (0)