Automating Email Responses Using CrewAI

Abhiraj Suresh Last Updated : 16 Dec, 2024
13 min read

Yet another holiday season has arrived. It’s indeed the most wonderful time of the year. It’s also that time of the year when working professionals set the same old out-of-office reply to every email they get. Well, the problem with automating email responses this way is that it gives the same flavourless replies to all the emails – both relevant and irrelevant ones. This is when even adults start wishing Santa would gift them an email workflow optimisation solution or an AI email assistant that gives smart replies. Well, this year, Santa has come dressed as CrewAI! In this blog, we’ll learn about automating email responses by building an agentic AI system with CrewAI to reply to your out-of-office emails smartly and ensure smart email management.

Understanding the Context

First, let’s try to understand the context of our problem statement.

Gmail inbox | Automating Email Responses Using CrewAI

This screenshot captures the essence of the problem. If you look closely, you will find emails where my direct intervention is required, and then you will find emails with subscribed newsletters and calendar notifications that do not require any reply.

The existing ‘Vacation Responder’ responds to all the messages with no capability to change the name of the recipient or the contents of the mail based on who it is responding to. Also, it responds to irrelevant emails, which include newsletters, verification code emails, OTP emails, etc.

Out-of-office reply on Gmail | Automating Email Responses Using CrewAI

This is where the CrewAI framework comes to the rescue for email response management. With CrewAI, you can quickly build an email responder agent system, with some simple coding. Relieved? So, let’s build an agentic AI system for automating email responses with CrewAI and bring a layer of optimisation to your email workflow.

Also Read: Automating Email Sorting and Labelling with CrewAI

Google Authentication

Before we jump to the code for automating email responses in Gmail, you need to enable the Gmail API and generate the OAuth 2.0 credentials. This will give your email responder agentic system access to your emails. Here’s how to get this done.

Step 1: Create a New Project in Google Cloud

Visit the Google Cloud console and log in with your email address. First-time users will need to create an account.

Then select “New Project” in the dropdown, give it a name, and click Create. This project will have the required API-related configurations. While adding the new project, choose your organisation name as the location, as we have selected analyticsvidhya.com.

Create New Project | Automating Email Responses Using CrewAI
Select a resource | Automating Email Responses Using CrewAI
New project details | Automating Email Responses Using CrewAI

Step 2: Enable Gmail API

Click the Navigation Menu from the console’s Dashboard and head to Explore and Enable APIs under the Getting Started section.

AI Email Assistant for out-of-office reply
AI Email Assistant for out-of-office reply

On the left-hand side of the screen, select Library, and search for “Gmail API”. Enable it for the project you created.

AI Email Assistant
AI Email Assistant for out-of-office reply
AI Email Assistant for out-of-office reply

Step 3: Set Up OAuth 2.0 Credentials

Next, set up the OAuth consent screen under APIs & Services. Then click Configure Consent Screen.

AI Email Assistant
AI Email Assistant

Choose the type (e.g., External for apps used by anyone). We will chose Internal since we are using it for our own email ID. Then click Create.

AI Email Assistant

Then, name your app and add the User support email and Developer contact information. Here’s where you should add your work email ID. Once done, click on SAVE AND CONTINUE at the bottom of the screen.

AI Email Assistant for out-of-office reply

Now, we need to define the scopes in the consent screen setup. Scopes, in the context of Google Console, dictate what the API can access. For email-related tasks, you’ll need the following: ‘https://www.googleapis.com/auth/gmail.modify‘. This scope will allow the email responder system to send and modify emails in your Gmail account. Click on ADD OR REMOVE SCOPES and then select the scope mentioned above.

AI Email Assistant for out-of-office reply
AI Email Assistant for out-of-office reply

Then click on Update. You can see that the scope has been added. Press SAVE AND CONTINUE.

AI Email Assistant for out-of-office reply

Now go through the summary, and then click BACK TO DASHBOARD.

AI Email Assistant for out-of-office reply

Step 4: Create Credentials

