-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #169 from crewAIInc/flow-examples
Flow examples
- Loading branch information
Showing
54 changed files
with
7,861 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.env | ||
__pycache__/ | ||
credentials.json |
1,946 changes: 1,946 additions & 0 deletions
1,946
email_auto_responder_flow/Automating_Tasks_with_CrewAI.md
Large diffs are not rendered by default.
Oops, something went wrong.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
# Email Auto Responder Flow | ||
|
||
Welcome to the Email Auto Responder Flow project, powered by [crewAI](https://crewai.com). This example demonstrates how you can leverage Flows from crewAI to automate the process of checking emails and creating draft responses. By utilizing Flows, the process becomes much simpler and more efficient. | ||
|
||
## Background | ||
|
||
In this project, we've taken one of our old example repositories, [CrewAI-LangGraph](https://github.com/crewAIInc/crewAI-examples/tree/main/CrewAI-LangGraph), and repurposed it to now use Flows. This showcases the power and simplicity of Flows in orchestrating AI agents to automate tasks like checking emails and creating drafts. Flows provide a more straightforward and powerful alternative to LangGraph, making it easier to build and manage complex workflows. | ||
|
||
### High-Level Diagram | ||
|
||
Below is a high-level diagram of the Email Auto Responder Flow: | ||
|
||
![High-level Diagram](./Email_Flow.png) | ||
|
||
This diagram illustrates the flow of tasks from fetching new emails to generating draft responses. | ||
|
||
## Overview | ||
|
||
This flow will guide you through the process of setting up an automated email responder. Here's a brief overview of what will happen in this flow: | ||
|
||
1. **Fetch New Emails**: The flow starts by using the `EmailFilterCrew` to check for new emails. It updates the state with any new emails and their IDs. | ||
|
||
2. **Generate Draft Responses**: Once new emails are fetched, the flow formats these emails and uses the `EmailFilterCrew` to generate draft responses for each email. | ||
|
||
This flow is a great example of using Flows as a background worker that runs continuously to help you out. By following this flow, you can efficiently automate the process of checking emails and generating draft responses, leveraging the power of multiple AI agents to handle different aspects of the email processing workflow. | ||
|
||
## Installation | ||
|
||
Ensure you have Python >=3.10 <=3.13 installed on your system. This project uses [Poetry](https://python-poetry.org/) for dependency management and package handling, offering a seamless setup and execution experience. | ||
|
||
First, if you haven't already, install Poetry: | ||
|
||
```bash | ||
pip install poetry | ||
``` | ||
|
||
Next, navigate to your project directory and install the dependencies: | ||
|
||
1. First lock the dependencies and then install them: | ||
|
||
```bash | ||
crewai install | ||
``` | ||
|
||
### Customizing & Dependencies | ||
|
||
**Add your `OPENAI_API_KEY` into the `.env` file** | ||
**Add your `SERPER_API_KEY` into the `.env` file** | ||
**Add your `TAVILY_API_KEY` into the `.env` file** | ||
**Add your `MY_EMAIL` into the `.env` file** | ||
|
||
To customize the behavior of the email auto responder, you can update the agents and tasks defined in the `EmailFilterCrew`. If you want to adjust the flow itself, you will need to modify the flow in `main.py`. | ||
|
||
- **Agents and Tasks**: Modify `src/email_auto_responder_flow/crews/email_filter_crew/email_filter_crew.py` to define your agents and tasks. This is where you can customize how emails are filtered and how draft responses are generated. | ||
|
||
- **Flow Adjustments**: Modify `src/email_auto_responder_flow/main.py` to adjust the flow. This is where you can change how the flow orchestrates the different crews and tasks. | ||
|
||
### Setting Up Google Credentials | ||
|
||
To enable the email auto responder to access your Gmail account, you need to set up a `credentials.json` file. Follow these steps: | ||
|
||
1. **Set Up Google Account**: Follow the [Google instructions](https://developers.google.com/gmail/api/quickstart/python#authorize_credentials_for_a_desktop_application) to set up your Google account and obtain the `credentials.json` file. | ||
|
||
2. **Download and Place `credentials.json`**: Once you’ve downloaded the file, name it `credentials.json` and place it in the root of the project. | ||
|
||
## Running the Project | ||
|
||
To kickstart your crew of AI agents and begin task execution, run this from the root folder of your project: | ||
|
||
```bash | ||
crewai run | ||
``` | ||
|
||
This command initializes the email_auto_responder_flow, assembling the agents and assigning them tasks as defined in your configuration. | ||
|
||
When you kickstart the flow, it will orchestrate multiple crews to perform the tasks. The flow will first fetch new emails, then create and run a crew to generate draft responses. | ||
|
||
## Understanding Your Flow | ||
|
||
The email_auto_responder_flow is composed of multiple AI agents, each with unique roles, goals, and tools. These agents collaborate on a series of tasks, defined in `config/tasks.yaml`, leveraging their collective skills to achieve complex objectives. The `config/agents.yaml` file outlines the capabilities and configurations of each agent in your flow. | ||
|
||
### Flow Structure | ||
|
||
1. **EmailFilterCrew**: This crew is responsible for checking for new emails and updating the state with any new emails and their IDs. | ||
|
||
2. **Generate Draft Responses**: Once new emails are fetched, this step formats the emails and uses the `EmailFilterCrew` to generate draft responses for each email. | ||
|
||
By understanding the flow structure, you can see how multiple crews are orchestrated to work together, each handling a specific part of the email processing workflow. This modular approach allows for efficient and scalable email automation. | ||
|
||
## Support | ||
|
||
For support, questions, or feedback regarding the Email Auto Responder Flow or crewAI: | ||
|
||
- Visit our [documentation](https://docs.crewai.com) | ||
- Reach out to us through our [GitHub repository](https://github.com/joaomdmoura/crewai) | ||
- [Join our Discord](https://discord.com/invite/X4JWnZnxPb) | ||
- [Chat with our docs](https://chatg.pt/DWjSBZn) | ||
|
||
Let's create wonders together with the power and simplicity of crewAI. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
[tool.poetry] | ||
name = "email_auto_responder_flow" | ||
version = "0.1.0" | ||
description = "email_auto_responder_flow using crewAI" | ||
authors = ["Your Name <you@example.com>"] | ||
|
||
[tool.poetry.dependencies] | ||
python = ">=3.10,<=3.13" | ||
crewai = { extras = ["tools"], version = ">=0.67.1,<1.0.0" } | ||
asyncio = "*" | ||
langchain-tools = "^0.1.34" | ||
crewai-tools = "^0.12.0" | ||
google-auth-oauthlib = "^1.2.1" | ||
google-api-python-client = "^2.145.0" | ||
|
||
[tool.poetry.scripts] | ||
email_auto_responder_flow = "email_auto_responder_flow.main:main" | ||
run_flow = "email_auto_responder_flow.main:main" | ||
plot_flow = "email_auto_responder_flow.main:plot" | ||
|
||
[build-system] | ||
requires = ["poetry-core"] | ||
build-backend = "poetry.core.masonry.api" |
Empty file.
9 changes: 9 additions & 0 deletions
9
...o_responder_flow/src/email_auto_responder_flow/crews/email_filter_crew/config/agents.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
email_followup_agent: | ||
role: > | ||
HR Coordinator | ||
goal: > | ||
Compose personalized follow-up emails to candidates based on their bio and whether they are being pursued for the job. | ||
If we are proceeding, request availability for a Zoom call. Otherwise, send a polite rejection email. | ||
backstory: > | ||
You are an HR professional with excellent communication skills and a talent for crafting personalized and thoughtful | ||
emails to job candidates. You understand the importance of maintaining a positive and professional tone in all correspondence. |
26 changes: 26 additions & 0 deletions
26
...to_responder_flow/src/email_auto_responder_flow/crews/email_filter_crew/config/tasks.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
send_followup_email: | ||
description: > | ||
Compose personalized follow-up emails for candidates who applied to a specific job. | ||
You will use the candidate's name, bio, and whether the company wants to proceed with them to generate the email. | ||
If the candidate is proceeding, ask them for their availability for a Zoom call in the upcoming days. | ||
If not, send a polite rejection email. | ||
CANDIDATE DETAILS | ||
----------------- | ||
Candidate ID: {candidate_id} | ||
Name: {name} | ||
Bio: | ||
{bio} | ||
PROCEEDING WITH CANDIDATE: {proceed_with_candidate} | ||
ADDITIONAL INSTRUCTIONS | ||
----------------------- | ||
- If we are proceeding, ask for their availability for a Zoom call within the next few days. | ||
- If we are not proceeding, send a polite rejection email, acknowledging their effort in applying and appreciating their time. | ||
expected_output: > | ||
A personalized email based on the candidate's information. It should be professional and respectful, | ||
either inviting them for a Zoom call or letting them know we are pursuing other candidates. | ||
agent: email_followup_agent |
77 changes: 77 additions & 0 deletions
77
...responder_flow/src/email_auto_responder_flow/crews/email_filter_crew/email_filter_crew.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
from crewai import Agent, Crew, Process, Task | ||
from crewai.project import CrewBase, agent, crew, task | ||
from crewai_tools import SerperDevTool | ||
from langchain_community.tools.gmail.get_thread import GmailGetThread | ||
from langchain_community.tools.tavily_search import TavilySearchResults | ||
from langchain_openai import ChatOpenAI | ||
|
||
from email_auto_responder_flow.tools.create_draft import CreateDraftTool | ||
|
||
|
||
@CrewBase | ||
class EmailFilterCrew: | ||
"""Email Filter Crew""" | ||
|
||
agents_config = "config/agents.yaml" | ||
tasks_config = "config/tasks.yaml" | ||
llm = ChatOpenAI(model="gpt-4o") | ||
|
||
@agent | ||
def email_filter_agent(self) -> Agent: | ||
search_tool = SerperDevTool() | ||
return Agent( | ||
config=self.agents_config["email_filter_agent"], | ||
tools=[search_tool], | ||
llm=self.llm, | ||
verbose=True, | ||
allow_delegation=True, | ||
) | ||
|
||
@agent | ||
def email_action_agent(self) -> Agent: | ||
gmail = GmailGetThread() | ||
return Agent( | ||
config=self.agents_config["email_action_agent"], | ||
llm=self.llm, | ||
verbose=True, | ||
tools=[ | ||
GmailGetThread(api_resource=gmail.api_resource), | ||
TavilySearchResults(), | ||
], | ||
) | ||
|
||
@agent | ||
def email_response_writer(self) -> Agent: | ||
gmail = GmailGetThread() | ||
return Agent( | ||
config=self.agents_config["email_response_writer"], | ||
llm=self.llm, | ||
verbose=True, | ||
tools=[ | ||
TavilySearchResults(), | ||
GmailGetThread(api_resource=gmail.api_resource), | ||
CreateDraftTool.create_draft, | ||
], | ||
) | ||
|
||
@task | ||
def filter_emails_task(self) -> Task: | ||
return Task(config=self.tasks_config["filter_emails"]) | ||
|
||
@task | ||
def action_required_emails_task(self) -> Task: | ||
return Task(config=self.tasks_config["action_required_emails"]) | ||
|
||
@task | ||
def draft_responses_task(self) -> Task: | ||
return Task(config=self.tasks_config["draft_responses"]) | ||
|
||
@crew | ||
def crew(self) -> Crew: | ||
"""Creates the Email Filter Crew""" | ||
return Crew( | ||
agents=self.agents, | ||
tasks=self.tasks, | ||
process=Process.sequential, | ||
verbose=True, | ||
) |
73 changes: 73 additions & 0 deletions
73
email_auto_responder_flow/src/email_auto_responder_flow/main.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
#!/usr/bin/env python | ||
import asyncio | ||
import time | ||
from typing import List | ||
|
||
from crewai.flow.flow import Flow, listen, start | ||
from pydantic import BaseModel | ||
|
||
from email_auto_responder_flow.types import Email | ||
from email_auto_responder_flow.utils.emails import check_email, format_emails | ||
|
||
from .crews.email_filter_crew.email_filter_crew import EmailFilterCrew | ||
|
||
|
||
class AutoResponderState(BaseModel): | ||
emails: List[Email] = [] | ||
checked_emails_ids: set[str] = set() | ||
|
||
|
||
class EmailAutoResponderFlow(Flow[AutoResponderState]): | ||
initial_state = AutoResponderState | ||
|
||
@start("wait_next_run") | ||
def fetch_new_emails(self): | ||
print("Kickoff the Email Filter Crew") | ||
new_emails, updated_checked_email_ids = check_email( | ||
checked_emails_ids=self.state.checked_emails_ids | ||
) | ||
|
||
self.state.emails = new_emails | ||
self.state.checked_emails_ids = updated_checked_email_ids | ||
|
||
@listen(fetch_new_emails) | ||
def generate_draft_responses(self): | ||
print("Current email queue: ", len(self.state.emails)) | ||
if len(self.state.emails) > 0: | ||
print("Writing New emails") | ||
emails = format_emails(self.state.emails) | ||
|
||
EmailFilterCrew().crew().kickoff(inputs={"emails": emails}) | ||
|
||
self.state.emails = [] | ||
|
||
print("Waiting for 180 seconds") | ||
time.sleep(180) | ||
|
||
|
||
async def run_flow(): | ||
""" | ||
Run the flow. | ||
""" | ||
email_auto_response_flow = EmailAutoResponderFlow() | ||
email_auto_response_flow.kickoff() | ||
|
||
|
||
async def plot_flow(): | ||
""" | ||
Plot the flow. | ||
""" | ||
email_auto_response_flow = EmailAutoResponderFlow() | ||
email_auto_response_flow.plot() | ||
|
||
|
||
def main(): | ||
asyncio.run(run_flow()) | ||
|
||
|
||
def plot(): | ||
asyncio.run(plot_flow()) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
20 changes: 20 additions & 0 deletions
20
email_auto_responder_flow/src/email_auto_responder_flow/tools/create_draft.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
from langchain.tools import tool | ||
from langchain_community.agent_toolkits import GmailToolkit | ||
from langchain_community.tools.gmail.create_draft import GmailCreateDraft | ||
|
||
|
||
class CreateDraftTool: | ||
@tool("Create Draft") | ||
def create_draft(data): | ||
""" | ||
Useful to create an email draft. | ||
The input to this tool should be a pipe (|) separated text | ||
of length 3 (three), representing who to send the email to, | ||
the subject of the email and the actual message. | ||
For example, `lorem@ipsum.com|Nice To Meet You|Hey it was great to meet you.`. | ||
""" | ||
email, subject, message = data.split("|") | ||
gmail = GmailToolkit() | ||
draft = GmailCreateDraft(api_resource=gmail.api_resource) | ||
result = draft({"to": [email], "subject": subject, "message": message}) | ||
return f"\nDraft created: {result}\n" |
8 changes: 8 additions & 0 deletions
8
email_auto_responder_flow/src/email_auto_responder_flow/types.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from pydantic import BaseModel | ||
|
||
|
||
class Email(BaseModel): | ||
id: str | ||
threadId: str | ||
snippet: str | ||
sender: str |
Oops, something went wrong.