Aller au contenu
Se connecter

Certifications

Série · Certification Claude Certified ArchitectÉpisode 01 / 20

Claude Architect, épisode 1 : la boucle agentique et stop_reason

Le moteur de tout agent Claude : les trois rôles, la boucle en quatre étapes, les deux valeurs de stop_reason, le tableau messages et les trois anti-patterns que l'examen adore. Code complet, schémas animés, quiz.

Par 22 min de lecture

Je prépare la certification Claude Certified Architect. Cette série me sert de carnet de révision, et je la publie pour que d’autres francophones puissent s’en servir. Chaque épisode couvre une partie du programme et suffit pour la réviser : des schémas, du code qui tourne, les pièges à reconnaître et un quiz pour finir.

Où on en est dans le programme

Domaine 1 — Architecture agentique et orchestration, le plus lourd de l'examen : 27 % des questions. Cet épisode couvre la tâche 1.1, la boucle agentique.

D'après le guide d'examen officiel, l'examen compte 60 questions à scénario en 120 minutes, et il faut un score de 720 sur une échelle de 100 à 1 000 pour le réussir. Tout le détail de l'examen est dans l'épisode 0.

Au programme de cet épisode

Chapitre Ce que vous saurez faire
0 Préparer son poste appeler l’API Claude depuis Python pour quelques centimes
1 Les trois rôles dire qui parle dans une conversation : system, user, assistant
2 Chat ou agent expliquer ce qui fait d’un programme un agent
3 Le cycle de la boucle dérouler les quatre étapes, et savoir quand on en sort
4 stop_reason réagir correctement à tool_use et à end_turn
5 Le tableau messages gérer l’état, parce que l’API n’en garde aucun
6 Le code complet lire et lancer un agent, respecter les règles de l’API, gérer un outil en échec
7 Qui choisit l’outil distinguer « piloté par le modèle » et « préconfiguré »
8 Les trois anti-patterns les reconnaître dans une question d’examen
9 Fiche, glossaire et quiz vérifier que tout est acquis, avec 8 questions corrigées

0. Préparer son poste

La partie pratique est facultative pour l’examen, mais écrire la boucle soi-même aide beaucoup à la retenir. Il faut quatre choses.

Un compte sur platform.claude.com, l’ancienne console.anthropic.com, avec du crédit : 5 $ au minimum, soit environ 50 dirhams. Chaque appel est facturé au jeton, et sans crédit l’API refuse de répondre. La page Limits indique les plafonds de requêtes par minute de chaque modèle.

Le modèle le moins cher. Tous les exemples utilisent Claude Haiku 4.5 (claude-haiku-4-5), pour ne pas vider les 5 $ en une après-midi. Un appel de test coûte une fraction de centime.

Une clé API, à placer dans une variable d’environnement, jamais dans le code :

bash
# macOS / Linux : ajoutez cette ligne à ~/.zshrc (ou ~/.bashrc), puis rechargez
export ANTHROPIC_API_KEY="sk-ant-..."
source ~/.zshrc

# Windows (PowerShell)
$env:ANTHROPIC_API_KEY = "sk-ant-..."

# Un environnement Python isolé, et le SDK
python3 -m venv .venv
source .venv/bin/activate
pip install anthropic
Bon à savoir

On voit souvent conseiller un fichier .env dans le projet. Attention : le SDK ne le lit pas tout seul. Il faut python-dotenv et un load_dotenv() au début du script, sinon la clé n'est jamais trouvée.

Un premier appel, pour vérifier que la clé fonctionne. Appelons ce fichier api_check.py :

python
import anthropic

client = anthropic.Anthropic()
# Le SDK lit ANTHROPIC_API_KEY dans l'environnement :
# inutile de passer la clé à la main.

response = client.messages.create(
    model="claude-haiku-4-5",
    max_tokens=100,
    messages=[
        {"role": "user", "content": "Say hello in one word."}
    ]
)

print(response.content[0].text)  # Doit afficher : Hello

Remarquez déjà le "role": "user". C’est le premier concept.

1. Les trois rôles : qui parle ?

Pensez à une salle de classe. L’élève a le rôle d’apprendre, le professeur celui d’enseigner. Échangez les rôles, et l’élève devient professeur. Un rôle ne dit pas qui vous êtes, il dit ce que vous faites dans la conversation.

Avec Claude, chaque message porte un rôle :

