第4章:Function CallingとTool Use

外部ツールとMCPでLLMアプリケーションを構築

読了時間:25-30分 コード例:15 演習問題:5 難易度:中級-上級
言語:日本語 | English

はじめに

Function Calling(Tool Useとも呼ばれる)は、LLMが外部システム、API、ツールと連携できる機能です。これにより、LLMはテキスト生成器から実際のアクションを取ることができる強力なエージェントに変わります。

この章を終えると、以下のことができるようになります:

Function Callingの基礎

Function Callingとは

Function Callingにより、LLMは以下のことができます:

  1. 利用可能なツールとその機能を理解する
  2. ユーザーのリクエストに基づいてツールを使用するタイミングを決定する
  3. ツール用に適切にフォーマットされた引数を生成する
  4. ツールの結果を応答に組み込む
sequenceDiagram participant U as ユーザー participant L as LLM participant T as ツール U->>L: "東京の天気は?" Note over L: 天気ツールを使用することを決定 L->>T: get_weather(location="東京") T->>L: {"temp": 22, "condition": "晴れ"} L->>U: "東京は22度で晴れです"

JSON Schemaでのツール定義

ツールはJSON Schemaを使用して定義され、関数名、説明、パラメータを記述します:

ツール定義の例

{
  "type": "function",
  "function": {
    "name": "get_weather",
    "description": "特定の場所の現在の天気を取得します。気温、状態、湿度を返します。",
    "parameters": {
      "type": "object",
      "properties": {
        "location": {
          "type": "string",
          "description": "都市名、例:'東京'、'ニューヨーク'、'ロンドン'"
        },
        "unit": {
          "type": "string",
          "enum": ["celsius", "fahrenheit"],
          "description": "温度単位(デフォルト:celsius)"
        }
      },
      "required": ["location"]
    }
  }
}

完全なFunction Callingの例

OpenAI Function Calling

from openai import OpenAI
import json

client = OpenAI()

# ツールを定義
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "場所の現在の天気を取得",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {"type": "string", "description": "都市名"}
                },
                "required": ["location"]
            }
        }
    }
]

# 実際の関数実装
def get_weather(location: str) -> dict:
    # 本番では天気APIを呼び出す
    return {"location": location, "temperature": 22, "unit": "celsius", "condition": "晴れ"}

# ステップ1: ツール付きでユーザーメッセージを送信
response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "東京の天気は?"}],
    tools=tools
)

# ステップ2: モデルがツールを使用したいか確認
message = response.choices[0].message

if message.tool_calls:
    # ステップ3: ツールを実行
    tool_call = message.tool_calls[0]
    arguments = json.loads(tool_call.function.arguments)
    result = get_weather(**arguments)

    # ステップ4: ツール結果をモデルに返送
    final_response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "user", "content": "東京の天気は?"},
            message,
            {"role": "tool", "tool_call_id": tool_call.id, "content": json.dumps(result)}
        ]
    )
    print(final_response.choices[0].message.content)

Model Context Protocol(MCP)

MCPはAnthropicが開発したオープンスタンダードで、LLMを外部データソースやツールに接続します。異なるLLMプロバイダーやアプリケーション間で機能する統一インターフェースを提供します。

MCPアーキテクチャ

graph LR subgraph "MCPホスト" H1[Claude Desktop] H2[IDE拡張] H3[カスタムアプリ] end subgraph "MCPサーバー" S1[ファイルシステム] S2[データベース] S3[API] S4[Web検索] end H1 <--> S1 H1 <--> S2 H2 <--> S3 H3 <--> S4 style H1 fill:#e3f2fd style H2 fill:#e3f2fd style H3 fill:#e3f2fd

MCPの概念

概念 説明
ホスト MCPサーバーに接続するアプリケーション(Claude Desktop、IDE)
サーバー ツール、リソース、プロンプトを提供するサービス
ツール LLMが呼び出せる関数(Function Callingと同様)
リソース LLMが読み取れるデータ(ファイル、データベースレコード)
プロンプト サーバーが提供する再利用可能なプロンプトテンプレート

MCPサーバーの構築

