function callingとは?LLMと外部ツールをつなぐ仕組みと実装
function callingは、LLMの依頼をアプリケーションが検証・実行する仕組みです。注文状況の照会を題材に、ツール定義から結果の返却までをPythonで解説。OpenAI・Anthropic・Geminiの形式の違い、引数の検証とアクセス制御、構造化出力やMCPとの使い分けまで整理します。
公開日

function calling(ファンクションコーリング)は、大規模言語モデル(LLM)がアプリケーションに処理を依頼するための仕組みです。役割分担の要点は3つあります。モデルが構造化された呼び出しを提案し、アプリケーション側のコードが検証して実行し、その結果をモデルに返します。データベースを接続するとは、この境界を実装することです。モデルにデータベースのパスワードを渡すことではありません。
function callingはどう動く?
モデルがリクエストを生成し、アプリケーションが処理を実行します。 LLMをAPIやデータベースにつなぐ開発者にとって、コードを書く前に押さえておきたいのが、この役割の違いです。
飲食店の注文伝票を思い浮かべてください。ホールのスタッフは料理を注文できますが、伝票そのものが調理するわけではありません。厨房が注文を確認し、対応できるものを調理して結果を伝えます。アプリケーションでは、ツール呼び出しが伝票に、実装した関数が厨房に当たります。
モデルには、利用できる処理とその入力項目を記述したツールスキーマを渡します。モデルは関数名と、各項目に渡す値である引数を返せます。アプリケーションはそのリクエストを確認して関数を呼び出し、実行結果をモデルに返します。モデルはその結果を使って回答したり、別の処理を依頼したりします。Googleのfunction callingガイドでも、実行を担うのはアプリケーションだと明記されています。
ツールが行うのは書き込みだけではありません。注文の照会、文書コレクションの検索、空き状況の確認も、データを変更しなくてもツールによる処理です。初めて連携を実装するなら、対象を絞った照会から始めるとよいでしょう。取り消しが必要になるトランザクションを導入せずに、一連の流れを確認できます。