Rôle Qui le porte En une phrase
system le développeur, une seule fois « qui tu es » : le personnage, les consignes
user celui qui demande la question, ou toute donnée qui entre dans Claude
assistant Claude ce que le modèle répond

Dans l’interface de Claude ou de ChatGPT, vous ne voyez jamais ces rôles : chaque message que vous tapez est étiqueté user en coulisses. Avec le SDK, c’est vous qui les écrivez.

Prenons une campagne marketing pour du thé au gingembre, rôle par rôle :

  1. system
    Tu es un expert de premier plan du marketing sur les réseaux sociaux.
  2. user
    Génère une campagne publicitaire pour du thé au gingembre.
  3. assistant
    Voici trois visuels et un post Instagram : « Un réveil épicé… »
  4. user
    Le post Instagram ne va pas, corrige-le.
  5. assistant
    Voici le post Instagram mis à jour : …

En code, cela donne :

python
response = client.messages.create(
    model="claude-haiku-4-5",
    max_tokens=1024,
    system="Tu es un expert de premier plan du marketing sur les réseaux sociaux.",
    messages=[
        {"role": "user", "content": "Génère une campagne publicitaire pour du thé au gingembre."},
        {"role": "assistant", "content": "Voici trois visuels et un post Instagram : ..."},
        {"role": "user", "content": "Le post Instagram ne va pas, corrige-le."},
    ],
)
Piège

Dans l'API Messages, le rôle system ne va pas dans le tableau messages. Il a son propre paramètre, system=. Le tableau ne contient que des messages user et assistant, et il commence toujours par user.

2. Chat ou agent : quelle différence ?

À gauche, un chat classique : un message, une réponse, terminé. À droite, une boucle agentique : Claude raisonne, demande un outil, le code l'exécute, Claude relit le résultat, et la boucle recommence jusqu'à la fin. Chat classique Boucle agentique L'utilisateur envoie un message Claude répond Terminé un seul aller-retour Message del'utilisateur Claude raisonneet agit Le code exécutel'outil Claude relitet raisonne
  • chez Claude (API)
  • dans votre code
Le point fait le tour de la boucle tant que la tâche n'est pas terminée. Un agent agit, observe le résultat, puis décide seul de la suite.

Une image aide : le LLM est le cerveau de l’agent, les outils sont ses mains. Le cerveau décide quelle main utiliser, et à quel moment.

Prenons un agent de réservation de voyages. On lui demande un vol entre l’Inde et l’Australie à certaines dates :

  1. Claude comprend qu’il faut l’outil de réservation de vols, et le demande.
  2. L’outil s’exécute et répond.
  3. Si le vol est réservé, c’est fini.
  4. Si les dates sont ambiguës, Claude demande une précision à l’utilisateur, relance l’outil avec la réponse, et ainsi de suite jusqu’au résultat voulu.

3. Le cycle de vie de la boucle agentique

Le cycle en quatre étapes : envoyer la requête, lire stop_reason, exécuter l'outil, ajouter le résultat à l'historique, puis recommencer. La seule sortie est end_turn. end_turn tool_use 1. Envoyer la requête messages + outils 2. Lire stop_reason tool_use ou end_turn ? Sortie réponse finale 3. Exécuter l'outil, en local 4. Ajouter à l'historique, puis 1
  • API Claude
  • votre code Python
  • seule sortie
Le cadre jaune parcourt les quatre étapes dans l'ordre. Une seule flèche sort de la boucle : celle de end_turn.
  1. Envoyer la requête : on appelle l’API avec le tableau messages et la liste des outils disponibles.
  2. Lire stop_reason : la réponse de Claude indique pourquoi il s’est arrêté.
  3. Exécuter : si Claude a demandé un outil (tool_use), votre code l’exécute. Claude ne fait que le demander.
  4. Ajouter : on ajoute la demande et le résultat à l’historique, puis on revient à l’étape 1.

On sort quand stop_reason vaut end_turn, et seulement dans ce cas.

4. stop_reason : les deux valeurs à connaître