Now choose Credentials under APIs & Services and click CREATE CREDENTIALS.

CrewAI Email Management for workflow optimization

Then select OAuth client ID.

CrewAI Email Management for workflow optimization

For local development, we will choose the Desktop App option, and then press CREATE.

CrewAI Email Management for workflow optimization

Step 5: Download the Credential.json

Now download the JSON file and save it locally at your preferred location.

CrewAI Email Management for workflow optimization

And that concludes our use of Google Search Console.

Get SerperDevTool API

To enable CrewAI agents to perform web searches and information retrieval from the internet, it will need the API to the SerperDev Tool. The SerperDev Tool is a Python utility that interfaces with the Serper API, a cost-effective and rapid Google Search API. It enables developers to programmatically retrieve and process Google Search results, including answer boxes, knowledge graphs, and organic listings.

CrewAI Email Management for workflow optimization

Let’s go through the steps to get the API.

  • Go to serper.dev and click on Sign up.
  • Create your account and login. You will see the Dashboard once you login.
CrewAI Email Management for workflow optimization
  • On the left of the screen, click on API Key.
CrewAI Email Management for workflow optimization
CrewAI Email Management for workflow optimization

Now, let’s jump to the Python code and build our AI email assistant for automated email responses.

Python Code for Automating Email Responses

Step 1: Import Necessary Libraries

We will begin by importing the relevant libraries to build an agentic system for automating email responses.

# Importing necessary libraries
import os  # Provides functions to interact with the operating system
# Importing modules from the CrewAI framework
# CrewAI is a framework for managing agents, tasks, processes, and tools.
from crewai import Agent, Task, Crew, Process  # Manage agents, tasks, and processes
from crewai_tools import SerperDevTool  # Tool from CrewAI for connecting to Google search
# Importing modules for Google OAuth2 authentication and API interaction
from google.auth.transport.requests import Request  # To handle token refresh requests
from google.oauth2.credentials import Credentials  # To manage OAuth2 credentials
from google_auth_oauthlib.flow import InstalledAppFlow  # To handle OAuth2 login flows
from googleapiclient.discovery import build  # To create service objects for Google APIs
# Importing modules for email creation
import base64  # To encode email messages in base64
from email.mime.text import MIMEText  # To create MIME-compliant email messages

Step 2: Set Scopes

Now, let’s set the SCOPES variable that defines permissions for the Gmail API. ‘gmail_modify’ allows reading, sending, and modifying emails, excluding permanent deletion, ensuring limited access.

# Gmail API setup
SCOPES = ['https://www.googleapis.com/auth/gmail.modify']

Next, we create the get_gmail_service function that authenticates and connects to the Gmail API. It checks for saved credentials in token.json, refreshing them if expired. If unavailable, it initiates a new login flow using credentials.json. It saves valid credentials for reuse, and returns a Gmail API service object for email operations.

# Function to authenticate and connect gmail API

def get_gmail_service():

    creds = None

    if os.path.exists('token.json'):

        creds = Credentials.from_authorized_user_file('token.json', SCOPES)

    if not creds or not creds.valid:

        if creds and creds.expired and creds.refresh_token:

            creds.refresh(Request())

        else:

            flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)

            creds = flow.run_local_server(port=0)

        with open('token.json', 'w') as token:

            token.write(creds.to_json())

    return build('gmail', 'v1', credentials=creds)

Step 3: Set Up Email Retrieval

Then, we create the get_unread_emails function to retrieve unread emails from the Gmail inbox. It uses the Gmail API service object to list messages with the labels ‘INBOX’ and ‘UNREAD’. The results are executed as a query, and the function returns a list of messages or an empty list if none exist.

# Function to retrieve unread emails

def get_unread_emails(service):

    results = service.users().messages().list(userId='me', labelIds=['INBOX', 'UNREAD']).execute()

    return results.get('messages', [])

