From cd77981102a8ac0dd1cdf5b481b976f122d86cdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moura?= Date: Mon, 29 Jan 2024 00:11:19 -0300 Subject: [PATCH] Adding support for expected output --- src/crewai/task.py | 24 +++++++++++++++++++++++- src/crewai/translations/el.json | 3 ++- src/crewai/translations/en.json | 3 ++- tests/task_test.py | 23 +++++++++++++++++++++++ 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/src/crewai/task.py b/src/crewai/task.py index 49b7e59fd..8a15588aa 100644 --- a/src/crewai/task.py +++ b/src/crewai/task.py @@ -6,12 +6,14 @@ from pydantic_core import PydanticCustomError from crewai.agent import Agent from crewai.tasks.task_output import TaskOutput +from crewai.utilities import I18N class Task(BaseModel): """Class that represent a task to be executed.""" __hash__ = object.__hash__ + i18n: I18N = I18N() description: str = Field(description="Description of the actual task.") agent: Optional[Agent] = Field( description="Agent responsible for the task.", default=None @@ -20,6 +22,10 @@ class Task(BaseModel): default_factory=list, description="Tools the agent are limited to use for this task.", ) + expected_output: str = Field( + description="Clear definition of expected output for the task.", + default=None, + ) output: Optional[TaskOutput] = Field( description="Task output, it's final result.", default=None ) @@ -54,9 +60,25 @@ class Task(BaseModel): raise Exception( f"The task '{self.description}' has no agent assigned, therefore it can't be executed directly and should be executed in a Crew using a specific process that support that, either consensual or hierarchical." ) + result = self.agent.execute_task( - task=self.description, context=context, tools=self.tools + task=self._prompt(), context=context, tools=self.tools ) self.output = TaskOutput(description=self.description, result=result) return result + + def _prompt(self) -> str: + """Prompt the task. + + Returns: + Prompt of the task. + """ + tasks_slices = [self.description] + + if self.expected_output: + output = self.i18n.slice("expected_output").format( + expected_output=self.expected_output + ) + tasks_slices = [self.description, output] + return "\n".join(tasks_slices) diff --git a/src/crewai/translations/el.json b/src/crewai/translations/el.json index 92a4ade5d..d1018311b 100644 --- a/src/crewai/translations/el.json +++ b/src/crewai/translations/el.json @@ -5,7 +5,8 @@ "memory": "Αυτή είναι η περίληψη της μέχρι τώρα δουλειάς σας:\n{chat_history}", "role_playing": "Είσαι {role}.\n{backstory}\n\nΟ προσωπικός σας στόχος είναι: {goal}", "tools": "ΕΡΓΑΛΕΙΑ:\n------\nΈχετε πρόσβαση μόνο στα ακόλουθα εργαλεία:\n\n{tools}\n\nΓια να χρησιμοποιήσετε ένα εργαλείο, χρησιμοποιήστε την ακόλουθη ακριβώς μορφή:\n\n```\nΣκέψη: Χρειάζεται να χρησιμοποιήσω κάποιο εργαλείο; Ναί\nΔράση: η ενέργεια που πρέπει να γίνει, πρέπει να είναι μία από τις[{tool_names}], μόνο το όνομα.\nΕνέργεια προς εισαγωγή: η είσοδος στη δράση\nΠαρατήρηση: το αποτέλεσμα της δράσης\n```\n\nΌταν έχετε μια απάντηση για την εργασία σας ή εάν δεν χρειάζεται να χρησιμοποιήσετε ένα εργαλείο, ΠΡΕΠΕΙ να χρησιμοποιήσετε τη μορφή:\n\n```\nΣκέψη: Χρειάζεται να χρησιμοποιήσω κάποιο εργαλείο; Οχι\nΤελική απάντηση: [η απάντησή σας εδώ]", - "task_with_context": "{task}\nΑυτό είναι το πλαίσιο με το οποίο εργάζεστε:\n{context}" + "task_with_context": "{task}\nΑυτό είναι το πλαίσιο με το οποίο εργάζεστε:\n{context}", + "expected_output": "Η τελική σας απάντηση πρέπει να είναι: {expected_output}" }, "errors": { "used_too_many_tools": "Έχω χρησιμοποιήσει πάρα πολλά εργαλεία για αυτήν την εργασία. Θα σας δώσω την απόλυτη ΚΑΛΥΤΕΡΗ τελική μου απάντηση τώρα και δεν θα χρησιμοποιήσω άλλα εργαλεία.", diff --git a/src/crewai/translations/en.json b/src/crewai/translations/en.json index 6025d2223..f091f0a81 100644 --- a/src/crewai/translations/en.json +++ b/src/crewai/translations/en.json @@ -5,7 +5,8 @@ "memory": "This is the summary of your work so far:\n{chat_history}", "role_playing": "You are {role}.\n{backstory}\n\nYour personal goal is: {goal}", "tools": "TOOLS:\n------\nYou have access to only the following tools:\n\n{tools}\n\nTo use a tool, please use the exact following format:\n\n```\nThought: Do I need to use a tool? Yes\nAction: the action to take, should be one of [{tool_names}], just the name.\nAction Input: the input to the action\nObservation: the result of the action\n```\n\nWhen you have a response for your task, or if you do not need to use a tool, you MUST use the format:\n\n```\nThought: Do I need to use a tool? No\nFinal Answer: [your response here]", - "task_with_context": "{task}\nThis is the context you're working with:\n{context}" + "task_with_context": "{task}\nThis is the context you're working with:\n{context}", + "expected_output": "Your final answer must be: {expected_output}" }, "errors": { "used_too_many_tools": "I've used too many tools for this task. I'm going to give you my absolute BEST Final answer now and not use any more tools.", diff --git a/tests/task_test.py b/tests/task_test.py index 46bd94852..8d286b32f 100644 --- a/tests/task_test.py +++ b/tests/task_test.py @@ -55,3 +55,26 @@ def test_task_tool_takes_precedence_ove_agent_tools(): ) assert task.tools == [fake_task_tool] + + +def test_task_prompt_includes_expected_output(): + researcher = Agent( + role="Researcher", + goal="Make the best research and analysis on content about AI and AI agents", + backstory="You're an expert researcher, specialized in technology, software engineering, AI and startups. You work as a freelancer and is now working on doing research and analysis for a new customer.", + allow_delegation=False, + ) + + task = Task( + description="Give me a list of 5 interesting ideas to explore for na article, what makes them unique and interesting.", + expected_output="Bullet point list of 5 interesting ideas.", + agent=researcher, + allow_delegation=False, + ) + + from unittest.mock import patch + + with patch.object(Agent, "execute_task") as execute: + execute.return_value = "ok" + task.execute() + execute.assert_called_once_with(task=task._prompt(), context=None, tools=[])