response.stop_reason est un aiguillage : tool_use renvoie dans la boucle, end_turn en sort avec la réponse finale. response.stop_reason tool_use Claude veut agir. → exécuter, ajouter, reboucler ne jamais renvoyer ça à l'utilisateur end_turn Claude a terminé. → sortir, extraire le texte c'est la réponse finale
tool_use end_turn
Ce que ça signifie Claude veut agir Claude a terminé
Ce que contient la réponse des blocs tool_use (parfois du texte avant) des blocs text
Ce que fait votre code exécuter, ajouter à l’historique, reboucler sortir de la boucle, extraire le texte
La règle ne jamais renvoyer ça à l’utilisateur c’est la réponse finale
Bon à savoir

Pour la boucle, ce sont bien ces deux valeurs qu'il faut maîtriser. Mais l'API en connaît six :

  • max_tokens : la réponse a été coupée par la limite de jetons, elle est incomplète ;
  • stop_sequence : une séquence d'arrêt définie par vous a été rencontrée ;
  • pause_turn : un tour long, avec des outils côté serveur, a été mis en pause et peut être relancé ;
  • refusal : Claude a refusé pour des raisons de sécurité, et stop_details donne la catégorie.

Une boucle de production les traite explicitement. On verra plus bas ce qu'il advient d'une boucle qui les oublie.

5. Pourquoi le tableau messages est vital

L’API Claude est 100 % sans état. Envoyez « Je m’appelle Yasmine », elle répond « Bonjour Yasmine ». Au prochain appel, elle a tout oublié. Chaque requête doit donc contenir tout l’historique de la conversation.

Dans une boucle agentique, cet historique grandit à chaque tour. Une phrase résume tout : c’est l’architecte qui est responsable de l’état. Un outil oublié dans la requête, et Claude invente une réponse. Un identifiant qui ne correspond pas, et la boucle casse.

Le tableau messages au fil des quatre états : la question de l'utilisateur, puis la demande d'outil de l'assistant, puis le résultat de l'outil, puis la réponse finale. user · question État 1 user · question assistant · tool_use État 2 user · question assistant · tool_use user · tool_result État 3 user · question assistant · tool_use user · tool_result assistant · end_turn État 4
Les états apparaissent l'un après l'autre. Règle 1 : on ajoute à la fois la demande d'outil (assistant) et son résultat (user). Règle 2 : le tool_use_id du résultat doit correspondre exactement à l'id de la demande.

Une question revient souvent : pourquoi le résultat d’un outil porte-t-il le rôle user, alors qu’aucun humain ne l’a tapé ? Parce que user désigne tout ce qui entre dans Claude depuis votre code. Ce qui sort de Claude est assistant, ce qui y entre est user.

Rejouez la boucle, étape par étape

Voici le vrai déroulé de l’agent de la section suivante, sur la question « Where is my order #4821? ». Cliquez sur les étapes, ou utilisez les flèches du clavier.

Itération 1. Le tableau ne contient que la question. On l'envoie avec la liste des outils.

  1. user : "Where is my order #4821?"

Claude répond avec stop_reason = "tool_use" et un bloc qui demande lookup_order({"order_id": "4821"}), avec l'id toolu_01….

Ajout 1, rôle assistant. On ajoute la réponse de Claude telle quelle (response.content), bloc tool_use compris.

  1. user : "Where is my order #4821?"
  2. assistant : [tool_use · id toolu_01… · lookup_order]

Ça doit arriver avant le résultat : un tool_result doit toujours répondre à un tool_use qui le précède.

Exécution, puis ajout 2, rôle user. Votre code appelle execute_tool, qui renvoie {"status": "shipped", "eta": "March 30", "carrier": "FedEx"}.

  1. user : "Where is my order #4821?"
  2. assistant : [tool_use · id toolu_01… · lookup_order]
  3. user : [tool_result · tool_use_id toolu_01… · {"status": "shipped", …}]

Les trois messages repartent à Claude : c'est l'itération 2.

Itération 2. Claude lit le résultat et répond stop_reason = "end_turn" avec un bloc texte : « Your order #4821 has been shipped! … »

Le code renvoie ce texte et la boucle s'arrête. Il a suffi de deux appels à l'API. Cette dernière réponse n'est pas ajoutée au tableau, puisqu'on n'en a plus besoin.

6. Le code complet : un agent de suivi de commande

Le cas d’usage est un service client. On demande « où est ma commande ? », l’agent consulte la commande et répond, sans personne pour chercher à la main. Pas de base de données ici : trois commandes sont écrites en dur pour garder l’exercice simple.

Voici agent.py, complet et prêt à lancer. Il se découpe en six étapes.

