From ca08a34a9869d0044254822fcba11299ea02a7c7 Mon Sep 17 00:00:00 2001 From: Harrison Chase Date: Mon, 1 May 2023 22:05:42 -0700 Subject: [PATCH] retry to parsing (#3696) --- langchain/agents/agent.py | 78 +++++++++++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 12 deletions(-) diff --git a/langchain/agents/agent.py b/langchain/agents/agent.py index ca382031bb..51972d69f9 100644 --- a/langchain/agents/agent.py +++ b/langchain/agents/agent.py @@ -17,7 +17,9 @@ from langchain.base_language import BaseLanguageModel from langchain.callbacks.base import BaseCallbackManager from langchain.callbacks.manager import ( AsyncCallbackManagerForChainRun, + AsyncCallbackManagerForToolRun, CallbackManagerForChainRun, + CallbackManagerForToolRun, Callbacks, ) from langchain.chains.base import Chain @@ -578,6 +580,25 @@ class Agent(BaseSingleActionAgent): } +class ExceptionTool(BaseTool): + name = "_Exception" + description = "Exception tool" + + def _run( + self, + query: str, + run_manager: Optional[CallbackManagerForToolRun] = None, + ) -> str: + return query + + async def _arun( + self, + query: str, + run_manager: Optional[AsyncCallbackManagerForToolRun] = None, + ) -> str: + return query + + class AgentExecutor(Chain): """Consists of an agent using tools.""" @@ -587,6 +608,7 @@ class AgentExecutor(Chain): max_iterations: Optional[int] = 15 max_execution_time: Optional[float] = None early_stopping_method: str = "force" + handle_parsing_errors: bool = False @classmethod def from_agent_and_tools( @@ -714,12 +736,28 @@ class AgentExecutor(Chain): Override this to take control of how the agent makes and acts on choices. """ - # Call the LLM to see what to do. - output = self.agent.plan( - intermediate_steps, - callbacks=run_manager.get_child() if run_manager else None, - **inputs, - ) + try: + # Call the LLM to see what to do. + output = self.agent.plan( + intermediate_steps, + callbacks=run_manager.get_child() if run_manager else None, + **inputs, + ) + except Exception as e: + if not self.handle_parsing_errors: + raise e + text = str(e).split("`")[1] + observation = "Invalid or incomplete response" + output = AgentAction("_Exception", observation, text) + tool_run_kwargs = self.agent.tool_run_logging_kwargs() + observation = ExceptionTool().run( + output.tool, + verbose=self.verbose, + color=None, + callbacks=run_manager.get_child() if run_manager else None, + **tool_run_kwargs, + ) + return [(output, observation)] # If the tool chosen is the finishing tool, then we end and return. if isinstance(output, AgentFinish): return output @@ -772,12 +810,28 @@ class AgentExecutor(Chain): Override this to take control of how the agent makes and acts on choices. """ - # Call the LLM to see what to do. - output = await self.agent.aplan( - intermediate_steps, - callbacks=run_manager.get_child() if run_manager else None, - **inputs, - ) + try: + # Call the LLM to see what to do. + output = await self.agent.aplan( + intermediate_steps, + callbacks=run_manager.get_child() if run_manager else None, + **inputs, + ) + except Exception as e: + if not self.handle_parsing_errors: + raise e + text = str(e).split("`")[1] + observation = "Invalid or incomplete response" + output = AgentAction("_Exception", observation, text) + tool_run_kwargs = self.agent.tool_run_logging_kwargs() + observation = await ExceptionTool().arun( + output.tool, + verbose=self.verbose, + color=None, + callbacks=run_manager.get_child() if run_manager else None, + **tool_run_kwargs, + ) + return [(output, observation)] # If the tool chosen is the finishing tool, then we end and return. if isinstance(output, AgentFinish): return output