Claude Agent SDKで自動化エージェントを構築する実践ガイド

Claude Agent SDKを使うことで、複数のタスクを自動的に実行し、外部ツールと連携するAIエージェントを数十行のコードで構築できます。この記事では、実際のビジネスユースケースで動作するエージェントの実装方法をステップバイステップで解説します。

Claude Agent SDKとは?初心者が知るべき基本

Claude Agent SDKは、Anthropicが提供する公式SDKの一部で、Claude APIを使用した自律的なエージェント開発を簡潔にするツールセットです。従来のAPI呼び出しとは異なり、エージェントは以下の特徴を持ちます:

  • ツール呼び出し能力:外部API、データベース、計算処理との統合
  • 反復的な推論:複数ステップのタスクを自動的に分解・実行
  • コンテキスト管理:会話履歴とツール実行結果を自動追跡

エージェントを使うべき場面は、「複数のツールを組み合わせた複雑なワークフロー」や「ユーザーの指示から最適なアクションを判断する必要がある場合」です。一方、単純な質問応答やテキスト分類などは、通常のAPI呼び出しで十分です。

環境構築から最初のエージェント起動まで

必要なライブラリのインストール

まず、Python環境を準備します。以下のコマンドでClaude Agent SDKと依存ライブラリをインストールしてください:

pip install anthropic --upgrade
pip install requests python-dotenv

テスト環境:macOS 14.1 / Python 3.11.7 / anthropic==0.28.0で動作確認済みです。

APIキーの設定

Claudeを使用するには、Anthropic ConsoleからAPIキーを取得し、環境変数として設定します:

# .envファイルに記述
ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxx

# Pythonコードで読み込み
import os
from dotenv import load_dotenv

load_dotenv()
api_key = os.getenv("ANTHROPIC_API_KEY")

実践例:天気情報・タスク管理エージェントの実装

ステップ1:ツール定義の作成

エージェントが使用できるツールを定義します。このツールがClaudeにどのような機能を持つかを伝えます:

import anthropic
import json
from datetime import datetime

# ツール定義
tools = [
    {
        "name": "get_weather",
        "description": "指定した都市の現在の天気情報を取得します",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {
                    "type": "string",
                    "description": "都市名(例:東京、大阪)"
                }
            },
            "required": ["city"]
        }
    },
    {
        "name": "add_task",
        "description": "タスクをリストに追加します",
        "input_schema": {
            "type": "object",
            "properties": {
                "task_name": {
                    "type": "string",
                    "description": "タスクの名前"
                },
                "priority": {
                    "type": "string",
                    "enum": ["high", "medium", "low"],
                    "description": "優先度"
                }
            },
            "required": ["task_name"]
        }
    }
]

ステップ2:ツール実行関数の実装

定義したツールが実際に呼び出された際の処理を実装します。ここでは、簡単なモック実装を示します:

def execute_tool(tool_name, tool_input):
    """ツールを実行し、結果を返す"""
    if tool_name == "get_weather":
        city = tool_input.get("city")
        # 実際にはWeather APIを呼び出します
        weather_data = {
            "東京": "晴れ、気温28℃",
            "大阪": "曇り、気温26℃",
            "京都": "雨、気温24℃"
        }
        result = weather_data.get(city, "情報が見つかりません")
        return f"{city}の天気:{result}"
    
    elif tool_name == "add_task":
        task_name = tool_input.get("task_name")
        priority = tool_input.get("priority", "medium")
        timestamp = datetime.now().isoformat()
        return f"タスク '{task_name}' (優先度: {priority}) をリストに追加しました。作成時刻:{timestamp}"
    
    return "未知のツール"

ステップ3:エージェントループの実装

Claudeとのやり取りを管理し、ツール呼び出しを繰り返すメインループです:

def run_agent(user_message):
    """ユーザー入力に基づいてエージェントを実行"""
    client = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
    messages = [{"role": "user", "content": user_message}]
    
    print(f"\n📝 ユーザー入力: {user_message}\n")
    
    # エージェントループ
    while True:
        # Claudeに処理を依頼
        response = client.messages.create(
            model="claude-3-5-sonnet-20241022",
            max_tokens=1024,
            tools=tools,
            messages=messages
        )
        
        # ストップ理由を確認
        if response.stop_reason == "end_turn":
            # テキストのみの応答
            for block in response.content:
                if hasattr(block, 'text'):
                    print(f"🤖 Claudeの応答: {block.text}")
            break
        
        elif response.stop_reason == "tool_use":
            # ツール呼び出しが含まれている
            for block in response.content:
                if hasattr(block, 'text'):
                    print(f"💭 推論過程: {block.text}")
                
                if block.type == "tool_use":
                    tool_name = block.name
                    tool_input = block.input
                    tool_use_id = block.id
                    
                    print(f"🔧 ツール実行: {tool_name}({tool_input})")
                    
                    # ツールを実行
                    tool_result = execute_tool(tool_name, tool_input)
                    print(f"✅ 実行結果: {tool_result}\n")
                    
                    # 会話履歴に追加
                    messages.append({"role": "assistant", "content": response.content})
                    messages.append({
                        "role": "user",
                        "content": [
                            {
                                "type": "tool_result",
                                "tool_use_id": tool_use_id,
                                "content": tool_result
                            }
                        ]
                    })
        else:
            print(f"予期しない終了理由: {response.stop_reason}")
            break

