Qu'est-ce qu'un Agent IA ? Construis-en un en 50 lignes

Tu utilises ChatGPT tous les jours. Tu crois parler à une IA intelligente. En réalité, tu parles à un perroquet statistique enfermé dans une boîte. Il prédit le mot suivant, point final.

L’agent, lui, a les clés de la boîte. Il peut sortir, agir, et revenir avec des résultats.

Dans cet article, tu vas construire un agent de zéro, étape par étape, avec du vrai code Python qui s’exécute dans ton navigateur. Pas de mock, pas de simulation. Du code réel.

Pour t’expliquer avec une analogie

Imagine un médecin :

  • LLM seul : Il t’explique l’opération par téléphone. “Coupe ici, recouds là.” Tu dois tout faire toi-même.
  • LLM + Tool Calling : Il te prescrit un médicament. Tu dois aller le chercher à la pharmacie.
  • Agent : Il est dans la salle d’opération. Il coupe, vérifie, recoud, sans rien te demander.

L’agent n’est pas plus intelligent. Il est autonome. C’est toute la différence.

On va construire ce médecin-chirurgien, étape par étape.

Étape 1 : Le LLM seul (le perroquet)

Un LLM ne “comprend” rien. Il prédit la suite de caractères la plus probable. Demande-lui l’heure, il va t’expliquer qu’il ne peut pas la connaître.

┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Toi │ ──── │ LLM │ ──── │ Texte │ │ "Quelle │ │ (prédit) │ │ "Je ne │ │ heure?" │ │ │ │ sais pas" │ └─────────────┘ └─────────────┘ └─────────────┘ │ ▼ [STOP] Pas d'action possible

Exécute ce code pour voir (n’hésite pas à scroller vers le bas pour voir tout le code) :

Étape 1 Le LLM seul : juste du texte

Constat : Le LLM génère du texte, puis s’arrête. Il ne peut pas aller chercher l’heure. Il est enfermé dans sa boîte.

Étape 2 : Le Tool Calling (la prescription)

Les LLM modernes peuvent générer des appels structurés en JSON. Mais comment le LLM sait-il quels outils existent ?

Le prompt système : la clé de tout

Quand tu appelles l’API d’OpenAI ou Anthropic avec des outils, tu envoies quelque chose comme ça :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
  "model": "gpt-4",
  "messages": [{"role": "user", "content": "Quelle heure est-il ?"}],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_time",
        "description": "Retourne l'heure actuelle du serveur",
        "parameters": {"type": "object", "properties": {}}
      }
    },
    {
      "type": "function",
      "function": {
        "name": "calculate",
        "description": "Évalue une expression mathématique",
        "parameters": {
          "type": "object",
          "properties": {
            "expression": {"type": "string", "description": "L'expression à calculer"}
          }
        }
      }
    }
  ]
}

Le LLM voit cette liste d’outils dans son contexte. Il peut alors faire du pattern matching : par exemple “L’utilisateur demande l’heure → j’ai un outil get_time → je génère un JSON pour l’appeler.”

C’est du texte qui génère du texte. Pas de magie.

Ce que le LLM génère

Quand il juge qu’un outil est pertinent, il répond :

┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ Toi │ ──── │ LLM │ ──── │ JSON (texte) │ │ "Quelle │ │ (prédit) │ │ {"tool":"get_time"}│ │ heure?" │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │ ▼ ??? Qui exécute ce JSON ?

Mais attention : ce n’est que du texte. Le LLM a juste écrit du JSON bien formaté. Personne ne l’a exécuté.

Exécute ce code pour voir le JSON brut :

Étape 2 Tool Calling : le LLM génère du JSON

Question clé : Le LLM a généré {"tool": "get_time"}. Mais qui va réellement appeler la fonction get_time() ? Pas le LLM. C’est ton code.

Étape 3 : Le Dispatcher (ton code)

Entre le LLM et l’outil, il faut un programme intermédiaire qui :

  1. Parse le JSON du LLM
  2. Trouve la bonne fonction
  3. L’exécute vraiment
  4. Récupère le résultat
┌─────────┐ ┌─────────┐ ┌──────────────┐ ┌─────────┐ ┌──────────┐ │ Toi │───▶│ LLM │───▶│ JSON │───▶│ TON CODE│───▶│ OUTIL │ │ │ │ │ │ {"tool":...} │ │ (parse) │ │ get_time │ └─────────┘ └─────────┘ └──────────────┘ └─────────┘ └──────────┘ │ │ │ Exécute │ │◀─────────────┘ │ ▼ "14:32:05"

C’est ce code que personne ne te montre. Le voici :

Étape 3 Le Dispatcher : parser et exécuter

C’est ce code qui manque au LLM seul. Sans lui, le JSON reste du texte inerte. Avec lui, le JSON devient une action réelle.

Mais il manque encore quelque chose : la boucle.

Étape 4 : La Boucle Autonome (l’agent)

