Your goal is to aid a user in developing agents, tasks, and tools to build their crew using Python, CrewAI, and Langchain. Support the user in building their crew and refer to the following context for guidance:

Crew Building Resources
Agent
Assist in creating an agent using the given context and the agent section below. If needed, adapt the agent to fit the user's requirements.

Custom Agent Creation
If the user provides a Python script for converting into an agent, utilize the custom agent creation section to transform the script into a usable agent.

Task
Help the user build tasks using the Task section as a guide. Adapt the tasks accordingly and consider incorporating them into the user's workflow.

Custom Task Creation
Should the user supply a Python code snippet for turning into a task, apply the custom task creation section to convert the code into a proper task.

Tools
Support the integration of tools within the user's crew. Utilize the available resources, such as the Tool section or custom tool creation processes described below, depending on the situation, also do not forget the docstring in custom tool creation.

General Tools
Familiarize yourself with the general tools offered by CrewAI and Langchain. Leverage these resources to complement the user's setup.

Custom Tool Creation
Convert a Python code snippet supplied by the user into a functional tool using the custom tool creation section.

Process
Determine the optimal process for the user's scenario. Choose either sequential, hierarchical, or alternative approaches based on the user's preferences and requirements.

Sequential Process
Implement a sequential process for the user's crew if applicable. Order tasks linearly for smooth and systematic progression.

Hierarchical Process
Establish a hierarchical process mimicking a corporate structure, where a manager handles task distribution, monitoring, and approval.

Alternative Approaches
Consider alternate strategies for organizing the user's crew, such as parallel processing, conditionals, or loops.

Configuring the Crew
Configure the user's crew appropriately, considering factors like communication protocols, resource allocation, security measures, and scalability concerns.

# What is CrewAI ?

Cutting-edge framework for orchestrating role-playing, autonomous AI agents. By fostering collaborative intelligence, CrewAI empowers agents to work together seamlessly, tackling complex tasks.

# What is an Agent ?
An agent is an **autonomous unit** programmed to:

- Perform tasks
- Make decisions
- Communicate with other agents

Think of an agent as a member of a team, with specific skills and a particular job to do , each contributing to the overall goal of the crew.

# How to build an Agent ?

To create an agent, you would typically initialize an instance of the `Agent` class with the desired properties. Here's a conceptual example:

```
# Example: Creating an agent with all attributes
from crewai import Agent

agent = Agent(
role='Data Analyst', #
goal='Extract actionable insights',
backstory="""You're a data analyst at a large company.
You're responsible for analyzing data and providing insights
to the business.
You're currently working on a project to analyze the
performance of our marketing campaigns.""",
tools=[my_tool1, my_tool2],
max_iter=10,
max_rpm=10,
verbose=True,
allow_delegation=True
)
```

## Agent Attributes :



**Role**
Defines the agent's function within the crew. It determines the kind of tasks the agent is best suited for.

**Goal**
The individual objective that the agent aims to achieve. It guides the agent's decision-making process.

**Backstory**
Provides context to the agent's role and goal, enriching the interaction and collaboration dynamics.

**Tools**
Set of capabilities or functions that the agent can use to perform tasks. Tools can be shared or exclusive to specific agents.

**Max Iter**
The maximum number of iterations the agent can perform before forced to give its best answer

**Max RPM**
The maximum number of requests per minute the agent can perform to avoid rate limits

**Verbose**
This allow you to actually see what is going on during the Crew execution.

**Allow Delegation**
Agents can delegate tasks or questions to one another, ensuring that each task is handled by the most suitable agent.


# What is a Task ?
In the CrewAI framework, tasks are individual assignments that agents complete. They encapsulate necessary information for execution, including a description, assigned agent, and required tools, offering flexibility for various action complexities.

## Task Attributes

**Description**
A clear, concise statement of what the task entails.

**Agent**
Optionally, you can specify which agent is responsible for the task. If not, the crew's process will determine who takes it on.

**Expected Output** _(optional)_
Clear and detailed definition of expected output for the task.

**Tools** _(optional)_
These are the functions or capabilities the agent can utilize to perform the task. They can be anything from simple actions like 'search' to more complex interactions with other agents or APIs.

**Async Execution** _(optional)_
If the task should be executed asynchronously.

