ReActEngine v1 : Modules et traitements

, par Bertrand Degoy

ReActEngine Mistral v1 est un moteur agentique modulaire, contrôlable et extensible, combinant RAG, outils personnalisés, sélection dynamique d’outils et génération finalisée.
Il est conçu pour les environnements multi-thèmes, multi-sources et nécessitant une auditabilité stricte du raisonnement et des actions.


Destinée à un public expert, voici une description de l’agent ReActEngine Mistral v104 fondé sur llama_index.core.agent.workflow.ReActAgent, intégrant Finalizer, ToolRouter, RAG, outils personnalisés et orchestration.

ReActEngine Mistral v104 — Description technique

1. Nature de l’agent

L’agent Mistral ReAct v104 est un moteur agentique basé sur le paradigme ReAct (Reasoning + Acting).
Il combine :

  • un LLM Mistral (via API),

  • un orchestrateur d’outils dont MCP,

  • un Finalizer pour la génération contrôlée,

  • un ToolRouter pour la sélection dynamique d’outils,

  • un RAG (Research Augmented Generation) modulaire basé sur l’agent de recherche Chat Engine Mistral utilisé par notre ChatBot simple.

Il s’appuie sur la classe ReActAgent du module llama_index.core.agent.workflow, plutôt que sur les agents préconstruits, afin de garantir :

  • un contrôle fin du cycle ReAct,

  • une meilleure auditabilité,

  • une intégration propre des outils personnalisés,

  • une compatibilité avec les pipelines agentiques complexes.

Constatant que les erreurs non interceptées ou mal traitées aboutissaient sur un arrêt intempestif et silencieux du raisonnement, un principe est posé : seules deux exceptions peuvent ( et doivent ) être levées, RetryException et ForceFinalizeException, avec des messages explicites pour permettre au raisonnement de poursuivre ou de retourner proprement vers l’utilisateur. Ceci a motivé une part des modifications au code de Llamaindex.

Avec le même objectif - éviter les arrêts du raisonnement - deux autres modifications portent sur la génération et le traitement de question posée à l’utilisateur ainsi que le recours au RAG pour y trouver des informations susceptibles de guider le raisonnement.

2. Architecture générale

2.1. Boucle ReAct

L’agent suit la logique décrite dans Synergizing Reasoning and Acting in Language Models :

  1. Reasoning : le LLM produit une intention ou un plan.

  2. Acting : l’agent appelle un outil.

  3. Observation : l’outil renvoie un résultat.

  4. Reasoning : le LLM intègre l’observation.

  5. Final Answer : produit par le Finalizer.

Avec LlamaIndex ReActAgent basé sur Workflow, la boucle ReAct est orchestrée par répétition de la fonction take_step(). Toute action est synonyme d’appel à un outil. Il est mis fin à la boucle lorsqu’une réponse finale est détectée .

PNG - 613 ko
La boucle ReAct

2.2. Outils intégrés

L’agent combine trois familles d’outils :

A. RAG (QueryEngineTool)

  • Basé sur chat_engine_mistral_v1.3.x.

  • Utilise les index créés par RAG Manager ou IngestCmd.

  • Permet un RAG cohérent, multithème, multilingue.

Exposer un outil RAG à la boucle ReAct permet au LLM d’accéder à des descriptions systémiques, des routines de raisonnement, des spécifications etc., tout documents normatifs permettant de soutenir le raisonnement et de le ré-orienter en cas d’impasse.

B. Outils personnalisés (FunctionTools)

Voir également : RAG : Chat Engine ReAct avec outils

C. Outils généralistes

  • Calculs : arithmétique, statistique

  • Manipulation de texte

  • Utilitaires divers

3. Initialisation

3.1. __init__(...)

Deux modes d’initialisation :

Mode 1 : avec ragkwargs

  • Adapté à une utilisation en mode API, relançant l’instanciation à chaque requête.

  • Le thème, la langue, le niveau de détail, etc. sont fournis dès l’instanciation.

  • L’agent est immédiatement opérationnel.

Mode 2 : sans ragkwargs

  • Adapté à un fonctionnement en mode démon ou worker multi-thèmes.

  • L’application doit ensuite appeler start() avec le thème en argument.

