Function callingは、どのプロバイダーでも一見同じに見えます——違いが現れるまでは。OpenAIはtool_callsを、ストリーミングチャンクを跨いで蓄積するデルタとして送ります。Anthropicはtool_useをtextブロックと対等なコンテンツブロックとして返します。GoogleはすべてをcandidatesにfunctionCallオブジェクトとして包みます。DeepSeekはOpenAIに忠実に従います——並列呼び出しで違ってくるまでは。
モデルを切り替えるたびに、エージェントコードが壊れます。このガイドはそれを修正します。4プロバイダーすべての動作コード。どこで何が壊れるかを示す差異表。ツール定義を一度書いてどこでも使える統合ラッパーパターン。Function callingはすべてのAIエージェント構築の基盤です——ツールループをマスターすれば、エージェントアーキテクチャは簡単になります。
Function callingの実際の仕組み
パターンは全プロバイダーで同じです。一度理解することが、各プロバイダーの構文を暗記するより重要です。
ツールループ:
- ツールを定義——名前、説明、パラメータのJSON Schema
- ユーザーメッセージ+ツール定義をモデルに送る
- モデルがテキストで応答するか、ツール呼び出しを要求するか決定
- ツール呼び出しなら:コードが関数名と引数をパース——関数を実行——結果を送り返す
- モデルが結果を処理——テキストで応答するか、別のツールを呼ぶか決定
- モデルがテキストで応答するか、最大反復回数に達するまで繰り返す
Function calling vs 構造化出力。 Function calling:モデルがいつツールを使うか決定。「必要な情報を判断して取得せよ」。構造化出力:モデルが常にあなたのスキーマを返す。「常にこれらのフィールドを持つJSONオブジェクトを返せ」——形式保証が必要なときに。
プロバイダー1:OpenAI Function Calling
OpenAIのfunction calling実装は最も成熟しており、他が従う参照標準です。
from openai import OpenAI
import json
client = OpenAI(
base_url="https://api.tokspan.com/v1",
api_key="ts-your-key-here"
)
tools = [{
"type": "function",
"function": {
"name": "get_stock_price",
"description": "Get the current stock price for a ticker symbol. Returns price in USD.",
"parameters": {
"type": "object",
"properties": {
"symbol": {"type": "string", "description": "Stock ticker, e.g. AAPL"}
},
"required": ["symbol"]
}
}
}]
response = client.chat.completions.create(
model="gpt-5.5",
messages=[{"role": "user", "content": "What's Apple's stock price?"}],
tools=tools,
tool_choice="auto"
)
msg = response.choices[0].message
if msg.tool_calls:
for tool_call in msg.tool_calls:
args = json.loads(tool_call.function.arguments)
result = execute_stock_lookup(args["symbol"])
# Send result back
messages = [
{"role": "user", "content": "What's Apple's stock price?"},
msg,
{"role": "tool", "tool_call_id": tool_call.id, "content": str(result)}
]
final = client.chat.completions.create(model="gpt-5.5", messages=messages)
print(final.choices[0].message.content)
OpenAI固有の詳細。 並列ツール呼び出し:GPT-5.5は1つの応答で複数ツールを要求できます——msg.tool_callsに複数アイテムがあるか確認。ストリーミング:tool_callsはデルタとして到着——index、function.name、function.argumentsをチャンク間で蓄積。構造化出力+function calling:strict: trueでツールパラメータを定義し、スキーマ準拠を保証。
プロバイダー2:Anthropic Tool Use
Claudeのtool useは構造的に異なります——ツールはメッセージ内のコンテンツブロックとして現れ、別フィールドではありません。
import anthropic
client = anthropic.Anthropic(
base_url="https://api.tokspan.com/anthropic",
api_key="ts-your-key-here"
)
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1000,
tools=[{
"name": "get_stock_price",
"description": "Get the current stock price for a ticker symbol.",
"input_schema": {
"type": "object",
"properties": {
"symbol": {"type": "string", "description": "Stock ticker, e.g. AAPL"}
},
"required": ["symbol"]
}
}],
messages=[{"role": "user", "content": "What's Apple's stock price?"}]
)
for block in response.content:
if block.type == "tool_use":
# Execute the tool Claude requested
result = execute_stock_lookup(block.input["symbol"])
# Build the conversation continuation —the full cycle:
# 1. The assistant message contains ALL content blocks from Claude's response
# 2. The user message contains tool_result blocks matching each tool_use
assistant_msg = {"role": "assistant", "content": response.content}
tool_result_msg = {
"role": "user",
"content": [{
"type": "tool_result",
"tool_use_id": block.id,
"content": str(result)
}]
}
# Send the result back and get Claude's final response
follow_up = client.messages.create(
model="claude-opus-4-8",
max_tokens=1000,
messages=[
{"role": "user", "content": "What's Apple's stock price?"},
assistant_msg,
tool_result_msg
]
)
# Claude will return a text block with the final answer
for follow_block in follow_up.content:
if follow_block.type == "text":
print(follow_block.text)
OpenAIとの主な違い。 ツール定義はparametersではなくinput_schemaを使用。ツール呼び出しはresponse.content内のtool_useコンテンツブロック——textブロックと対等で、別フィールドではありません。ツール結果はユーザーメッセージ内のtool_resultコンテンツブロックとして送信。ストリーミングには部分的なtool_useブロックが含まれます——ツール名と引数を段階的に取得。
プロバイダー3:Google Gemini Function Calling
# Gemini uses a different structure —function declarations with OpenAPI-like schema
tools = [{
"function_declarations": [{
"name": "get_stock_price",
"description": "Get the current stock price for a ticker symbol.",
"parameters": {
"type": "object",
"properties": {
"symbol": {"type": "string", "description": "Stock ticker, e.g. AAPL"}
},
"required": ["symbol"]
}
}]
}]
# Response structure:
# response.candidates[0].content.parts[0].function_call.name
# response.candidates[0].content.parts[0].function_call.args
Gemini固有の詳細。 自動function calling:Geminiは単一APIリクエストで関数を呼び出し・実行できます——ツール設定でautomatic_function_callingを設定。検索グラウンディング:組み込みの「ツール」が、検索APIを実装せずにGoogle検索結果で応答をグラウンディング。
プロバイダー4:DeepSeek Function Calling
DeepSeekはOpenAI形式に従います。同じツール定義、同じ応答構造。実際の違い:並列ツール呼び出しがGPT-5.5より信頼性が低い——並列に呼ばれるべきツールが順番に呼ばれることがあります。GPT-5.5からDeepSeekに切り替えるなら、マルチツールシナリオを特にテストしてください。
# Identical to OpenAI code —just change base_url and model
client = OpenAI(
base_url="https://api.tokspan.com/v1",
api_key="ts-your-key-here"
)
# Same tool definitions, same response handling as OpenAI example above
クロスプロバイダー差異表
| 機能 | OpenAI | Anthropic | DeepSeek | |
|---|---|---|---|---|
| ツール定義形式 | function.parameters(JSON Schema) | input_schema(JSON Schema) | function_declarations.parameters | OpenAIと同じ |
| レスポンスの場所 | message.tool_calls[] | content[] ブロック | candidates[].content.parts[] | OpenAIと同じ |
| 並列ツール呼び出し | はい、信頼性あり | はい、信頼性あり | はい | 部分的、信頼性は低め |
| ストリーミングツール | デルタ、蓄積が必要 | 部分ブロック | 部分candidates | OpenAIと同じ |
| ツール選択制御 | tool_choice: "auto"/"required"/"none" | 同様のオプションを持つtool_choice | function_calling_config | OpenAIと同じ |
| リクエストあたりの最大ツール数 | 128 | 非公開(大きい) | 非公開 | OpenAIに従う |
| 切り替え時のコード変更 | — | 100%(別SDK) | ~80% | 0%(OpenAIから) |
よくある落とし穴
これらは本番に出荷されるバグです。どれも午後ひとつで実装できる修正があります——ただし、ユーザーより先に探すことを知っていれば。
1. ストリーミングtool_callsの蓄積バグ。 ストリーミングモードでは、tool_callsが複数のチャンクに分かれて到着します——それぞれがindex、部分的なfunction.name、部分的なfunction.arguments文字列を持ちます。ミス:finish_reason: "tool_calls"を持つ最終デルタチャンクが到着する前に、引数文字列にjson.loads()を呼ぶ。毎回JSONDecodeErrorになり、部分状態が破損するためリトライロジックが悪化させます。indexごとにチャンクを跨いで引数を蓄積。ストリームが完了を示したときだけパース。これは最も一般的な本番ツール呼び出し障害モードの1つです。
2. プロバイダー固有のJSON Schema差異。 OpenAIはツールパラメータスキーマで$ref、anyOf、ネストoneOfをサポート。Geminiは$ref定義を黙って無視——ツールは動きますが、モデルは参照されたスキーマを決して見ません。AnthropicはOpenAIより厳格なサーバーサイド検証を実行——GPT-5.5で通るスキーマがClaudeで不明瞭な検証エラーの400を返す。スキーマをすべてのプロバイダーに対してCIでテスト——ローンチ前日に手動ではなく。各プロバイダーのAPIでのCIスキーマ検証ステップが、これを数分で捕捉します。
3. 並列ツール呼び出しID不一致。 モデルが1つの応答でget_price("AAPL")とget_price("GOOGL")を返す。両方を並列実行。結果が順序不同で到着。位置が実行順と一致すると仮定して、間違ったtool_call_idにマップし直す。モデルがAAPLのIDの下でGOOGLの価格を受け取り、自信満々で筋の通った完全に間違った回答を生成。結果メッセージを構築する前に、常にtool_call_idで結果をインデックス。配列の位置に依存しない。
4. 正当なデータとして通るツールエラー。 get_stock_price関数のHTTP呼び出しがタイムアウト。例外をキャッチして文字列"Error: connection timeout"を返す。モデルがその文字列をデータとして読み、「現在の価格はError: connection timeoutです」と応答。ツールエラーをTOOL_ERROR: <type> —<message>のような認識可能なプレフィックスでフォーマット。ツールのdescriptionフィールドでエラー処理を説明し、モデルがリトライするか失敗を伝えることを知るように。シグナルを与えなければ、モデルはバグと異常データを区別できません。
統合function callingラッパー
ラッパーパターン:ツールをプロバイダー非依存の形式で一度定義。呼び出し時に各プロバイダーのネイティブ形式に変換。応答を統合形式に正規化。
class UnifiedToolClient:
"""One tool definition. Any provider. Automatic translation."""
def __init__(self, base_url: str, api_key: str):
self.openai_client = OpenAI(base_url=base_url, api_key=api_key)
def call_with_tools(self, model: str, messages: list, tools: list):
"""Provider-agnostic tool calling. Handles translation internally."""
# Tools defined in OpenAI format —works for OpenAI, DeepSeek, and
# platforms that translate to Anthropic/Google natively
response = self.openai_client.chat.completions.create(
model=model,
messages=messages,
tools=tools,
tool_choice="auto"
)
return self._normalize_response(response)
def _normalize_response(self, response):
"""Return a unified format regardless of which provider served the request."""
msg = response.choices[0].message
return {
"text": msg.content,
"tool_calls": [
{"name": tc.function.name, "arguments": json.loads(tc.function.arguments)}
for tc in (msg.tool_calls or [])
] if msg.tool_calls else []
}
集約プラットフォームの近道。 このラッパーは30行のコードです。しかしOpenAI互換形式を話すプロバイダー向けの変換しか処理しません。Anthropicネイティブ機能(思考+ツール使用の組み合わせ、ストリーミング部分ツール結果)とGoogleネイティブ機能(自動function calling)には、各プロバイダーのネイティブプロトコルをサポートするプラットフォームが必要です——さもないと3つの別コードパスを維持することになります。マルチプロトコルサポートを持つプラットフォームはこれをインフラレベルで処理します。コードはプロバイダー非依存のままで、各プロバイダーの独自機能は利用可能なまま。統合ツール呼び出しを始めたばかりなら、TokSpanクイックスタートガイドが5分以内に最初のマルチプロバイダーツールリクエストの設定を案内します。
FAQ
どのプロバイダーのfunction callingが最良ですか?
GPT-5.5:最も信頼性が高く、並列呼び出しが最良、エコシステム最強。Claude Opus:推論深度が重要な複雑な多段階ツールチェーンに最良。Gemini:自動function callingは単純ツールの利便性で勝ち。DeepSeek:単純ツールに十分、並列呼び出しで時々信頼性が低い。ツール信頼性が重要ならGPT-5.5。ツール推論深度が生の信頼性より重要ならClaude。
全プロバイダーで同じツール定義を使えますか?
ネイティブにはできません。JSON Schemaは共有されますが、ラッパー形式は異なります。変換レイヤー(30行のPython)か、自動変換する集約プラットフォームを使いましょう。ツール定義——名前、説明、パラメータスキーマ——はラッパー形式が移植可能でなくても、ツール定義自体は移植可能です。
リクエストあたりいくつのツールを定義できますか?
OpenAI:128。Anthropic:文書化されていませんが大きい。Google:ハード制限なし。実際には、10ツール超で選択精度が劣化——モデルが似た名前のツールを混同し始めます。アクティブなツールセットを絞りましょう。
自分でラッパーを構築すべきですか、それともプラットフォームを使うべきですか?
1〜2プロバイダーを使い、ツール呼び出しループを特定制御したいなら構築。プロバイダーを自由に切り替えたい、4つのコードパスを維持したくないならプラットフォーム。このガイドのラッパーパターンは実装・維持に30分。プラットフォームならゼロ——4プロバイダーすべての変換と正規化を処理するプラットフォームレベルAPIはTokSpanドキュメントをご覧ください。
Function callingはすべてのAIエージェントの基盤です。しかし業界が答えていない厄介な問いがあります:なぜ2026年になっても、各LLMプロバイダーがツール定義にわずかに異なる形式を持っているのか? JSON Schemaは共有されています。「tool_call」の概念も共有。それでもラッパー形式——input_schema対parameters、tool_useブロック対tool_calls配列——は頑固にプロバイダー固有のまま。標準化団体が6か月のワーキンググループでこれを解決できるはず。今のところ、誰も招集していません。問い:市場はOpenAI互換デフォルトを通じて標準化を強制するのか、それともネイティブツール使用機能が高度に差別化され、クロスプロバイダー互換性が永久に放棄されるのか?
統合function callingを試す——1つのツール定義。4つのプロバイダー。ゼロラッパーコード——業界が標準化が本当に来るのか考える間も。