9月29日、Collabnixが「Using Function Calling to Build AI Agents with OpenAI and Claude」と題した記事を公開した。LLMに「天気を調べて」と頼むだけで、モデルが自律的に外部APIを叩いて結果を返す——そんな「エージェント的な動作」を実現するFunction Callingを、OpenAIとAnthropicのClaude両方の実装パターンで解説した実践的な内容だ。
なぜ今、Function Callingが重要か
LLMは会話や文章生成には長けているが、そのままでは「外部APIを叩く」「リアルタイムデータを取得する」「別のサービスに書き込む」といったアクション系の処理が苦手だ。この弱点を補う仕組みがFunction Calling(Anthropicは"Tool Use"とも呼ぶ)である。
Function Callingは、モデルがユーザーの意図を解釈し、あらかじめ定義された関数を適切なタイミングで選択・呼び出す機能だ。たとえば「ニューヨークの天気は?」という入力に対して、モデルが自律的にget_weather関数を選び、引数を構築して実行し、その結果を会話に組み込む。単なるチャットボットを、外部システムと連携できるエージェントへ引き上げる核心機能といえる。
2024年以降、このFunction Calling/Tool Useの概念はさらに発展し、複数のエージェントやツールを束ねるMCP(Model Context Protocol)といった標準化の動きにもつながっている。Function Callingはそのような大きなエコシステムの出発点として押さえておくべき基礎技術だ。
環境構築と認証
記事ではPython 3.11以降を前提としている。まず仮想環境を作成し、必要なライブラリをインストールする。
python -m venv ai-env
source ai-env/bin/activate # Unix/macOS
# ai-env\Scripts\activate # Windows
pip install openai anthropic requests
openaiはOpenAI API、anthropicはClaude API、requestsはHTTP通信に使う。
APIキーは環境変数で管理するのが基本だ。ハードコードは避ける。現行ライブラリでは、OpenAIはopenai.OpenAI()、Claudeはanthropic.Anthropic()でクライアントを初期化する。
import os
from openai import OpenAI
import anthropic
openai_client = OpenAI(api_key=os.getenv('OPENAI_API_KEY'))
claude_client = anthropic.Anthropic(api_key=os.getenv('CLAUDE_API_KEY'))
なお、旧来のopenai.api_key = ...やanthropic.Client()といった書き方は古いAPIであり、現行ライブラリでは非推奨または廃止されている。公式の移行ガイド(OpenAI Python v1.0.0 Migration Guide、Anthropic Python SDK)を参照してほしい。
OpenAIのFunction Calling実装:Chat Completions APIを使う
Function CallingはChat Completions API(openai.chat.completions.create())の機能であり、旧来のCompletions API(openai.completions.create())では使用できない点に注意が必要だ。誤ったAPIにfunctionsパラメータを渡してもエラーになる。
まずはスタブ(ダミー実装)で動作フローを確認する例:
from openai import OpenAI
import json
client = OpenAI(api_key=os.getenv('OPENAI_API_KEY'))
# 関数の定義(モデルに公開するスキーマ)
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name, e.g. New York"
}
},
"required": ["location"]
}
}
}
]
# 実際の関数実装(スタブ)
def get_weather(location):
return {"temperature": 70, "unit": "Fahrenheit", "description": "Partly cloudy"}
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "What is the current weather in New York?"}],
tools=tools,
tool_choice="auto"
)
# モデルが関数呼び出しを選択した場合の処理
tool_call = response.choices[0].message.tool_calls[0]
args = json.loads(tool_call.function.arguments)
result = get_weather(**args)
print(result)
フローの核心は「モデルが関数を実行するのではなく、どの関数をどの引数で呼ぶかを決定するだけ」という点だ。実際の実行はアプリケーション側が担う。この非対称性を理解しておくと、エラー処理や権限管理の設計が明確になる。
次に、requestsで実際の外部APIと接続するケース:
import requests
def get_weather_api(location):
response = requests.get(
f'https://api.weatherapi.com/v1/current.json?q={location}&key=YOUR_API_KEY'
)
return response.json()
ClaudeのTool Use実装:anthropic.Anthropic()を使う
AnthropicのClaudeも同様のFunction Calling機能を「Tool Use」として提供している(公式ドキュメント)。OpenAIとはスキーマの書き方が一部異なる点に注意が必要だ。
import anthropic
import json
client = anthropic.Anthropic(api_key=os.getenv('CLAUDE_API_KEY'))
# Claudeのツール定義(OpenAIの"tools"に相当)
tools = [
{
"name": "get_weather",
"description": "Get the current weather for a location",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name, e.g. New York"
}
},
"required": ["location"]
}
}
]
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
tools=tools,
messages=[{"role": "user", "content": "What is the current weather in New York?"}]
)
# ツール呼び出しブロックの取り出し
for block in response.content:
if block.type == "tool_use":
args = block.input
result = get_weather(**args)
print(result)
OpenAIとClaudeの主な仕様の差異をまとめると:
- スキーマキー:OpenAIは
parameters、Claudeはinput_schema - レスポンス取得:OpenAIは
response.choices[0].message.tool_calls、Claudeはresponse.contentのブロックをイテレートする - モデル名:OpenAIは
gpt-4o等、Claudeはclaude-3-5-sonnet-*等
プロバイダーを抽象化したラッパー関数を設計しておくと、モデルの切り替えコストを下げられる。
外部APIとの統合:タスク管理ツールの例
より複雑な自動化の例として、タスク管理ツールのAPIと連携するケースも紹介されている。
def create_task(task_name, due_date):
return requests.post('https://api.taskmanager.com/tasks', json={
'name': task_name,
'due_date': due_date
}).json()
「明日までにAI記事を書くタスクを作成して」という自然言語の指示が、そのままタスク管理APIへのPOSTリクエストに変換される。この構造が、AIエージェントを「会話するだけのシステム」から「実際に何かを動かすシステム」に変える。
注意点とベストプラクティス
記事ではCommon PitfallsとPerformance Optimizationのセクションも設けられており、以下の点が強調されている。
- APIキーは必ず環境変数で管理し、バージョン管理システムに含めない
- ライブラリは定期的に
pip install --upgradeでアップデートする。メジャーバージョンアップ時にはAPIの破壊的変更が伴うことがある - Docker等のコンテナ環境でのデプロイも視野に入れると、スケーラブルな構成が組みやすい
- 公式ドキュメントは常に最新版を確認する:OpenAI API Reference・Anthropic API Reference
OpenAIとClaudeはスキーマの書き方こそ異なるが、「関数定義→モデルによる選択→アプリ側での実行→結果の返却」という4ステップのフローは共通だ。この共通構造を理解した上でプロバイダーを抽象化しておけば、将来的なモデル乗り換えや並列利用にも対応しやすくなる。Function Callingを起点に、外部サービスとつながるエージェントの実装パターンを手を動かしながら学べる内容になっている。
詳細はUsing Function Calling to Build AI Agents with OpenAI and Claudeを参照していただきたい。