Étapes 1 à 3 : le client et la définition de l’outil. Un outil a trois champs. Le name, c’est son nom dans les blocs tool_use. La description permet à Claude de décider quand s’en servir. L’input_schema décrit ses paramètres en JSON Schema.

python
# ÉTAPE 1 : installer et importer — pip install anthropic
import anthropic
import json
import os

# ÉTAPE 2 : créer le client.
# Il lit ANTHROPIC_API_KEY dans l'environnement. Ne jamais écrire la clé en dur ici.
client = anthropic.Anthropic()

# ÉTAPE 3 : définir les outils.
#   name          - le nom que Claude utilise dans ses blocs tool_use
#   description   - ce qui permet à Claude de décider QUAND s'en servir (épisode 05)
#   input_schema  - le schéma JSON des paramètres
tools = [
    {
        "name": "lookup_order",
        "description": (
            "Look up an order by its order ID. "
            "Returns current status, estimated delivery date, and carrier name. "
            "Use this when the customer asks where their order is or when it will arrive."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "order_id": {
                    "type": "string",
                    "description": "The numeric order ID (e.g. '4821')"
                }
            },
            "required": ["order_id"]
        }
    }
]

Étape 4 : la fonction de l’outil. C’est elle qui fait le travail. Claude ne peut pas l’appeler : il la demande, et votre code l’exécute.

python
# ÉTAPE 4 : implémenter les fonctions des outils.
# Claude NE PEUT PAS appeler ces fonctions lui-même.
# Claude DEMANDE un appel -> votre code l'exécute -> vous renvoyez le résultat.
def execute_tool(tool_name: str, tool_input: dict) -> str:
    """Exécute un outil et renvoie son résultat sous forme de chaîne JSON."""
    if tool_name == "lookup_order":
        order_id = tool_input.get("order_id", "")
        # Fausse base de données
        mock_orders = {
            "4821": {"status": "shipped",    "eta": "March 30", "carrier": "FedEx"},
            "9910": {"status": "processing", "eta": "April 2",  "carrier": "UPS"},
            "0042": {"status": "delivered",  "eta": "March 25", "carrier": "DHL"},
        }
        if order_id in mock_orders:
            return json.dumps(mock_orders[order_id])
        else:
            return json.dumps({"error": f"Order {order_id} not found"})

    # Outil inconnu : renvoyer une erreur en résultat (ne jamais lever d'exception ici)
    return json.dumps({"error": f"Unknown tool: {tool_name}"})

Étape 5 : la boucle agentique. C’est le cœur du fichier, et c’est lui que l’examen interroge.

python
# ÉTAPE 5 : la boucle agentique
def run_agent(user_message: str) -> str:
    """Fait tourner la boucle jusqu'à ce que Claude produise une réponse finale."""

    # On commence avec le seul message de l'utilisateur.
    # Le tableau messages grandit à chaque tour de boucle.
    messages = [
        {"role": "user", "content": user_message}
    ]

    # Soupape de sécurité : PAS la condition d'arrêt principale.
    MAX_ITERATIONS = 50
    iteration = 0

    while iteration < MAX_ITERATIONS:
        iteration += 1

        # APPEL API : les messages actuels + les outils disponibles.
        # Claude renvoie une réponse accompagnée d'un stop_reason.
        response = client.messages.create(
            model="claude-haiku-4-5",   # rapide et peu cher : idéal pour apprendre
            max_tokens=4096,
            tools=tools,
            messages=messages
        )

        # SORTIE : "end_turn" signifie que Claude a terminé.
        # C'est la SEULE sortie principale valide de la boucle.
        if response.stop_reason == "end_turn":
            for block in response.content:
                if block.type == "text":
                    return block.text
            return ""  # end_turn sans texte (rare, mais possible)

        # APPEL D'OUTIL : "tool_use" signifie que Claude veut appeler des outils.
        # À faire : exécuter les outils, puis ajouter les DEUX messages à l'historique.
        if response.stop_reason == "tool_use":

            # AJOUT 1 : le message de l'assistant, AVANT les résultats.
            messages.append({
                "role": "assistant",
                "content": response.content   # contient les blocs tool_use
            })

            # Exécuter chaque outil demandé par Claude
            tool_results = []
            for block in response.content:
                if block.type == "tool_use":
                    print(f"  → Calling tool: {block.name}({block.input})")
                    result = execute_tool(block.name, block.input)
                    print(f"  ← Result: {result}")

                    tool_results.append({
                        "type": "tool_result",
                        "tool_use_id": block.id,   # ← DOIT correspondre à l'id du message assistant
                        "content": result
                    })

            # AJOUT 2 : les résultats, rôle "user" : ces données ENTRENT dans Claude
            # depuis votre code. Aucun humain ne les a tapées, mais c'est le rôle user.
            messages.append({
                "role": "user",
                "content": tool_results
            })

            # La boucle continue : Claude va lire les résultats et décider de la suite.

    # Soupape déclenchée : ne devrait jamais arriver en fonctionnement normal
    return "Error: agent did not complete within the iteration limit"


