Loading...
  • Construire
  • Admin
  • Modèles & tarification
  • SDKs clients
  • Référence API
Search...
⌘K
Log in
Tutoriel : Créer un agent utilisant des outils
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
Construire/Outils

Tutoriel : Construire un agent utilisant des outils

Une procédure pas à pas guidée, du simple appel d'outil à une boucle agentique prête pour la production.

Ce tutoriel construit un agent de gestion de calendrier en cinq anneaux concentriques. Chaque anneau est un programme complet et exécutable qui ajoute exactement un concept à l'anneau précédent. À la fin, vous aurez écrit la boucle agentique à la main, puis l'aurez remplacée par l'abstraction du SDK Tool Runner.

L'outil d'exemple est create_calendar_event. Son schéma utilise des objets imbriqués, des tableaux et des champs optionnels, vous verrez donc comment Claude gère des formes d'entrée réalistes plutôt qu'une simple chaîne plate.

Chaque anneau s'exécute de manière autonome. Copiez n'importe quel anneau dans un fichier vierge et il s'exécutera sans le code des anneaux précédents.

Anneau 1 : Un outil, un tour

Le plus petit programme possible utilisant des outils : un outil, un message utilisateur, un appel d'outil, un résultat. Le code est fortement commenté pour que vous puissiez mapper chaque ligne au cycle de vie de l'utilisation d'outils.

La requête envoie un tableau tools aux côtés du message utilisateur. Quand Claude décide d'appeler un outil, la réponse revient avec stop_reason: "tool_use" et un bloc de contenu tool_use contenant le nom de l'outil, un id unique et l'input structuré. Votre code exécute l'outil, puis renvoie le résultat dans un bloc tool_result dont le tool_use_id correspond à l'id de l'appel.

# 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)

À quoi s'attendre

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.

Le premier stop_reason est tool_use car Claude attend le résultat du calendrier. Après avoir envoyé le résultat, le second stop_reason est end_turn et le contenu est du langage naturel pour l'utilisateur.

Anneau 2 : La boucle agentique

L'anneau 1 supposait que Claude appellerait l'outil exactement une fois. Les tâches réelles nécessitent souvent plusieurs appels : Claude pourrait créer un événement, lire la confirmation, puis en créer un autre. La solution est une boucle while qui continue à exécuter les outils et à renvoyer les résultats jusqu'à ce que stop_reason ne soit plus "tool_use".

L'autre changement est l'historique de la conversation. Au lieu de reconstruire le tableau messages à partir de zéro à chaque requête, conservez une liste en cours et ajoutez-y. Chaque tour voit le contexte antérieur complet.

# 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)

À quoi s'attendre

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

La boucle peut s'exécuter une ou plusieurs fois selon la façon dont Claude décompose la tâche. Votre code n'a plus besoin de le savoir à l'avance.

Anneau 3 : Plusieurs outils, appels parallèles

Les agents ont rarement une seule capacité. Ajoutez un deuxième outil, list_calendar_events, pour que Claude puisse vérifier le calendrier existant avant de créer quelque chose de nouveau.

Quand Claude a plusieurs appels d'outils indépendants à faire, il peut retourner plusieurs blocs tool_use dans une seule réponse. Votre boucle doit tous les traiter et renvoyer tous les résultats ensemble dans un seul message utilisateur. Itérez sur chaque bloc tool_use dans response.content, pas seulement le premier.

# 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)

À quoi s'attendre

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.

Pour plus d'informations sur l'exécution concurrente et les garanties de commande, consultez Utilisation parallèle d'outils.

Anneau 4 : Gestion des erreurs

Les outils échouent. Une API de calendrier pourrait rejeter un événement avec trop de participants, ou une date pourrait être mal formée. Quand un outil lève une erreur, renvoyez le message d'erreur avec is_error: true au lieu de planter. Claude lit l'erreur et peut réessayer avec une entrée corrigée, demander une clarification à l'utilisateur, ou expliquer la limitation.

# 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)

À quoi s'attendre

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.

Le drapeau is_error est la seule différence par rapport à un résultat réussi. Claude voit le drapeau et le texte d'erreur, et répond en conséquence. Consultez Gérer les appels d'outils pour la référence complète de gestion des erreurs.

Anneau 5 : L'abstraction du SDK Tool Runner

Les anneaux 2 à 4 ont écrit la même boucle à la main : appeler l'API, vérifier stop_reason, exécuter les outils, ajouter les résultats, répéter. Tool Runner le fait pour vous. Définissez chaque outil comme une fonction, passez la liste à tool_runner, et récupérez le message final une fois la boucle terminée. L'enveloppe d'erreur, le formatage des résultats et la gestion de la conversation sont gérés en interne.

Le SDK Python utilise le décorateur @beta_tool pour déduire le schéma à partir des indices de type et de la docstring. Le SDK TypeScript utilise betaZodTool avec un schéma Zod.

Tool Runner est disponible dans les SDK Python, TypeScript et Ruby. Les onglets Shell et CLI affichent une note à la place du code ; conservez la boucle Ring 4 pour les scripts basés sur 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)

À quoi s'attendre

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.

La sortie est identique à l'anneau 3. La différence est dans le code : environ la moitié des lignes, pas de boucle manuelle, et le schéma vit à côté de l'implémentation.

Ce que vous avez construit

Vous avez commencé avec un simple appel d'outil codé en dur et vous avez terminé avec un agent de forme prête pour la production qui gère plusieurs outils, des appels parallèles et des erreurs, puis vous avez réduit tout cela à Tool Runner. En chemin, vous avez vu chaque partie du protocole d'utilisation d'outils : les blocs tool_use, les blocs tool_result, la correspondance tool_use_id, la vérification stop_reason et la signalisation is_error.

Étapes suivantes

Affinez vos schémas

Spécification de schéma et meilleures pratiques.

Plongée profonde dans Tool Runner

La référence complète de l'abstraction SDK.

Dépannage

Corrigez les erreurs courantes d'utilisation d'outils.

Was this page helpful?

  • Anneau 1 : Un outil, un tour
  • Anneau 2 : La boucle agentique
  • Anneau 3 : Plusieurs outils, appels parallèles
  • Anneau 4 : Gestion des erreurs
  • Anneau 5 : L'abstraction du SDK Tool Runner
  • Ce que vous avez construit
  • Étapes suivantes