Loading...
  • Costruisci
  • Amministrazione
  • Modelli e prezzi
  • Client SDK
  • Riferimento API
Search...
⌘K
Log in
Tutorial: Costruisci un agente che usa strumenti
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...

Solutions

  • AI agents
  • Code modernization
  • Coding
  • Customer support
  • Education
  • Financial services
  • Government
  • Life sciences

Partners

  • Amazon Bedrock
  • Google Cloud's Vertex AI

Learn

  • Blog
  • Courses
  • Use cases
  • Connectors
  • Customer stories
  • Engineering at Anthropic
  • Events
  • Powered by Claude
  • Service partners
  • Startups program

Company

  • Anthropic
  • Careers
  • Economic Futures
  • Research
  • News
  • Responsible Scaling Policy
  • Security and compliance
  • Transparency

Learn

  • Blog
  • Courses
  • Use cases
  • Connectors
  • Customer stories
  • Engineering at Anthropic
  • Events
  • Powered by Claude
  • Service partners
  • Startups program

Help and security

  • Availability
  • Status
  • Support
  • Discord

Terms and policies

  • Privacy policy
  • Responsible disclosure policy
  • Terms of service: Commercial
  • Terms of service: Consumer
  • Usage policy
Costruisci/Strumenti

Tutorial: Costruisci un agente che utilizza strumenti

Una guida passo dopo passo da una singola chiamata di strumento a un ciclo agentico pronto per la produzione.

Questo tutorial costruisce un agente di gestione del calendario in cinque anelli concentrici. Ogni anello è un programma completo e eseguibile che aggiunge esattamente un concetto all'anello precedente. Alla fine avrai scritto il ciclo agentico manualmente e poi lo avrai sostituito con l'astrazione SDK di Tool Runner.

Lo strumento di esempio è create_calendar_event. Il suo schema utilizza oggetti annidati, array e campi facoltativi, quindi vedrai come Claude gestisce forme di input realistiche piuttosto che una singola stringa piatta.

Ogni anello viene eseguito in modo autonomo. Copia qualsiasi anello in un file nuovo e si eseguirà senza il codice degli anelli precedenti.

Anello 1: Un singolo strumento, un singolo turno

Il programma più piccolo possibile che utilizza strumenti: uno strumento, un messaggio utente, una chiamata di strumento, un risultato. Il codice è pesantemente commentato in modo che tu possa mappare ogni riga al ciclo di vita dell'uso dello strumento.

La richiesta invia un array tools insieme al messaggio dell'utente. Quando Claude decide di chiamare uno strumento, la risposta ritorna con stop_reason: "tool_use" e un blocco di contenuto tool_use contenente il nome dello strumento, un id univoco e l'input strutturato. Il tuo codice esegue lo strumento, quindi invia il risultato in un blocco tool_result il cui tool_use_id corrisponde all'id della chiamata.

# Ring 1: Single tool, single turn.
# Source for <CodeSource> in build-a-tool-using-agent.mdx.

import json

import anthropic

# Create a client. It reads ANTHROPIC_API_KEY from the environment.
client = anthropic.Anthropic()

# Define one tool. The input_schema is a JSON Schema object describing
# the arguments Claude should pass when it calls this tool. This schema
# includes nested objects (recurrence), arrays (attendees), and optional
# fields, which is closer to real-world tools than a flat string argument.
tools = [
    {
        "name": "create_calendar_event",
        "description": "Create a calendar event with attendees and optional recurrence.",
        "input_schema": {
            "type": "object",
            "properties": {
                "title": {"type": "string"},
                "start": {"type": "string", "format": "date-time"},
                "end": {"type": "string", "format": "date-time"},
                "attendees": {
                    "type": "array",
                    "items": {"type": "string", "format": "email"},
                },
                "recurrence": {
                    "type": "object",
                    "properties": {
                        "frequency": {"enum": ["daily", "weekly", "monthly"]},
                        "count": {"type": "integer", "minimum": 1},
                    },
                },
            },
            "required": ["title", "start", "end"],
        },
    }
]