# ÉTAPE 6 : lancer l'agent
if __name__ == "__main__":
    print("Running agent...")
    answer = run_agent("Where is my order #4821?")
    print(f"\nFinal answer: {answer}")

Et voici ce que donne python3 agent.py :

console
$ python3 agent.py
Running agent...
  → Calling tool: lookup_order({'order_id': '4821'})
  ← Result: {"status": "shipped", "eta": "March 30", "carrier": "FedEx"}

Final answer: Your order #4821 has been **shipped**! Here are the details:

- **Status:** Shipped
- **Carrier:** FedEx
- **Estimated Delivery Date:** March 30

Your order is on its way. You can track it further through FedEx using your tracking number…

Ce que l’API vérifie à chaque tour

Ces règles ne sont pas des conventions : si l’une est violée, l’API rejette la requête avec une erreur 400. L’examen les teste en décrivant un historique mal construit.

Règle Exemple de violation
le tableau messages commence par un message user un historique qui démarre par la réponse de Claude
chaque tool_result répond à un tool_use du message assistant juste avant le résultat ajouté sans la demande qui le précède
le tool_use_id reprend exactement l’id du bloc tool_use un id recopié à la main, ou celui d’un tour précédent
chaque tool_use reçoit son tool_result Claude demande deux outils, on n’en renvoie qu’un
dans le message user, les blocs tool_result viennent en premier un texte placé avant les résultats

Une conséquence importante : tous les résultats d’un même tour partent dans un seul message user. Si Claude demande trois outils en parallèle, sa réponse contient trois blocs tool_use. La liste tool_results en contient alors trois, et un seul append les envoie. Les répartir sur trois messages casse la règle d’alternance.

Quand un outil échoue

Un outil peut planter : base injoignable, commande introuvable, délai dépassé. Ne levez pas d’exception qui interromprait la boucle : renvoyez l’échec à Claude, comme un résultat. Il pourra alors réessayer, demander une précision à l’utilisateur, ou expliquer le problème.

Le code ci-dessus renvoie un JSON {"error": ...}, ce qui fonctionne. L’API prévoit aussi un champ dédié, plus explicite :

python
tool_results.append({
    "type": "tool_result",
    "tool_use_id": block.id,
    "content": "Order 7777 not found",
    "is_error": True,        # signale à Claude que l'appel a échoué
})
Piège : le trou dans la boucle

Ce code ne traite que deux cas. Si la réponse est coupée (stop_reason = "max_tokens"), aucun des deux if ne s'exécute. On ne sort pas, on n'ajoute rien, et la boucle renvoie exactement la même requête, jusqu'à 50 fois. Cinquante appels facturés pour rien.

Une version de production termine la boucle par un cas par défaut : lever une erreur, ou journaliser le stop_reason inattendu et sortir.

7. Pourquoi c’est le modèle qui choisit l’outil

Dans l’exemple, personne n’a écrit « si la question parle de commande, appelle lookup_order ». C’est Haiku qui l’a décidé : il a lu la description de l’outil, l’a jugé utile, et l’a demandé avec stop_reason = "tool_use". Votre code l’a exécuté et a renvoyé le résultat. Claude a jugé que c’était suffisant et a terminé avec end_turn.

Piloté par le modèle (agentique) Préconfiguré (pas agentique)
Claude lit tout le contexte de la conversation une logique if / else codée en dur
il choisit l’outil suivant en raisonnant une séquence d’outils rigide, toujours la même
il peut appeler les outils dans n’importe quel ordre, ou en sauter fragile dès qu’une situation nouvelle se présente
l’intelligence est dans le modèle l’intelligence est dans le code
Ce que l'examen cherche (tâche 1.1)

