AI Email Assistant
Smart email drafting, response suggestions, and inbox management.
Workflow Steps
- 1
Classifier Agent prioritizes and categorizes emails
- 2
Summarizer Agent creates quick summaries of long threads
- 3
Draft Agent generates response drafts
- 4
Scheduler Agent suggests optimal send times
- 5
FollowUp Agent tracks unanswered emails
Download
Documentation
AI Email Assistant
Overview
This workflow automates email management: drafting responses, prioritizing inbox, generating summaries, and tracking follow-ups. It transforms email from a time sink into an efficient communication channel.
Difficulty
Easy - Can be set up with Gmail API and Claude.
Tools Required
- LangChain: Agent orchestration and tool integration
- Claude / GPT-4: Email drafting and summarization
- Gmail API: Email access and management
- Notion: Task tracking for email follow-ups
- Google Calendar: Meeting scheduling from emails
Workflow Steps
Step 1: Classifier Agent
Prioritizes and categorizes incoming emails.
from googleapiclient.discovery import build
from datetime import datetime, timedelta
def classify_emails(service, hours: int = 24) -> dict:
"""
Classify and prioritize unread emails.
Args:
service: Gmail API service
hours: Lookback period
Returns:
Classified emails by priority
"""
# Fetch unread emails
results = service.users().messages().list(
userId='me',
q='is:unread newer_than:{}d'.format(hours // 24)
).execute()
messages = results.get('messages', [])
classified = {
"urgent": [],
"important": [],
"normal": [],
"newsletter": [],
"spam": []
}
for msg in messages:
message = service.users().messages().get(
userId='me',
id=msg['id'],
format='full'
).execute()
email = parse_email(message)
# Classify using Claude
priority = classify_priority(email, service)
classified[priority].append(email)
return classified
def classify_priority(email: dict, service) -> str:
"""
Determine email priority using AI.
"""
prompt = f"""
Classify this email's priority:
From: {email['from']}
Subject: {email['subject']}
Preview: {email['preview'][:200]}
Priority levels:
- urgent: Needs immediate attention (today)
- important: Should be addressed soon (this week)
- normal: Can wait, no urgency
- newsletter: Marketing/subscription content
- spam: Unwanted or suspicious
Return only the priority level.
"""
response = call_claude(prompt)
return response.strip().lower()
# Example output
"""
{
"urgent": [
{
"id": "msg_123",
"from": "boss@company.com",
"subject": "URGENT: Client meeting moved to 2pm",
"preview": "We need to reschedule...",
"priority": "urgent"
}
],
"important": [
{
"id": "msg_124",
"from": "client@startup.com",
"subject": "Question about pricing",
"preview": "Hi, I have a question...",
"priority": "important"
}
],
"normal": [...],
"newsletter": [...],
"spam": []
}
"""
Step 2: Summarizer Agent
Creates quick summaries of long email threads.
def summarize_thread(emails: list) -> dict:
"""
Summarize a long email thread.
Args:
emails: List of emails in thread
Returns:
Thread summary with key points
"""
# Combine all emails in thread
thread_text = "\n\n".join([
f"From: {e['from']}\nDate: {e['date']}\n{e['body']}"
for e in emails
])
prompt = f"""
Summarize this email thread:
{thread_text[:4000]}
Provide:
1. Thread topic (one sentence)
2. Key decisions made
3. Open questions
4. Action items (who, what, when)
5. Recommended response (if any)
"""
summary = call_claude(prompt)
return {
"topic": summary["topic"],
"decisions": summary["decisions"],
"open_questions": summary["open_questions"],
"action_items": summary["action_items"],
"recommended_response": summary.get("recommended_response")
}
# Example summary
"""
{
"topic": "Q3 marketing budget approval",
"decisions": [
"Budget increased from $50K to $75K",
"Focus on content marketing and events"
],
"open_questions": [
"Which events to prioritize?",
"Timeline for content production?"
],
"action_items": [
{"who": "You", "what": "Finalize event list", "when": "Friday"},
{"who": "Sarah", "what": "Get vendor quotes", "when": "Thursday"}
],
"recommended_response": "Acknowledge budget approval, propose Friday meeting to finalize events"
}
"""
Step 3: Draft Agent
Generates response drafts.
def generate_draft(email: dict, context: dict = None) -> dict:
"""
Generate an email response draft.
Args:
email: Received email
context: Additional context (previous emails, notes)
Returns:
Draft response with options
"""
prompt = f"""
Draft a response to this email:
From: {email['from']}
Subject: {email['subject']}
Body: {email['body'][:1500]}
{f"Context: {context}" if context else ""}
Guidelines:
- Professional but warm tone
- Address all points in the email
- Keep it concise (under 200 words)
- Include clear next steps if needed
- Match the sender's level of formality
Return JSON with: subject, body, tone, suggested_followup
"""
draft = call_claude(prompt)
return {
"to": email["from"],
"subject": draft["subject"] or f"Re: {email['subject']}",
"body": draft["body"],
"tone": draft["tone"],
"suggested_followup": draft.get("suggested_followup"),
"created_at": datetime.utcnow().isoformat()
}
# Example draft
"""
{
"to": "client@startup.com",
"subject": "Re: Question about pricing",
"body": "Hi there,\n\nThanks for reaching out! I'd be happy to clarify our pricing.\n\nOur Pro plan is $99/month when billed annually, which includes:\n- Up to 10 team members\n- Unlimited projects\n- Priority support\n- Advanced analytics\n\nFor teams larger than 10, we offer custom enterprise pricing. Would you like me to set up a quick call to discuss your specific needs?\n\nBest,\n[Your name]",
"tone": "professional-friendly",
"suggested_followup": "Send pricing PDF attachment"
}
"""
Step 4: Scheduler Agent
Suggests optimal send times and creates calendar events.
def suggest_send_time(email: dict, recipient_tz: str = "UTC") -> datetime:
"""
Suggest optimal time to send email.
Args:
email: Draft email
recipient_tz: Recipient's timezone
Returns:
Suggested send time
"""
# Analyze recipient's email patterns
# (Would need historical data)
# Default to business hours in recipient's timezone
now = datetime.now(pytz.timezone(recipient_tz))
if now.hour < 8:
# Early morning - send at 9am
suggested = now.replace(hour=9, minute=0, second=0)
elif now.hour > 17:
# After hours - send tomorrow 9am
suggested = (now + timedelta(days=1)).replace(hour=9, minute=0, second=0)
elif now.hour > 12 and now.hour < 14:
# Lunch time - send at 2pm
suggested = now.replace(hour=14, minute=0, second=0)
else:
# Send now
suggested = now
return suggested
def create_calendar_event(email: dict, draft: dict) -> dict:
"""
Create calendar event for follow-up mentioned in email.
Args:
email: Original email
draft: Generated draft
Returns:
Created calendar event
"""
# Extract meeting request from email
meeting_info = extract_meeting_request(email["body"])
if not meeting_info:
return None
event = {
'summary': meeting_info.get('topic', 'Meeting'),
'description': f"Follow-up from email: {email['subject']}",
'start': {
'dateTime': meeting_info['start'].isoformat(),
'timeZone': recipient_tz
},
'end': {
'dateTime': meeting_info['end'].isoformat(),
'timeZone': recipient_tz
},
'attendees': [{'email': email['from']}],
'reminders': {'useDefault': True}
}
created = service.events().insert(calendarId='primary', body=event).execute()
return created
Step 5: FollowUp Agent
Tracks unanswered emails and sends reminders.
def track_followups(classified: dict, days: int = 3) -> list:
"""
Track emails that need follow-up.
Args:
classified: Classified emails
days: Days to wait before follow-up
Returns:
Emails needing follow-up
"""
followups = []
for email in classified["important"] + classified["urgent"]:
# Check if email was replied to
thread = get_email_thread(email["id"])
if not has_my_response(thread):
# Check if follow-up is needed
email_date = parse_date(email["date"])
days_waiting = (datetime.utcnow() - email_date).days
if days_waiting >= days:
followups.append({
"email_id": email["id"],
"from": email["from"],
"subject": email["subject"],
"days_waiting": days_waiting,
"priority": email["priority"],
"suggested_action": generate_followup_prompt(email)
})
return followups
def generate_followup_prompt(email: dict) -> str:
"""
Generate a follow-up email draft.
"""
prompt = f"""
Write a polite follow-up email for this unanswered message:
Original: {email['subject']}
Guidelines:
- Friendly, not pushy
- Reference the original email
- Offer to provide more information
- Suggest a call if appropriate
Draft:
"""
return call_claude(prompt)
# Example follow-up
"""
Hi [Name],
Just following up on my previous email about [topic]. I know things get busy, so I wanted to make sure this didn't get lost in your inbox.
Happy to provide more details or hop on a quick call if that's easier.
Best,
[Your name]
"""
Example Usage
# Morning email workflow
1. Classifier Agent: 25 unread emails → 2 urgent, 5 important, 15 normal, 3 newsletters
2. Summarizer Agent: 3 long threads summarized in 30 seconds
3. Draft Agent: 5 response drafts generated
4. Scheduler Agent: Optimal send times calculated
5. FollowUp Agent: 2 emails flagged for follow-up (3+ days old)
# Output
- Inbox zero achieved in 15 minutes
- 5 responses ready to review and send
- 2 follow-ups scheduled for later today
- Calendar event created for meeting request
Pros
- ✅ Reduces email time by 50%+
- ✅ Never miss important emails
- ✅ Professional responses every time
- ✅ Automatic follow-up tracking
- ✅ Thread summarization for quick context
Cons
- ❌ Drafts need human review before sending
- ❌ May miss nuanced context in complex emails
- ❌ Requires Gmail API access
- ❌ Privacy considerations for email content
When to Use
Use this workflow when:
- You receive 50+ emails per day
- You struggle with email prioritization
- You want consistent professional responses
- You frequently forget to follow up
Consider alternatives when:
- You receive very few emails (< 10/day)
- Your emails are highly sensitive/confidential
- You prefer writing all emails yourself
