fixing parser, tools and executor

This commit is contained in:
João Moura
2024-09-15 18:41:59 -03:00
parent c2795307f6
commit f216b0a78c
98 changed files with 21270 additions and 26437 deletions

View File

@@ -73,6 +73,10 @@ class Agent(BaseAgent):
default=None,
description="Callback to be executed after each step of the agent execution.",
)
use_stop_words: bool = Field(
default=True,
description="Use stop words for the agent.",
)
use_system_prompt: Optional[bool] = Field(
default=True,
description="Use system prompt for the agent.",
@@ -238,6 +242,7 @@ class Agent(BaseAgent):
stop_words=stop_words,
max_iter=self.max_iter,
tools_handler=self.tools_handler,
use_stop_words=self.use_stop_words,
tools_names=self.__tools_names(parsed_tools),
tools_description=self._render_text_description_and_args(parsed_tools),
step_callback=self.step_callback,

View File

@@ -20,7 +20,6 @@ class CrewAgentExecutorMixin:
task: Optional["Task"]
iterations: int
have_forced_answer: bool
max_iter: int
_i18n: I18N
def _should_force_answer(self) -> bool:

View File

@@ -12,7 +12,12 @@ from crewai.utilities.exceptions.context_window_exceeding_exception import (
from crewai.utilities.logger import Logger
from crewai.utilities.training_handler import CrewTrainingHandler
from crewai.llm import LLM
from crewai.agents.parser import AgentAction, AgentFinish, OutputParserException
from crewai.agents.parser import (
AgentAction,
AgentFinish,
OutputParserException,
FINAL_ANSWER_AND_PARSABLE_ACTION_ERROR_MESSAGE,
)
class CrewAgentExecutor(CrewAgentExecutorMixin):
@@ -28,6 +33,7 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
max_iter: int,
tools: List[Any],
tools_names: str,
use_stop_words: bool,
stop_words: List[str],
tools_description: str,
tools_handler: ToolsHandler,
@@ -52,6 +58,7 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
self.tools_handler = tools_handler
self.original_tools = original_tools
self.step_callback = step_callback
self.use_stop_words = use_stop_words
self.tools_description = tools_description
self.function_calling_llm = function_calling_llm
self.respect_context_window = respect_context_window
@@ -73,7 +80,6 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
user_prompt = self._format_prompt(self.prompt.get("prompt", ""), inputs)
self.messages.append(self._format_msg(user_prompt))
self.ask_for_human_input = bool(inputs.get("ask_for_human_input", False))
formatted_answer = self._invoke_loop()
if self.ask_for_human_input:
@@ -92,36 +98,46 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
while not isinstance(formatted_answer, AgentFinish):
if not self.request_within_rpm_limit or self.request_within_rpm_limit():
answer = LLM(
self.llm, stop=self.stop, callbacks=self.callbacks
self.llm,
stop=self.stop if self.use_stop_words else None,
callbacks=self.callbacks,
).call(self.messages)
if not self.use_stop_words:
try:
self._format_answer(answer)
except OutputParserException as e:
if (
FINAL_ANSWER_AND_PARSABLE_ACTION_ERROR_MESSAGE
in e.error
):
answer = answer.split("Observation:")[0].strip()
self.iterations += 1
formatted_answer = self._format_answer(answer)
if isinstance(formatted_answer, AgentAction):
action_result = self._use_tool(formatted_answer)
formatted_answer.text += f"\nObservation: {action_result}"
if self.step_callback:
formatted_answer.result = action_result
self.step_callback(formatted_answer)
if self._should_force_answer():
if self.have_forced_answer:
return {
"output": self._i18n.errors(
return AgentFinish(
output=self._i18n.errors(
"force_final_answer_error"
).format(formatted_answer.text)
}
).format(formatted_answer.text),
text=formatted_answer.text,
)
else:
formatted_answer.text += (
f'\n{self._i18n.errors("force_final_answer")}'
)
self.have_forced_answer = True
self.messages.append(
self._format_msg(formatted_answer.text, role="assistant")
)
except OutputParserException as e:
self.messages.append({"role": "assistant", "content": e.error})
self._invoke_loop(formatted_answer)
@@ -132,7 +148,8 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
):
self._handle_context_length()
self._invoke_loop(formatted_answer)
else:
raise e
return formatted_answer
def _use_tool(self, agent_action: AgentAction) -> Any:

View File

@@ -74,7 +74,7 @@ class CrewAgentParser:
if action_match:
if includes_answer:
raise OutputParserException(
f"{FINAL_ANSWER_AND_PARSABLE_ACTION_ERROR_MESSAGE}: {text}"
f"{FINAL_ANSWER_AND_PARSABLE_ACTION_ERROR_MESSAGE}"
)
action = action_match.group(1)
clean_action = self._clean_action(action)

View File

@@ -300,7 +300,11 @@ class ToolUsage:
return "\n--\n".join(descriptions)
def _is_gpt(self, llm) -> bool:
return False if not llm else "gpt" in llm.lower()
return (
"gpt" in str(llm).lower()
or "o1-preview" in str(llm).lower()
or "o1-mini" in str(llm).lower()
)
def _tool_calling(
self, tool_string: str
@@ -313,7 +317,7 @@ class ToolUsage:
else ToolCalling
)
converter = Converter(
text=f"Only tools available:\n###\n{self._render()}\n\nReturn a valid schema for the tool, the tool name must be exactly equal one of the options, use this text to inform the valid output schema:\n\n{tool_string}```",
text=f"Only tools available:\n###\n{self._render()}\n\nReturn a valid schema for the tool, the tool name must be exactly equal one of the options, use this text to inform the valid output schema:\n\n### TEXT \n{tool_string}",
llm=self.function_calling_llm,
model=model,
instructions=dedent(

View File

@@ -5,15 +5,15 @@
"backstory": "You are a seasoned manager with a knack for getting the best out of your team.\nYou are also known for your ability to delegate work to the right people, and to ask the right questions to get the best out of your team.\nEven though you don't perform tasks by yourself, you have a lot of experience in the field, which allows you to properly evaluate the work of your team members."
},
"slices": {
"observation": "\nObservation",
"observation": "\nObservation:",
"task": "\nCurrent Task: {input}\n\nBegin! This is VERY important to you, use the tools available and give your best Final Answer, your job depends on it!\n\nThought:",
"memory": "\n\n# Useful context: \n{memory}",
"role_playing": "You are {role}. {backstory}\nYour personal goal is: {goal}",
"tools": "\nYou ONLY have access to the following tools, and should NEVER make up tools that are not listed here:\n\n{tools}\n\nUse the following format:\n\nThought: you should always think about what to do\nAction: the action to take, only one name of [{tool_names}], just the name, exactly as it's written.\nAction Input: the input to the action, just a simple python dictionary, enclosed in curly braces, using \" to wrap keys and values.\nObservation: the result of the action\n\nOnce all necessary information is gathered:\n\nThought: I now know the final answer\nFinal Answer: the final answer to the original input question\n",
"no_tools": "\nTo give my best complete final answer to the task use the exact following format:\n\nThought: I now can give a great answer\nFinal Answer: Your final answer must be the great and the most complete as possible, it must be outcome described.\n\nI MUST use these formats, my job depends on it!",
"format": "I MUST either use a tool (use one at time) OR give my best final answer. To Use the following format:\n\nThought: you should always think about what to do\nAction: the action to take, should be one of [{tool_names}]\nAction Input: the input to the action, dictionary enclosed in curly braces\nObservation: the result of the action\n... (this Thought/Action/Action Input/Observation can repeat N times)\nThought: I now can give a great answer\nFinal Answer: Your final answer must be the great and the most complete as possible, it must be outcome described\n\n ",
"format": "I MUST either use a tool (use one at time) OR give my best final answer not both at the same time. To Use the following format:\n\nThought: you should always think about what to do\nAction: the action to take, should be one of [{tool_names}]\nAction Input: the input to the action, dictionary enclosed in curly braces\nObservation: the result of the action\n... (this Thought/Action/Action Input/Result can repeat N times)\nThought: I now can give a great answer\nFinal Answer: Your final answer must be the great and the most complete as possible, it must be outcome described\n\n ",
"final_answer_format": "If you don't need to use any more tools, you must give your best complete final answer, make sure it satisfy the expect criteria, use the EXACT format below:\n\nThought: I now can give a great answer\nFinal Answer: my best complete final answer to the task.\n\n",
"format_without_tools": "\nSorry, I didn't use the right format. I MUST either use a tool (among the available ones), OR give my best final answer.\nI just remembered the expected format I must follow:\n\nQuestion: the input question you must answer\nThought: you should always think about what to do\nAction: the action to take, should be one of [{tool_names}]\nAction Input: the input to the action\nObservation: the result of the action\n... (this Thought/Action/Action Input/Observation can repeat N times)\nThought: I now can give a great answer\nFinal Answer: Your final answer must be the great and the most complete as possible, it must be outcome described\n\n",
"format_without_tools": "\nSorry, I didn't use the right format. I MUST either use a tool (among the available ones), OR give my best final answer.\nI just remembered the expected format I must follow:\n\nQuestion: the input question you must answer\nThought: you should always think about what to do\nAction: the action to take, should be one of [{tool_names}]\nAction Input: the input to the action\nObservation: the result of the action\n... (this Thought/Action/Action Input/Result can repeat N times)\nThought: I now can give a great answer\nFinal Answer: Your final answer must be the great and the most complete as possible, it must be outcome described\n\n",
"task_with_context": "{task}\n\nThis is the context you're working with:\n{context}",
"expected_output": "\nThis is the expect criteria for your final answer: {expected_output}\nyou MUST return the actual complete content as the final answer, not a summary.",
"human_feedback": "You got human feedback on your work, re-evaluate it and give a new Final Answer when ready.\n {human_feedback}",
@@ -23,7 +23,7 @@
"summary": "This is a summary of our conversation so far:\n{merged_summary}"
},
"errors": {
"force_final_answer_error": "I can't keep going, this was the best I could do.\n {formatted_answer.text}",
"force_final_answer_error": "You can't keep going, this was the best you could do.\n {formatted_answer.text}",
"force_final_answer": "Now it's time you MUST give your absolute best final answer. You'll ignore all previous instructions, stop using any tools, and just return your absolute BEST Final answer.",
"agent_tool_unexsiting_coworker": "\nError executing tool. coworker mentioned not found, it must be one of the following options:\n{coworkers}\n",
"task_repeated_usage": "I tried reusing the same input, I must stop using this action input. I'll try something else instead.\n\n",

View File

@@ -1,7 +1,7 @@
from .converter import Converter, ConverterError
from .file_handler import FileHandler
from .i18n import I18N
from .instructor import Instructor
from .internal_instructor import InternalInstructor
from .logger import Logger
from .parser import YamlParser
from .printer import Printer
@@ -16,7 +16,7 @@ __all__ = [
"ConverterError",
"FileHandler",
"I18N",
"Instructor",
"InternalInstructor",
"Logger",
"Printer",
"Prompts",

View File

@@ -61,11 +61,10 @@ class Converter(OutputConverter):
def _create_instructor(self):
"""Create an instructor."""
from crewai.utilities import Instructor
from crewai.utilities import InternalInstructor
inst = Instructor(
inst = InternalInstructor(
llm=self.llm,
max_attempts=self.max_attempts,
model=self.model,
content=self.text,
instructions=self.instructions,
@@ -90,7 +89,11 @@ class Converter(OutputConverter):
@property
def is_gpt(self) -> bool:
"""Return if llm provided is of gpt from openai."""
return "gpt" in str(self.llm).lower()
return (
"gpt" in str(self.llm).lower()
or "o1-preview" in str(self.llm).lower()
or "o1-mini" in str(self.llm).lower()
)
def convert_to_model(
@@ -214,7 +217,11 @@ def get_conversion_instructions(model: Type[BaseModel], llm: Any) -> str:
def is_gpt(llm: Any) -> bool:
"""Return if llm provided is of gpt from openai."""
return "gpt" in str(llm).lower()
return (
"gpt" in str(llm).lower()
or "o1-preview" in str(llm).lower()
or "o1-mini" in str(llm).lower()
)
def create_converter(

View File

@@ -92,7 +92,11 @@ class TaskEvaluator:
return converter.to_pydantic()
def _is_gpt(self, llm) -> bool:
return "gpt" in str(self.llm).lower()
return (
"gpt" in str(self.llm).lower()
or "o1-preview" in str(self.llm).lower()
or "o1-mini" in str(self.llm).lower()
)
def evaluate_training_data(
self, training_data: dict, agent_id: str

View File

@@ -2,29 +2,27 @@ from typing import Any, Optional, Type
import instructor
from litellm import completion
from pydantic import BaseModel, Field, PrivateAttr, model_validator
class Instructor(BaseModel):
class InternalInstructor:
"""Class that wraps an agent llm with instructor."""
_client: Any = PrivateAttr()
content: str = Field(description="Content to be sent to the instructor.")
agent: Optional[Any] = Field(
description="The agent that needs to use instructor.", default=None
)
llm: Optional[str] = Field(
description="The agent that needs to use instructor.", default=None
)
instructions: Optional[str] = Field(
description="Instructions to be sent to the instructor.",
default=None,
)
model: Type[BaseModel] = Field(
description="Pydantic model to be used to create an output."
)
def __init__(
self,
content: str,
model: Type,
agent: Optional[Any] = None,
llm: Optional[str] = None,
instructions: Optional[str] = None,
):
self.content = content
self.agent = agent
self.llm = llm
self.instructions = instructions
self.model = model
self._client = None
self.set_instructor()
@model_validator(mode="after")
def set_instructor(self):
"""Set instructor."""
if self.agent and not self.llm:
@@ -34,7 +32,6 @@ class Instructor(BaseModel):
completion,
mode=instructor.Mode.TOOLS,
)
return self
def to_json(self):
model = self.to_pydantic()
@@ -44,7 +41,6 @@ class Instructor(BaseModel):
messages = [{"role": "user", "content": self.content}]
if self.instructions:
messages.append({"role": "system", "content": self.instructions})
model = self._client.chat.completions.create(
model=self.llm, response_model=self.model, messages=messages
)

View File

@@ -24,26 +24,17 @@ class RPMController(BaseModel):
return self
def check_or_wait(self):
print("check_or_waitcheck_or_waitcheck_or_waitcheck_or_wait")
if self.max_rpm is None:
return True
def _check_and_increment():
print(
"_check_and_increment_check_and_increment_check_and_increment_check_and_increment"
)
if self.max_rpm is not None and self._current_rpm < self.max_rpm:
self._current_rpm += 1
print("111111111111111")
print("self._current_rpm", self._current_rpm)
print("self.max_rpm", self.max_rpm)
return True
elif self.max_rpm is not None:
print("22222222222222")
self.logger.log(
"info", "Max RPM reached, waiting for next minute to start."
)
print("CARALHO")
self._wait_for_next_minute()
self._current_rpm = 1
return True