Une question décrit un agent qui enchaîne des étapes fixes, ou qui teste le texte de la réponse pour décider de la suite ? Ce n'est pas une boucle agentique. La bonne réponse fait décider le modèle, et fait réagir le code à stop_reason, à rien d'autre.

8. Les trois anti-patterns testés à l’examen

Ils ont une cause commune : remplacer le signal structuré de l’API par une astuce bricolée.

Anti-pattern 1 : lire les mots de Claude

python
# ✗ Mauvais
if "I have completed" in response.text:
    break

# ✓ Bon
if response.stop_reason == "end_turn":
    break

Pourquoi ça échoue. Claude ne formule jamais deux fois sa fin de la même façon. Et lors d’un tour tool_use, il peut n’y avoir aucun bloc texte : lire le texte provoque alors une IndexError et tout plante. C’est pour ça que le code de la section 6 prévoit return "". La règle : ne lisez pas le langage naturel, lisez le signal mécanique de l’API.

Anti-pattern 2 : un plafond d’itérations comme condition d’arrêt

python
# ✗ Mauvais : le plafond décide de la fin
if iterations >= 5:
    break

# ✓ Bon : stop_reason décide, le plafond ne sert que de disjoncteur
while iteration < MAX_ITERATIONS:      # garde-fou
    ...
    if response.stop_reason == "end_turn":   # vraie sortie
        ...

Pourquoi ça échoue. Un plafond arbitraire ignore la réalité de la tâche. Une tâche qui finit en 3 itérations gaspille des appels si on la force à continuer. Une tâche qui en demande 8 est coupée au milieu. Le plafond reste utile, mais comme un disjoncteur : il protège l’installation, il n’éteint pas la lumière à votre place.

Anti-pattern 3 : tester le type du contenu

La réponse contient un bloc texte suivi d'un bloc tool_use. Le code qui sort dès que le premier bloc est du texte abandonne l'appel d'outil sans aucune erreur. if response.content[0].type == "text": break response.content = [0] text : « Let me look that up… » [1] tool_use : lookup_order("4821") jamais exécuté, et aucune erreur le code sort ici
Claude « réfléchit à voix haute » avant d'appeler un outil. Le premier bloc est du texte, mais la réponse demande bien un outil.

Pourquoi ça échoue. Claude écrit souvent une phrase avant d’appeler un outil (« Laissez-moi vérifier… »), puis place le bloc tool_use. Sortir dès le premier bloc texte abandonne l’appel d’outil en silence. L’agent échoue sans lever la moindre erreur. Le type de contenu et stop_reason mesurent deux choses différentes : on ne remplace jamais l’un par l’autre.

9. L’implémentation de référence

Réduite à l’essentiel, voici la boucle de référence. Chaque ligne correspond à une règle :

python
while iteration < MAX_ITERATIONS:                      # la soupape de sécurité
    response = client.messages.create(...)

    if response.stop_reason == "end_turn":             # la sortie principale
        break

    messages.append({"role": "assistant",              # ajout 1 : la demande d'outil
                     "content": response.content})

    # ... exécuter l'outil demandé dans response ...

    messages.append({"role": "user", "content": [      # ajout 2 : le résultat, même id
        {"type": "tool_result", "tool_use_id": block.id, "content": result}
    ]})

Écrire la boucle soi-même, ou la déléguer

En production, on n’écrit pas toujours cette boucle à la main :

  • le SDK Anthropic fournit un tool runner qui envoie la requête, exécute vos fonctions outils et reboucle jusqu’à end_turn ;
  • le Claude Agent SDK va plus loin : il embarque la boucle complète de Claude Code, avec ses outils intégrés (fichiers, shell, recherche).

Ces outils automatisent exactement ce qui précède. Ils ne changent pas les règles : les mêmes stop_reason, les mêmes paires tool_use / tool_result, le même historique. C’est la mécanique que l’examen vérifie, et on la diagnostique d’autant mieux quand on l’a écrite une fois soi-même.

Fiche de révision

Les 5 règles d’or
1 end_turn est la seule sortie valide de la boucle
2 tool_use signifie : exécuter, puis continuer
3 toujours ajouter le message assistant d’abord, le message user ensuite
4 le tool_use_id doit correspondre exactement à l’id de la demande
5 l’API est sans état : renvoyer tout l’historique à chaque appel
Anti-pattern Conséquence
lire les mots de Claude plantages (IndexError)
plafond d’itérations comme arrêt tâches coupées trop tôt, appels gaspillés
tester le type du contenu outils abandonnés en silence
Comment l'examen le teste