function callingとtool callingの違いは?
独自に定義した関数を使う場合、どちらも同じ仕組みを指すことが多いです。 OpenAIは両方の名称を使い、Anthropicもtool useはfunction callingとも呼ばれると説明しています。ただし、より広い意味を持つ「ツール」には、自分で実装する関数だけでなく、プロバイダーがホストする機能も含まれます。名称より大切なのは、どこで実行されるかです。自分のデータベースにアクセスする関数なら、その実行はアプリケーションが担います。
この違いは、後述する3社のドキュメントを見ると具体的に分かります。メッセージ形式はプロバイダーごとに異なるため、リクエストと結果の形式は必ず同じプロバイダーの仕様でそろえてください。
function callingを最小構成で実装する
注文状況を照会するツールを公開し、データベースが返した状況だけを回答に使います。 以下の例では、ローカルのSQLiteデータベースに架空の注文ORD-1042を用意します。説明用のテストデータであり、実際の顧客レコードや、モデルから取得した応答ではありません。
OpenAI function callingでリクエストから結果の返却までを試す
OpenAIのfunction callingドキュメントには、Responses APIでの一連の流れが示されています。関数を宣言し、function_callを受け取り、自分のコードを実行して、同じcall_idを持つfunction_call_outputを返します。
python -m pip install openaiでPython SDKをインストールし、環境変数にOPENAI_API_KEYを設定して、以下をorder_tool.pyとして保存してください。この例では、現行ドキュメントのResponsesのサンプルで使われているgpt-6-astraを採用しています。このモデルだけが適しているという意味ではありません。スクリプトを実行すると実際のAPIリクエストが送られますが、データベース自体はメモリ内にとどまります。
import json
import re
import sqlite3
from openai import OpenAI
client = OpenAI()
model = "gpt-6-astra"
# Demo identity. In a web app, obtain this from your authenticated session.
tenant_id = "tenant_demo"
db = sqlite3.connect(":memory:")
db.execute("CREATE TABLE orders (tenant_id TEXT, order_id TEXT, status TEXT)")
db.execute(
"INSERT INTO orders VALUES (?, ?, ?)",
(tenant_id, "ORD-1042", "shipped"),
)
# The tool schema: a name, a purpose, and an input contract.
tool = {
"type": "function",
"name": "get_order_status",
"description": (
"Look up the current status of an existing order by order ID. "
"Use for status questions, not refunds or address changes. "
"Returns the order ID and status, or an error."
),
"strict": True,
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "The order ID supplied by the user, such as ORD-1042.",
}
},
"required": ["order_id"],
"additionalProperties": False,
},
}
def execute(name, raw_arguments):
if name != "get_order_status":
return {"error": "unknown_tool"}
try:
args = json.loads(raw_arguments)
except (json.JSONDecodeError, TypeError):
return {"error": "invalid_arguments"}
if not isinstance(args, dict) or set(args) != {"order_id"}:
return {"error": "invalid_arguments"}
order_id = args["order_id"]
if not isinstance(order_id, str) or not re.fullmatch(r"ORD-[0-9]+", order_id):
return {"error": "invalid_order_id"}
row = db.execute(
"SELECT status FROM orders WHERE tenant_id = ? AND order_id = ?",
(tenant_id, order_id),
).fetchone()
if row is None:
return {"error": "order_not_found"}
return {"order_id": order_id, "status": row[0]}
history = [{"role": "user", "content": "What is the status of order ORD-1042?"}]
response = client.responses.create(
model=model,
input=history,
tools=[tool],
tool_choice={"type": "function", "name": "get_order_status"},
parallel_tool_calls=False,
)
# Keep every output item, including any reasoning items.
history.extend(response.output)
calls = [item for item in response.output if item.type == "function_call"]
if len(calls) != 1:
raise RuntimeError("Expected one tool call; inspect the response before continuing.")
call = calls[0]
result = execute(call.name, call.arguments)
history.append({
"type": "function_call_output",
"call_id": call.call_id,
"output": json.dumps(result),
})
answer = client.responses.create(
model=model,
input=history,
tools=[tool],
tool_choice="none",
instructions="Answer from the tool result. If it contains an error, report the failure.",
)
print(answer.output_text)
db.close()この例では、仕組みを確認しやすいように動作範囲を絞っています。正常に処理が進むと、モデルへのリクエストが2回、データベース照会が1回発生します。最初のリクエストでは指定の照会を必ず呼び出し、次のリクエストでは追加のツール呼び出しを無効にしています。汎用的なアシスタントを作るなら、適したツールがない場合、情報が不足している場合、追加の照会が必要な場合にも対応する必要があります。
ツールスキーマにtenant_idがないのは、誰がサインインしているかをアプリケーションがすでに把握しているからです。データベースへのクエリでは、この信頼できる識別情報と、リクエストされた注文IDを組み合わせます。本人の識別情報までモデルが指定する引数に移してしまうと、この例で示したい境界が失われます。
モデルの呼び出しと、アプリケーションが返す結果
リクエストと結果は別々のメッセージで、呼び出し識別子によってひも付けられます。 典型的な呼び出しは次のようになります。識別子と値は説明用であり、実際に取得したAPI応答ではありません。
{
"type": "function_call",
"id": "fc_demo",
"call_id": "call_demo",
"name": "get_order_status",
"arguments": "{\"order_id\":\"ORD-1042\"}"
}ハンドラーがテストデータの注文状況を返したら、アプリケーションは次の結果を会話に追加します。実際の応答を扱う際は、受け取ったcall.call_idをそのまま使い、別の識別子を作らないでください。
{
"type": "function_call_output",
"call_id": "call_demo",
"output": "{\"order_id\":\"ORD-1042\",\"status\":\"shipped\"}"
}これでモデルは、「注文ORD-1042は発送済みです」といった回答の根拠を得られます。注文状況の出どころはデータベースです。モデルが担ったのは、依頼を引数に変換し、返ってきたデータを回答文にすることです。
3社のドキュメントで見る共通点と違い
実行の基本パターンは共通でも、通信上のデータ形式は異なります。 以下の対応表は、2026年10月17日に各社の公開ドキュメントで確認した内容です。名前が似たフィールドを互換とみなさず、現行のサンプルの違いを整理しています。
Anthropicのtool use
Anthropicのtool useドキュメントでは、関数の入力をinput_schemaで定義します。クライアントツールの場合、アプリケーション側のコードがアシスタントのtool_useブロックを処理し、次のユーザーメッセージ内でtool_resultを送ります。その際、アシスタントのメッセージも会話内に保持します。
ドキュメントでは、実行場所の境界が明確です。クライアントツールはアプリケーション内で動き、サーバーツールはAnthropicのインフラ上で動きます。したがって、ホスト型の検索ツールが使えるからといって、Claudeが非公開のデータベース関数を直接実行できるわけではありません。その実装は引き続きアプリケーション側で用意します。
Geminiのfunction calling
GoogleのGemini function callingドキュメントも、モデルからのリクエストとアプリケーションによる実行を分けています。現行のガイドはInteractions APIを使い、function_callとfunction_resultのステップをやり取りし、previous_interaction_idで対話を継続します。
利用しているAPIに対応するサンプルに従ってください。あるAPIの宣言形式と、別のAPIの結果メッセージを組み合わせても、移植性は得られません。get_order_statusのような共通の処理は維持しつつ、その宣言と結果を包む形式をセットで調整します。
エージェントの処理ループをツール呼び出しが支える理由
ツールを使うエージェントは、処理を選び、結果を受け取り、次に何をするかを決める流れを繰り返します。 function callingは、その判断を、アプリケーションが実行できる処理へつなぐ受け渡し役です。
たとえば注文対応のアシスタントなら、配送情報を照会し、返ってきた配送業者の参照情報を確認してから、追跡情報を取得して回答できます。後の処理は、先の処理の結果に依存します。このように結果を受けて進むループは、あらかじめ全手順をモデルに考えさせるよりも有用です。
Anthropicのエージェント設計に関するガイドは、手順が固定されたワークフローと、自らツールの使い方を決めるエージェントを区別しています。ツールからのフィードバックと停止条件も、ループを構成する要素として説明されています。
これを「すべてのエージェントの土台」と呼ぶのは、概要を伝える表現としては便利ですが、限界があります。エージェントと呼ばれるすべてのシステムが特定のfunction calling APIを使うと、ドキュメントが示しているわけではありません。押さえるべき考え方は、モデルの判断を実行につなぐループです。単発の照会なら固定のワークフローのままでよく、自律的に計画を立てる仕組みは必要ありません。
本番環境でつまずきやすい3つの問題
スキーマの品質、ツールの選択、引数の検証は、それぞれ別の問題として扱ってください。 どれかを改善しても、残りが解決したことにはなりません。
曖昧なスキーマ:形式は合っていても意味を取り違える
idというフィールド名だけでは、注文番号、顧客識別子、配送の参照番号のどれが必要なのか、モデルには分かりません。actionという制約のない文字列を用意すると、ハンドラーが認識できない値を渡される余地が生まれます。周囲に指示を付け足す前に、入力の契約そのものを改善してください。
パラメーターには明確な名前を付け、意味を説明し、受け付ける値が決まっているなら列挙型を使います。最初のツールは、目的を平易な文で説明できる程度に範囲を絞りましょう。上の例ではorder_idで対象を明示し、説明文で返金や住所変更を用途から除外しています。
OpenAIのstrictモードでは、オブジェクトにadditionalProperties: falseを指定し、すべてのプロパティをrequiredに含める必要があります。値がない場合も想定する項目では、nullを許容できます。これらのstrictモードの要件はガイドに記載されています。Googleもスキーマの制限を説明しているため、ローカルのバリデーターを通ったスキーマが、すべてのモデルAPIで受け付けられるとは限りません。
ここが限界です。 スキーマで表現形式は制約できますが、形式上は正しい注文IDでも、対象の注文を取り違えている可能性があります。それを確認するのはアプリケーションです。
ツールの選択ミス:引数は正しくても処理が違う
「返金はどうなっていますか」という問い合わせに必要なのは状況の照会であって、新たな返金の実行ではありません。どちらのツールにも「返金を扱う」とだけ書いてあれば、選択が曖昧になります。
各ツールを使うべき場面と使うべきでない場面を説明し、現在のタスクとユーザーに適した処理だけを公開してください。Anthropicのツール定義ガイドも、目的、パラメーターの意味、利用範囲、制限を説明するよう具体的に勧めています。
実行すべき処理がアプリケーション側ですでに決まっているなら、対応するツール選択の制御機能を使うか、その処理を直接呼び出します。上の例で照会を必ず実行させているのは、仕組みを説明するためです。「ユーザーが何か送るたびにツールを動かすべきだ」と受け取らないでください。あいさつや、注文情報が足りない質問には、会話で応じる方が適切な場合があります。
ここが限界です。 引数の形式が正しくても、モデルが適切な業務処理を選んだかどうかは分かりません。呼び出しを行うべきでないケースも含め、代表的なユーザーリクエストに対して選ばれたツールを評価してください。
引数の検証不足:形式が正しくてもアクセス境界を越える
ハンドラーは、外部からの入力を受け止める境界です。関数名を明示的な許可リストと照合し、引数を解析して、その構造と意味を検証します。アクセス制御には、アプリケーションが認証したユーザーの識別情報を使います。
注文の例なら、別のテナント、つまり別の顧客アカウントに属する注文を指定するテストが有効です。識別子が文字列として完全に妥当でも、その注文をクエリが返してはいけません。注文IDの欠落、想定外のフィールド、ハンドラーが実装していない処理の要求も、確認すべきケースです。
Anthropicの概要ドキュメントは、情報が不足していると、モデルが引数の値を推測する場合があると説明しています。必須フィールドが埋まっていることを、ユーザーがその値を指定した証拠とみなさないでください。重大な影響を伴う処理では、要求された操作と入力を、ユーザーが実際に許可した内容と照合します。
実行に失敗したら、失敗だと分かる結果を返してください。タイムアウトは、処理が行われなかった証拠にはなりません。書き込みを再試行する前に、アプリケーション側で冪等性を保つ仕組みを使います。同じ意図の操作を識別し、その効果が重複して生じるのを防ぐ仕組みです。これらは実装上の推奨事項であり、モデルAPIが保証するものではありません。
構造化出力だけで足りるのはどんなとき?
手元にある情報を決まった形式で回答させるなら構造化出力を使い、外部の処理が必要ならツールを使います。 サポートへの問い合わせを分類し、カテゴリと緊急度を返すだけなら、構造化出力で対応できます。一方、現在の注文状況を答えるアシスタントは、照会によってその状態を取得する必要があります。
OpenAIのStructured Outputsガイドは、アプリケーションの機能を接続することと、モデルの回答形式を整えることを区別しています。また、strictな関数スキーマは内部でStructured Outputsを使うとも説明しています。両者は補完し合う機能であり、どちらか一方しか選べない製品ではありません。

