Adding new tool usage and parsing logic

This commit is contained in:
João Moura
2024-02-19 22:43:10 -03:00
parent 6da94c1bba
commit 3cfc8dd4e0
9 changed files with 256 additions and 101 deletions

View File

@@ -1,3 +1,4 @@
from .cache.cache_handler import CacheHandler
from .executor import CrewAgentExecutor
from .parser import CrewAgentParser
from .tools_handler import ToolsHandler

View File

@@ -18,7 +18,7 @@ from crewai.utilities import I18N
class CrewAgentExecutor(AgentExecutor):
i18n: I18N = I18N()
_i18n: I18N = I18N()
llm: Any = None
iterations: int = 0
task: Any = None
@@ -105,14 +105,12 @@ class CrewAgentExecutor(AgentExecutor):
"""
try:
intermediate_steps = self._prepare_intermediate_steps(intermediate_steps)
# 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,
)
if self._should_force_answer():
if isinstance(output, AgentAction) or isinstance(output, AgentFinish):
output = output
@@ -121,7 +119,7 @@ class CrewAgentExecutor(AgentExecutor):
f"Unexpected output type from agent: {type(output)}"
)
yield AgentStep(
action=output, observation=self.i18n.errors("force_final_answer")
action=output, observation=self._i18n.errors("force_final_answer")
)
return
@@ -140,14 +138,14 @@ class CrewAgentExecutor(AgentExecutor):
text = str(e)
if isinstance(self.handle_parsing_errors, bool):
if e.send_to_llm:
observation = str(e.observation)
observation = f"\n{str(e.observation)}"
text = str(e.llm_output)
else:
observation = "Invalid or incomplete response"
elif isinstance(self.handle_parsing_errors, str):
observation = self.handle_parsing_errors
observation = f"\n{self.handle_parsing_errors}"
elif callable(self.handle_parsing_errors):
observation = self.handle_parsing_errors(e)
observation = f"\n{self.handle_parsing_errors(e)}"
else:
raise ValueError("Got unexpected type of `handle_parsing_errors`")
output = AgentAction("_Exception", observation, text)
@@ -164,7 +162,7 @@ class CrewAgentExecutor(AgentExecutor):
if self._should_force_answer():
yield AgentStep(
action=output, observation=self.i18n.errors("force_final_answer")
action=output, observation=self._i18n.errors("force_final_answer")
)
return
@@ -183,27 +181,26 @@ class CrewAgentExecutor(AgentExecutor):
if run_manager:
run_manager.on_agent_action(agent_action, color="green")
# Otherwise we lookup the tool
if agent_action.tool in name_to_tool_map:
tool = name_to_tool_map[agent_action.tool]
return_direct = tool.return_direct
color_mapping[agent_action.tool]
tool_run_kwargs = self.agent.tool_run_logging_kwargs()
if return_direct:
tool_run_kwargs["llm_prefix"] = ""
observation = ToolUsage(
tools_handler=self.tools_handler,
tools=self.tools,
tools_description=self.tools_description,
tools_names=self.tools_names,
function_calling_llm=self.function_calling_llm,
llm=self.llm,
task=self.task,
).use(agent_action.log)
tool_usage = ToolUsage(
tools_handler=self.tools_handler,
tools=self.tools,
tools_description=self.tools_description,
tools_names=self.tools_names,
function_calling_llm=self.function_calling_llm,
llm=self.llm,
task=self.task,
)
tool_calling = tool_usage.parse(agent_action.log)
if tool_calling.tool_name.lower().strip() in [
name.lower().strip() for name in name_to_tool_map
]:
observation = tool_usage.use(tool_calling, agent_action.log)
else:
tool_run_kwargs = self.agent.tool_run_logging_kwargs()
observation = InvalidTool().run(
{
"requested_tool_name": agent_action.tool,
"requested_tool_name": tool_calling.tool_name,
"available_tool_names": list(name_to_tool_map.keys()),
},
verbose=self.verbose,

View File

@@ -0,0 +1,61 @@
from typing import Union
from langchain.agents.output_parsers import ReActSingleInputOutputParser
from langchain_core.agents import AgentAction, AgentFinish
from langchain_core.exceptions import OutputParserException
from crewai.utilities import I18N
TOOL_USAGE_SECTION = "Use Tool:"
FINAL_ANSWER_ACTION = "Final Answer:"
FINAL_ANSWER_AND_TOOL_ERROR_MESSAGE = "You are trying to use a tool and give a final answer at the same time, choose only one."
class CrewAgentParser(ReActSingleInputOutputParser):
"""Parses Crew-style LLM calls that have a single tool input.
Expects output to be in one of two formats.
If the output signals that an action should be taken,
should be in the below format. This will result in an AgentAction
being returned.
```
Use Tool: All context for using the tool here
```
If the output signals that a final answer should be given,
should be in the below format. This will result in an AgentFinish
being returned.
```
Final Answer: The temperature is 100 degrees
```
"""
_i18n: I18N = I18N()
def parse(self, text: str) -> Union[AgentAction, AgentFinish]:
includes_answer = FINAL_ANSWER_ACTION in text
includes_tool = TOOL_USAGE_SECTION in text
if includes_tool:
if includes_answer:
raise OutputParserException(f"{FINAL_ANSWER_AND_TOOL_ERROR_MESSAGE}")
return AgentAction("", "", text)
elif includes_answer:
return AgentFinish(
{"output": text.split(FINAL_ANSWER_ACTION)[-1].strip()}, text
)
error = self._i18n.errors("unexpected_format")
format = self._i18n.slice("format_without_tools")
error = f"{error}\n{format}"
raise OutputParserException(
error,
observation=error,
llm_output=text,
send_to_llm=True,
)