AI Agent 개발이 활발해지는 가운데, 도구 호출(tool calling) 프레임워크 선택이 시스템 성능을 좌우하는 핵심 과제가 되었습니다. 본 기사에서는 현재 가장 널리 사용되는 두 가지 접근법인 ReAct(Reasoning + Acting)Plan-and-Execute를 심층 비교하고, HolySheep AI를 활용한 실전 구현 방법을 소개합니다.

背景:なぜ工具调用フレームワークが重要か

ECプラットフォームのAIカスタマーサービスでは、ユーザーの質問に応じて「在庫確認」「注文履歴取得」「返金の複雑条件判定」を連続実行する必要があります。こうした複雑なタスクをAI Agentに実行させるには、以下の要件が必要です:

私は以前、某EC企業のRAGシステムを構築する際、ReActとPlan-and-Executeの両方を試しました。結論として、プロジェクトの特性によって最適な選択が異なることを実感しました。以下、その知見を共有します。

ReActとは:同期的推論・実行パターン

核心概念

ReAct(Reasoning + Acting)は、「推論」と「実行」を1ステップ内で交互に行う同期的アプローチです。各ステップで以下のプロセスを繰り返します:

  1. Think:現在の状況分析与次のアクション决定
  2. Act:ツール呼び出しの実行
  3. Observe:実行結果の观察

メリット

デメリット

Plan-and-Executeとは:分離型アーキテクチャ

核心概念

Plan-and-Executeは、任务を「計画立案」と「実行」の2段階で分離するアプローチです。最初に完全なアクションプランを作成し、その後一括実行を行います。

メリット

デメリット

実戦コード比較:HolySheep AI API活用

ReAct実装例

import requests
import json

class ReActAgent:
    def __init__(self, api_key: str):
        self.base_url = "https://api.holysheep.ai/v1"
        self.headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json"
        }
    
    def call_llm(self, messages: list, tools: list = None) -> dict:
        """HolySheep AI API呼び出し"""
        payload = {
            "model": "gpt-4.1",
            "messages": messages,
            "temperature": 0.7,
            "max_tokens": 2048
        }
        if tools:
            payload["tools"] = tools
        
        response = requests.post(
            f"{self.base_url}/chat/completions",
            headers=self.headers,
            json=payload
        )
        return response.json()
    
    def execute_tool(self, tool_name: str, args: dict) -> str:
        """ツールエミュレーション"""
        tools_map = {
            "get_order_status": self._get_order_status,
            "check_inventory": self._check_inventory,
            "process_refund": self._process_refund
        }
        return tools_map.get(tool_name, lambda x: "Unknown tool")(args)
    
    def _get_order_status(self, args):
        order_id = args.get("order_id")
        return json.dumps({"order_id": order_id, "status": "shipped", "eta": "2days"})
    
    def _check_inventory(self, args):
        sku = args.get("sku")
        return json.dumps({"sku": sku, "available": True, "quantity": 50})
    
    def _process_refund(self, args):
        order_id = args.get("order_id")
        amount = args.get("amount", 0)
        return json.dumps({"refund_id": f"REF-{order_id}", "amount": amount, "status": "processed"})

    def run(self, user_query: str, max_iterations: int = 10):
        """ReAct実行ループ"""
        messages = [
            {"role": "system", "content": """あなたはECサイトのAIカスタマーエージェントです。
            ユーザーは注文関連の問題で連絡してきます。
            利用可能なツール:get_order_status, check_inventory, process_refund
            回答は日本語で行ってください。"""},
            {"role": "user", "content": user_query}
        ]
        
        tools = [
            {
                "type": "function",
                "function": {
                    "name": "get_order_status",
                    "description": "注文状況を確認",
                    "parameters": {"type": "object", "properties": {"order_id": {"type": "string"}}}
                }
            },
            {
                "type": "function",
                "function": {
                    "name": "check_inventory",
                    "description": "在庫確認",
                    "parameters": {"type": "object", "properties": {"sku": {"type": "string"}}}
                }
            },
            {
                "type": "function",
                "function": {
                    "name": "process_refund",
                    "description": "返金処理",
                    "parameters": {"type": "object", "properties": {"order_id": {"type": "string"}, "amount": {"type": "number"}}}
                }
            }
        ]
        
        for i in range(max_iterations):
            print(f"--- Iteration {i+1} ---")
            response = self.call_llm(messages, tools)
            
            if "choices" not in response:
                print(f"API Error: {response}")
                break
            
            choice = response["choices"][0]
            if choice.get("finish_reason") == "stop":
                final_response = choice["message"]["content"]
                messages.append({"role": "assistant", "content": final_response})
                break
            
            # ツール呼び出し処理
            if "tool_calls" in choice["message"]:
                tool_call = choice["message"]["tool_calls"][0]
                tool_name = tool_call["function"]["name"]
                args = json.loads(tool_call["function"]["arguments"])
                
                print(f"Executing: {tool_name} with {args}")
                result = self.execute_tool(tool_name, args)
                
                messages.append({
                    "role": "assistant",
                    "content": None,
                    "tool_calls": choice["message"]["tool_calls"]
                })
                messages.append({
                    "role": "tool",
                    "tool_call_id": tool_call["id"],
                    "name": tool_name,
                    "content": result
                })
            else:
                messages.append({"role": "assistant", "content": choice["message"]["content"]})
                break
        
        return messages[-1]["content"]

実行例

api_key = "YOUR_HOLYSHEEP_API_KEY" agent = ReActAgent(api_key) result = agent.run("注文番号ORD-12345の状況を教えていただき、在庫があれば交換の手配をお願いします。") print(f"\n最終回答:\n{result}")

Plan-and-Execute実装例

import requests
import json
from typing import List, Dict, Any
from enum import Enum

class TaskStatus(Enum):
    PENDING = "pending"
    COMPLETED = "completed"
    FAILED = "failed"

class PlanStep:
    def __init__(self, step_id: int, action: str