mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-22 14:48:13 +00:00
fixing parser, tools and executor
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user