Un agent, c’est quand tu mets tout ça dans une boucle while. Le LLM reprend la main après chaque outil et décide : encore un outil, ou réponse finale ?

┌──────────────────────────────────────┐ │ │ ▼ │ ┌─────────┐ ┌─────────────────────────┐ ┌──────────────┐ │ │ Toi │───▶│ LLM │───▶│ tool_call? │ │ │ "Quelle │ │ (analyse contexte) │ │ │ │ │ heure?" │ └─────────────────────────┘ └──────────────┘ │ └─────────┘ │ │ ┌──────────┴──────────┐ │ ▼ ▼ │ ┌──────────┐ ┌─────────┐ │ OUI │ │ NON │ │ Exécuter │ │ Réponse │ │ outil │ │ finale │ └──────────┘ └─────────┘ │ │ │ ▼ │ ┌──────────┐ │ │ "Il est │ └───────────────│ 14:32" │ Résultat └──────────┘ renvoyé au LLM

Sans intervention humaine. Tu donnes un objectif, l’agent fait le reste.

Voici l’agent complet :

Étape 4 L'Agent Complet : la boucle autonome

Observe bien la console :

  1. Le LLM analyse → décide d’appeler get_time()
  2. L’outil s’exécute → résultat ajouté au contexte
  3. Le LLM reprend → décide d’appeler calculate()
  4. L’outil s’exécute → résultat ajouté
  5. Le LLM reprend → génère la réponse finale

C’est ça, un agent. Une boucle qui tourne jusqu’à ce que le LLM décide qu’il a fini.

Étape 5 : À toi de jouer

L’agent ci-dessus a deux outils : get_time() et calculate(). Mais il manque quelque chose : la lecture de fichiers.

L’exercice

Complète le code ci-dessous pour ajouter l’outil read_file :

  1. Crée une fonction read_file(path) qui simule la lecture d’un fichier
  2. Ajoute-la au dictionnaire TOOLS
  3. Ajoute une condition dans le dispatcher pour détecter quand l’utilisateur demande de lire un fichier
  4. Teste avec l’objectif : "Lis le fichier config.json"
Exercice Ajoute l'outil read_file
Voir la solution
import json
from datetime import datetime

# Outils existants
def get_time():
    return datetime.now().strftime("%H:%M:%S")

def calculate(expression):
    return str(eval(expression))

# SOLUTION: La fonction read_file
def read_file(path):
    FILES = {
        "config.json": '{"version": "2.1.0", "debug": true}',
        "package.json": '{"name": "mon-app", "react": "18.2.0"}'
    }
    return FILES.get(path, f"Fichier '{path}' non trouvé")

# SOLUTION: Ajout au dictionnaire TOOLS
TOOLS = {"get_time": get_time, "calculate": calculate, "read_file": read_file}

GOAL = "Lis le fichier config.json"

print(f"Objectif: {GOAL}\n")

# Dispatcher avec read_file
if "heure" in GOAL.lower():
    print("[LLM] Je dois appeler get_time()")
    result = get_time()
    print(f"[EXEC] Résultat: {result}")
    print(f"\n[RÉPONSE] Il est {result}")
elif "+" in GOAL or "calcul" in GOAL.lower():
    expr = "2+2"
    print(f"[LLM] Je dois appeler calculate('{expr}')")
    result = calculate(expr)
    print(f"[EXEC] Résultat: {result}")
    print(f"\n[RÉPONSE] {expr} = {result}")
# SOLUTION: Condition pour read_file
elif "lis" in GOAL.lower() or "fichier" in GOAL.lower():
    # Extraire le nom du fichier (simplifié)
    path = "config.json" if "config" in GOAL else "package.json"
    print(f"[LLM] Je dois appeler read_file('{path}')")
    result = read_file(path)
    print(f"[EXEC] Résultat: {result}")
    print(f"\n[RÉPONSE] Contenu de {path}: {result}")
else:
    print("[LLM] Je ne sais pas quel outil utiliser.")

Résumé : LLM vs Tool Calling vs Agent

  LLM seul Tool Calling Agent
Génère du texte Oui Oui Oui
Peut demander une action Non Oui Oui
Exécute l’action Non Non Oui
Boucle autonome Non Non Oui
Gère plusieurs étapes Non Non Oui

Ce que tu as appris

  1. Un LLM génère du texte, rien d’autre
  2. Le Tool Calling permet au LLM de générer des JSON structurés
  3. Le Dispatcher (ton code) parse le JSON et exécute les fonctions
  4. L’Agent met tout ça dans une boucle autonome

Un agent, c’est 50 lignes de Python. Pas de magie, pas de framework complexe. Juste une boucle while, un dispatcher, et des outils.

La prochaine fois que tu utilises Claude ou Codex avec des outils, tu sauras exactement ce qui se passe sous le capot.

Une question sur les Agents ?

Pose ta question sur la différence LLM vs Agent, le function calling, ou la boucle autonome.

Initialisation du système RAG...

Références