たとえば、注文レコードをすでに渡してあり、画面表示用に{order_id, status, explanation}だけが必要なら、構造化された回答で十分です。レコードがない場合や、最新の情報を取り直す必要がある場合は、先に照会を呼び出します。そのうえで最終的な回答も構造化できます。
「メールを送信しました」と書かれたJSONオブジェクトも、それ自体は出力データにすぎません。メールを送るには、処理を実行し、確認できる結果を得る必要があります。
function callingとMCPはどう違う?
function callingは、モデルが処理を依頼するための仕組みです。Model Context Protocol(MCP)は、ツールやその他のコンテキストを対応アプリケーションに公開する共通インターフェースを提供します。 独立したツールサーバーを導入する前に、関数をアプリケーションへ直接組み込むことができます。
MCPとツール呼び出し:共通化が役立つタイミング
複数のアプリケーションが同じ注文照会を必要とするなら、MCPサーバーでツールを公開し、検出して呼び出せるようにできます。MCPのアーキテクチャドキュメントでは、ツールの検出にtools/listを、実行の要求にtools/callを使うと説明しています。サーバー側には引き続き、処理の実装とアクセス確認が必要です。
次の段階に進む際は、MCPとは何か、ホスト・クライアント・サーバーがどう連携するかを参照してください。直接の照会が動いたら、MCPサーバー実装ガイドで、機能を再利用できる形にまとめる方法を確認できます。MCPを導入しても、要求された処理を検証する必要はなくなりません。
function callingに期待しすぎてはいけないこと
スキーマ検証に通ったという事実が示すのは、形式の制約を満たしたことだけです。タスクの完了は保証しません。 処理の選択が不適切かもしれませんし、レコードにアクセスできなかったり、接続先のサービスが利用できなかったりする可能性もあります。流暢な最終回答が得られても、これらの問題が解決したことにはなりません。
ツールの一覧を用意するだけでエージェントができるわけでもありません。どのツールを利用可能にするか、結果をどう会話に取り込むか、いつ停止するか、エラー後にどう対応するかを決める必要があります。フレームワークでこうした処理をまとめることはできますが、実行方針は設計する側が決めるものです。
最初の到達点は、大がかりである必要はありません。リクエストが意図したハンドラーに届き、許可されたデータだけが返り、回答がその結果を反映することです。システムに与える権限を広げる前に、この経路を確認できるようにしてください。
最初に何を実装して公開すべきか
繰り返し寄せられる質問に答える、読み取り専用の処理から始めてください。 注文状況の例は、期待する結果とアクセスの境界を確認しやすいため、取り組みやすいパターンです。
開発者は、アプリケーションが確実に取得できるデータに回答が依存しているなら、実装に着手できます。次の処理が決まっている場合は、固定のワークフローを維持してください。モデルが書き込み処理を選ぶ仕組みは、明確な権限、失敗への対応、同じ効果の重複を防ぐ方法がそろってから導入します。
運用担当者は、何が実行され、何が返ったかを確認してください。選ばれたツール、検証結果、実行状況、呼び出し同士の対応関係を記録し、機密性の高い引数は通常のログに残さないようにします。業務に合ったタイムアウトとツール実行ラウンド数の上限を設け、会話のループが際限なく続くのを防いでください。
導入を検討する担当者は、成功した回答だけでなく、アクセスを拒否された照会や、実行に失敗した処理も実装チームに見せてもらってください。渡された情報を要約するだけの製品なら、function callingは必要ないかもしれません。構造化出力や通常のテキスト生成だけで足りる場合があります。
週明けに取り組むなら、範囲を絞った照会を実装し、アクセス可能なレコード、アクセス不可のレコード、情報が足りないリクエストを試して、それぞれでモデルに返る結果を確認してください。各リクエストがなぜ受け付けられ、なぜ拒否されたのかを説明できるようになってから、ツールを増やしましょう。
よくある質問
function callingとは何ですか?
モデルが関数名と引数を指定して、処理を依頼する仕組みです。自分で定義した関数の場合、その実行と会話への結果の返却はアプリケーションが担います。
LLMのtool callingとfunction callingはどう違いますか?
独自に定義した関数については、各社が同じ意味で使うことが多い用語です。ただし、tool useにはプロバイダーがホストする機能も含まれるため、名称だけで判断せず、実行場所を確認してください。
function callingの具体例を教えてください。
モデルがget_order_statusを要求し、order_idにORD-1042を指定します。アプリケーション側のコードがリクエストの検証とアクセス確認を行い、注文を照会してその状況を返します。この記事に登場する注文と状況は、説明のために作った架空のデータです。
GPT-5でfunction callingを使うにはどうすればよいですか?
プロジェクトで利用できる対応モデルを選び、ドキュメントにあるResponsesの流れに従います。ツール定義を送り、関数呼び出しの出力項目を処理し、対応する呼び出しIDを付けて結果を返します。会話を続ける際は、推論項目も保持してください。上の実行可能なサンプルでは、アカウントで特定のGPT-5モデルが有効だと決めつけず、現行ガイドに掲載されているモデルを使っています。
AIを使った開発の実践ガイドを受け取るには、ニュースレターに登録してください。
- 公開日
- カテゴリー
- Build
- 言語