Next, we create the get_email_content function to retrieve and parse email details from Gmail using the message ID. It fetches the full email, extracts the subject and sender from headers, and decodes the body. It supports multi-part emails (extracting plain text) and single-part emails by decoding the Base64-encoded content. The function returns a dictionary containing the email’s subject, sender, and body, ensuring comprehensive handling of different email formats.

def get_email_content(service, msg_id):

    message = service.users().messages().get(userId='me', id=msg_id, format='full').execute()

    payload = message['payload']

    headers = payload['headers']

    subject = next(header['value'] for header in headers if header['name'] == 'Subject')

    sender = next(header['value'] for header in headers if header['name'] == 'From')

    body = ''

    if 'parts' in payload:

        for part in payload['parts']:

            if part['mimeType'] == 'text/plain':

                body = base64.urlsafe_b64decode(part['body']['data']).decode('utf-8')

                break

    else:

        body = base64.urlsafe_b64decode(payload['body']['data']).decode('utf-8')

    return {'subject': subject, 'sender': sender, 'body': body}

Step 4: Set Up Email Filters

Now, we come to the most important piece of code. The part that helps us filter out irrelevant emails based on certain keywords in the body of the mail or the sender. For me, I do not want my agentic system to respond to emails that are- “subscribed newsletters”, “marketing emails”, “automated reports”, “calendar notifications”, “verification code emails”, “OTP emails”, “HRMS”, “emails containing the terms like ‘do not reply’, ‘no-reply’, ‘accepted your invitation’, ‘rejected your invitation’, etc.”

So, we will create the filter_emails function that identifies emails to ignore based on predefined criteria. It uses two lists: ignore_keywords for phrases like “newsletter,” “OTP,” or “marketing” that indicate irrelevant content, and ignore_senders for sender patterns like “noreply” or “calendar-notification.” The function checks if the sender matches any ignored patterns or if the subject or body contains any ignored keywords. It converts text(sender’s email id + body of the email) to lowercase for consistent, case-insensitive comparisons. If an email matches any criteria, the function returns True to filter it out; otherwise, it returns False.

# Function to filter out emails

def filter_emails(email_content, sender, subject):

    ignore_keywords = [

        "newsletter", "marketing", "automated report", "calendar notifications", "verification code", "otp", "Join with Google Meet",

        "HRMS", "do not reply", "no-reply", "accepted your invitation", "rejected your invitation", "Accepted:"

    ]

    ignore_senders = [

        "[email protected]", "calendar-notification", "noreply", "no-reply", "calendar-server.bounces.google.com"

    ]

    # Check sender

    if any(ignore in sender.lower() for ignore in ignore_senders):

        return True

    # Check subject and body for keywords

    if any(keyword in subject.lower() or keyword in email_content.lower() for keyword in ignore_keywords):

        return True

    return False

Step 5: Create the Send Reply Function

Next, we create the send_reply function that replies to an email using the Gmail API. To maintain the context of the conversation, we will create a message with the specified recipient (to), body, and thread ID. The message is Base64-encoded and sent via the API. This function ensures replies are linked to the original thread for seamless communication.

def send_reply(service, to, subject, body, thread_id):

    message = MIMEText(body)

    message['to'] = to

    raw_message = base64.urlsafe_b64encode(message.as_bytes()).decode('utf-8')

    return service.users().messages().send(

        userId='me',

        body={'raw': raw_message, 'threadId': thread_id}

    ).execute()

Step 6: Build the AI Agents

Now, we will build the three agents required to execute the task: the email_analyzer, response_drafter, and proofreader agents. I have added the relevant prompts to each agent as per my preference. I recommend you go through the backstory and goal accordingly.

# Define agents

email_analyzer = Agent(

    role="Email Analyzer",

    goal="Analyze incoming emails and determine appropriate responses to relevant emails",

    backstory="You're an expert at understanding email content and context. "

              "With this understanding, you determine whether to reply to a particular email or not. "

              "You need to ignore emails that are newsletters, marketing emails, Google document comments, google document notifications,"

              "calendar notifications, HRMS mails, automated reports, etc.",    

    verbose=True,

    tools=[SerperDevTool()],

    allow_delegation=False

)