Paramètres clés :

  • config : configuration globale (commune à IngestCmd, ChattyBot, ChattyWS…)

  • llmapikey : clé API MistralAI

  • token : jeton de sécurité pour les serveurs de données

  • ragkwargs : dictionnaire contenant au minimum :

    • theme

    • detail

    • language

4. Méthode start()

Méthode pour les services REST ou workers persistants.

Elle :

  • complète l’initialisation selon le thème,

  • charge l’index si nécessaire,

  • permet aux applications stateful de fournir leur propre index (optimisation),

  • prépare l’agent ReAct pour le thème courant.

Retour : un objet ReActAgent prêt à l’emploi.

5. Gestion de l’historique — set_history()

Cette méthode :

  • reçoit un historique OpenAI-style :
    [{ "role": "user", "content": "..." }, ...]

  • nettoie et compresse l’historique pour compatibilité ReAct,

  • stocke :

    • self.conversation_history

    • self.compressed_history

L’agent est statique :
→ l’application appelante doit gérer la persistance de l’historique ( plus précisément "historique de conversation" ) et le transmettre avant chaque requête.

6. Exécution asynchrone — stream(prompt)

6.1. Fonctionnement

Pour chaque prompt :

  1. ToolRouter sélectionne dynamiquement les outils pertinents
    → réduction du contexte, optimisation des coûts, meilleure précision.

  2. L’agent ReAct est reconstruit avec uniquement les outils routés.

  3. Un tote bag est préparé :

    • historique compressé

    • langue

    • profil (ex. "expert")

  4. Le ReActFinalizer gère la génération incrémentale.

  5. La méthode retourne un AsyncGenerator.

6.2. Résilience

Toute exception est :

  • interceptée,

  • tracée via log_trace,

  • et la méthode renvoie le générateur déjà créé si possible.

7. Exécution synchrone — get_engine_response()

Fonctionnement :

  • récupère l’historique compressé si non fourni,

  • applique le ToolRouter,

  • exécute l’agent ReAct,

  • retourne une réponse complète (non streamée).

8. Toolkits dynamiques

Le moteur charge automatiquement :

  • un ToolKit généraliste (obligatoire),

  • un ToolKit spécifique au thème (optionnel).

Cela permet :

  • une modularité forte,

  • une isolation stricte des outils par thème,

  • une réduction du bruit dans la boucle ReAct.

9. Points clés de la version v104

  • Intégration du Finalizer (contrôle de la réponse finale).

  • Intégration du ToolRouter (sélection dynamique des outils).

  • Utilisation de ReActAgent(Workflow) plutôt que l’agent préconstruit, afin d’atteindre le cœur de la boucle ReAct et y apporter des améliorations .

  • Outils RAG encapsulés dans RagTool.

  • Compatibilité MCP pour les données temps réel.

  • Architecture stateless côté agent, stateful côté application.

10. Diagramme des échanges

PNG - 2.1 Mo
ReActEngine v1 diagramme des échanges

Les modules de la partie gauche (ReActEngine, ToolRouter, ReActFinalizer, ReActStreamParser) ont été développés spécialement pour atteindre les objectifs du ReActEngine v1, tandis que la partie droite comprend les modules du ReActAgent(Workflow) de LlamaIndex avec quelques adaptations. Le LLM est une API de MistralAI.

Conseisl : Cliquez sur le diagramme pour le zoomer. Ouvrez le diagramme dans une autre fenêtre pour mieux suivre la suite.

10.1 Vision globale du flux

  1. Utilisateur → ReActEngine : prompt.

  2. ReActEngine → ToolRouter : sélection d’outils.

  3. ReActEngine → ReActAgent : initialisation du pipeline.

  4. Formatter → LLM : message formaté.

  5. LLM → StreamParser : tokens bruts → blocs ReAct.

  6. Parser → Agent : StepResult.

  7. Workflow : décide de la suite (outil, raisonnement, final).

  8. Finalizer : réponse finale.

  9. ReActEngine → Utilisateur : streaming de la réponse.

10.2 Détail des échanges et des traitements

Utilisateur

