Chargement

La boucle de l'agent expliquée

Leçon 47 min
Chargement

La boucle de l'agent expliquée

Vous avez effectué des appels API, mais un seul appel ne renvoie qu'une seule réponse. Si vous souhaitez automatiser un flux de travail, Claude doit agir, examiner le résultat, décider de la suite et continuer. C'est ce schéma que l'on désigne lorsqu'on parle de flux de travail agentiques.

Ce qu'est réellement un agent

Un agent est une version autonome de Claude, exécutant les deux côtés de la boucle de messagerie sans intervention humaine intermédiaire. Un agent reçoit une tâche, choisit un outil et exécute du code en boucle jusqu'à ce que Claude décide que la tâche est terminée.

La manière la plus simple d'implémenter une boucle d'agent ressemble à ceci :

  1. Envoyez un message à Claude avec des outils disponibles.
  2. Claude répond soit par une réponse finale, soit par une demande d'utilisation d'un outil que vous avez défini.
  3. Votre code exécute cet outil.
  4. Vous renvoyez le résultat à Claude.
  5. Répétez tant que le motif d'arrêt est tool_use. end_turn signifie que Claude a terminé, et tout autre motif d'arrêt met également fin à la boucle.

Pensez-y comme à une conversation où les tours alternent : l'utilisateur lance les choses, l'agent appelle un outil, l'outil renvoie un résultat, et l'agent continue jusqu'à ce qu'il ait une réponse.

Un exemple fonctionnel minimal

Pour voir cette boucle s'exécuter de bout en bout sans faire intervenir une base de données ou une interface utilisateur, nous allons câbler un faux outil appelé get_weather et demander à Claude ce qu'il faut porter à Austin aujourd'hui. Claude n'a aucun moyen de connaître la météo par lui-même, il doit donc appeler l'outil, lire le résultat, puis vous donner une réponse.

Voici le script complet :

python
import anthropic

client = anthropic.Anthropic()

# Le tableau tools indique à Claude ce qui est disponible :
# un nom, une description et un schéma JSON pour les entrées.
tools = [
    {
        "name": "get_weather",
        "description": "Get the current weather for a city.",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {
                    "type": "string",
                    "description": "The city to get weather for",
                }
            },
            "required": ["city"],
        },
    }
]

# run_tool n'est qu'une simple recherche codée en dur.
# Dans une vraie appli, cela interrogerait votre base de données, une API, etc.
def run_tool(name, tool_input):
    if name == "get_weather":
        return f"Weather in {tool_input['city']}: 95F, sunny"
    raise ValueError(f"Unknown tool: {name}")

messages = [
    {"role": "user", "content": "What should I wear in Austin today?"}
]

# La boucle de l'agent. Chaque itération envoie des messages à Claude
# et bascule selon le motif d'arrêt de la réponse.
while True:
    response = client.messages.create(
        model="claude-sonnet-5",
        max_tokens=1024,
        tools=tools,
        messages=messages,
    )

    if response.stop_reason == "end_turn":
        # Claude a terminé. Afficher le texte final et sortir.
        for block in response.content:
            if block.type == "text":
                print(block.text)
        break

    elif response.stop_reason == "tool_use":
        # Trouver les blocs d'utilisation d'outils dans la réponse et exécuter chacun.
        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": result,
                    }
                )

        # Ajouter la réponse de l'assistant et nos résultats d'outils
        # dans messages, puis boucler de nouveau pour que Claude puisse répondre.
        messages.append({"role": "assistant", "content": response.content})
        messages.append({"role": "user", "content": tool_results})

    else:
        # Tout autre motif d'arrêt, tel que max_tokens ou un refus.
        # Rien n'a changé dans messages, donc on s'arrête ici. Boucler
        # à nouveau paierait pour la même requête encore et encore.
        print(f"Stopped early: {response.stop_reason}")
        break

Trois éléments à noter :

  • Le tableau tools indique à Claude ce qui est disponible : un nom, une description et un schéma JSON pour les entrées.
  • run_tool n'est qu'une simple recherche codée en dur. Dans une application réelle, cela interrogerait votre base de données, une API, ou autre chose.
  • La boucle est la boucle d'agent. Chaque itération envoie les messages à Claude et réagit selon le motif d'arrêt de la réponse. En cas de end_turn, Claude a terminé, donc on affiche le texte final et on sort de la boucle. En cas de tool_use, on trouve les blocs d'utilisation d'outils, on exécute chacun d'eux, on renvoie la réponse de l'assistant et les résultats de vos outils dans messages, puis on boucle à nouveau pour que Claude puisse répondre. Pour tout autre cas, on affiche le motif d'arrêt et on sort de la boucle.

Ne négligez pas cette dernière branche. Sans elle, une réponse tronquée à max_tokens n'ajoute rien à messages, donc la boucle envoie à nouveau la même requête et vous la payez à chaque fois. La documentation répertorie tous les motifs d'arrêt et la conduite à tenir.

Exécution

Lorsque vous exécutez le script, vous verrez deux tours :

  1. Tour un : le motif d'arrêt est tool_use. Claude demande get_weather pour Austin, et votre code renvoie la température et les conditions.
  2. Tour deux : le motif d'arrêt est end_turn, et Claude vous indique de porter quelque chose de léger et respirant.

Sortie terminal de la boucle d'agent : le tour 1 s'arrête avec tool_use et appelle get_weather pour Austin, puis le tour 2 s'arrête avec end_turn et Claude affiche ses recommandations vestimentaires finales

Deux appels API, une exécution d'outil, une réponse finale. C'est toute la boucle. Tout ce que vous construisez avec l'API Claude sera similaire à ceci.

La même boucle en production

Dans un environnement réel, cette même boucle alimente quelque chose comme un point de terminaison de révision automatique : un agent de conformité qui lit un rapport structurel, recherche les codes du bâtiment pertinents via un outil, et écrit les constats de risque dans la base de données un par un au fur et à mesure qu'il travaille.

Un tableau de bord de révision de conformité listant les rapports structurels téléversés, chacun avec un bouton Lancer la révision automatique qui déclenche l'agent

La forme de la boucle est identique à celle que vous venez d'exécuter. Les différences sont :

  • De véritables outils au lieu d'une simulation de recherche météo.
  • Les résultats sont diffusés en flux vers l'interface utilisateur sous forme d'événements envoyés par le serveur.
  • Les constats sont persistés dans une table de constats de risque.

La trace de révision d'un agent de conformité en cours d'exécution : des dizaines d'appels d'outils recherchant dans la bibliothèque de codes du bâtiment et consultant des sections de code spécifiques au fil des itérations de la boucle

Récapitulatif

  • Un agent, c'est Claude dans une boucle : observer, décider, agir, répéter.
  • La boucle est simple : envoyer des messages avec des outils, exécuter tout outil demandé par Claude, renvoyer le résultat, et s'arrêter lorsque le motif d'arrêt est autre chose que tool_use.
  • Vous êtes responsable de la boucle et des outils. Claude est responsable du raisonnement.
  • La même forme de boucle s'étend d'une démo météo simulée à un agent de conformité en production — seuls les outils et la plomberie changent.
  • Lorsque vous ne souhaitez pas gérer la boucle vous-même, les agents gérés exécutent cette même boucle pour vous sur l'infrastructure d'Anthropic.