response_drafter = Agent(

    role="Response Drafter",

    goal="Draft appropriate responses to emails",

    backstory="You're skilled at crafting professional and contextually appropriate email responses."

              "Do not Generate responses to emails that are unread newsletters, marketing emails,"

              "googl document comments(from: [email protected]), calendar notifications, HRMS mails, automated reports, etc."

              "Make the responses crisp. Ensure it is known to people that the I am celebrating the Holidays and will be able to send any required documents once I am join back on 3rd January 2025."

              "YOUR NAME is my name and this is the name to be used in the sign-off for each mail you generate response for."

              "In the salutation use the recipient's name after 'Hi'",

    verbose=True

)

proofreader = Agent(

    role="Proofreader",

    goal="Ensure email responses are error-free and polished",

    backstory="You have a keen eye for detail and excellent grammar skills. Ensure the response is crisp and to the point"

              "Also discard email replies generated for emails that are newsletters, marketing emails, googl document comments, calendar notifications, HRMS mails, automated reports, etc.",

    verbose=True

)

Now we will define three different tasks for the 3 agents we have created. The description will reflect the directions written in the backstory of their respective agents.

# Define task for email_analyzer agent

def analyze_email(email_content):

    return Task(

        description=f"Analyze the content and context of the following email:\n\n{email_content}\n"

                    f"Determine if this email requires a response. Ignore newsletters, marketing emails, "

                    f"Google document comments (from: [email protected]), Google document notifications, calendar invites, "

                    f"HRMS mails, automated reports, etc.",

        expected_output="A detailed analysis of the email content and context, including whether it requires a response",

        agent=email_analyzer

    )

# Define task for response_drafter agent

def draft_response(analysis):

    return Task(

        description=(

            f"Draft a professional and contextually appropriate response to the following email analysis:\n\n{analysis}\n\n"

            f"Ensure the response is crisp and engaging. Avoid generating email responses for newsletters, marketing emails, "

            f"Google document comments (from: [email protected]), Google document notifications, "

            f"calendar invites, HRMS mails, and automated reports. "

            f"Additionally, include a note indicating that the sender is celebrating the holidays and will be able to provide "

            f"any required documents or assistance after returning."

            f"YOUR NAME is the name of the sender and this should be used in sign-off for each mail you generate response for."

            f"In the salutation use the recipient's name after 'Hi'"

        ),

        expected_output="A well-crafted email response based on the analysis",

        agent=response_drafter

    )

# Define task for proofreader agent

def proofread_response(draft):

    return Task(

        description=f"Review and refine the following drafted email response:\n\n{draft}",

        expected_output="A polished, error-free email response",

        agent=proofreader

    )

Step 7: Add the Process Email Function

Next, we create the process_email function to process an email by first applying filter_emails to ignore irrelevant messages. If the email passes the filter, a crew instance manages the sequential execution of tasks: analyzing the email, drafting a response, and proofreading it. The result is evaluated to check if a response is required. If not, it returns None; otherwise, it returns the processed output. This function automates email handling efficiently with clear decision-making at each step.

# Process_email to include filtering logic

def process_email(email_content, sender, subject):

    if filter_emails(email_content, sender, subject):

        print(f"Filtered out email from: {sender}")

        return None

    crew = Crew(

        agents=[email_analyzer, response_drafter, proofreader],

        tasks=[

            analyze_email(email_content),

            draft_response(""),

            proofread_response("")

        ],

        process=Process.sequential,

        verbose=True

    )

    result = crew.kickoff()

    # Check if the result is a CrewOutput object

    if hasattr(result, 'result'):

        final_output = result.result

    else:

        final_output = str(result)

    # Now check if a response is required

    if "requires response: false" in final_output.lower():

        return None

    return final_output

Step 8: Create the Send Reply Function