# Send the user's request along with the tool definition. Claude decides
# whether to call the tool based on the request and the tool description.
response = client.messages.create(
    model="claude-opus-4-6",
    max_tokens=1024,
    tools=tools,
    tool_choice={"type": "auto", "disable_parallel_tool_use": True},
    messages=[
        {
            "role": "user",
            "content": "Schedule a 30-minute sync with [email protected] and [email protected] next Monday at 10am.",
        }
    ],
)

# When Claude calls a tool, the response has stop_reason "tool_use"
# and the content array contains a tool_use block alongside any text.
print(f"stop_reason: {response.stop_reason}")

# Find the tool_use block. A response may contain text blocks before the
# tool_use block, so scan the content array rather than assuming position.
tool_use = next(block for block in response.content if block.type == "tool_use")
print(f"Tool: {tool_use.name}")
print(f"Input: {tool_use.input}")

# Execute the tool. In a real system this would call your calendar API.
# Here the result is hardcoded to keep the example self-contained.
result = {"event_id": "evt_123", "status": "created"}

# Send the result back. The tool_result block goes in a user message and
# its tool_use_id must match the id from the tool_use block above. The
# assistant's previous response is included so Claude has the full history.
followup = client.messages.create(
    model="claude-opus-4-6",
    max_tokens=1024,
    tools=tools,
    tool_choice={"type": "auto", "disable_parallel_tool_use": True},
    messages=[
        {
            "role": "user",
            "content": "Schedule a 30-minute sync with [email protected] and [email protected] next Monday at 10am.",
        },
        {"role": "assistant", "content": response.content},
        {
            "role": "user",
            "content": [
                {
                    "type": "tool_result",
                    "tool_use_id": tool_use.id,
                    "content": json.dumps(result),
                }
            ],
        },
    ],
)

# With the tool result in hand, Claude produces a final natural-language
# answer and stop_reason becomes "end_turn".
print(f"stop_reason: {followup.stop_reason}")
final_text = next(block for block in followup.content if block.type == "text")
print(final_text.text)

Cosa aspettarsi

Output
stop_reason: tool_use
Tool: create_calendar_event
Input: {'title': 'Sync', 'start': '2026-03-30T10:00:00', 'end': '2026-03-30T10:30:00', 'attendees': ['[email protected]', '[email protected]']}
stop_reason: end_turn
I've scheduled your 30-minute sync with Alice and Bob for next Monday at 10am.

Il primo stop_reason è tool_use perché Claude sta aspettando il risultato del calendario. Dopo aver inviato il risultato, il secondo stop_reason è end_turn e il contenuto è linguaggio naturale per l'utente.

Anello 2: Il ciclo agentico

L'Anello 1 presupponeva che Claude avrebbe chiamato lo strumento esattamente una volta. I compiti reali spesso richiedono diverse chiamate: Claude potrebbe creare un evento, leggere la conferma, quindi crearne un altro. La soluzione è un ciclo while che continua a eseguire strumenti e alimentare i risultati finché stop_reason non è più "tool_use".

L'altro cambiamento è la cronologia della conversazione. Invece di ricostruire l'array messages da zero ad ogni richiesta, mantieni un elenco in esecuzione e aggiungi ad esso. Ogni turno vede il contesto precedente completo.

# Ring 2: The agentic loop.
# Source for <CodeSource> in build-a-tool-using-agent.mdx.

import json

import anthropic

client = anthropic.Anthropic()

tools = [
    {
        "name": "create_calendar_event",
        "description": "Create a calendar event with attendees and optional recurrence.",
        "input_schema": {
            "type": "object",
            "properties": {
                "title": {"type": "string"},
                "start": {"type": "string", "format": "date-time"},
                "end": {"type": "string", "format": "date-time"},
                "attendees": {
                    "type": "array",
                    "items": {"type": "string", "format": "email"},
                },
                "recurrence": {
                    "type": "object",
                    "properties": {
                        "frequency": {"enum": ["daily", "weekly", "monthly"]},
                        "count": {"type": "integer", "minimum": 1},
                    },
                },
            },
            "required": ["title", "start", "end"],
        },
    }
]


def run_tool(name, tool_input):
    if name == "create_calendar_event":
        return {"event_id": "evt_123", "status": "created", "title": tool_input["title"]}
    return {"error": f"Unknown tool: {name}"}