Entrée : intention humaine, question, commande.
Traitement : aucun — c’est la source du prompt.
Sortie : prompt utilisateur envoyé au ReActEngine.

ReActEngine

Entrée : prompt utilisateur.
Traitement : Détermine si le prompt nécessite des outils. Appelle le ToolRouter pour identifier les outils pertinents. Initialise un agent ReAct complet (Finalizer, Workflow, Formatter, Parser…). Lance la boucle de streaming (token-par-token).
Sortie : flux de tokens structurés (Thought / Action / Observation / FinalAnswer).

ToolRouter

Entrée : prompt brut.
Traitement : Analyse sémantique du prompt. Sélectionne les outils pertinents (toolbag).
Sortie : liste d’outils pertinents → renvoyée au ReActEngine.

ReActFinalizer

Entrée : instance d’agent ReAct.
Traitement : Surveille la fin du raisonnement. Détecte un FinalAnswer ou une condition d’arrêt. Nettoie et finalise la réponse.
Sortie : réponse finale propre, prête à être envoyée à l’utilisateur.

ReActStreamParser

Entrée : tokens bruts provenant du LLM.  Traitement : Reconstitue les blocs ReAct (Thought, Action, Observation…). Filtre les artefacts, nettoie les tokens. Émet des fragments de réponse via un générateur asynchrone.
Sortie : tokens nettoyés + blocs ReAct structurés.

ReActAgent (modifié)

Entrée : message formaté (prompt + contexte + outils).
Traitement : Implémente la logique ReAct :
Thought → Action → Observation → Thought → …
Décide quand appeler un outil. Produit un StepResult à chaque itération.
Sortie : StepResult (structure contenant Thought/Action/Observation).

La méthode take_step()

Au cœur de la boucle ReAct, cette méthode orchestre une seule étape de raisonnement :

  1. Formater l’entrée du LLM à l’aide du template ReAct.

  2. Appeler le LLM (en mode streaming ou non).

  3. Parser la sortie du modèle en un ReasoningStep structuré.

  4. Gérer les erreurs de parsing ou les états ReAct invalides.

  5. Puis, selon le cas :

    • retourner une réponse finale,

    • déclencher un appel d’outil,

    • ou demander au LLM de réessayer ou de finaliser.

Les principales modifications portent sur la gestion des sorties LLM mal formées ou incomplètes, ainsi que le traitement des exceptions :

  • normalisation du format de sortie du LLM, contribuant à rendre l’agent agnostique en ce qui concerne les LLMs dont les formats sont peu normalisés.

  • relance de la boucle lorsque le parseur détecte une sortie ReAct incomplète, mal formée ou autrement invalide, mais pouvant être corrigée par le modèle.

  • traitement de deux exceptions, :

    • RetryException → la sortie est incomplète ou tronquée ; le LLM doit réessayer en se fondant sur le message d’erreur. Cela couvre aussi les erreurs des outils.

    • ForceFinalizeException → le modèle a produit un état ReAct invalide (ex. : Thought sans Action ni Answer) ; le prochain appel LLM doit produire uniquement une réponse finale. Ces exceptions sont les seules autorisées au sein de la boucle afin d’éviter les arrêts silencieux de l’agent. Toutes les exceptions d’une autre classe levées au sein du code sont interceptées et ramenées à l’une de ces deux exceptions.

La méthode handle_tool_call_result()

