Les agents ReAct dans LangGraph : étape par étape, réflexion, action et observation.
Mettre en œuvre le cycle ReAct sous forme de nœuds graphiques explicites avec un état typé, des appels d’outils et des conditions de arrêt pouvant être testées.
Ce guide permet de reconstruire un chemin opérationnel pour : ReAct Agents Explained : Une mise en œuvre pas à pas utilisant LangGraph. L’accent est mis sur les contrats, les vérifications et le code que l’on peut intégrer dans un dépôt sans devoir deviner l’intention. Pour une vue d’ensemble, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans avoir à deviner l’état caché. Préférez des unités petites et testables plutôt que des scripts complexes. Lorsqu’une étape échoue, l’échec doit indiquer une seule responsabilité plutôt qu’un processus embrouillé.
Introduction
Pour l’introduction, définissez les entrées, le responsable de l’étape et les critères de fin avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Nommez les artefacts, définissez des vérifications de succès et refusez toute exécution partielle silencieuse. Séparez la planification de l’exécution par les outils. Le planificateur propose ; l’exécutant modifie ; le vérificateur compare les résultats à l’objectif.
Qu’est-ce qu’un agent ReAct ?
Pour « Qu’est-ce qu’un agent ReAct ? », il convient de définir les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu, sans avoir à deviner l’état caché. Enregistrez les temps d’exécution et les coûts à côté des résultats fonctionnels. Une visibilité précoce évite des factures inattendues lorsque le parcours passe d’un environnement de démonstration à un environnement partagé. Séparez la planification de l’exécution des outils : le planificateur propose ; l’exécutant modifie ; le vérificateur contrôle les résultats par rapport à l’objectif.
Pourquoi ReAct est meilleur que la simple chaîne de réflexion
Pour comprendre pourquoi ReAct est supérieur au Chain-of-Thought pur, il faut définir les entrées, le responsable de chaque étape ainsi que les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans avoir à lire l’ensemble du système. Séparez la planification de l’exécution des outils. Le planificateur propose ; l’exécutant met à jour ; le vérificateur contrôle les résultats par rapport à l’objectif. Pour comprendre pourquoi ReAct est supérieur au Chain-of-Thought pur, il faut définir les entrées, le responsable de chaque étape ainsi que les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Préférez des unités petites et testables aux scripts complexes. Lorsqu’une étape échoue, l’échec doit permettre d’identifier une seule responsabilité.
plutôt qu’un pipeline enchevêtré.Thought: I don’t know the answer yet. I should search.
Action: Search("Paris weather this week")
Observation: It will rain on Thursday.
Thought: I should suggest indoor activities.
Final Answer: ...
ReAct Prompting
Au sein de ReAct Prompting, il convient de définir les entrées, le responsable de chaque étape ainsi que les critères d’arrêt avant de modifier du code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu, sans avoir à deviner l’état caché. Considérez cette phase comme un contrat entre les entrées et les sorties validées. Donnez des noms aux artefacts, définites des vérifications de succès et refusez les complétions partielles silencieuses. Restreignez strictement les schémas des outils. Des arguments de texte libre trop larges favorisent les injections et rendent les audits coûteux.
Objectif de ReAct Prompting
Afin d’utiliser la technique ReAct Prompting, il convient de définir les entrées, le responsable de chaque étape ainsi que les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu, sans avoir à deviner l’état caché. Il faut enregistrer les temps d’exécution et les coûts à côté des résultats fonctionnels. Une visibilité précoce permet d’éviter des factures inattendues lorsque le processus passe de l’environnement de démonstration à des environnements partagés. Les schémas des outils doivent être strictement encadrés ; des arguments de texte libre trop larges favorisent les injections et rendent les audits coûteux.
Éléments clés de ReAct Prompting
Pour les éléments clés de l’approche ReAct Prompting, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier du code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans avoir à lire l’ensemble du système. Restreignez strictement les schémas des outils. Des arguments de texte libre trop larges favorisent les injections et rendent les audits coûteux. Pour les éléments clés de l’approche ReAct Prompting, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier du code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Préférez des unités petites et testables aux scripts complexes. Lorsqu’une étape échoue, l’échec doit indiquer une seule responsabilité plutôt qu’un processus embrouillé.
1. Raisonnement par chaîne de pensée
Pour le 1. Raisonnement par chaîne de pensée, il convient de définir les entrées, le responsable de chaque étape ainsi que les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Nommez les artefacts, définissez des vérifications de succès et refusez toute exécution partielle silencieuse. Créez un point de contrôle après chaque appel coûteux au modèle afin qu’un nouvel essai ne génère pas de frais supplémentaires pour le même travail.
2. Espace d’action explicite
2. Espace d’action explicite : définissez les entrées, le responsable de l’étape et les critères de sortie avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu, sans avoir à deviner l’état caché. Enregistrez les temps d’exécution et les coûts à côté des résultats fonctionnels. Une visibilité précoce évite des factures inattendues lorsque le parcours passe d’un environnement de démonstration à un environnement partagé. Prévoyez un point de contrôle après des appels de modèle coûteux afin qu’un nouvel essai ne génère pas à nouveau des frais pour le même travail.
3. Intégration de l’observation
Pour 3. Intégration des observations, définissez les entrées, le responsable de l’étape et les critères de sortie avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans devoir lire l’ensemble du système. Créez un point de contrôle après des appels coûteux au modèle afin qu’un nouvel essai ne reprenne pas le même travail. Pour 3. Intégration des observations, définissez les entrées, le responsable de l’étape et les critères de sortie avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Préférez des unités petites et testables plutôt que des scripts complexes. Lorsqu’une étape échoue, l’échec doit indiquer une seule responsabilité et non un pipeline embrouillé.
4. Boucles itératives
Pour la 4e étape : boucle itérative, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Considérez cette phase comme un contrat entre les entrées et les sorties validées. Donnez des noms aux artefacts, définites des vérifications de succès et refusez toute exécution partielle silencieuse. Séparez la planification de l’exécution par les outils. Le planificateur propose ; l’exécutant modifie ; le vérificateur compare les résultats à l’objectif.
5. Génération de la réponse finale
Pour la 5e étape : génération de la réponse finale, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Enregistrez les temps d’exécution et les coûts à côté des résultats fonctionnels. Une visibilité précoce évite des factures inattendues lorsque le processus passe d’un environnement de démonstration à un environnement partagé. Séparez la planification de l’exécution des outils : le planificateur propose, l’exécutant modifie, et le vérificateur contrôle les résultats par rapport à l’objectif.
Structure canonique du prompt ReAct
Pour une structure de prompt ReAct canonique, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans devoir lire l’ensemble du système. Séparez la planification de l’exécution des outils. Le planificateur propose ; l’exécutant modifie ; le vérificateur contrôle les résultats par rapport à l’objectif. Pour une structure de prompt ReAct canonique, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Préférez des unités petites et testables plutôt que des scripts complexes. Lorsqu’une étape échoue, l’échec doit indiquer une seule responsabilité plutôt qu’un ensemble d’éléments entremêlés.
Question: <user question>Thought: <reason about what to do next>
Action: <selected tool>
Action Input: <tool input>
Observation: <tool output>
... (repeat as needed)
Thought: I now know the final answer
Final Answer: <answer to the user>
Incitation ReAct sans exemple préalable
Pour l’incitation ReAct sans exemple préalable, il faut définir les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Donnez des noms aux artefacts, définites des vérifications de succès et refusez les complétions partielles silencieuses. Restreignez strictement les schémas des outils. Des arguments de texte libre trop larges favorisent les injections et rendent les audits coûteux.
Incitation ReAct vs Agents ReAct
Pour ReAct Prompting par rapport à ReAct Agents, il convient de définir les entrées, le responsable de chaque étape ainsi que les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu, sans avoir à deviner l’état caché. Enregistrez les temps d’exécution et les coûts à côté des résultats fonctionnels. Une visibilité précoce permet d’éviter des factures inattendues lorsque le processus passe de l’environnement de démonstration à des environnements partagés. Restreignez strictement les schémas des outils ; des arguments de texte libre trop larges facilitent les injections et rendent les audits coûteux.
Pourquoi LangGraph pour ReAct Agents ?
Pour comprendre pourquoi choisir LangGraph pour les agents ReAct, il convient de définir les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans avoir à lire l’ensemble du graphe. Restreignez strictement les schémas des outils. Des arguments de texte libre trop larges favorisent les injections et rendent les audits coûteux. Pour comprendre pourquoi choisir LangGraph pour les agents ReAct, il convient de définir les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Préférez des unités petites et testables plutôt que des scripts complexes. Lorsqu’une étape échoue, l’échec doit pointer vers une seule responsabilité et non vers un processus embrouillé.
Le problème fondamental : ReAct est une machine à états, pas un prompt
Pour le problème fondamental : ReAct est une machine à états, pas un prompt, il convient de définir les entrées, le responsable de chaque étape ainsi que les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Considérez cette phase comme un contrat entre les entrées et les sorties validées. Nommez les artefacts, définissez des vérifications de succès et refusez toute complétion partielle silencieuse. Créez un point de contrôle après chaque appel coûteux au modèle afin qu’un nouvel essai ne reprenne pas le même travail.
Ce qui ne fonctionne plus sans LangGraph
Pour ce qui échoue sans LangGraph, définissez les entrées, le responsable de l’étape et les critères de sortie avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Enregistrez les temps d’exécution et les coûts à côté des résultats fonctionnels. Une visibilité précoce évite des factures inattendues lorsque le chemin passe d’un environnement de démonstration à un environnement partagé. Créez un point de contrôle après des appels de modèle coûteux afin qu’un nouvel essai ne facture pas à nouveau le même travail.
1. Flux de contrôle implicite
Pour 1. Flux de contrôle implicite, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans avoir à lire l’ensemble du système. Créez un point de contrôle après des appels coûteux au modèle afin qu’un nouvel essai ne fasse pas payer à nouveau le même travail. Pour 1. Flux de contrôle implicite, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Préférez des unités petites et testables aux scripts complexes. Lorsqu’une étape échoue, l’échec doit indiquer une seule responsabilité plutôt qu’un processus embrouillé.
while True:
llm_output = llm(prompt)
if "Action:" in llm_output:
tool_result = call_tool(...)
else:
break
2. Gestion fragile de l’état
Pour 2. Gestion fragile de l’état, définissez les entrées, le responsable de l’étape et les critères de sortie avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Nommez les artefacts, définissez des vérifications de succès et refusez toute complétion partielle silencieuse. Séparez la planification de l’exécution des outils. Le planificateur propose ; l’exécutant modifie ; le vérificateur compare les résultats à l’objectif.
3. Aucune sémantique de boucle de première classe
Pour le point 3 : en l’absence de sémantique de boucle de première classe, il convient de définir les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu, sans avoir à deviner l’état caché. Enregistrer les temps d’exécution et les coûts à côté des résultats fonctionnels permet d’éviter des factures inattendues lorsque le parcours passe de l’environnement de démonstration à des environnements partagés. Séparer la planification de l’exécution par l’outil : le planificateur propose, l’exécutant modifie, et le vérificateur contrôle les résultats par rapport à l’objectif.
4. Faible prêt pour la production
Pour le point 4 : faible prêt à la production, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier du code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans devoir lire l’ensemble du système. Séparez la planification de l’exécution des outils. Le planificateur propose ; l’exécutant modifie ; le vérificateur contrôle les résultats par rapport à l’objectif. Pour le point 4 : faible prêt à la production, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier du code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Préférez des unités petites et testables aux scripts complexes. Lorsqu’une étape échoue, l’échec doit indiquer une seule responsabilité plutôt qu’un processus embrouillé.
Concepts clés dans LangGraph (vue axée sur l’agent)
Pour les concepts clés dans LangGraph (vue axée sur l’agent), il convient de définir les entrées, le responsable de chaque étape ainsi que les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Considérez cette phase comme un contrat entre les entrées et les sorties validées : donnez des noms aux artefacts, définites des vérifications de succès et refusez toute complétion partielle silencieuse. Restreignez strictement les schémas des outils ; des arguments de texte libre trop larges facilitent les injections et rendent les audits coûteux.
1. État : la mémoire de l’agent
Pour l’état 1 : La mémoire de l’agent, il faut définir les entrées, le responsable de l’étape et les critères de sortie avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Enregistrer les temps d’exécution et les coûts à côté des résultats fonctionnels permet d’éviter des factures inattendues lorsque le parcours passe de l’environnement de démonstration à des environnements partagés. Restreignez strictement les schémas des outils ; des arguments de texte libre trop larges facilitent les injections et rendent les audits coûteux.
2. Nœuds : unités cognitives et opérationnelles
Pour les nœuds 2 : unités cognitives et opérationnelles, définissez les entrées, le responsable de l’étape ainsi que les critères de sortie avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans avoir à lire l’ensemble du système. Restreignez strictement les schémas des outils. Des arguments de texte libre trop larges favorisent les injections et rendent les audits coûteux. Pour les nœuds 2 : unités cognitives et opérationnelles, définissez les entrées, le responsable de l’étape ainsi que les critères de sortie avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Préférez des unités petites et testables aux scripts complexes. Lorsqu’une étape échoue, l’échec doit pointer vers une seule responsabilité plutôt que vers un processus embrouillé.
3. Bords : flux de contrôle explicite
Pour la section 3. Bords : flux de contrôle explicite, il faut définir les entrées, le responsable de l’étape et les critères de sortie avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Donnez des noms aux artefacts, définites des vérifications de succès et refusez les terminations partielles silencieuses. Créez un point de contrôle après des appels coûteux au modèle afin qu’un nouvel essai ne facture pas à nouveau le même travail.
4. Exécution déterministe avec flexibilité
Pour 4. Exécution déterministe avec flexibilité, définissez les entrées, le responsable de l’étape et les critères de sortie avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Enregistrez les temps d’exécution et les coûts à côté des résultats fonctionnels. Une visibilité précoce évite des factures inattendues lorsque le parcours passe d’un environnement de démonstration à un environnement partagé. Prévoyez un point de contrôle après des appels de modèles coûteux afin qu’un nouvel essai ne fasse pas payer à nouveau le même travail.
ReAct + LangGraph : une combinaison parfaite
Pour ReAct + LangGraph : une combinaison idéale. Définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les flags fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans devoir lire l’ensemble du graphe. Créez un point de contrôle après des appels coûteux au modèle afin qu’un nouvel essai ne reprenne pas le même travail. Pour ReAct + LangGraph : une combinaison idéale. Définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Préférez des unités petites et testables plutôt que des scripts complexes. Lorsqu’une étape échoue, l’échec doit indiquer une seule responsabilité et non un pipeline embrouillé.
Cas d’usage : Assistant à l’annulation d’hôtel (politique + calcul du remboursement)
Pour le cas d’usage « Assistant à l’annulation d’hôtel (politique + calcul du remboursement) », il convient de définir les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Donnez des noms aux artefacts, définites des vérifications de succès et refusez les terminaisons partielles silencieuses. Séparez la planification de l’exécution des outils. Le planificateur propose ; l’exécutant modifie ; le vérificateur compare les résultats à l’objectif.
Énoncé du problème
Pour la description du problème, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Séparez la planification de l’exécution des outils. Le planificateur propose ; l’exécutant modifie ; le vérificateur compare les résultats à l’objectif.
Étape 1 : Installer les dépendances
Séparez la planification de l’exécution des outils. Le planificateur propose ; l’exécutant modifie ; le vérificateur compare les résultats à l’objectif.
pip install -U langgraph langchain langchain-openai
export OPENAI_API_KEY="..."
Étape 2 : Définir les outils (vos « Actions »)
Restreignez strictement les schémas des outils. Des arguments de texte libre trop larges favorisent les injections et rendent les audits coûteux.
from typing import TypedDict, Annotated
from datetime import datetime
import json
from pydantic import BaseModel
from langchain_openai import ChatOpenAI
from langchain_core.messages import (
BaseMessage,
HumanMessage,
ToolMessage,
SystemMessage
)
from langchain_core.tools import tool
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import tools_condition
@tool
def get_cancellation_policy(rate_plan: str) -> str:
"""
Returns cancellation policy text for a given rate plan.
"""
policies = {
"flexible": "Free cancellation until 24 hours before check-in. After that, first night is charged.",
"semi-flex": "Free cancellation until 72 hours before check-in. After that, 50% of the stay is charged.",
"non-refundable": "No refund after booking. Full stay amount is charged on cancellation."
}
key = rate_plan.strip().lower()
return policies.get(key, "Policy not found. Supported: flexible, semi-flex, non-refundable.")
@tool
def calculate_refund(
rate_plan: str,
check_in: str,
cancel_date: str,
nightly_rate: float,
nights: int
) -> str:
"""
Calculates refund amount based on a simplified policy model.
Dates format: YYYY-MM-DD
"""
rp = rate_plan.strip().lower()
ci = datetime.strptime(check_in, "%Y-%m-%d").date()
cd = datetime.strptime(cancel_date, "%Y-%m-%d").date()
total = nightly_rate * nights
days_before = (ci - cd).days
if rp == "non-refundable":
refund = 0.0
charged = total
rule = "Non-refundable: no refund."
elif rp == "flexible":
if days_before >= 1:
refund = total
charged = 0.0
rule = "Flexible: cancelled >= 24h before check-in, full refund."
else:
charged = nightly_rate # 1 night penalty
refund = max(total - charged, 0.0)
rule = "Flexible: late cancel, 1 night charged."
elif rp == "semi-flex":
if days_before >= 3:
refund = total
charged = 0.0
rule = "Semi-flex: cancelled >= 72h before check-in, full refund."
else:
charged = 0.5 * total
refund = total - charged
rule = "Semi-flex: late cancel, 50% charged."
else:
return "Unsupported rate plan. Use: flexible, semi-flex, non-refundable."
return (
f"Rule: {rule}\n"
f"Days before check-in: {days_before}\n"
f"Total: ${total:.2f}\n"
f"Charged: ${charged:.2f}\n"
f"Refund: ${refund:.2f}"
)
Étape 3 : Créer un cycle ReAct dans LangGraph (Raisonnement → Outil → Raisonnement)
Restreignez strictement les schémas des outils. Des arguments de texte libre trop larges favorisent les injections et rendent les audits coûteux.
from typing import TypedDict, Annotated
from langchain_core.messages import BaseMessage, HumanMessage
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langchain_openai import ChatOpenAI
from langchain_core.tools import Tool
from langgraph.prebuilt import ToolNode, tools_condition
# 1) Define state
class AgentState(TypedDict):
messages: Annotated[list[BaseMessage], add_messages]
booking_id: str
# 2) Define structured Output schema
class RefundDecision(BaseModel):
booking_id: str
rate_plan: str
total_amount: float
charged_amount: float
refund_amount: float
policy_summary: str
explanation: str
# 2) Choose model and System prompt
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
SYSTEM_PROMPT = SystemMessage(
content="""
You are a hotel cancellation assistant.
Rules:
- Use tools when needed.
- Never guess policy or refund.
- Final answer MUST be valid JSON with this schema:
{
"booking_id": "...",
"rate_plan": "...",
"total_amount": number,
"charged_amount": number,
"refund_amount": number,
"policy_summary": "...",
"explanation": "..."
}
"""
)
# 3) Register tools
tools = [get_cancellation_policy, calculate_refund]
def safe_tool_node(state):
last_msg = state["messages"][-1]
if not hasattr(last_msg, "tool_calls") or not last_msg.tool_calls:
return {}
tool_call = last_msg.tool_calls[0]
tool_name = tool_call["name"]
if tool_name not in ALLOWED_TOOLS:
return {
"messages": [
ToolMessage(
content=f"Tool '{tool_name}' is not allowed.",
tool_call_id=tool_call["id"]
)
]
}
for tool in tools:
if tool.name == tool_name:
result = tool.invoke(tool_call["args"])
return {
"messages": [
ToolMessage(
content=result,
tool_call_id=tool_call["id"]
)
]
}
# 4)Before reasoning, check if we already processed this booking.
REFUND_MEMORY = {}
def memory_lookup_node(state):
booking_id = state["booking_id"]
if booking_id in REFUND_MEMORY:
return {
"messages": [
HumanMessage(
content=f"Cached decision found:\n{REFUND_MEMORY[booking_id]}"
)
]
}
return {}
# 5) Reasoning node: LLM decides next action (tool call) or final answer
def agent_node(state: AgentState):
# Bind tools so the model can produce tool calls
llm_with_tools = llm.bind_tools(tools)
response = llm_with_tools.invoke(state["messages"])
return {"messages": [response]}
# 6) After final decision, store it.
def memory_write_node(state):
booking_id = state["booking_id"]
final_answer = state["messages"][-1].content
REFUND_MEMORY[booking_id] = final_answer
return {}
Construire et compiler le graphe
Les schémas des outils doivent être strictement définis. Des arguments de texte libre trop larges favorisent les injections et rendent les audits coûteux.
# 7) Build the graph
builder = StateGraph(AgentState)
# Nodes
builder.add_node("memory_lookup", memory_lookup_node)
builder.add_node("agent", agent_node)
builder.add_node("tools", safe_tool_node)
builder.add_node("memory_write", memory_write_node)
# Flow
builder.add_edge(START, "memory_lookup")
builder.add_edge("memory_lookup", "agent")
builder.add_conditional_edges(
"agent",
tools_condition,
{
"tools": "tools", # model wants to act
END: "memory_write" # model finished reasoning
}
)
builder.add_edge("tools", "agent")
builder.add_edge("memory_write", END)
graph = builder.compile()
Étape 4 : Exécuter l’agent sur le cas d’utilisation
Les schémas des outils doivent être strictement définis. Des arguments de texte libre trop larges favorisent les injections et rendent les audits coûteux.
query = """
Booking details:
Rate plan: Non-Refundable
Check-in: 2026-01-20
Nights: 2
Nightly rate: 120
Cancelled on: 2026-01-18
"""
result = graph.invoke({
"booking_id": "BKG-12345",
"messages": [
SYSTEM_PROMPT,
HumanMessage(content=query)
]
})
final_output = result["messages"][-1].content
print(final_output)
Résultat
Les schémas des outils doivent être strictement définis. Des arguments de texte libre trop larges favorisent les injections et rendent les audits coûteux.
{
"booking_id": "BKG-12345",
"rate_plan": "Non-Refundable",
"total_amount": 240.0,
"charged_amount": 240.0,
"refund_amount": 0.0,
"policy_summary": "Non-refundable bookings do not allow refunds after confirmation.",
"explanation": "The booking was made under a non-refundable rate plan, which charges the full stay amount regardless of cancellation timing."
}
Ce qui se passe en interne (comportement ReAct)
Les schémas des outils doivent être strictement définis. Des arguments de texte libre trop larges favorisent les injections et rendent les audits coûteux.
1) Réflexion (Raisonnement)
Les schémas des outils doivent être strictement définis. Des arguments de texte libre trop larges favorisent les injections et rendent les audits coûteux.
2) Action (Appel d’outil)
Les schémas des outils doivent être strictement définis. Des arguments de texte libre trop larges favorisent les injections et rendent les audits coûteux.
3) Observation (Résultats des outils)
Point de contrôle après les appels coûteux au modèle afin qu’un nouvel essai ne facture pas à nouveau le même travail.
4) Réponse finale
Point de contrôle après les appels coûteux au modèle afin qu’un nouvel essai ne facture pas à nouveau le même travail.
Pourquoi c’est « ReAct » (et pas seulement des outils)
Point de contrôle après les appels coûteux au modèle afin qu’un nouvel essai ne facture pas à nouveau le même travail.
Conclusion
Séparer la planification de l’exécution des outils. Le planificateur propose ; l’exécutant modifie ; le vérificateur contrôle les résultats par rapport à l’objectif.
Liste de vérification opérationnelle
Restreindre strictement les schémas des outils. Des arguments de texte libre trop larges facilitent les injections et rendent les audits coûteux.
Les arêtes conditionnelles doivent encoder les règles métier sous forme de fonctions nommées, et non dans du texte d’instruction caché.
Colocaliser les types avec les composants et limiter le nombre de propriétés. Des ensembles de propriétés trop larges deviennent la dette que TypeScript était conçu pour éviter.
Rédigez un petit guide opérationnel : comment faire tourner les clés, comment vider la file d’attente, comment annuler la dernière modification.