# Keep the full conversation history in a list so each turn sees prior context.
messages = [
    {
        "role": "user",
        "content": "Schedule a weekly team standup every Monday at 9am for the next 4 weeks. Invite the whole team: [email protected], [email protected], [email protected].",
    }
]

response = client.messages.create(
    model="claude-opus-4-6",
    max_tokens=1024,
    tools=tools,
    tool_choice={"type": "auto", "disable_parallel_tool_use": True},
    messages=messages,
)

# Loop until Claude stops asking for tools. Each iteration runs the requested
# tool, appends the result to history, and asks Claude to continue.
while response.stop_reason == "tool_use":
    tool_use = next(block for block in response.content if block.type == "tool_use")
    result = run_tool(tool_use.name, tool_use.input)

    messages.append({"role": "assistant", "content": response.content})
    messages.append(
        {
            "role": "user",
            "content": [
                {
                    "type": "tool_result",
                    "tool_use_id": tool_use.id,
                    "content": json.dumps(result),
                }
            ],
        }
    )

    response = client.messages.create(
        model="claude-opus-4-6",
        max_tokens=1024,
        tools=tools,
        tool_choice={"type": "auto", "disable_parallel_tool_use": True},
        messages=messages,
    )

final_text = next(block for block in response.content if block.type == "text")
print(final_text.text)

Cosa aspettarsi

Output
I've set up your weekly team standup for the next 4 Mondays at 9am with Alice, Bob, and Carol invited.

Il ciclo può essere eseguito una o più volte a seconda di come Claude scompone il compito. Il tuo codice non ha più bisogno di saperlo in anticipo.

Anello 3: Più strumenti, chiamate parallele

Gli agenti raramente hanno una sola capacità. Aggiungi un secondo strumento, list_calendar_events, in modo che Claude possa controllare la pianificazione esistente prima di creare qualcosa di nuovo.

Quando Claude ha più chiamate di strumenti indipendenti da effettuare, potrebbe restituire diversi blocchi tool_use in una singola risposta. Il tuo ciclo deve elaborarli tutti e inviare indietro tutti i risultati insieme in un unico messaggio utente. Itera su ogni blocco tool_use in response.content, non solo il primo.

# Ring 3: Multiple tools, parallel calls.
# Source for <CodeSource> in build-a-tool-using-agent.mdx.

import json

import anthropic

client = anthropic.Anthropic()

tools = [
    {
        "name": "create_calendar_event",
        "description": "Create a calendar event with attendees and optional recurrence.",
        "input_schema": {
            "type": "object",
            "properties": {
                "title": {"type": "string"},
                "start": {"type": "string", "format": "date-time"},
                "end": {"type": "string", "format": "date-time"},
                "attendees": {
                    "type": "array",
                    "items": {"type": "string", "format": "email"},
                },
                "recurrence": {
                    "type": "object",
                    "properties": {
                        "frequency": {"enum": ["daily", "weekly", "monthly"]},
                        "count": {"type": "integer", "minimum": 1},
                    },
                },
            },
            "required": ["title", "start", "end"],
        },
    },
    {
        "name": "list_calendar_events",
        "description": "List all calendar events on a given date.",
        "input_schema": {
            "type": "object",
            "properties": {
                "date": {"type": "string", "format": "date"},
            },
            "required": ["date"],
        },
    },
]


def run_tool(name, tool_input):
    if name == "create_calendar_event":
        return {"event_id": "evt_123", "status": "created", "title": tool_input["title"]}
    if name == "list_calendar_events":
        return {"events": [{"title": "Existing meeting", "start": "14:00", "end": "15:00"}]}
    return {"error": f"Unknown tool: {name}"}


messages = [
    {
        "role": "user",
        "content": "Check what I have next Monday, then schedule a planning session that avoids any conflicts.",
    }
]

response = client.messages.create(
    model="claude-opus-4-6",
    max_tokens=1024,
    tools=tools,
    messages=messages,
)

