Aller au contenu
Craft Solution Tech

ReAct expliqué aux devs : le pattern derrière les agents IA

Raisonner, agir, observer, recommencer. Le pattern qui fait tourner LangChain, LangGraph et CrewAI, et pourquoi le LLM n'exécute jamais lui-même les actions.

7 min de lecture

Quand on parle d'agents IA, on imagine souvent une boîte noire magique qui "réfléchit" et "agit" toute seule.

En réalité, derrière la plupart des agents — ceux de LangChain, LangGraph, CrewAI — il y a un pattern simple et élégant : ReAct.

Si tu es dev JavaScript ou TypeScript et que tu veux comprendre comment un agent IA fonctionne vraiment sous le capot, cet article est pour toi.


Le problème : un LLM ne peut pas agir

Un LLM classique (GPT, Claude, Llama...) a une limite fondamentale : il ne peut que générer du texte.

Il ne peut pas :

  • chercher sur Google
  • lire une base de données
  • appeler une API
  • exécuter du code

Si tu lui demandes "Quel temps fait-il à Paris ?", il va te répondre quelque chose de plausible... mais probablement faux, parce qu'il n'a aucun accès au monde réel.

C'est le problème d'hallucination : le modèle "invente" quand il ne sait pas.


La solution : ReAct (Reasoning + Acting)

ReAct vient d'un paper de recherche de Princeton et Google publié en 2022. L'idée tient en une phrase :

Faire réfléchir le modèle à voix haute, et lui donner des outils pour vérifier ses réflexions.

Le nom est la fusion de deux mots :

  • Reasoning — le modèle raisonne étape par étape
  • Acting — le modèle peut appeler des outils externes

Avant ReAct, ces deux capacités existaient séparément :

ApprocheCe qu'elle faitLe problème
Chain-of-ThoughtLe modèle raisonne étape par étapeIl ne peut pas vérifier → il hallucine
Action-onlyLe modèle appelle des outilsIl ne raisonne pas sur pourquoi → il agit à l'aveugle

ReAct combine les deux dans une boucle : raisonner → agir → observer le résultat → raisonner à nouveau.


Le cycle ReAct en 4 étapes

Voici ce qui se passe quand tu poses une question à un agent ReAct :

📝 Question : "Qui a gagné le dernier Ballon d'Or ?"

💭 Thought  : "Je ne connais pas le résultat le plus récent.
               Je vais vérifier sur Wikipédia."

⚡ Action   : WikipediaSearch("Ballon d'Or 2024")

👁️ Observation : "Le Ballon d'Or 2024 a été décerné à..."

💭 Thought  : "J'ai l'information, je peux répondre."

✅ Final Answer : "Le Ballon d'Or 2024 a été remporté par..."

C'est une boucle. Si la première recherche ne suffit pas, l'agent refait un tour : Thought → Action → Observation, jusqu'à avoir assez d'info pour conclure.

Le schéma complet

Deux schémas côte à côte : à gauche un LLM simple qui reçoit une requête et renvoie une réponse ; à droite la boucle ReAct, où l'agent enchaîne raisonnement du LLM, appel d'outil, environnement, puis retour du résultat vers une nouvelle génération.
À gauche le répondeur simple. À droite la boucle ReAct : le LLM décide, le runtime exécute, l'observation revient dans le prompt.

Le point clé : le LLM ne fait PAS les actions

C'est la chose la plus importante à comprendre.

Quand l'agent écrit Action: WikipediaSearch("Ballon d'Or"), il ne cherche pas réellement sur Wikipédia. Il génère du texte qui dit qu'il veut chercher.

C'est le runtime (LangGraph, LangChain, ou tout autre framework) qui :

  1. Parse la sortie du LLM
  2. Détecte qu'il y a une action à exécuter
  3. Exécute l'outil (appel API, requête DB, etc.)
  4. Réinjecte le résultat comme "Observation" dans le prompt
  5. Rappelle le LLM avec tout le contexte mis à jour

