watch·ia
AccueilActusTutosGlossaireCette semaineTendancesSources
/
À chaud

Construire un agent IA autonome : boucle, outils et mémoire

mercredi 17 juin 202607:126 min de lecture
L'essentiel — 3 points
  • 01Un agent = un LLM dans une boucle observer → décider → agir, jusqu'à résolution.
  • 02Trois piliers : un jeu d'outils, une mémoire de travail, et une condition d'arrêt.
  • 03Sans garde-fous (limite d'itérations, budget, validation), un agent part en vrille — ils ne sont pas optionnels.
AGENTS

À la fin de ce tuto, tu auras un agent autonome qui tourne : un script Python qui reçoit un objectif, choisit seul quels outils appeler, observe les résultats et recommence jusqu'à avoir terminé — avec les garde-fous qui l'empêchent de partir en vrille. Tu comprendras l'architecture au point de pouvoir y brancher tes propres outils.

Durée : 40 min. Prérequis : avoir fait le tuto function calling (un agent, c'est le function calling mis en boucle), Python, une clé API.

Ce qu'est vraiment un agent

Un agent IA n'est pas un modèle plus intelligent : c'est un LLM placé dans une boucle, équipé d'outils et d'une mémoire, qui itère jusqu'à atteindre un objectif. La différence avec un simple appel d'outil : l'agent décide lui-même de la prochaine action en fonction de ce qu'il vient d'observer.

La boucle agentique

Au cœur de tout agent, le même cycle se répète :

objectif
  └─> [le LLM décide] ──> appelle un outil ──> observe le résultat
         ^                                          │
         └──────────────────────────────────────────┘
                  jusqu'à : objectif atteint
  1. On donne un objectif à l'agent.
  2. Le LLM décide de la prochaine action (appeler un outil, ou conclure).
  3. Le système exécute l'outil et renvoie le résultat (l'observation).
  4. Le LLM intègre cette observation et recommence — jusqu'à juger la tâche finie.

Les trois piliers

  1. Les outils : ce que l'agent peut faire (chercher sur le web, lire un fichier, requêter une base, appeler une API). La qualité d'un agent dépend directement de la clarté de ses outils.
  2. La mémoire de travail : l'historique des actions et observations, réinjecté à chaque tour pour que l'agent sache où il en est. Dans le code ci-dessous, c'est simplement la liste messages.
  3. La condition d'arrêt : comment l'agent sait qu'il a terminé. Sans elle, il boucle indéfiniment.

L'agent minimal, en Python

On construit un agent qui répond à des questions en combinant deux outils : une recherche web (simulée ici) et une calculatrice. La boucle gère plusieurs appels d'affilée.

pip install openai

1. Les outils (du Python normal)

import json
from openai import OpenAI

client = OpenAI()

def web_search(query: str) -> str:
    # En vrai : un appel à une API de recherche. Ici, simulé.
    fake = {"population france": "68 millions (2025)"}
    return fake.get(query.lower(), "aucun résultat")

def calculate(expression: str) -> str:
    # ⚠️ eval est dangereux — voir le tuto sécurité. Ici borné à l'arithmétique.
    allowed = set("0123456789+-*/(). ")
    if not set(expression) <= allowed:
        return "expression non autorisée"
    try:
        return str(eval(expression))
    except Exception as e:
        return f"erreur: {e}"

dispatch = {"web_search": web_search, "calculate": calculate}

2. La description des outils (ce que voit l'agent)

tools = [
    {
        "type": "function",
        "function": {
            "name": "web_search",
            "description": "Cherche une information factuelle sur le web.",
            "parameters": {
                "type": "object",
                "properties": {"query": {"type": "string"}},
                "required": ["query"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "calculate",
            "description": "Évalue une expression arithmétique simple.",
            "parameters": {
                "type": "object",
                "properties": {"expression": {"type": "string"}},
                "required": ["expression"],
            },
        },
    },
]

3. La boucle agentique, avec garde-fous

MAX_ITERS = 8  # garde-fou : jamais de boucle infinie

def agent(objectif: str) -> str:
    messages = [
        {"role": "system", "content":
            "Tu es un agent. Utilise les outils pas à pas pour atteindre "
            "l'objectif, puis donne la réponse finale."},
        {"role": "user", "content": objectif},
    ]

    for i in range(MAX_ITERS):
        resp = client.chat.completions.create(
            model="gpt-4o-mini", messages=messages, tools=tools,
        )
        msg = resp.choices[0].message

        # Condition d'arrêt : plus d'outil demandé → réponse finale
        if not msg.tool_calls:
            return msg.content

        messages.append(msg)  # mémoire de travail : on garde la décision
        for tc in msg.tool_calls:
            args = json.loads(tc.function.arguments)
            observation = dispatch[tc.function.name](**args)
            print(f"  [tour {i+1}] {tc.function.name}({args}) -> {observation}")
            messages.append({
                "role": "tool",
                "tool_call_id": tc.id,
                "content": str(observation),
            })

    return "Limite d'itérations atteinte sans conclusion."

print(agent("Quelle est la population de la France divisée par 2 ?"))

Lance-le : l'agent cherche d'abord la population (web_search), puis calcule la division (calculate), puis conclut — deux outils enchaînés sans que tu lui dises l'ordre. C'est ça, l'autonomie.

Les garde-fous : partie non négociable

Un agent laissé libre peut boucler à l'infini, brûler ton budget ou enchaîner des actions destructrices. Pose-les systématiquement :

  • Limite d'itérations (MAX_ITERS ci-dessus) — contre les boucles infinies.
  • Budget de tokens/coût par exécution, avec arrêt si dépassé. Additionne resp.usage à chaque tour et coupe au-delà d'un seuil.
  • Validation humaine sur les actions sensibles ou irréversibles (voir ci-dessous).
  • Périmètre d'outils restreint : ne donne accès qu'au strict nécessaire.
  • Journal de chaque décision (le print ici) pour pouvoir auditer après coup.

Exemple de barrière sur une action irréversible :

def delete_file(path: str, confirm: bool = False) -> str:
    if not confirm:
        return "CONFIRMATION REQUISE : rappelle delete_file avec confirm=true"
    # suppression réelle seulement après confirmation hors du contrôle du modèle

Le piège du « plus d'outils = mieux »

Donner 30 outils à un agent dégrade ses performances : il se perd dans le choix. Mieux vaut 5 outils bien décrits que 30 vagues. Si le périmètre est large, spécialise plutôt plusieurs agents (sujet du tuto multi-agents) que de surcharger un seul.

Concevoir la boucle, pas seulement le prompt

L'erreur classique est de tout miser sur un prompt parfait. Un bon agent se joue surtout dans l'ingénierie de la boucle : comment tu gères la mémoire (faut-il l'élaguer quand elle gonfle ?), comment tu formates les observations, quand tu coupes. Le prompt compte, mais l'architecture autour compte davantage.

L'adapter à tes besoins

  1. Remplace les outils par les tiens (read_file, query_db, send_email…) — du Python normal dans dispatch + leur description dans tools.
  2. Ajoute le suivi du budget : accumule resp.usage.total_tokens et stoppe au plafond.
  3. Gère la mémoire longue : sur des tâches longues, résume ou élague les vieux tours pour ne pas exploser le contexte (et la facture).

À retenir

Un agent, c'est un LLM dans une boucle observer → décider → agir, porté par trois piliers : des outils, une mémoire de travail, une condition d'arrêt. Les garde-fous (limite d'itérations, budget, validation humaine, périmètre restreint, journal) ne sont pas optionnels — ce sont eux qui séparent un agent utile d'un agent qui part en vrille. Et soigne la boucle autant que le prompt.

Réagir :
Partager —XLinkedIn