Cherchez l'ajout à l'historique qui manque, l'identifiant qui ne correspond pas, ou la condition d'arrêt bricolée. Dans les questions sur cet épisode, la mauvaise réponse est presque toujours l'une de ces trois-là.

Glossaire

Terme Définition
agent un programme où le modèle décide lui-même des actions à enchaîner pour atteindre un but
boucle agentique le cycle requête → stop_reason → exécution → ajout, répété jusqu’à end_turn
outil (tool) une fonction de votre code, décrite à Claude par un nom, une description et un schéma
tool_use un bloc de la réponse où Claude demande un outil, avec un id, un name et un input
tool_result un bloc envoyé par votre code avec le résultat, rattaché par tool_use_id
stop_reason le champ de la réponse qui dit pourquoi Claude s’est arrêté
sans état (stateless) l’API ne garde rien d’un appel à l’autre : l’historique est à votre charge
soupape de sécurité le plafond d’itérations, qui protège sans jamais servir de sortie normale

Quiz d’entraînement

Ces questions sont de moi, écrites dans le style de l’examen. Ce ne sont pas des questions officielles. Choisissez une réponse : la correction s’affiche aussitôt, et le score se calcule en bas. Connecté, votre meilleur score est gardé dans votre compte, et « Recommencer » efface seulement vos réponses à l’écran. Sans compte, rien n’est enregistré.

Un agent doit s'arrêter quand Claude a fini sa tâche. Quelle condition utiliser ?

Réponse B. A, C et D sont exactement les trois anti-patterns. Seul stop_reason est un signal structuré et fiable.

Votre code vient d'exécuter un outil. Quel rôle porte le message qui renvoie le résultat à Claude ?

Réponse C. Tout ce qui entre dans Claude depuis votre code porte le rôle user, même si aucun humain ne l'a tapé. Le rôle tool n'existe pas dans l'API Messages.

Un développeur ajoute le tool_result à l'historique, mais oublie d'ajouter avant lui la réponse de Claude qui contenait le tool_use. Que se passe-t-il ?

Réponse A. L'API est sans état. Un tool_result doit répondre au tool_use du message assistant qui le précède, avec le même identifiant. C'est la règle 3 : assistant d'abord, user ensuite.

Claude répond par un bloc texte (« Je vérifie… ») suivi d'un bloc tool_use. Le code sort de la boucle dès que content[0].type == "text". Conséquence ?

Réponse D. C'est l'anti-pattern 3. Il est dangereux parce qu'il ne lève aucune erreur : l'agent a l'air de fonctionner.

Quel est le rôle légitime de MAX_ITERATIONS dans une boucle agentique ?

Réponse B. C'est un disjoncteur, pas un interrupteur. En fonctionnement normal, on sort toujours par end_turn avant de l'atteindre.

Un agent enchaîne toujours « chercher le client → chercher la commande → répondre », codé en if / else. Comment le qualifier ?

Réponse A. Utiliser des outils ne suffit pas. Un agent laisse le modèle choisir l'outil suivant, dans n'importe quel ordre, à partir de tout le contexte.

Claude demande trois outils dans la même réponse. Comment renvoyer les résultats ?

Réponse D. Une demande groupée appelle une réponse groupée : tous les tool_result du tour partent dans un seul message user, chacun avec son tool_use_id. B ne fonctionne pas : chaque tool_use doit recevoir son résultat.

La base de données est injoignable pendant l'exécution de lookup_order. Quelle est la meilleure réaction du code ?

Réponse C. L'échec est une information comme une autre : Claude peut réessayer, prévenir l'utilisateur ou changer d'approche. A coupe la boucle. B casse la règle « chaque tool_use reçoit son résultat ». D ouvre la porte aux hallucinations.

Votre score

Quiz terminé. En dessous de 6, relisez la fiche de révision et les trois anti-patterns, puis recommencez.

Pour la suite

L’épisode 2 passe aux systèmes multi-agents et au modèle du coordinateur. Un agent coordinateur répartit le travail entre des sous-agents, en étoile. La bonne nouvelle : le coordinateur utilise exactement la boucle de cet épisode. La seule différence, c’est que ses outils lancent d’autres agents.