Tool Calling
モデルにツール呼び出しを提案させ、アプリケーションで安全に実行して結果を返します。
Tool Callingを使用すると、モデルが会話に基づいて1つ以上のツールを選択し、呼び出しに必要な構造化引数を生成できます。モデルがアプリケーションの代わりにデータベースへアクセスしたり、内部サービスを呼び出したり、書き込み操作を実行したりすることはありません。実際のツール実行は常にプログラムが担当します。
注文の照会、天気情報の取得、ナレッジベースの検索、データ計算、認可された明確な業務操作の実行に適しています。
モデルが生成した引数を直接実行しないでください
ツール名と引数はモデルの出力です。実行前にJSON解析、Schema検証、権限確認、業務ホワイトリストの確認を行ってください。支払い、削除、メッセージ送信などの高リスク操作では、ユーザー確認と冪等性制御も必要です。
完全な呼び出しループ
1. ツールを定義する
明確な名前、用途説明、JSON Schemaを使用して、ツールを利用できる状況と引数の構造をモデルに伝えます。
2. モデルへリクエストする
ユーザー入力とツール定義を一緒に送信します。モデルは直接回答することも、0件、1件、または複数のツール呼び出しを返すこともあります。
3. 検証して実行する
ツール名からローカル実装を検索し、すべての引数と呼び出し元の権限を検証してから、タイムアウトを設定して実行します。モデルが任意の関数、URL、SQL、Shellコマンドを指定できるようにしないでください。
4. ツール結果を返す
プロトコルから返された呼び出しIDを使用し、実行結果と元のツール呼び出しを関連付けて、再度モデルへリクエストします。
5. 最終回答を読み取る
モデルはツール結果に基づいてユーザー向けの回答を生成することも、新たなツール呼び出しを提案することもあります。アプリケーションは最大ループ回数を設定し、終了判定を行う必要があります。
読み取り専用ツールを定義する
次のツールは天気を照会するだけで、外部への書き込みは行いません。
{
"type": "function",
"function": {
"name": "get_weather",
"description": "指定した都市の現在の天気を取得する",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "都市名(例:東京)"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["city", "unit"],
"additionalProperties": false
}
}
}ツール説明には「何をするか」と「いつ使用するか」を記載してください。引数名を安定させ、列挙値をできるだけ限定し、requiredとadditionalPropertiesで不要な入力を制限します。
Chatのツールループを完了する
次の例は、ツールの宣言、呼び出しの読み取り、ローカル関数の実行、結果の返送、最終回答の読み取りという主要な流れを示します。
import json
from openai import OpenAI
client = OpenAI(
api_key="your-api-key",
base_url="https://api.agtcloud.cn/v1",
)
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "指定した都市の現在の天気を取得する",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string"},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
},
},
"required": ["city", "unit"],
"additionalProperties": False,
},
},
}
]
def get_weather(city: str, unit: str) -> dict[str, str | int]:
if city != "東京" or unit != "celsius":
raise ValueError("この例では東京の摂氏温度のみ照会できます")
return {"city": city, "unit": unit, "temperature": 25}
messages = [{"role": "user", "content": "東京の現在の気温は何度ですか?"}]
response = client.chat.completions.create(
model="MODEL_ID",
messages=messages,
tools=tools,
tool_choice="auto",
)
assistant_message = response.choices[0].message
messages.append(assistant_message)
for tool_call in assistant_message.tool_calls or []:
if tool_call.function.name != "get_weather":
raise ValueError("モデルが登録されていないツールを要求しました")
arguments = json.loads(tool_call.function.arguments)
result = get_weather(**arguments)
messages.append(
{
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result, ensure_ascii=False),
}
)
if assistant_message.tool_calls:
final_response = client.chat.completions.create(
model="MODEL_ID",
messages=messages,
tools=tools,
)
print(final_response.choices[0].message.content)
else:
print(assistant_message.content)本番アプリケーションでは、JSON Schema検証ライブラリを使用してargumentsを検証し、ツール実装ごとに権限、タイムアウト、エラー種別、監査記録を設定してください。
プロトコルごとのツール呼び出し表現
| プロトコル | ツール定義 | モデルの呼び出し | ツール結果の返送 |
|---|---|---|---|
| Chat Completions | tools[].function | Assistant Message内のtool_calls | role: toolとtool_call_id |
| Responses | tools内のFunction Tool | function_call Output Item | function_call_outputとcall_id |
| Anthropic Messages | toolsとinput_schema | tool_use Content Block | tool_result Content Block |
| Gemini | functionDeclarations | Part内のfunctionCall | Part内のfunctionResponse |
フィールド名と入れ子構造を混在させないでください。完全な構造は、Chat Completions、Responses、Anthropic Messages、Gemini Generate Contentの各Referenceを参照してください。
複数の呼び出しとStreaming
- 1回につき1つのツールだけが呼び出されると仮定せず、呼び出しIDごとに検証、実行、返送します。
- 相互に独立し、業務上許可された読み取り専用の呼び出しだけを並列実行します。
- ストリーミング引数は複数の差分に分かれる場合があるため、完全に連結してからJSONを解析します。
- ツール結果を返した後もモデルが次の呼び出しを提案する場合があるため、最大ターン数と合計タイムアウトを設定します。
- 同じ書き込み操作を再試行するときは、業務上の冪等キーを使用して、重複課金、送信、リソース作成を防ぎます。
失敗の処理
ツール実行が失敗した場合、成功したように見せる結果を作成しないでください。引数不正、権限不足、上流サービスの一時的な利用不可など、機密情報を除いたエラー分類をモデルへ返し、ユーザーへの説明や許可された代替手段の選択に使用できます。
内部スタック、データベース構造、アクセストークン、プライベートネットワークアドレス、機密情報を除いていない第三者レスポンスは、モデルやユーザーへ直接返さないでください。
公開前の確認
- ツールはレジストリまたはホワイトリストからのみ選択できる。
- 引数は実行前にSchemaと業務ルールで検証される。
- 各ツールに権限、タイムアウト、最大結果サイズ、エラー処理がある。
- 高リスクの書き込み操作はユーザー確認を経て、冪等性と監査に対応する。
- 呼び出しループには最大ターン数があり、モデルが無制限に継続できない。
- ストリーミングのツール引数は完全に終了してから実行される。
ツール引数を受信しながら連結する方法はStreamingを、最終回答をJSON Schemaに従わせる方法はStructured Outputを参照してください。