Le LLM est le cerveau qui décide quoi faire. Le runtime est les mains qui exécutent.

┌─────────────────────────────────┐
│           LLM (cerveau)         │
│                                 │
│  "Je veux chercher sur Wiki..." │
│  (génère du texte)              │
└───────────────┬─────────────────┘
                │  texte parsé
                ▼
┌─────────────────────────────────┐
│     Runtime / LangGraph (mains) │
│                                 │
│  → Parse l'action               │
│  → Appelle l'API Wikipédia      │
│  → Récupère le résultat         │
│  → Réinjecte dans le prompt     │
└─────────────────────────────────┘

La mémoire de travail : le Scratchpad

Un LLM est stateless — il n'a aucune mémoire entre deux appels. Chaque appel, c'est comme s'il se réveillait pour la première fois.

Alors comment l'agent se souvient de ce qu'il a déjà trouvé ?

Grâce au scratchpad (littéralement : "bloc-notes"). C'est un texte qui accumule tout l'historique des tours précédents (Thought + Action + Observation) et qui est renvoyé au LLM à chaque nouvel appel.

Exemple concret

L'agent doit comparer le PIB de la France et de l'Allemagne.

Tour 1 — le scratchpad est vide :

Thought: Je dois chercher le PIB de la France.
Action: WikiSearch("PIB France 2024")
Observation: 2 800 milliards €

Tour 2 — le scratchpad contient le tour 1 :

[tour 1 complet visible par le LLM]

Thought: J'ai la France. Maintenant l'Allemagne.
Action: WikiSearch("PIB Allemagne 2024")
Observation: 3 500 milliards €

Tour 3 — le scratchpad contient les tours 1 + 2 :

[tours 1 et 2 visibles par le LLM]

Thought: J'ai les deux chiffres, je peux comparer.
Final Answer: Le PIB de l'Allemagne (3 500 Mds) dépasse
              celui de la France (2 800 Mds)...

Le problème de la taille

Plus l'agent fait de tours, plus le scratchpad grossit, et plus ça consomme de tokens (= de l'argent).

Les stratégies pour gérer ça :

  • Rolling window : ne garder que les N derniers tours
  • Résumé : compresser les anciens tours en un paragraphe (via un appel LLM dédié)
  • Épisodique : ne garder que les résultats clés, pas le raisonnement intermédiaire

C'est un vrai sujet d'ingénierie quand ton agent fait 10+ appels d'outils.


Et les Function Calls dans tout ça ?

Si tu as utilisé l'API d'OpenAI ou Claude récemment, tu connais le function calling : le modèle renvoie directement un objet JSON structuré { "tool": "...", "args": {...} } au lieu de texte libre.

C'est plus fiable que le parsing de texte, mais le pattern conceptuel reste le même :

ReAct classique (texte)Function calling moderne
FormatTexte libre parsé avec regexJSON structuré
RaisonnementVisible ("Thought: ...")Implicite (dans la tête du modèle)
FiabilitéFragile (parsing)Robuste (typé)
DébugFacile (tu lis le raisonnement)Plus difficile

Les frameworks comme LangGraph supportent les deux, et dans la pratique, les agents modernes utilisent le function calling sous le capot tout en suivant la logique ReAct.


Pourquoi c'est important pour toi en tant que dev

Si tu es développeur JavaScript/TypeScript et que tu veux construire des agents IA, comprendre ReAct c'est comprendre le pattern fondamental sur lequel tout repose.

Que tu utilises LangGraph, LangChain, le Vercel AI SDK, ou que tu codes ton propre agent from scratch — c'est toujours la même boucle :

Raisonner → Décider → Agir → Observer → Boucler

Une fois que tu as compris ça, le reste (RAG, multi-agents, planification) c'est des variations et des compositions de ce même pattern.

Sujets abordés

  • IA
  • LLM
  • agents
  • LangGraph
  • TypeScript

Dites-nous ce que vous cherchez à construire.

Nous écrire sur WhatsApp
Retour au blog