# 実行例
if __name__ == "__main__":
    run_agent("東京の天気を教えてください。その後、『レポート作成』を優先度高で追加してください。")

よくあるハマりポイントと解決策

問題1:tool_useがend_turnと同時に現れない

Claudeが複数のツールを順番に呼び出す場合、各ツール呼び出しの後に必ず会話履歴に tool_result を追加する必要があります。追加し忘れると、エージェントが意図した動作をしません。

# ❌ 間違い:tool_resultを追加していない
messages.append({"role": "assistant", "content": response.content})

# ✅ 正解:tool_resultを必ず追加
messages.append({"role": "assistant", "content": response.content})
messages.append({
    "role": "user",
    "content": [{"type": "tool_result", "tool_use_id": tool_use_id, "content": result}]
})

問題2:タイムアウトエラー(429 Too Many Requests)

APIレート制限に達した場合は、リトライロジックを実装してください:

import time
from anthropic import RateLimitError

def run_agent_with_retry(user_message, max_retries=3):
    """リトライロジック付きのエージェント実行"""
    for attempt in range(max_retries):
        try:
            return run_agent(user_message)
        except RateLimitError:
            wait_time = 2 ** attempt  # 指数バックオフ
            print(f"⚠️ レート制限に達しました。{wait_time}秒待機します...")
            time.sleep(wait_time)
    print("❌ 最大リトライ回数に達しました")

問題3:ツール入力の型エラー

ツール定義の input_schema と実装が一致していないと、エラーが発生します。必ずスキーマに準拠した入力を返すようにしてください:

# スキーマ定義で priority は enum ["high", "medium", "low"] と定義済み
# 実装でも必ずこれらの値のみを返す
priority_map = {"高": "high", "中": "medium", "低": "low"}
priority = priority_map.get(user_priority, "medium")

実装時のベストプラクティス

ツール呼び出しのログ記録

本番環境では、どのツールが呼び出されたか、どのような入力が与えられたかを記録することが重要です:

import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def execute_tool_with_logging(tool_name, tool_input):
    """ロギング機能付きのツール実行"""
    logger.info(f"Tool execution: {tool_name} with input {tool_input}")
    result = execute_tool(tool_name, tool_input)
    logger.info(f"Tool result: {result}")
    return result

ツール数と複雑さの管理

ツールが多すぎるとClaudeが正しいツールを選択しづらくなります。関連ツールは1つに統合し、最大10個程度に抑えるのが推奨されます。

Claude Agent SDKと他のツールの比較

LangChain / LlamaIndexと比較して、Claude Agent SDKは軽量で学習曲線が低いメリットがあります。ただし、複雑なエージェント構成や複数のLLMの組み合わせが必要な場合はLangChainが便利です。

よくある質問

max_tokens の値を制限したり、ツール呼び出しの回数上限を設けることで防げます。また、各ステップで明確なゴール条件を定義し、Claudeに「このタスクが完了したら終了してください」と指示することが効果的です。

はい。各ユーザーセッションで独立した messages リストを管理することで対応できます。本番環境では、セッション管理機能を持つWebフレームワーク(FastAPI等)の使用をお勧めします。

ツール定義の description を詳しく書き、具体的な使用例を示すことが重要です。また、system メッセージでClaudeの役割や行動ガイドラインを明確に指定すると、精度が向上します。

まとめ

  • Claude Agent SDKはツール定義、ツール実行、エージェントループの3つの要素で構成される
  • tool_resultを会話履歴に追加することで、複数ステップのタスク実行が可能になる
  • レート制限やタイムアウトに備えて、リトライロジックを実装する
  • ツール数は10個以下に抑え、スキーマ定義は明確かつ詳細に書く
  • 複雑なワークフロー自動化に適した技術で、単純な質問応答はAPI直接呼び出しで十分

この記事で紹介したコード例を実装することで、すぐに実務レベルのエージェント開発が可能です。

← 前の記事MySQLインデックスを正しく貼る:クエリ速度を10倍にする実装方法
K
AWS・Python・生成AIを専門とするソフトウェアエンジニア。AI・クラウド・開発ワークフローの実践ガイドを執筆しています。詳しく見る →