while response.stop_reason == "tool_use":
    # A single response can contain multiple tool_use blocks. Process all of
    # them and return all results together in one user message.
    tool_results = []
    for block in response.content:
        if block.type == "tool_use":
            result = run_tool(block.name, block.input)
            tool_results.append(
                {
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": json.dumps(result),
                }
            )

    messages.append({"role": "assistant", "content": response.content})
    messages.append({"role": "user", "content": tool_results})

    response = client.messages.create(
        model="claude-opus-4-6",
        max_tokens=1024,
        tools=tools,
        messages=messages,
    )

final_text = next(block for block in response.content if block.type == "text")
print(final_text.text)

Cosa aspettarsi

Output
I checked your calendar for next Monday and found an existing meeting from 2pm to 3pm. I've scheduled the planning session for 10am to 11am to avoid the conflict.

Per ulteriori informazioni sull'esecuzione concorrente e le garanzie di ordinamento, vedi Uso parallelo dello strumento.

Anello 4: Gestione degli errori

Gli strumenti falliscono. Un'API del calendario potrebbe rifiutare un evento con troppi partecipanti, oppure una data potrebbe essere malformata. Quando uno strumento genera un errore, invia il messaggio di errore indietro con is_error: true invece di arrestarsi. Claude legge l'errore e può riprovare con input corretto, chiedere chiarimenti all'utente o spiegare il limite.

# Ring 4: Error handling.
# Source for <CodeSource> in build-a-tool-using-agent.mdx.

import json

import anthropic

client = anthropic.Anthropic()

tools = [
    {
        "name": "create_calendar_event",
        "description": "Create a calendar event with attendees and optional recurrence.",
        "input_schema": {
            "type": "object",
            "properties": {
                "title": {"type": "string"},
                "start": {"type": "string", "format": "date-time"},
                "end": {"type": "string", "format": "date-time"},
                "attendees": {
                    "type": "array",
                    "items": {"type": "string", "format": "email"},
                },
                "recurrence": {
                    "type": "object",
                    "properties": {
                        "frequency": {"enum": ["daily", "weekly", "monthly"]},
                        "count": {"type": "integer", "minimum": 1},
                    },
                },
            },
            "required": ["title", "start", "end"],
        },
    },
    {
        "name": "list_calendar_events",
        "description": "List all calendar events on a given date.",
        "input_schema": {
            "type": "object",
            "properties": {
                "date": {"type": "string", "format": "date"},
            },
            "required": ["date"],
        },
    },
]


def run_tool(name, tool_input):
    if name == "create_calendar_event":
        if "attendees" in tool_input and len(tool_input["attendees"]) > 10:
            raise ValueError("Too many attendees (max 10)")
        return {"event_id": "evt_123", "status": "created", "title": tool_input["title"]}
    if name == "list_calendar_events":
        return {"events": [{"title": "Existing meeting", "start": "14:00", "end": "15:00"}]}
    raise ValueError(f"Unknown tool: {name}")


messages = [
    {
        "role": "user",
        "content": "Schedule an all-hands with everyone: " + ", ".join(f"user{i}@example.com" for i in range(15)),
    }
]

response = client.messages.create(
    model="claude-opus-4-6",
    max_tokens=1024,
    tools=tools,
    messages=messages,
)

while response.stop_reason == "tool_use":
    tool_results = []
    for block in response.content:
        if block.type == "tool_use":
            try:
                result = run_tool(block.name, block.input)
                tool_results.append(
                    {"type": "tool_result", "tool_use_id": block.id, "content": json.dumps(result)}
                )
            except Exception as exc:
                # Signal failure so Claude can retry or ask for clarification.
                tool_results.append(
                    {
                        "type": "tool_result",
                        "tool_use_id": block.id,
                        "content": str(exc),
                        "is_error": True,
                    }
                )

    messages.append({"role": "assistant", "content": response.content})
    messages.append({"role": "user", "content": tool_results})

    response = client.messages.create(
        model="claude-opus-4-6",
        max_tokens=1024,
        tools=tools,
        messages=messages,
    )

final_text = next(block for block in response.content if block.type == "text")
print(final_text.text)

Cosa aspettarsi

Output
I tried to schedule the all-hands but the calendar only allows 10 attendees per event. I can split this into two sessions, or you can let me know which 10 people to prioritize.

