ReAct : Question posée par l’agent

, par Bertrand Degoy

Au cours du raisonnement, le LLM peut avoir besoin d’une donnée que seul l’utilisateur peut fournir.
Pour ne pas sortir du processus sous peine de perdre l’état interne du raisonnement, la bonne solution est d’utiliser un outil ’ask_user’.

Position du problème

Il y a des questions posées par l’utilisateur comportant implicitement une connaissance que seul cet utilisateur connaît. Par exemple l’endroit où l’utilisateur se trouve alors que la question est "à quelle distance se trouve Paris". L’agent devra poser une question telle que " Où te trouves tu ? ".

Une difficulté réside dans le fait que l’agent ReAct de Llama-index est statique (il ne peut gérer la persistance de son état interne). Au moment de l’interruption pour retourner vers l’utilisateur avec la question, il ne faut pas sortir du processus (ne pas terminer le raisonnement) sous peine de perdre son état interne.

Solution

Une bonne méthode est de mettre à la disposition du LLM un outil ’ask_user’ que le raisonnement pourra intégrer sans nécessiter l’arrêt.

Puisque que nous devons rester dans la boucle ReAct, c’est là que nous devons y insérer un nouvel échange system -> user -> system. Dans la classe ReActAgent :

  1. async def handle_tool_call_results(
  2.         self, ctx: Context, results: List[ToolCallResult], memory: BaseMemory
  3.     ) -> None:
  4.         """Handle tool call results for React agent."""
  5.         current_reasoning: list[BaseReasoningStep] = await ctx.store.get(
  6.             self.reasoning_key, default=[]
  7.         )
  8.         for tool_call_result in results:
  9.             ...  
  10.             # Question user
  11.             if tool_call_result.tool_name == "ask_user":
  12.                 current_reasoning.append(
  13.                     ResponseReasoningStep(
  14.                         thought="I need to ask the user a question.",
  15.                         response=obs_step.observation,  # contient la question
  16.                         is_streaming=False,
  17.                     )
  18.                 )
  19.                 break
  20.             ...
  21.         await ctx.store.set(self.reasoning_key, current_reasoning)

Télécharger

Pour assurer la cohérence entre l’outil et son usage, nous avons confié à la classe ReActAgent le soin de fournir l’outil ask_user :

  1. @staticmethod
  2.     def get_ask_user_tool() -> FunctionTool:  
  3.         """
  4.            Retourne un outil permettant au modèle de poser une question à l'utilisateur.
  5.        """
  6.    
  7.         def ask_user(question: str) -> str:
  8.             """Ask the user a question and wait for their answer; use this when information is missing to proceed.
  9.            Example:
  10.                Thought: I need the user's client number to proceed.
  11.                Action: ask_user
  12.                Action Input: "Quel est votre numéro de client ?"
  13.            """
  14.             return f"[USER_INPUT_REQUIRED] {question}"
  15.  
  16.         ask_user_tool = FunctionTool.from_defaults(
  17.             fn=ask_user,
  18.             name="ask_user",
  19.             description= "Ask the user with a question and wait for their answer.",
  20.             return_direct=True,
  21.         )
  22.        
  23.         return ask_user_tool

Télécharger

Et dans l’Orchestrateur, au moment de la construction de la liste des outils :

  1.  # Ajouter l'outil de dialogue avec l'utilisateur
  2.         try:
  3.             ask_user = ReActAgent.get_ask_user_tool()  
  4.             tools.append(ask_user)
  5.         except:
  6.             pass

Télécharger