シンプルなMCPサーバー(Python)

from mcp.server import Server
from mcp.types import Tool, TextContent
import mcp.server.stdio

# サーバーインスタンスを作成
server = Server("weather-server")

# ツールを定義
@server.list_tools()
async def list_tools():
    return [
        Tool(
            name="get_weather",
            description="都市の現在の天気を取得",
            inputSchema={
                "type": "object",
                "properties": {
                    "city": {"type": "string", "description": "都市名"}
                },
                "required": ["city"]
            }
        )
    ]

# ツール実行を実装
@server.call_tool()
async def call_tool(name: str, arguments: dict):
    if name == "get_weather":
        city = arguments["city"]
        weather_data = fetch_weather(city)  # 実際の天気API呼び出し
        return [TextContent(
            type="text",
            text=f"{city}の天気: {weather_data['temp']}度, {weather_data['condition']}"
        )]

# サーバーを実行
async def main():
    async with mcp.server.stdio.stdio_server() as (read, write):
        await server.run(read, write)

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())

エラーハンドリング

堅牢なエラーハンドリングはツール使用アプリケーションにとって重要です。ツールはネットワーク問題、無効な入力、レート制限、外部サービスの停止により失敗する可能性があります。

包括的なエラーハンドリング

def execute_tool(tool_name: str, arguments: dict):
    """包括的なエラーハンドリング付きでツールを実行"""
    try:
        if not validate_arguments(tool_name, arguments):
            raise ToolError("無効な引数が提供されました", recoverable=True)

        if tool_name == "get_weather":
            return get_weather(**arguments)
        else:
            raise ToolError(f"不明なツール: {tool_name}", recoverable=False)

    except ConnectionError as e:
        return {
            "error": True,
            "type": "network_error",
            "message": f"サービスに接続できません: {e}",
            "suggestion": "サービスが一時的に利用できない可能性があります。再試行してください。"
        }

    except RateLimitError as e:
        return {
            "error": True,
            "type": "rate_limit",
            "message": "APIレート制限を超えました",
            "retry_after": e.retry_after,
            "suggestion": f"{e.retry_after}秒後に再試行してください。"
        }

マルチツールオーケストレーション

複雑なタスクは複数のツールの調整を必要とすることがよくあります。効果的なオーケストレーションには、ツールシーケンスの計画と依存関係の処理が含まれます。

チェーンされたツール実行

def handle_complex_request(user_message: str, max_iterations: int = 5):
    """複数のツール呼び出しが必要な可能性のあるリクエストを処理"""

    messages = [{"role": "user", "content": user_message}]

    for i in range(max_iterations):
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=messages,
            tools=tools
        )

        assistant_message = response.choices[0].message
        messages.append(assistant_message)

        # モデルが完了したか確認(ツール呼び出しなし)
        if not assistant_message.tool_calls:
            return assistant_message.content

        # リクエストされたすべてのツールを実行
        for tool_call in assistant_message.tool_calls:
            result = execute_tool(
                tool_call.function.name,
                json.loads(tool_call.function.arguments)
            )
            messages.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": json.dumps(result)
            })

    return "許可されたステップ内でタスクを完了できませんでした。"

セキュリティの考慮事項

セキュリティベストプラクティス

演習問題

演習1:ツール定義(難易度:易)

課題:以下を含むtranslate_textツールのJSON Schemaを定義してください:

演習2:エラーハンドリング(難易度:中)

課題:以下を行うfetch_stock_priceツールのエラーハンドリングを実装してください:

演習3:MCPサーバー(難易度:中)

課題:「メモ取り」アプリケーション用のMCPサーバーを設計してください。以下のツールを含む:

演習4:マルチツールオーケストレーション(難易度:上級)

課題:以下のツールを持つ「旅行計画アシスタント」のシステムプロンプトとツール定義を作成してください:

演習5:セキュリティ実装(難易度:上級)

課題:以下を含むセキュアなツール実行ラッパーを実装してください:

章のまとめ

重要ポイント

次のステップ

第5章では、すべてを実践プロジェクトでまとめます:


参考文献


更新履歴

免責事項