Il flag is_error è l'unica differenza da un risultato riuscito. Claude vede il flag e il testo di errore e risponde di conseguenza. Vedi Gestisci le chiamate di strumento per il riferimento completo sulla gestione degli errori.

Anello 5: L'astrazione SDK di Tool Runner

Gli Anelli 2 attraverso 4 hanno scritto lo stesso ciclo manualmente: chiama l'API, controlla stop_reason, esegui strumenti, aggiungi risultati, ripeti. Tool Runner lo fa per te. Definisci ogni strumento come una funzione, passa l'elenco a tool_runner e recupera il messaggio finale una volta completato il ciclo. Il wrapping degli errori, la formattazione dei risultati e la gestione della conversazione vengono gestiti internamente.

L'SDK Python utilizza il decoratore @beta_tool per dedurre lo schema dai suggerimenti di tipo e dalla docstring. L'SDK TypeScript utilizza betaZodTool con uno schema Zod.

Tool Runner è disponibile negli SDK Python, TypeScript e Ruby. Le schede Shell e CLI mostrano una nota invece del codice; mantieni il ciclo Ring 4 per gli script basati su shell.

# Ring 5: The Tool Runner SDK abstraction.
# Source for <CodeSource> in build-a-tool-using-agent.mdx.

import json

import anthropic
from anthropic import beta_tool

client = anthropic.Anthropic()


@beta_tool
def create_calendar_event(
    title: str,
    start: str,
    end: str,
    attendees: list[str] | None = None,
    recurrence: dict | None = None,
) -> str:
    """Create a calendar event with attendees and optional recurrence.

    Args:
        title: Event title.
        start: Start time in ISO 8601 format.
        end: End time in ISO 8601 format.
        attendees: Email addresses to invite.
        recurrence: Dict with 'frequency' (daily, weekly, monthly) and 'count'.
    """
    if attendees and len(attendees) > 10:
        raise ValueError("Too many attendees (max 10)")
    return json.dumps({"event_id": "evt_123", "status": "created", "title": title})


@beta_tool
def list_calendar_events(date: str) -> str:
    """List all calendar events on a given date.

    Args:
        date: Date in YYYY-MM-DD format.
    """
    return json.dumps({"events": [{"title": "Existing meeting", "start": "14:00", "end": "15:00"}]})


final_message = client.beta.messages.tool_runner(
    model="claude-opus-4-6",
    max_tokens=1024,
    tools=[create_calendar_event, list_calendar_events],
    messages=[
        {
            "role": "user",
            "content": "Check what I have next Monday, then schedule a planning session that avoids any conflicts.",
        }
    ],
).until_done()

for block in final_message.content:
    if block.type == "text":
        print(block.text)

Cosa aspettarsi

Output
I checked your calendar for next Monday and found an existing meeting from 2pm to 3pm. I've scheduled the planning session for 10am to 11am to avoid the conflict.

L'output è identico all'Anello 3. La differenza è nel codice: circa la metà delle righe, nessun ciclo manuale e lo schema vive accanto all'implementazione.

Cosa hai costruito

Hai iniziato con una singola chiamata di strumento hardcoded e sei terminato con un agente di forma pronta per la produzione che gestisce più strumenti, chiamate parallele ed errori, quindi hai compresso tutto in Tool Runner. Nel processo hai visto ogni parte del protocollo di uso dello strumento: blocchi tool_use, blocchi tool_result, corrispondenza tool_use_id, controllo stop_reason e segnalazione is_error.

Passaggi successivi

Affina i tuoi schemi

Specifica dello schema e migliori pratiche.

Approfondimento su Tool Runner

Il riferimento completo dell'astrazione SDK.

Risoluzione dei problemi

Correggi gli errori comuni di uso dello strumento.

Was this page helpful?

  • Anello 1: Un singolo strumento, un singolo turno
  • Anello 2: Il ciclo agentico
  • Anello 3: Più strumenti, chiamate parallele
  • Anello 4: Gestione degli errori
  • Anello 5: L'astrazione SDK di Tool Runner
  • Cosa hai costruito
  • Passaggi successivi