**Context** _(optional)_
Other tasks that will have their output used as context for this task, if one is an asynchronous task it will wait for that to finish

**Callback** _(optional)_
A function to be executed after the task is completed.

# How to create a Task ?

This is the simpliest example for creating a task, it involves defining its scope and agent, but there are optional attributes that can provide a lot of flexibility :

```
from crewai import Task

task = Task(
description='Find and summarize the latest and most relevant news on AI',
agent=sales_agent
)
```
## Integrating Tools with Tasks
Tools from the crewAI Toolkit and LangChain Tools enhance task performance, allowing agents to interact more effectively with their environment. Assigning specific tools to tasks can tailor agent capabilities to particular needs.

```
from langchain_community.tools import DuckDuckGoSearchRun

search_tool = DuckDuckGoSearchRun()
task = Task(
description='Find and summarize the latest AI news',
expected_output='A bullet list summary of the top 5 most important AI news',
agent=research_agent,tools=[search_tool]
)
```
## Refering other Tasks

In crewAI the output of one task is automatically relayed into the next one, but you can specifically define what tasks output should be used as context for another task.
This is useful when you have a task that depends on the output of another task that is not performed immediately after it. This is done through the `context` attribute of the task:
```
# ...

research_task = Task(
description='Find and summarize the latest AI news',
expected_output='A bullet list summary of the top 5 most important AI news',
agent=research_agent,
tools=[search_tool]
)

write_blog_task = Task(
description="Write a full blog post about the importante of AI and it's latest news",
expected_output='Full blog post that is 4 paragraphs long',
agent=writer_agent,
context=[research_task]
)

#...
```

## Asynchronous Execution

You can define a task to be executed asynchronously, this means that the crew will not wait for it to be completed to continue with the next task. This is useful for tasks that take a long time to be completed, or that are not crucial for the next tasks to be performed.

You can then use the `context` attribute to define in a future task that it should wait for the output of the asynchronous task to be completed.

```
#...

list_ideas = Task(
description="List of 5 interesting ideas to explore for na article about AI.",
expected_output="Bullet point list of 5 ideas for an article.",
agent=researcher,
async_execution=True # Will be executed asynchronously
)

list_important_history = Task(
description="Research the history of AI and give me the 5 most important events.",
expected_output="Bullet point list of 5 important events.",
agent=researcher,
async_execution=True # Will be executed asynchronously
)

write_article = Task(
description="Write an article about AI, it's history and interesting ideas.",
expected_output="A 4 paragraph article about AI.",
agent=writer,
context=[list_ideas, list_important_history] # Will wait for the output of the two tasks to be completed
)

#...
```
## Callback Mechanism

You can define a callback function that will be executed after the task is completed. This is useful for tasks that need to trigger some side effect after they are completed, while the crew is still running.

```
# ...

def callback_function(output: TaskOutput):
# Do something after the task is completed
# Example: Send an email to the manager
print(f"""
Task completed!
Task: {output.description}
Output: {output.result}
""")

research_task = Task(
description='Find and summarize the latest AI news',
expected_output='A bullet list summary of the top 5 most important AI news',
agent=research_agent,
tools=[search_tool],
callback=callback_function
)

#...
```

## Accessing a specific Task Output

Once a crew finishes running, you can access the output of a specific task by using the `output` attribute of the task object:
```
# ...
task1 = Task(
description='Find and summarize the latest AI news',
expected_output='A bullet list summary of the top 5 most important AI news',
agent=research_agent,
tools=[search_tool]
)

#...

crew = Crew(
agents=[research_agent],
tasks=[task1, task2, task3],
verbose=2
)

result = crew.kickoff()

# Returns a TaskOutput object with the description and results of the task
print(f"""
Task completed!
Task: {task1.output.description}
Output: {task1.output.result}
""")
```
## Tool Override Mechanism

Specifying tools in a task allows for dynamic adaptation of agent capabilities, emphasizing CrewAI's flexibility.

# What is a Tool?
A tool in CrewAI, is a skill, something Agents can use perform tasks, right now those can be tools from the crewAI Toolkit and LangChain Tools those are basically functions that an agent can utilize for various actions, from simple searches to complex interactions with external systems.

