1. Utiliser `ReActAgent` depuis `llama_index.core.agent.workflow`
Définition
Utilisation de l’agent ReAct préconfiguré :
from llama_index.core.agent.workflow import ReActAgent
agent = ReActAgent.from_tools(tools=[...], llm=..., memory=...)
response = agent.chat("Quel est le prix du bitcoin ?")Caractéristiques
– Agent ReAct prêt à l’emploi avec moteur de raisonnement, formatter et parser intégrés.
– Ne nécessite pas de définition de graphe ou d’étapes personnalisées.
– Moins flexible pour les cas avancés.
Cas d’usage
Approche adaptée pour :
– Prototypage rapide ou démonstration.
– Cas simples de question-réponse avec outils.
– Utilisation sans personnalisation profonde du raisonnement.
2. Hériter de `Workflow` pour construire un agent personnalisé
Définition
Création d’un agent en héritant explicitement de la classe `Workflow` :
from llama_index.core.workflow import Workflow
class ReActAgent(Workflow):
...Caractéristiques
– Permet de définir un graphe explicite d’étapes connectées via `add_step(...)` et `connect(...)`.
– Offre un contrôle total sur la logique de raisonnement, l’injection de mémoire, la gestion du contexte, l’utilisation des outils, etc.
– Autorise l’ajout d’étapes personnalisées (prétraitement, validation, résumés, appels API…).
– Requiert une bonne compréhension des événements (`StartEvent`, `PrepEvent`, etc.) et du moteur `Workflow`.
Voici, par exemple, une écriture de l’étape handle_tool_call qui permet une validation via introspection Pydantic de l’appel aux outils, avant l’étape d’observation (lignes 15 à 49). En effet, ReAct a la mauvaise habitude d’essayer d’utiliser les outils avant de prendre en compte la définition des schéma d’entrée. On gagne du temps en bloquant les appels faits avec des formats erronés.
- @step
- async def handle_tool_calls(
- self, ctx: Context, ev: ToolCallEvent
- ) -> PrepEvent:
- """ Appeller les outils en toute sécurité en gérant les erreurs et en ajoutant leurs résultats au raisonnement en cours. Ensuite, en émettant un PrepEvent, effectuer une nouvelle itération d'invite et d'analyse ReAct.
- """
- tool_calls = ev.tool_calls
- tools_by_name = {tool.metadata.get_name(): tool for tool in self.tools}
- current_reasoning = await ctx.store.get(
- "current_reasoning", default=[]
- )
- sources = await ctx.store.get("sources", default=[])
- '''
- validation anticipée via introspection Pydantic
- On intercepte chaque appel, et pour chaque outil :
- 1. Identifier sa classe d’entrée Pydantic.
- 2. Instancier cette classe avec les tool_kwargs.
- 3. Si la validation échoue → bloquer l’appel et injecter une observation.
- 4. Sinon exécuter normalement.
- '''
- for tool_call in tool_calls:
- tool_name = tool_call.tool_name
- tool = tools_by_name.get(tool_name)
- if not tool:
- current_reasoning.append(
- ObservationReasoningStep(observation=f"Tool {tool_name} does not exist")
- )
- continue
- # Étape 1 : récupérer la classe Pydantic d'entrée
- input_model = getattr(tool.metadata, "input_model", None)
- if input_model:
- try:
- # Étape 2 : valider les kwargs via Pydantic
- input_model(**tool_call.tool_kwargs)
- except ValidationError as ve:
- # Étape 3 : bloquer l’appel et injecter l’erreur
- current_reasoning.append(
- ObservationReasoningStep(
- observation=(
- f"Appel bloqué : les paramètres fournis pour l’outil '{tool_name}' "
- f"sont invalides selon sa définition. Détail : {ve.errors()}"
- )
- )
- )
- continue
- # Étape 4 : poursuivre si tout est valide
- try:
- tool_output = tool(**tool_call.tool_kwargs)
- sources.append(tool_output)
- current_reasoning.append(
- ObservationReasoningStep(observation=tool_output.content)
- )
- except Exception as e:
- current_reasoning.append(
- ObservationReasoningStep(
- observation=f"Error calling tool {tool.metadata.get_name()}: {e}"
- )
- )
Cas d’usage
Approche recommandée pour :
– Architectures modulaires et extensibles.
– Tracabilité et audit du raisonnement étape par étape.
– Utilisation du debugger Python dans le moteur ReAct.
– Intégration de politiques techniques explicites.
– Meilleur contrôle de l’utilisation des outils.
– Développement d’agents complexes ou hybrides.
Tableau comparatif
| Critère | `class ReActAgent(Workflow)` | `ReActAgent` préconstruit |
| Architecture | Personnalisée, modulaire | Préconfigurée |
| Graphe d’étapes | Défini manuellement (`add_step`) | Interne, non modifiable |
| Injection mémoire/contexte | Libre et dynamique | Partielle via `.chat()` |
| Contrôle sur le raisonnement | Total | Limité |
| Extensibilité | Élevée | Faible |
| Complexité de mise en œuvre | Moyenne à élevée | Faible |
| Cas d’usage | Agents complexes, auditables, debuggables | Agents simples, rapides |
Conclusion
L’approche par héritage de `Workflow` est recommandée pour les systèmes robustes, auditables et évolutifs. Elle permet une maîtrise fine du raisonnement, de l’utilisation des outils et une architecture extensible.
L’agent ReAct préconstruit est utile pour des cas simples ou des prototypes rapides, mais offre peu de flexibilité.