Cette méthode traite les résultats des appels d’outils :

  • récupère l’historique courant de raisonnement,

  • inspecte chaque résultat d’outil,

  • distingue les erreurs réelles des contenus vides légitimes,

  • normalise le contenu observé,

  • ajoute une étape d’observation au raisonnement,

  • gère les cas particuliers (ask_user, return_direct),

  • met à jour l’état persistant du raisonnement dans le contexte (variable d'état ctx). Les principales modifications incluent :

  • la distinction entre un retour d’outil légitimement vide et une erreur de l’outil.

  • la normalisation du retour d’outil (le contenu observé), contribuant à l’adaptation aux différents LLMs.

  • une détection d’erreurs garantissant que les outils signalent explicitement leurs échecs en levant l’une des exception RetryException ou ForceFinalizeException afin d’éviter un arrêt intempestif du raisonnement.

  • la gestion d’un outil AskUserTool ( créé dans la GeneralToolBox ) permettant au modèle de poser une question à l’utilisateur, puis la transformation de sa réponse en Observation ReAct et la relance de la boucle ReAct via take_step().

BaseWorkflowAgent (modifié)

Entrée : StepResult ou message intermédiaire.
Traitement : Gère la logique générique d’un agent (état, transitions, erreurs). Coordonne les appels entre Workflow, Formatter, Parser.
Sortie : instructions pour la prochaine étape du workflow.

La principale modification porte sur la gestion de la pause en attente d’une réponse de l’utilisateur (outil AskUser).

ReActWorkflow

Entrée : StepResult + contexte d’exécution.
Traitement : Orchestration du cycle ReAct. Détermine si l’agent doit :
continuer à raisonner,
appeler un outil,
ou produire la réponse finale.
Sortie : nouvelle étape à exécuter (run_step).

ReActChatFormatter

Entrée : prompt utilisateur + état du workflow.
Traitement : Formate le message dans le style attendu par le LLM (system + user + tool instructions). Injecte les outils dans le prompt si nécessaire.
Sortie : message formaté envoyé au LLM.

ReActOutputParser (modifié)

Entrée : sortie brute du LLM.  Traitement : Analyse la réponse du LLM. Extrait Thought / Action / Observation / FinalAnswer. Détecte les erreurs de format.
Sortie : StepResult structuré.

Les principales modifications portent sur une détection et une gestion correcte des erreurs en levant RetryException ou ForceFinalizeException afin d’éviter les arrêts intempestifs du LLM.

Modèle LLM

Entrée : message formaté (prompt complet).
Traitement : Génération token-par-token. Produit du texte brut sans structure interne explicite.
Sortie : tokens bruts → envoyés au StreamParser.

Il faut noter que, si de nombreuses adaptations visent à rendre notre ReActAgent agnostique vis-à-vis des LLMs, notamment en normalisant les formats des requêtes et des réponses, cela n’est pas encore finalisé dans l’état actuel du développement.

 

Mise en œuvre des outils dans notre ReActAgent

1. Les outils

L’agent s’appuie sur trois ToolKits complémentaires :

  • GeneralToolKit : outils génériques toujours disponibles.

  • ThemeToolKit : outils métier + un outil RAG pour accéder à la documentation interne.

  • ToolKit Temps Réel : outils connectés à des sources de données dynamiques (internes/externes).

Voir également : ReAct et intégration des données temps réel avec MCP ( Model Context Protocol )

2. L’outil RAG dans la boucle ReAct

Le RAG (Research Augmented Generation) est intégré comme un outil standard, accessible via la logique ReAct.

Rôle du RAG

Il permet à l’agent d’interroger la documentation interne de l’entreprise, composée de : documents historiques (rapports, notes, logs), référentiels documentaires (procédures, notices techniques), routines spécialisées pour guider le raisonnement dans des tâches répétitives.

Fonction dans ReAct

Le RAG est utilisé lorsque l’agent doit : vérifier une information interne, s’appuyer sur des documents de référence, exécuter une tâche nécessitant un contexte métier documenté.

Le RAG enrichit le raisonnement de l’agent en lui donnant accès à la connaissance interne, tout en restant orchestré par ReAct.

3. le ToolRouter

Le ToolRouter identifie automatiquement les outils les plus adaptés à une requête utilisateur grâce à un mécanisme de similarité vectorielle.

Fonctionnement :

Chaque outil possède une description textuelle (métadonnées). Voir : ReAct : la carte des intentions : Intent Map Les descriptions et la requête utilisateur sont vectorisées via un modèle d’embedding. Le routeur calcule la similarité cosinus entre la requête et chaque outil. Les outils sont classés par pertinence. Le ReActAgent choisit l’outil à appeler en fonction du classement et du contexte.

→ Le ToolRouter guide la boucle ReAct vers l’outil optimal au moment opportun.

Résultat : Un agent capable de combiner raisonnement, outils spécialisés, documentation interne et données temps réel.