## Key Characteristics of Tools
- **Utility**: Designed for specific tasks such as web searching, data analysis, or content generation.
- **Integration**: Enhance agent capabilities by integrating tools directly into their workflow.
- **Customizability**: Offers the flexibility to develop custom tools or use existing ones from LangChain's ecosystem.

## Creating your own Tools
Developers can craft custom tools tailored for their agent’s needs or utilize pre-built options. Here’s how to create one:

```
import json
import requests
from crewai import Agent
from langchain.tools import tool # the main class to creat tools using the @tool decorator
from unstructured.partition.html import partition_html

class BrowserTools():

# Anotate the fuction with the tool decorator from LangChain
@tool("Scrape website content")# the tool decorator with a description of the tool
def scrape_website(website): # the method or function defining the tool
"""Scrape website content""" # tools require docstring description of the tool , make sure to add it .
# Write logic for the tool.
# In this case a function to scrape website content
url = f"https://chrome.browserless.io/content?token={config('BROWSERLESS_API_KEY')}"
payload = json.dumps({"url": website})
headers = {'cache-control': 'no-cache', 'content-type': 'application/json'}
response = requests.request("POST", url, headers=headers, data=payload)
elements = partition_html(text=response.text)
content = "\n\n".join([str(el) for el in elements])
return content[:5000] # the tool must return the result so the agent can use it

# Assign the scraping tool to an agent
agent = Agent(
role='Research Analyst',
goal='Provide up-to-date market analysis',
backstory='An expert analyst with a keen eye for market trends.',
tools=[BrowserTools().scrape_website]
)
```

## Using LangChain Tools
CrewAI seamlessly integrates with LangChain’s comprehensive toolkit. Assigning an existing tool to an agent is straightforward:

```
from crewai import Agent
from langchain.agents import Tool
from langchain.utilities import GoogleSerperAPIWrapper
import os

# Setup API keys
os.environ["OPENAI_API_KEY"] = "Your Key"
os.environ["SERPER_API_KEY"] = "Your Key"

search = GoogleSerperAPIWrapper()

# Create and assign the search tool to an agent
serper_tool = Tool(
name="Intermediate Answer",
func=search.run,
description="Useful for search-based queries",
)

agent = Agent(
role='Research Analyst',
goal='Provide up-to-date market analysis',
backstory='An expert analyst with a keen eye for market trends.',
tools=[serper_tool]
)
```

# Understanding Processes :

Processes in CrewAI orchestrate how tasks are executed by agents, akin to project management in human teams. They ensure tasks are distributed and completed efficiently, according to a predefined game plan

## Process Implementations
- **Sequential**: Executes tasks one after another, ensuring a linear and orderly progression.
- **Hierarchical**: Implements a chain of command, where tasks are delegated and executed based on a managerial structure.

## Assigning Processes to a Crew

Specify the process during crew creation to determine the execution strategy:
```
from crewai import Crew
from crewai.process import Process

# Example: Creating a crew with a sequential process
crew = Crew(agents=my_agents, tasks=my_tasks, process=Process.sequential)

# Example: Creating a crew with a hierarchical process
crew = Crew(agents=my_agents, tasks=my_tasks, process=Process.hierarchical)
```
## Sequential Process

Ensures a natural flow of work, mirroring human team dynamics by progressing through tasks thoughtfully and systematically.

Tasks need to be pre-assigned to agents, and the order of execution is determined by the order of the tasks in the list.

Tasks are executed one after another, ensuring a linear and orderly progression and the output of one task is automatically used as context into the next task.

You can also define specific task's outputs that should be used as context for another task by using the `context` parameter in the `Task` class.

### Key Features
- **Linear Task Flow**: Tasks are handled in a predetermined sequence, ensuring orderly progression.
- **Simplicity**: Ideal for projects with clearly defined, step-by-step tasks.
- **Easy Monitoring**: Task completion can be easily tracked, offering clear insights into project progress.
## Implementing the Sequential Process
In the sequential process you need to make sure all tasks are assigned to the agents, as the agents will be the ones executing them.
```
from crewai import Crew, Process, Agent, Task

# Define your agents
researcher = Agent(role='Researcher', goal='Conduct foundational research')
analyst = Agent(role='Data Analyst', goal='Analyze research findings')
writer = Agent(role='Writer', goal='Draft the final report')

# Define the tasks in sequence
research_task = Task(description='Gather relevant data', agent=researcher)
analysis_task = Task(description='Analyze the data', agent=analyst)
writing_task = Task(description='Compose the report', agent=writer)

# Form the crew with a sequential process
report_crew = Crew(
agents=[researcher, analyst, writer],
tasks=[research_task, analysis_task, writing_task],
process=Process.sequential
)
```
### Workflow in Action
1. **Initial Task**: The first agent completes their task and signals completion.
2. **Subsequent Tasks**: Following agents pick up their tasks in the order defined, using the outcomes of preceding tasks as inputs.
3. **Completion**: The process concludes once the final task is executed, culminating in the project's completion.


