Exercices Projets guidés La boucle agent : reflechir, agir, observer
🎉

Bravo!

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

La boucle agent : reflechir, agir, observer

Tu as les briques : des outils (recherche web, lecture de page, calculatrice) et un mecanisme pour les appeler (function calling). Maintenant il faut le chef d'orchestre : la boucle agent.

Un agent, c'est un programme qui repete un cycle jusqu'a avoir assez d'informations pour repondre :

1. REFLECHIR : envoyer la question et le contexte au LLM
2. AGIR : si le LLM demande un outil, l'executer
3. OBSERVER : ajouter le resultat de l'outil au contexte
4. Recommencer jusqu'a ce que le LLM reponde directement (sans outil)

Ce cycle s'appelle le ReAct pattern (Reasoning + Acting). C'est le coeur de tous les agents : LangChain, CrewAI, AutoGPT, et meme le browsing de ChatGPT.

Pour eviter les boucles infinies (un agent qui chercherait indefiniment), on fixe un nombre maximum d'iterations.

Ecris une classe AgentSimple qui prend une boite_a_outils (dict de fonctions), une liste de schemas (pour l'API), et un max_iterations (par defaut 5).

La methode principale est run(question, llm_fn=None). Le parametre llm_fn est une fonction qui simule le LLM. Elle prend une liste de messages et la liste des schemas, et renvoie un dictionnaire qui represente la reponse du LLM :

Si le LLM veut appeler un outil :
{"type": "tool_call", "tool_calls": [{"id": "...", "function": {"name": "...", "arguments": "..."}}]}

Si le LLM veut repondre directement :
{"type": "message", "content": "La reponse finale"}

La methode run doit :
Maintenir un historique de messages
Boucler : appeler llm_fn, executer les outils si demande, ajouter les resultats
S'arreter quand le LLM repond directement ou apres max_iterations
Renvoyer un dictionnaire {"reponse": "...", "iterations": N, "outils_utilises": [...]}

Exemple :

agent = AgentSimple(boite_a_outils={"chercher": chercher}, schemas=[schema_chercher], max_iterations=5)
resultat = agent.run("Qui a invente Python ?", llm_fn=mon_faux_llm)
resultat["reponse"] contient la reponse finale
resultat["iterations"] le nombre de tours de boucle

Tests (4/5)

Reponse directe sans outil
import json
def fake_llm(messages, schemas):
    return {"type": "message", "content": "La reponse est 42."}

agent = AgentSimple(boite_a_outils={}, schemas=[], max_iterations=5)
r = agent.run("question ?", llm_fn=fake_llm)
assert r["reponse"] == "La reponse est 42."
assert r["iterations"] == 1
assert r["outils_utilises"] == []
Un appel d'outil puis reponse
import json
appels = [0]

def fake_llm(messages, schemas):
    appels[0] += 1
    if appels[0] == 1:
        return {"type": "tool_call", "tool_calls": [
            {"id": "c1", "function": {"name": "chercher", "arguments": json.dumps({"q": "Python"})}}
        ]}
    return {"type": "message", "content": "Python a ete cree par Guido."}

def chercher(q): return f"Resultats pour {q}"
agent = AgentSimple(boite_a_outils={"chercher": chercher}, schemas=[], max_iterations=5)
r = agent.run("Qui a cree Python ?", llm_fn=fake_llm)
assert "Guido" in r["reponse"]
assert r["iterations"] == 2
assert "chercher" in r["outils_utilises"]
Max iterations respecte
import json
def fake_llm_infini(messages, schemas):
    return {"type": "tool_call", "tool_calls": [
        {"id": "c1", "function": {"name": "chercher", "arguments": json.dumps({"q": "boucle"})}}
    ]}

def chercher(q): return "rien"
agent = AgentSimple(boite_a_outils={"chercher": chercher}, schemas=[], max_iterations=3)
r = agent.run("boucle infinie ?", llm_fn=fake_llm_infini)
assert r["iterations"] == 3, f"Doit s'arreter apres 3 iterations, pas {r['iterations']}"
assert len(r["outils_utilises"]) == 3
Plusieurs outils dans une iteration
import json
appels = [0]

def fake_llm(messages, schemas):
    appels[0] += 1
    if appels[0] == 1:
        return {"type": "tool_call", "tool_calls": [
            {"id": "c1", "function": {"name": "chercher", "arguments": json.dumps({"q": "A"})}},
            {"id": "c2", "function": {"name": "calculer", "arguments": json.dumps({"expr": "1+1"})}}
        ]}
    return {"type": "message", "content": "Fait."}

def chercher(q): return "ok"
def calculer(expr): return "2"
agent = AgentSimple(boite_a_outils={"chercher": chercher, "calculer": calculer}, schemas=[], max_iterations=5)
r = agent.run("test", llm_fn=fake_llm)
assert len(r["outils_utilises"]) == 2, f"Deux outils doivent avoir ete utilises, pas {len(r['outils_utilises'])}"

+ 0 tests cachés

Indices (3 disponibles)

solution.py