Skip to content

Commit 7954f6b

Browse files
authored
Reliability improvements (#77)
* fixing identation for AgentTools * updating gitignore to exclude quick test script * startingprompt translation * supporting individual task output * adding agent to task output * cutting new version * Updating README example
1 parent 234a2c7 commit 7954f6b

File tree

11 files changed

+125
-123
lines changed

11 files changed

+125
-123
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ __pycache__
44
dist/
55
.env
66
assets/*
7-
.idea
7+
.idea
8+
test.py

README.md

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,14 @@ pip install duckduckgo-search
4444
import os
4545
from crewai import Agent, Task, Crew, Process
4646

47+
os.environ["OPENAI_API_KEY"] = "YOUR KEY"
48+
4749
# You can choose to use a local model through Ollama for example.
48-
# In this case we will use OpenHermes 2.5 as an example.
4950
#
5051
# from langchain.llms import Ollama
5152
# ollama_llm = Ollama(model="openhermes")
5253

53-
# If you are using an ollama like above you don't need to set OPENAI_API_KEY.
54-
os.environ["OPENAI_API_KEY"] = "Your Key"
55-
56-
# Define your tools, custom or not.
5754
# Install duckduckgo-search for this example:
58-
#
5955
# !pip install -U duckduckgo-search
6056

6157
from langchain.tools import DuckDuckGoSearchRun
@@ -65,50 +61,54 @@ search_tool = DuckDuckGoSearchRun()
6561
researcher = Agent(
6662
role='Senior Research Analyst',
6763
goal='Uncover cutting-edge developments in AI and data science in',
68-
backstory="""You are a Senior Research Analyst at a leading tech think tank.
69-
Your expertise lies in identifying emerging trends and technologies in AI and
70-
data science. You have a knack for dissecting complex data and presenting
64+
backstory="""You work at a leading tech think tank.
65+
Your expertise lies in identifying emerging trends.
66+
You have a knack for dissecting complex data and presenting
7167
actionable insights.""",
7268
verbose=True,
7369
allow_delegation=False,
7470
tools=[search_tool]
75-
# (optional) llm=ollama_llm, If you wanna use a local modal through Ollama, default is GPT4 with temperature=0.7
76-
71+
# You can pass an optional llm attribute specifying what mode you wanna use.
72+
# It can be a local model through Ollama / LM Studio or a remote
73+
# model like OpenAI, Mistral, Antrophic of others (https://python.langchain.com/docs/integrations/llms/)
74+
#
75+
# Examples:
76+
# llm=ollama_llm # was defined above in the file
77+
# llm=ChatOpenAI(model_name="gpt-3.5", temperature=0.7)
7778
)
7879
writer = Agent(
7980
role='Tech Content Strategist',
8081
goal='Craft compelling content on tech advancements',
81-
backstory="""You are a renowned Tech Content Strategist, known for your insightful
82-
and engaging articles on technology and innovation. With a deep understanding of
83-
the tech industry, you transform complex concepts into compelling narratives.""",
82+
backstory="""You are a renowned Content Strategist, known for
83+
your insightful and engaging articles.
84+
You transform complex concepts into compelling narratives.""",
8485
verbose=True,
85-
# (optional) llm=ollama_llm, If you wanna use a local modal through Ollama, default is GPT4 with temperature=0.7
86-
allow_delegation=True
86+
allow_delegation=True,
87+
# (optional) llm=ollama_llm
8788
)
8889

8990
# Create tasks for your agents
9091
task1 = Task(
9192
description="""Conduct a comprehensive analysis of the latest advancements in AI in 2024.
9293
Identify key trends, breakthrough technologies, and potential industry impacts.
93-
Compile your findings in a detailed report. Your final answer MUST be a full analysis report""",
94+
Your final answer MUST be a full analysis report""",
9495
agent=researcher
9596
)
9697

9798
task2 = Task(
98-
description="""Using the insights from the researcher's report, develop an engaging blog
99+
description="""Using the insights provided, develop an engaging blog
99100
post that highlights the most significant AI advancements.
100101
Your post should be informative yet accessible, catering to a tech-savvy audience.
101-
Aim for a narrative that captures the essence of these breakthroughs and their
102-
implications for the future. Your final answer MUST be the full blog post of at least 3 paragraphs.""",
102+
Make it sound cool, avoid complex words so it doesn't sound like AI.
103+
Your final answer MUST be the full blog post of at least 4 paragraphs.""",
103104
agent=writer
104105
)
105106

106107
# Instantiate your crew with a sequential process
107108
crew = Crew(
108109
agents=[researcher, writer],
109110
tasks=[task1, task2],
110-
verbose=2, # Crew verbose more will let you know what tasks are being worked on, you can set it to 1 or 2 to different logging levels
111-
process=Process.sequential # Sequential process will have tasks executed one after the other and the outcome of the previous one is passed as extra content into this next.
111+
verbose=2, # You can set it to 1 or 2 to different logging levels
112112
)
113113

114114
# Get your crew to work!

crewai/agent.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,9 @@ def __create_agent_executor(self) -> CrewAgentExecutor:
155155
)
156156
executor_args["memory"] = summary_memory
157157
agent_args["chat_history"] = lambda x: x["chat_history"]
158-
prompt = Prompts.TASK_EXECUTION_WITH_MEMORY_PROMPT
158+
prompt = Prompts().task_execution_with_memory()
159159
else:
160-
prompt = Prompts.TASK_EXECUTION_PROMPT
160+
prompt = Prompts().task_execution()
161161

162162
execution_prompt = prompt.partial(
163163
goal=self.goal,

crewai/crew.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -111,21 +111,21 @@ def __sequential_loop(self) -> str:
111111
Returns:
112112
Output of the crew.
113113
"""
114-
task_outcome = None
114+
task_output = None
115115
for task in self.tasks:
116116
# Add delegation tools to the task if the agent allows it
117117
if task.agent.allow_delegation:
118-
tools = AgentTools(agents=self.agents).tools()
119-
task.tools += tools
118+
agent_tools = AgentTools(agents=self.agents).tools()
119+
task.tools += agent_tools
120120

121121
self.__log("debug", f"Working Agent: {task.agent.role}")
122-
self.__log("info", f"Starting Task: {task.description} ...")
122+
self.__log("info", f"Starting Task: {task.description}")
123123

124-
task_outcome = task.execute(task_outcome)
125-
126-
self.__log("debug", f"Task output: {task_outcome}")
127-
128-
return task_outcome
124+
task_output = task.execute(task_output)
125+
self.__log(
126+
"debug", f"\n\n[{task.agent.role}] Task output: {task_output}\n\n"
127+
)
128+
return task_output
129129

130130
def __log(self, level, message):
131131
"""Log a message"""

crewai/prompts.py

Lines changed: 40 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,53 @@
11
"""Prompts for generic agent."""
2-
3-
from textwrap import dedent
4-
from typing import ClassVar
2+
import json
3+
import os
4+
from typing import ClassVar, Dict, Optional
55

66
from langchain.prompts import PromptTemplate
7-
from pydantic import BaseModel
7+
from pydantic import BaseModel, Field, PrivateAttr, model_validator
88

99

1010
class Prompts(BaseModel):
1111
"""Prompts for generic agent."""
1212

13-
TASK_SLICE: ClassVar[str] = dedent(
14-
"""\
15-
Begin! This is VERY important to you, your job depends on it!
16-
17-
Current Task: {input}"""
13+
_prompts: Optional[Dict[str, str]] = PrivateAttr()
14+
language: Optional[str] = Field(
15+
default="en",
16+
description="Language of crewai prompts.",
1817
)
1918

20-
SCRATCHPAD_SLICE: ClassVar[str] = "\n{agent_scratchpad}"
21-
22-
MEMORY_SLICE: ClassVar[str] = dedent(
23-
"""\
24-
This is the summary of your work so far:
25-
{chat_history}"""
26-
)
27-
28-
ROLE_PLAYING_SLICE: ClassVar[str] = dedent(
29-
"""\
30-
You are {role}.
31-
{backstory}
32-
33-
Your personal goal is: {goal}"""
34-
)
35-
36-
TOOLS_SLICE: ClassVar[str] = dedent(
37-
"""\
38-
39-
40-
TOOLS:
41-
------
42-
You have access to the following tools:
43-
44-
{tools}
45-
46-
To use a tool, please use the exact following format:
19+
@model_validator(mode="after")
20+
def load_prompts(self) -> "Prompts":
21+
"""Load prompts from file."""
22+
dir_path = os.path.dirname(os.path.realpath(__file__))
23+
prompts_path = os.path.join(dir_path, f"prompts/{self.language}.json")
4724

48-
```
49-
Thought: Do I need to use a tool? Yes
50-
Action: the action to take, should be one of [{tool_names}], just the name.
51-
Action Input: the input to the action
52-
Observation: the result of the action
53-
```
25+
with open(prompts_path, "r") as f:
26+
self._prompts = json.load(f)["slices"]
27+
return self
5428

55-
When you have a response for your task, or if you do not need to use a tool, you MUST use the format:
56-
57-
```
58-
Thought: Do I need to use a tool? No
59-
Final Answer: [your response here]
60-
```"""
61-
)
62-
63-
VOTING_SLICE: ClassVar[str] = dedent(
64-
"""\
65-
You are working on a crew with your co-workers and need to decide who will execute the task.
66-
67-
These are your format instructions:
68-
{format_instructions}
69-
70-
These are your co-workers and their roles:
71-
{coworkers}"""
72-
)
73-
74-
TASK_EXECUTION_WITH_MEMORY_PROMPT: ClassVar[str] = PromptTemplate.from_template(
75-
ROLE_PLAYING_SLICE + TOOLS_SLICE + MEMORY_SLICE + TASK_SLICE + SCRATCHPAD_SLICE
76-
)
77-
78-
TASK_EXECUTION_PROMPT: ClassVar[str] = PromptTemplate.from_template(
79-
ROLE_PLAYING_SLICE + TOOLS_SLICE + TASK_SLICE + SCRATCHPAD_SLICE
80-
)
29+
SCRATCHPAD_SLICE: ClassVar[str] = "\n{agent_scratchpad}"
8130

82-
CONSENSUNS_VOTING_PROMPT: ClassVar[str] = PromptTemplate.from_template(
83-
ROLE_PLAYING_SLICE + VOTING_SLICE + TASK_SLICE + SCRATCHPAD_SLICE
84-
)
31+
def task_execution_with_memory(self) -> str:
32+
return PromptTemplate.from_template(
33+
self._prompts["role_playing"]
34+
+ self._prompts["tools"]
35+
+ self._prompts["memory"]
36+
+ self._prompts["task"]
37+
+ self.SCRATCHPAD_SLICE
38+
)
39+
40+
def task_execution_without_tools(self) -> str:
41+
return PromptTemplate.from_template(
42+
self._prompts["role_playing"]
43+
+ self._prompts["task"]
44+
+ self.SCRATCHPAD_SLICE
45+
)
46+
47+
def task_execution(self) -> str:
48+
return PromptTemplate.from_template(
49+
self._prompts["role_playing"]
50+
+ self._prompts["tools"]
51+
+ self._prompts["task"]
52+
+ self.SCRATCHPAD_SLICE
53+
)

crewai/prompts/en.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"slices": {
3+
"task": "Begin! This is VERY important to you, your job depends on it!\n\nCurrent Task: {input}",
4+
"memory": "This is the summary of your work so far:\n{chat_history}",
5+
"role_playing": "You are {role}.\n{backstory}\n\nYour personal goal is: {goal}",
6+
"tools": "TOOLS:\n------\nYou have access to 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]"
7+
}
8+
}

crewai/task.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from pydantic_core import PydanticCustomError
66

77
from crewai.agent import Agent
8+
from crewai.tasks.task_output import TaskOutput
89

910

1011
class Task(BaseModel):
@@ -19,6 +20,9 @@ class Task(BaseModel):
1920
default_factory=list,
2021
description="Tools the agent are limited to use for this task.",
2122
)
23+
output: Optional[TaskOutput] = Field(
24+
description="Task output, it's final result.", default=None
25+
)
2226
id: UUID4 = Field(
2327
default_factory=uuid.uuid4,
2428
frozen=True,
@@ -46,9 +50,12 @@ def execute(self, context: str = None) -> str:
4650
Output of the task.
4751
"""
4852
if self.agent:
49-
return self.agent.execute_task(
53+
result = self.agent.execute_task(
5054
task=self.description, context=context, tools=self.tools
5155
)
56+
57+
self.output = TaskOutput(description=self.description, result=result)
58+
return result
5259
else:
5360
raise Exception(
5461
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."

crewai/tasks/task_output.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from typing import Optional
2+
3+
from pydantic import BaseModel, Field, model_validator
4+
5+
6+
class TaskOutput(BaseModel):
7+
"""Class that represents the result of a task."""
8+
9+
description: str = Field(description="Description of the task")
10+
summary: Optional[str] = Field(description="Summary of the task", default=None)
11+
result: str = Field(description="Result of the task")
12+
13+
@model_validator(mode="after")
14+
def set_summary(self):
15+
excerpt = " ".join(self.description.split(" ")[0:10])
16+
self.summary = f"{excerpt}..."
17+
return self

crewai/tools/agent_tools.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99

1010
class AgentTools(BaseModel):
11-
"""Tools for generic agent."""
11+
"""Default tools around agent delegation"""
1212

1313
agents: List[Agent] = Field(description="List of agents in this crew.")
1414

@@ -20,12 +20,12 @@ def tools(self):
2020
description=dedent(
2121
f"""\
2222
Useful to delegate a specific task to one of the
23-
following co-workers: [{', '.join([agent.role for agent in self.agents])}].
24-
The input to this tool should be a pipe (|) separated text of length
25-
three, representing the co-worker you want to ask it to (one of the options),
23+
following co-workers: [{', '.join([agent.role for agent in self.agents])}].
24+
The input to this tool should be a pipe (|) separated text of length
25+
three, representing the co-worker you want to ask it to (one of the options),
2626
the task and all actual context you have for the task.
2727
For example, `coworker|task|context`.
28-
"""
28+
"""
2929
),
3030
),
3131
Tool.from_function(
@@ -34,12 +34,12 @@ def tools(self):
3434
description=dedent(
3535
f"""\
3636
Useful to ask a question, opinion or take from on
37-
of the following co-workers: [{', '.join([agent.role for agent in self.agents])}].
38-
The input to this tool should be a pipe (|) separated text of length
39-
three, representing the co-worker you want to ask it to (one of the options),
37+
of the following co-workers: [{', '.join([agent.role for agent in self.agents])}].
38+
The input to this tool should be a pipe (|) separated text of length
39+
three, representing the co-worker you want to ask it to (one of the options),
4040
the question and all actual context you have for the question.
4141
For example, `coworker|question|context`.
42-
"""
42+
"""
4343
),
4444
),
4545
]

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
[tool.poetry]
33
name = "crewai"
4-
version = "0.1.16"
4+
version = "0.1.23"
55
description = "Cutting-edge framework for orchestrating role-playing, autonomous AI agents. By fostering collaborative intelligence, CrewAI empowers agents to work together seamlessly, tackling complex tasks."
66
authors = ["Joao Moura <[email protected]>"]
77
readme = "README.md"

0 commit comments

Comments
 (0)