Upload 4 files
Browse files- crewai/agents/exceptions.py +7 -3
- crewai/agents/executor.py +90 -7
- crewai/agents/output_parser.py +6 -7
crewai/agents/exceptions.py
CHANGED
@@ -5,15 +5,19 @@ class TaskRepeatedUsageException(OutputParserException):
|
|
5 |
"""Exception raised when a task is used twice in a roll."""
|
6 |
|
7 |
error: str = "TaskRepeatedUsageException"
|
8 |
-
message: str = "
|
9 |
|
10 |
-
def __init__(self, tool: str, tool_input: str):
|
|
|
11 |
self.tool = tool
|
12 |
self.tool_input = tool_input
|
13 |
self.message = self.message.format(tool=tool, tool_input=tool_input)
|
14 |
|
15 |
super().__init__(
|
16 |
-
error=self.error,
|
|
|
|
|
|
|
17 |
)
|
18 |
|
19 |
def __str__(self):
|
|
|
5 |
"""Exception raised when a task is used twice in a roll."""
|
6 |
|
7 |
error: str = "TaskRepeatedUsageException"
|
8 |
+
message: str = "I just used the {tool} tool with input {tool_input}. So I already know the result of that and don't need to use it now.\n"
|
9 |
|
10 |
+
def __init__(self, tool: str, tool_input: str, text: str):
|
11 |
+
self.text = text
|
12 |
self.tool = tool
|
13 |
self.tool_input = tool_input
|
14 |
self.message = self.message.format(tool=tool, tool_input=tool_input)
|
15 |
|
16 |
super().__init__(
|
17 |
+
error=self.error,
|
18 |
+
observation=self.message,
|
19 |
+
send_to_llm=True,
|
20 |
+
llm_output=self.text,
|
21 |
)
|
22 |
|
23 |
def __str__(self):
|
crewai/agents/executor.py
CHANGED
@@ -1,4 +1,6 @@
|
|
1 |
-
|
|
|
|
|
2 |
|
3 |
from langchain.agents import AgentExecutor
|
4 |
from langchain.agents.agent import ExceptionTool
|
@@ -6,13 +8,85 @@ from langchain.agents.tools import InvalidTool
|
|
6 |
from langchain.callbacks.manager import CallbackManagerForChainRun
|
7 |
from langchain_core.agents import AgentAction, AgentFinish, AgentStep
|
8 |
from langchain_core.exceptions import OutputParserException
|
|
|
9 |
from langchain_core.tools import BaseTool
|
|
|
10 |
|
11 |
from ..tools.cache_tools import CacheTools
|
12 |
from .cache.cache_hit import CacheHit
|
13 |
|
14 |
|
15 |
class CrewAgentExecutor(AgentExecutor):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
def _iter_next_step(
|
17 |
self,
|
18 |
name_to_tool_map: Dict[str, BaseTool],
|
@@ -34,6 +108,14 @@ class CrewAgentExecutor(AgentExecutor):
|
|
34 |
callbacks=run_manager.get_child() if run_manager else None,
|
35 |
**inputs,
|
36 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
except OutputParserException as e:
|
38 |
if isinstance(self.handle_parsing_errors, bool):
|
39 |
raise_error = not self.handle_parsing_errors
|
@@ -70,6 +152,11 @@ class CrewAgentExecutor(AgentExecutor):
|
|
70 |
callbacks=run_manager.get_child() if run_manager else None,
|
71 |
**tool_run_kwargs,
|
72 |
)
|
|
|
|
|
|
|
|
|
|
|
73 |
yield AgentStep(action=output, observation=observation)
|
74 |
return
|
75 |
|
@@ -90,12 +177,8 @@ class CrewAgentExecutor(AgentExecutor):
|
|
90 |
color_mapping[tool.name] = color_mapping[action.tool]
|
91 |
|
92 |
actions: List[AgentAction]
|
93 |
-
if isinstance(output, AgentAction)
|
94 |
-
|
95 |
-
else:
|
96 |
-
actions = output
|
97 |
-
for agent_action in actions:
|
98 |
-
yield agent_action
|
99 |
for agent_action in actions:
|
100 |
if run_manager:
|
101 |
run_manager.on_agent_action(agent_action, color="green")
|
|
|
1 |
+
import time
|
2 |
+
from textwrap import dedent
|
3 |
+
from typing import Any, Dict, Iterator, List, Optional, Tuple, Union
|
4 |
|
5 |
from langchain.agents import AgentExecutor
|
6 |
from langchain.agents.agent import ExceptionTool
|
|
|
8 |
from langchain.callbacks.manager import CallbackManagerForChainRun
|
9 |
from langchain_core.agents import AgentAction, AgentFinish, AgentStep
|
10 |
from langchain_core.exceptions import OutputParserException
|
11 |
+
from langchain_core.pydantic_v1 import root_validator
|
12 |
from langchain_core.tools import BaseTool
|
13 |
+
from langchain_core.utils.input import get_color_mapping
|
14 |
|
15 |
from ..tools.cache_tools import CacheTools
|
16 |
from .cache.cache_hit import CacheHit
|
17 |
|
18 |
|
19 |
class CrewAgentExecutor(AgentExecutor):
|
20 |
+
iterations: int = 0
|
21 |
+
max_iterations: Optional[int] = 15
|
22 |
+
force_answer_max_iterations: Optional[int] = None
|
23 |
+
|
24 |
+
@root_validator()
|
25 |
+
def set_force_answer_max_iterations(cls, values: Dict) -> Dict:
|
26 |
+
values["force_answer_max_iterations"] = values["max_iterations"] - 2
|
27 |
+
return values
|
28 |
+
|
29 |
+
def _should_force_answer(self) -> bool:
|
30 |
+
return True if self.iterations == self.force_answer_max_iterations else False
|
31 |
+
|
32 |
+
def _force_answer(self, output: AgentAction):
|
33 |
+
return AgentStep(
|
34 |
+
action=output,
|
35 |
+
observation=dedent(
|
36 |
+
"""\
|
37 |
+
I've used too many tools for this task.
|
38 |
+
I'm going to give you my absolute BEST Final answer now and
|
39 |
+
not use any more tools."""
|
40 |
+
),
|
41 |
+
)
|
42 |
+
|
43 |
+
def _call(
|
44 |
+
self,
|
45 |
+
inputs: Dict[str, str],
|
46 |
+
run_manager: Optional[CallbackManagerForChainRun] = None,
|
47 |
+
) -> Dict[str, Any]:
|
48 |
+
"""Run text through and get agent response."""
|
49 |
+
# Construct a mapping of tool name to tool for easy lookup
|
50 |
+
name_to_tool_map = {tool.name: tool for tool in self.tools}
|
51 |
+
# We construct a mapping from each tool to a color, used for logging.
|
52 |
+
color_mapping = get_color_mapping(
|
53 |
+
[tool.name for tool in self.tools], excluded_colors=["green", "red"]
|
54 |
+
)
|
55 |
+
intermediate_steps: List[Tuple[AgentAction, str]] = []
|
56 |
+
# Let's start tracking the number of iterations and time elapsed
|
57 |
+
self.iterations = 0
|
58 |
+
time_elapsed = 0.0
|
59 |
+
start_time = time.time()
|
60 |
+
# We now enter the agent loop (until it returns something).
|
61 |
+
while self._should_continue(self.iterations, time_elapsed):
|
62 |
+
next_step_output = self._take_next_step(
|
63 |
+
name_to_tool_map,
|
64 |
+
color_mapping,
|
65 |
+
inputs,
|
66 |
+
intermediate_steps,
|
67 |
+
run_manager=run_manager,
|
68 |
+
)
|
69 |
+
if isinstance(next_step_output, AgentFinish):
|
70 |
+
return self._return(
|
71 |
+
next_step_output, intermediate_steps, run_manager=run_manager
|
72 |
+
)
|
73 |
+
|
74 |
+
intermediate_steps.extend(next_step_output)
|
75 |
+
if len(next_step_output) == 1:
|
76 |
+
next_step_action = next_step_output[0]
|
77 |
+
# See if tool should return directly
|
78 |
+
tool_return = self._get_tool_return(next_step_action)
|
79 |
+
if tool_return is not None:
|
80 |
+
return self._return(
|
81 |
+
tool_return, intermediate_steps, run_manager=run_manager
|
82 |
+
)
|
83 |
+
self.iterations += 1
|
84 |
+
time_elapsed = time.time() - start_time
|
85 |
+
output = self.agent.return_stopped_response(
|
86 |
+
self.early_stopping_method, intermediate_steps, **inputs
|
87 |
+
)
|
88 |
+
return self._return(output, intermediate_steps, run_manager=run_manager)
|
89 |
+
|
90 |
def _iter_next_step(
|
91 |
self,
|
92 |
name_to_tool_map: Dict[str, BaseTool],
|
|
|
108 |
callbacks=run_manager.get_child() if run_manager else None,
|
109 |
**inputs,
|
110 |
)
|
111 |
+
if self._should_force_answer():
|
112 |
+
if isinstance(output, AgentAction):
|
113 |
+
output = output
|
114 |
+
else:
|
115 |
+
output = output.action
|
116 |
+
yield self._force_answer(output)
|
117 |
+
return
|
118 |
+
|
119 |
except OutputParserException as e:
|
120 |
if isinstance(self.handle_parsing_errors, bool):
|
121 |
raise_error = not self.handle_parsing_errors
|
|
|
152 |
callbacks=run_manager.get_child() if run_manager else None,
|
153 |
**tool_run_kwargs,
|
154 |
)
|
155 |
+
|
156 |
+
if self._should_force_answer():
|
157 |
+
yield self._force_answer(output)
|
158 |
+
return
|
159 |
+
|
160 |
yield AgentStep(action=output, observation=observation)
|
161 |
return
|
162 |
|
|
|
177 |
color_mapping[tool.name] = color_mapping[action.tool]
|
178 |
|
179 |
actions: List[AgentAction]
|
180 |
+
actions = [output] if isinstance(output, AgentAction) else output
|
181 |
+
yield from actions
|
|
|
|
|
|
|
|
|
182 |
for agent_action in actions:
|
183 |
if run_manager:
|
184 |
run_manager.on_agent_action(agent_action, color="green")
|
crewai/agents/output_parser.py
CHANGED
@@ -52,24 +52,23 @@ class CrewAgentOutputParser(ReActSingleInputOutputParser):
|
|
52 |
regex = (
|
53 |
r"Action\s*\d*\s*:[\s]*(.*?)[\s]*Action\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)"
|
54 |
)
|
55 |
-
action_match
|
56 |
-
if action_match:
|
57 |
action = action_match.group(1).strip()
|
58 |
action_input = action_match.group(2)
|
59 |
tool_input = action_input.strip(" ")
|
60 |
tool_input = tool_input.strip('"')
|
61 |
|
62 |
-
last_tool_usage
|
63 |
-
if last_tool_usage:
|
64 |
usage = {
|
65 |
"tool": action,
|
66 |
"input": tool_input,
|
67 |
}
|
68 |
if usage == last_tool_usage:
|
69 |
-
raise TaskRepeatedUsageException(
|
|
|
|
|
70 |
|
71 |
-
result
|
72 |
-
if result:
|
73 |
action = AgentAction(action, tool_input, text)
|
74 |
return CacheHit(action=action, cache=self.cache)
|
75 |
|
|
|
52 |
regex = (
|
53 |
r"Action\s*\d*\s*:[\s]*(.*?)[\s]*Action\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)"
|
54 |
)
|
55 |
+
if action_match := re.search(regex, text, re.DOTALL):
|
|
|
56 |
action = action_match.group(1).strip()
|
57 |
action_input = action_match.group(2)
|
58 |
tool_input = action_input.strip(" ")
|
59 |
tool_input = tool_input.strip('"')
|
60 |
|
61 |
+
if last_tool_usage := self.tools_handler.last_used_tool:
|
|
|
62 |
usage = {
|
63 |
"tool": action,
|
64 |
"input": tool_input,
|
65 |
}
|
66 |
if usage == last_tool_usage:
|
67 |
+
raise TaskRepeatedUsageException(
|
68 |
+
tool=action, tool_input=tool_input, text=text
|
69 |
+
)
|
70 |
|
71 |
+
if result := self.cache.read(action, tool_input):
|
|
|
72 |
action = AgentAction(action, tool_input, text)
|
73 |
return CacheHit(action=action, cache=self.cache)
|
74 |
|