Exercices Projets guidés La boucle agent
🎉

Bravo!

Intermédiaire 🧠 Fondamentaux 20 XP 0 personnes ont réussi

La boucle agent

C'est ici que tout prend vie. Un agent, c'est un LLM qui tourne en boucle : il recoit une instruction, decide quoi faire, execute une action, regarde le resultat, et recommence jusqu'a ce que la tache soit terminee.

Quand tu dis a Claude Code "corrige le bug dans app.py", voici ce qui se passe en coulisses :

1. Le LLM lit ta demande et decide : "je dois d'abord lire app.py"
2. Il appelle l'outil read_file, recoit le contenu
3. Il analyse le code et trouve le bug
4. Il decide d'ecrire la correction avec write_file
5. Il verifie en executant les tests
6. Il te repond "Bug corrige, voila ce que j'ai fait"

Cette boucle s'appelle le "agent loop". Le LLM est le cerveau qui decide, les outils sont ses mains.

Ecris une fonction boucle_agent(instruction, decideur, dispatcher, max_iterations=10) qui implemente cette boucle.

Parametres :

instruction : la demande initiale de l'utilisateur (string)
decideur : une fonction qui simule le LLM. Elle recoit la conversation (liste de messages) et renvoie une decision.
dispatcher : un objet OutilsDispatcher (de l'exercice precedent, ou un equivalent)
max_iterations : nombre maximum de tours de boucle

Le decideur renvoie un dictionnaire :

{"action": "outil", "outil": "lire_fichier", "arguments": {"chemin": "app.py"}}
quand il veut utiliser un outil

{"action": "repondre", "reponse": "Voila ce que j'ai fait..."}
quand il a fini et veut repondre a l'utilisateur

La boucle doit :

Commencer par envoyer l'instruction au decideur.
Si le decideur veut utiliser un outil, l'executer et renvoyer le resultat au decideur.
Si le decideur veut repondre, arreter la boucle et renvoyer la reponse.
Si on depasse max_iterations, arreter et renvoyer un message d'erreur.
Garder une trace de toutes les etapes (actions + resultats) pour le debug.

Format de retour :

{"succes": True, "reponse": "...", "iterations": 3, "etapes": [...]}
{"succes": False, "erreur": "Nombre maximum d'iterations atteint (10)", "iterations": 10, "etapes": [...]}

Chaque etape dans la liste :

{"type": "outil", "outil": "lire_fichier", "arguments": {...}, "resultat": {...}}
{"type": "reponse", "reponse": "..."}

Exemple :

def mon_decideur(conversation):
# Premier appel : lire un fichier
if len(conversation) == 1:
return {"action": "outil", "outil": "lire", "arguments": {"x": "test"}}
# Deuxieme appel : repondre
return {"action": "repondre", "reponse": "Termine !"}

class SimpleDispatcher:
def executer(self, nom, args):
return {"succes": True, "resultat": "contenu"}

r = boucle_agent("Fais quelque chose", mon_decideur, SimpleDispatcher(), max_iterations=5)
# r["succes"] == True, r["reponse"] == "Termine !", r["iterations"] == 2

Tests (4/5)

Reponse directe en 1 iteration
def decideur_simple(conv):
    return {"action": "repondre", "reponse": "Bonjour !"}

class FakeDispatcher:
    def executer(self, nom, args):
        return {"succes": True, "resultat": "ok"}

r = boucle_agent("Dis bonjour", decideur_simple, FakeDispatcher())
assert r["succes"] == True
assert r["reponse"] == "Bonjour !"
assert r["iterations"] == 1
Utiliser un outil puis repondre
appels = []
def decideur_outil(conv):
    appels.append(1)
    if len(appels) == 1:
        return {"action": "outil", "outil": "lire", "arguments": {"fichier": "app.py"}}
    return {"action": "repondre", "reponse": "Fichier lu !"}

class FakeDispatcher:
    def executer(self, nom, args):
        return {"succes": True, "resultat": "contenu du fichier"}

r = boucle_agent("Lis app.py", decideur_outil, FakeDispatcher())
assert r["succes"] == True
assert r["iterations"] == 2
assert len(r["etapes"]) == 2
assert r["etapes"][0]["type"] == "outil"
assert r["etapes"][1]["type"] == "reponse"
Max iterations atteint
def decideur_infini(conv):
    return {"action": "outil", "outil": "boucle", "arguments": {}}

class FakeDispatcher:
    def executer(self, nom, args):
        return {"succes": True, "resultat": "encore"}

r = boucle_agent("Tourne en rond", decideur_infini, FakeDispatcher(), max_iterations=3)
assert r["succes"] == False, "Doit echouer si max_iterations est atteint"
assert r["iterations"] == 3
assert "3" in r["erreur"]
Les etapes contiennent les resultats des outils
compteur = [0]
def decideur_multi(conv):
    compteur[0] += 1
    if compteur[0] <= 2:
        return {"action": "outil", "outil": f"outil_{compteur[0]}", "arguments": {"x": compteur[0]}}
    return {"action": "repondre", "reponse": "Fini"}

class FakeDispatcher:
    def executer(self, nom, args):
        return {"succes": True, "resultat": f"resultat de {nom}"}

r = boucle_agent("Fais 2 actions", decideur_multi, FakeDispatcher())
assert r["succes"] == True
assert r["iterations"] == 3
etapes_outils = [e for e in r["etapes"] if e["type"] == "outil"]
assert len(etapes_outils) == 2
assert etapes_outils[0]["outil"] == "outil_1"
assert etapes_outils[1]["outil"] == "outil_2"

+ 0 tests cachés

Indices (3 disponibles)

solution.py