Files
2025-11-29 18:16:40 +08:00

6.9 KiB

Phase 2: Multi-Tool Agent Implementation

Objective: Implement agent with multiple validated tools

Python Implementation

from anthropic import Anthropic
from typing import Dict, Any, List

client = Anthropic()

# Define tools
TOOLS = [
    {
        "name": "search_flights",
        "description": "Search for available flights",
        "strict": True,
        "input_schema": {
            "type": "object",
            "properties": {
                "origin": {"type": "string", "description": "Departure city"},
                "destination": {"type": "string", "description": "Arrival city"},
                "departure_date": {"type": "string", "format": "date"},
                "travelers": {"type": "integer", "enum": [1, 2, 3, 4, 5, 6]}
            },
            "required": ["origin", "destination", "departure_date", "travelers"],
            "additionalProperties": False
        }
    },
    {
        "name": "book_flight",
        "description": "Book a selected flight",
        "strict": True,
        "input_schema": {
            "type": "object",
            "properties": {
                "flight_id": {"type": "string", "description": "Flight identifier"},
                "passengers": {
                    "type": "array",
                    "items": {
                        "type": "object",
                        "properties": {
                            "name": {"type": "string"},
                            "passport": {"type": "string"}
                        },
                        "required": ["name", "passport"],
                        "additionalProperties": False
                    }
                }
            },
            "required": ["flight_id", "passengers"],
            "additionalProperties": False
        }
    },
    {
        "name": "search_hotels",
        "description": "Search for hotels in a city",
        "strict": True,
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": "City name"},
                "check_in": {"type": "string", "format": "date"},
                "check_out": {"type": "string", "format": "date"},
                "guests": {"type": "integer", "enum": [1, 2, 3, 4]}
            },
            "required": ["city", "check_in", "check_out", "guests"],
            "additionalProperties": False
        }
    }
]

# Tool execution functions
def search_flights(origin: str, destination: str, departure_date: str, travelers: int) -> Dict:
    """Execute flight search - calls actual API."""
    # Implementation here
    return {"flights": [...]}

def book_flight(flight_id: str, passengers: List[Dict]) -> Dict:
    """Book the flight - calls actual API."""
    # Implementation here
    return {"confirmation": "ABC123", "status": "confirmed"}

def search_hotels(city: str, check_in: str, check_out: str, guests: int) -> Dict:
    """Search hotels - calls actual API."""
    # Implementation here
    return {"hotels": [...]}

# Tool registry
TOOL_FUNCTIONS = {
    "search_flights": search_flights,
    "book_flight": book_flight,
    "search_hotels": search_hotels,
}

# Agent loop
def run_agent(user_request: str, max_turns: int = 10):
    """Run agent with tool validation."""
    messages = [{"role": "user", "content": user_request}]

    for turn in range(max_turns):
        response = client.beta.messages.create(
            model="claude-sonnet-4-5",
            max_tokens=2048,
            betas=["structured-outputs-2025-11-13"],
            messages=messages,
            tools=TOOLS,
        )

        # Process response
        if response.stop_reason == "end_turn":
            # Agent finished
            return extract_final_answer(response)

        if response.stop_reason == "tool_use":
            # Execute tools
            tool_results = []

            for block in response.content:
                if block.type == "tool_use":
                    # Tool input is GUARANTEED to match schema
                    tool_name = block.name
                    tool_input = block.input  # Already validated!

                    # Execute tool
                    tool_function = TOOL_FUNCTIONS[tool_name]
                    result = tool_function(**tool_input)  # Type-safe!

                    tool_results.append({
                        "type": "tool_result",
                        "tool_use_id": block.id,
                        "content": str(result)
                    })

            # Add assistant response and tool results to conversation
            messages.append({"role": "assistant", "content": response.content})
            messages.append({"role": "user", "content": tool_results})

        else:
            raise Exception(f"Unexpected stop reason: {response.stop_reason}")

    raise Exception("Max turns reached")

# Usage
result = run_agent("Book a flight from SF to Paris for 2 people, departing May 15")
print(result)

TypeScript Implementation

import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });

const TOOLS: Anthropic.Tool[] = [
  {
    name: "search_flights",
    description: "Search for available flights",
    strict: true,
    input_schema: {
      type: "object",
      properties: {
        origin: { type: "string", description: "Departure city" },
        destination: { type: "string", description: "Arrival city" },
        departure_date: { type: "string", format: "date" },
        travelers: { type: "integer", enum: [1, 2, 3, 4, 5, 6] }
      },
      required: ["origin", "destination", "departure_date", "travelers"],
      additionalProperties: false
    }
  },
  // ... other tools
];

async function runAgent(userRequest: string, maxTurns: number = 10) {
  const messages: Anthropic.MessageParam[] = [
    { role: "user", content: userRequest }
  ];

  for (let turn = 0; turn < maxTurns; turn++) {
    const response = await client.beta.messages.create({
      model: "claude-sonnet-4-5",
      max_tokens: 2048,
      betas: ["structured-outputs-2025-11-13"],
      messages,
      tools: TOOLS,
    });

    if (response.stop_reason === "end_turn") {
      return extractFinalAnswer(response);
    }

    if (response.stop_reason === "tool_use") {
      const toolResults: Anthropic.ToolResultBlockParam[] = [];

      for (const block of response.content) {
        if (block.type === "tool_use") {
          // Input guaranteed to match schema!
          const result = await executeTool(block.name, block.input);

          toolResults.push({
            type: "tool_result",
            tool_use_id: block.id,
            content: JSON.stringify(result)
          });
        }
      }

      messages.push({ role: "assistant", content: response.content });
      messages.push({ role: "user", content: toolResults });
    }
  }

  throw new Error("Max turns reached");
}

Output

Working multi-tool agent with validated tool schemas.