Function calling(ツール呼び出し)とは?仕組みと実装の流れ
Function calling(ツール呼び出し)とは、LLMが自然言語で答える代わりに「この関数を、この引数で呼んでほしい」という構造化されたリクエストを返す仕組みです。 天気の取得、DB検索、社内APIの実行など、モデル単体ではできない処理を、あなたのコードに橋渡しするための機能です。
大事な点を先に言うと、モデルは何も実行しません。実行するのは常にあなたのコードで、モデルは「呼ぶべき関数と引数」を決めるだけです。この記事では、OpenAI互換APIでのリクエストとレスポンスの形、2往復の実装手順、そしてMCPとの関係を整理します。
Function calling とは
通常のChat Completions APIでは、モデルは messages を受け取って文章を返します。Function calling では、リクエストに tools(呼び出せる関数の一覧) を添えます。モデルは質問に答えるために関数が必要だと判断すると、文章ではなく tool_calls(関数名と引数のJSON) を返します。
流れは次の2往復です。
- あなた → モデル:質問と
toolsの定義を送る - モデル → あなた:「
get_weatherを{"city": "東京"}で呼んで」と返す - あなた:実際に関数を実行し、結果を
role: "tool"のメッセージとして追加 - あなた → モデル:会話全体を再送し、最終的な回答文を受け取る
つまり「モデルが決め、コードが実行し、モデルがまとめる」という分業です。
リクエストの形:tools と tool_choice
関数は JSON Schema で定義します。description はモデルが「いつ呼ぶか」を判断する材料になるので、具体的に書くほど精度が上がります。
curl https://api.fastmetal.ai/v1/chat/completions \
-H "Authorization: Bearer $FASTMETAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic-claude-haiku-4-5",
"max_tokens": 100,
"messages": [{"role": "user", "content": "東京の天気を教えて"}],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "指定した都市の現在の天気を取得する",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"]
}
}
}],
"tool_choice": "auto"
}'
tool_choice は次のように使い分けます。
| 値 | 挙動 |
|---|---|
"auto" | 必要なときだけ関数を呼ぶ(既定) |
"none" | 関数を呼ばず文章で答える |
{"type": "function", "function": {"name": "get_weather"}} | 指定した関数を必ず呼ぶ |
レスポンスの形:tool_calls を読む
上のcurlを実際にFastMetal経由で送ると、モデルは文章ではなく次のような応答を返しました。
{
"choices": [{
"finish_reason": "tool_calls",
"message": {
"role": "assistant",
"content": null,
"tool_calls": [{
"id": "tooluse_zkeBIqkFBMVBb1LFnySfQu",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\": \"東京\"}"
}
}]
}
}],
"usage": {"prompt_tokens": 634, "completion_tokens": 66}
}
読み方のポイントは3つです。
finish_reasonが"tool_calls"なら、モデルは関数の実行を求めています。"stop"なら通常の回答です。- **
argumentsはJSONオブジェクトではなく「JSON文字列」**です。必ずjson.loadsなどでパースしてから使います。 idは次の往復で必要です。実行結果を返すときに、どの呼び出しへの答えかをtool_call_idで示します。
もう一つ、usage に注目してください。「東京の天気を教えて」という短い質問なのに prompt_tokens が634あります。tools の定義はプロンプトの一部として毎回送られ、入力トークンとして課金されます。 関数を何十個も登録すると、質問が一言でも毎回それなりの入力量になる点は、設計時に覚えておくと良いでしょう。
実装の流れ:2往復をPythonで書く
OpenAI SDK の base_url を差し替えるだけで、そのままFastMetalに向けられます。
import json
from openai import OpenAI
client = OpenAI(
api_key="<FASTMETAL_API_KEY>",
base_url="https://api.fastmetal.ai/v1",
)
def get_weather(city: str) -> dict:
# 実際には天気APIなどを呼ぶ
return {"city": city, "weather": "晴れ", "temp_c": 31}
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "指定した都市の現在の天気を取得する",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
},
}]
messages = [{"role": "user", "content": "東京の天気を教えて"}]
# 1往復目:モデルに「どの関数を呼ぶか」を決めてもらう
first = client.chat.completions.create(
model="anthropic-claude-haiku-4-5",
messages=messages,
tools=tools,
tool_choice="auto",
)
msg = first.choices[0].message
if first.choices[0].finish_reason == "tool_calls":
# assistant のメッセージ(tool_calls 入り)をそのまま履歴に追加
messages.append(msg)
for call in msg.tool_calls:
args = json.loads(call.function.arguments) # 文字列 → dict
result = get_weather(**args) # 実行するのは自分のコード
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result, ensure_ascii=False),
})
# 2往復目:結果を踏まえた最終回答をもらう
final = client.chat.completions.create(
model="anthropic-claude-haiku-4-5",
messages=messages,
tools=tools,
)
print(final.choices[0].message.content)
else:
print(msg.content)
複数の tool_calls が一度に返ることもあるので、ループで全件処理してから2往復目に進むのが基本形です。
MCP との関係
Function calling と混同されやすいのがMCP(Model Context Protocol)です。役割は次のように分かれます。
- Function calling:1回のリクエストの中で「関数をどう呼ぶか」を表現する仕組み。APIのパラメータそのもの。
- MCP:ツールをどう公開し、どう発見し、どう接続するかを標準化したプロトコル。エージェントやエディタが外部ツールを共通の方法で扱うための「規格」。
ざっくり言えば、MCPで接続したツールの一覧が、最終的には tools としてモデルに渡され、Function calling で呼び出されます。FastMetalもMCPサーバーを提供しており、Claude Code などのエージェントから同じAPIキーで利用できます。
使うときの注意点
- 対応状況はモデルごとに異なります。 互換APIでも、ツール呼び出しの精度や
tool_choiceの細かな挙動はモデル依存です。本番で使う前に、対象モデルで一度実際に呼んで確認してください。利用できるモデルはモデルカタログにまとめています。 - 引数の検証は自分で行う。 モデルが返す
argumentsはスキーマに沿う「はず」ですが、必須項目の欠落や型の揺れは起こりえます。実行前にバリデーションを挟むのが安全です。 - 副作用のある関数は慎重に。 送金や削除のような処理は、モデルの判断だけで実行させず、確認ステップを入れましょう。
よくある質問
Q. モデルが関数を実行してくれるのですか?
いいえ。モデルは「どの関数を、どの引数で呼ぶべきか」を返すだけです。実行するのはあなたのコードで、結果を role: "tool" のメッセージとして返すことで、モデルが最終回答をまとめます。
Q. arguments はそのまま使えますか?
使えません。arguments はJSON形式の「文字列」なので、json.loads などでパースしてから関数に渡します。パース後に型や必須項目の検証も行うと安全です。
Q. Function calling と MCP はどう違いますか?
Function calling は1回のリクエスト内で関数呼び出しを表現するAPIの仕組み、MCP はツールの公開・発見・接続を標準化するプロトコルです。MCPで接続したツールが、最終的に tools としてモデルに渡されるという関係です。
まとめ
Function calling は「モデルが決め、コードが実行し、モデルがまとめる」2往復の仕組みです。tools の定義、finish_reason: "tool_calls" の判定、arguments のパース、role: "tool" での結果返却──この4点を押さえれば、OpenAI互換APIのどのモデルでも同じコードで扱えます。まずはドキュメントを参考に、上のcurlをそのまま送ってみてください。