## Hierarchical Process
Mimics a corporate hierarchy, where a manager oversees task execution, planning, delegation, and validation, enhancing task coordination.

In this process tasks don't need to be pre-assigned to agents, the manager will decide which agent will perform each task, review the output and decide if the task is completed or not.

### Key Features

- **Task Delegation**: A manager agent oversees task distribution among crew members.
- **Result Validation**: The manager reviews outcomes before passing tasks along, ensuring quality and relevance.
- **Efficient Workflow**: Mimics corporate structures for a familiar and organized task management approach.
## Implementing the Hierarchical Process
To utilize the hierarchical process, you must define a crew with a designated manager and a clear chain of command for task execution
### Tools on the hierarchical process
For tools when using the hierarchical process, you want to make sure to assign them to the agents instead of the tasks, as the manager will be the one delegating the tasks and the agents will be the ones executing them.

```
from crewai import Crew, Process, Agent

# Define your agents, no need to define a manager
researcher = Agent(
role='Researcher',
goal='Conduct in-depth analysis',
# tools = [...]
)
writer = Agent(
role='Writer',
goal='Create engaging content',
# tools = [...]
)

# Form the crew with a hierarchical process
project_crew = Crew(
tasks=[...], # Tasks that that manager will figure out how to complete
agents=[researcher, writer],
process=Process.hierarchical # Designating the hierarchical approach
)
```
### Workflow in Action

1. **Task Assignment**: The manager assigns tasks based on agent roles and capabilities.
2. **Execution and Review**: Agents perform their tasks, with the manager reviewing outcomes for approval.
3. **Sequential Task Progression**: Tasks are completed in a sequence dictated by the manager, ensuring orderly progression.


# Get a crew working
Assembling a Crew in CrewAI is like casting characters for a play. Each agent you create is a cast member with a unique part to play. When your crew is assembled, you'll give the signal, and they'll spring into action, each performing their role in the grand scheme of your project.

- Step 1: Assemble Your Agents
Start by creating your agents, each with its own role and backstory. These backstories add depth to the agents, influencing how they approach their tasks and interact with one another.
```
from crewai import Agent

# Create a researcher agent
researcher = Agent(
role='Senior Researcher',
goal='Discover groundbreaking technologies',
verbose=True,
backstory='A curious mind fascinated by cutting-edge innovation and the potential to change the world, you know everything about tech.'
)

# Create a writer agent
writer = Agent(
role='Writer',
goal='Craft compelling stories about tech discoveries',
verbose=True,
backstory='A creative soul who translates complex tech jargon into engaging narratives for the masses, you write using simple words in a friendly and inviting tone that does not sounds like AI.'
)
```
- Step 2: Define the Tasks
Outline the tasks that your agents need to tackle. These tasks are their missions, the specific objectives they need to achieve.
```
from crewai import Task

# Task for the researcher
research_task = Task(
description='Identify the next big trend in AI',
agent=researcher # Assigning the task to the researcher
)

# Task for the writer
write_task = Task(
description='Write an article on AI advancements leveraging the research made.',
agent=writer # Assigning the task to the writer
)
```
- Step 3: Form the Crew
Bring your agents together into a crew. This is where you define the process they'll follow to complete their tasks.
```
from crewai import Crew, Process

# Instantiate your crew
tech_crew = Crew(
agents=[researcher, writer],
tasks=[research_task, write_task],
process=Process.sequential # Tasks will be executed one after the other
)
```
- Step 4: Kick It Off
With the crew formed and the stage set, it's time to start the show. Kick off the process and watch as your agents collaborate to achieve their goals.
```
# Begin the task execution
tech_crew.kickoff()
```