And now we come to our final function for this code. We will create the run_email_replier function which automates email management for CrewAI agents. It fetches unread emails, analyzes their content, and responds if needed. It does this by retrieving detailed email information (body, sender, subject), processing it with process_email to filter irrelevant messages, and determining if a response is required. If so, it sends a reply while maintaining the email thread; otherwise, it skips the email. This streamlined process efficiently handles email triage, ensuring only relevant emails receive attention and automating responses where necessary.

# Run_email_replier to pass sender and subject to process_email

def run_email_replier():

    service = get_gmail_service()

    unread_emails = get_unread_emails(service)

    for email in unread_emails:

        email_content = get_email_content(service, email['id'])

        response = process_email(email_content['body'], email_content['sender'], email_content['subject'])

        if response:

            send_reply(service, email_content['sender'], email_content['subject'], response, email['threadId'])

            print(f"Replied to email: {email_content['subject']}")

        else:

            print(f"Skipped email: {email_content['subject']}")

Step 9: Set the Environment Variables for API Keys

Finally, we set the environment variables for API keys (OPENAI_API_KEY, SERPER_API_KEY that you saved) required for email processing. It executes the run_email_replier function to automate email management using CrewAI agents, including analyzing, filtering, and replying to unread emails. The if __name__ == “__main__”: block ensures the process runs only when executed directly.

# sets environment variables 
if __name__ == "__main__":

    # Set up environment variables

    os.environ['OPENAI_API_KEY'] = 'Your API Key'

    os.environ['SERPER_API_KEY'] = 'Your API Key'

    # Run the email replier

    run_email_replier()

And that’s our email response management agent in full action!

CrewAI Email Management for workflow optimization

Let’s have a look at the emails. As you can see, for emails where my direct intervention was required, it has generated a customized email as per the context.

And for emails such as newsletters and notifications from HRMs, where replies are not required, it has not given any reply.

CrewAI Email Management for workflow optimization

And there you have it! A fully functional autonomous agentic system for automating email responses for out-of-office replies This way you can use AI agents for email workflow optimization. If you are satisfied with the responses you can set up a task scheduler to automatically run this code at specific times during the day. When the code is run it will automatically reply to the relevant unread emails.

Also Read: Build LLM Agents on the Fly Without Code With CrewAI

Conclusion

Automating email responses with Agents(Crew AI, Langchain, AutoGen) can transform how we manage replies to out-of-office emails. Moreover, setting up such a system offers a glimpse into a more hopeful future for workplace efficiency. As AI continues to evolve, tools like CrewAI will empower us to maintain seamless communication without compromise, paving the way for a future where technology enhances both productivity and personal well-being. The possibilities are bright, and the future is promising!

Frequently Asked Questions

Q1. What is crewAI?

A. CrewAI is an open-source Python framework designed to support the development and management of multi-agent AI systems. Using crewAI, you can build LLM backed AI agents that can autonomously make decisions within an environment based on the variables present.

Q2. Can I use crewAI for Automating Email Responses?

A. Yes! You can use crewAI for email workflow optimisation by automating email responses.

Q3. How many agents do I need to build using crewAI for automating email responses in Gmail?

A. For automating email responses in Gmail, you can use as many agents as you like. It depends on your agentic system structure. It is recommended that you build one agent per task.

Q4. What tasks can crewAI do?

A. crewAI can perform various tasks, including sorting and writing emails, planning projects, generating articles, scheduling and posting social media content, and more.

Q5. How can email sorting be automated using crewAI?

A. To automate email sorting using crewAI, you simply need to define the agents with a descriptive backstory within the Agent function, define tasks for each agent using Task functionality, and then create a Crew to enable different agents to collaborate with each other.

My name is Abhiraj. I am currently a manager for the Instruction Design team at Analytics Vidhya. My interests include badminton, voracious reading, and meeting new people. On a daily basis I love learning new things and spreading my knowledge.

Responses From Readers

Clear

We use cookies essential for this site to function well. Please click to help us improve its usefulness with additional cookies. Learn about our use of cookies in our Privacy Policy & Cookies Policy.

Show details