Function calling(tool callingまたはtool useとも呼ばれます)は、モデルにコードへのアクセス権を与える方法です。「注文を検索する」や「配送料を見積もる」といった関数を説明すると、モデルが呼び出す関数と引数を決定し、コードがそれを実行して結果を返します。ストアのサポートボットから Claude Code まで、あらゆるエージェントはこのループを基盤に構築されています。
GetVibe APIでは、Function callingはOpenAI形式の標準/v1/chat/completionsエンドポイントと、Anthropic形式の/v1/messagesを通じて利用できます。以下は、3つの異なるメーカーの3つのモデルで実行した完全なループです。
ループの仕組み
toolsのリストとともにメッセージを送信します。各ツールには、名前、説明、パラメータ用のJSON Schemaが含まれます。- モデルはテキストの代わりに
tool_callsを返します。そこには、呼び出す関数と引数が含まれます。finish_reasonはtool_callsです。 - コードで関数を実行し、呼び出しごとに、同じ
tool_call_idを持つtoolロールのメッセージを1つ追加します。 - 履歴を再び送信します。モデルは追加の呼び出しを求めるか、テキストでユーザーに回答します。
モデル自身が何かを実行することはありません。モデルは呼び出しを要求するだけです。実際に何を実行するか、どの権限で実行するかは、コードによって決まります。
完全な例:ストアのサポートアシスタント
2つの関数を用意します。注文状況の確認と、配送料の見積もりです。ユーザーが両方について一度に質問し、モデルが1ターンで両方を呼び出します。
import json
import os
from openai import OpenAI
client = OpenAI(base_url="https://getvibeapi.com/v1", api_key=os.environ["GETVIBE_API_KEY"])
MODEL = "gpt-6-luna"
def get_order_status(order_id: str) -> dict:
# データベースまたはCRMでの検索処理をここに記述します
return {"order_id": order_id, "status": "out for delivery", "eta": "2026-10-24"}
def get_shipping_quote(zip_code: str) -> dict:
return {"zip_code": zip_code, "price_usd": 7.5, "days": 2}
TOOLS = [
{"type": "function", "function": {
"name": "get_order_status",
"description": "Look up an order by its ID",
"parameters": {"type": "object",
"properties": {"order_id": {"type": "string", "description": "Order ID, e.g. A-1042"}},
"required": ["order_id"]}}},
{"type": "function", "function": {
"name": "get_shipping_quote",
"description": "Courier shipping price and delivery time to a US ZIP code",
"parameters": {"type": "object",
"properties": {"zip_code": {"type": "string"}},
"required": ["zip_code"]}}},
]
FUNCTIONS = {"get_order_status": get_order_status, "get_shipping_quote": get_shipping_quote}
messages = [
{"role": "system", "content": "あなたはストアのサポートアシスタントです。簡潔に回答してください。"},
{"role": "user", "content": "注文 A-1042 はどこですか?また、94110 への送料はいくらですか?"},
]
for _ in range(5): # ラウンド数の上限
resp = client.chat.completions.create(model=MODEL, messages=messages, tools=TOOLS)
msg = resp.choices[0].message
if not msg.tool_calls:
print(msg.content)
break
messages.append(msg.model_dump(exclude_none=True))
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = FUNCTIONS[call.function.name](**args)
messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)})
このコードを変更せずにgpt-6-luna、deepseek-v4.1-flash、claude-haiku-5.5で実行し、変更したのはMODELだけです。最初のラウンドでは、3つのモデルすべてが両方の関数を呼び出しました。get_order_statusにはA-1042を、get_shipping_quoteには94110を渡しています。2回目のラウンドでは、3つのモデルすべてがテキストで回答し、配送状況、到着予定時刻、7.50の料金、2日間の配送時間を返しました。
このコードで重要な点
- モデルのtool-callメッセージ全体を履歴に追加します。これを行わないと、
toolメッセージがモデルから見えない呼び出しに回答することになります。その結果、多くのモデルは同じ関数を再び呼び出すだけになり、ループが無限に繰り返されます。 - 呼び出しごとに、対応する
tool_call_idを持つtoolメッセージを1つ送信します。今回の例のように、モデルが一度に複数の呼び出しを求める場合があります。 - 引数は文字列として届きます。
argumentsは文字列内のJSONです。モデルが値を間違える可能性があるため、解析して検証してください。 - ラウンド数に上限を設定します。上限がないと、関数のバグによってループが無限に続き、トークン料金も無限に発生する可能性があります。
tool_choice:呼び出しを必須にする場合
デフォルトでは、ツールを呼び出すかテキストで回答するかをモデルが判断します("auto")。tool_choiceを使うと、この動作を変更できます。
"none"— ツールを呼び出しません。"required"— 少なくとも1つのツールを呼び出します。{"type": "function", "function": {"name": "..."}}— 指定した関数を呼び出します。
最後の形式は、データ抽出に便利です。関数のパラメータスキーマが回答の形式になります。
curl https://getvibeapi.com/v1/chat/completions \
-H "Authorization: Bearer $GETVIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4.1-flash",
"messages": [{"role": "user", "content": "Jane Doe, [email protected], wants a demo on Thursday"}],
"tools": [{
"type": "function",
"function": {
"name": "save_lead",
"description": "Save a sales lead to the CRM",
"parameters": {
"type": "object",
"properties": {"name": {"type": "string"}, "email": {"type": "string"}, "request": {"type": "string"}},
"required": ["name", "email", "request"]
}
}
}],
"tool_choice": {"type": "function", "function": {"name": "save_lead"}}
}'
レスポンスはsave_leadの呼び出しとなり、nameは「Jane Doe」、emailは「[email protected]」、requestは「Wants a demo on Thursday」でした。
注意点が1つあります。Claude Sonnet 5.5やClaude Opus 5.5など、一部のClaudeモデルは強制されたtool choiceを受け付けません。GetVibeはこのようなリクエストを、同じツールを使うautoに変換します。そのためエラーの代わりに回答を得られますが、その後に呼び出しを行うかどうかはモデル自身が判断します。これらのモデルから厳密なJSONを取得するには、structured outputsを使用してください。
Anthropic形式:/v1/messages
Anthropic SDKでは、ツールの形式が少し異なります。パラメータスキーマはinput_schemaに指定し、呼び出しはtool_useのcontent blockとして返されます。
curl https://getvibeapi.com/v1/messages \
-H "x-api-key: $GETVIBE_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-haiku-5.5",
"max_tokens": 512,
"tools": [{
"name": "get_order_status",
"description": "Look up an order by its ID",
"input_schema": {"type": "object", "properties": {"order_id": {"type": "string"}}, "required": ["order_id"]}
}],
"messages": [{"role": "user", "content": "Where is order A-1042?"}]
}'
レスポンスのstop_reasonはtool_useで、order_idにA-1042を指定してget_order_statusを呼び出すtool_useブロックが含まれていました。結果は、次のユーザーメッセージ内のtool_resultブロックとして、同じtool_use_idを付けて返します。
ツールにはどのモデルを使うべきか
- 大量トラフィックのボットやシンプルなツール — DeepSeek V4.1 Flash($0.0112 入力 · $0.0445 出力 100万トークンあたり)またはClaude Haiku 5.5($0.0267 入力 · $0.1334 出力 100万トークンあたり)。
- 多数のツールと長い計画を扱うエージェント — GPT-6 Luna($0.0055 入力 · $0.0274 出力 100万トークンあたり)またはClaude Sonnet 5.5。
エージェントのループでは、ラウンドごとに同じ長いプレフィックスを再送信するため、ここではprompt cachingが非常に重要です。リクエストフィールドの詳細については、tool callingのドキュメントをご覧ください。
