🔀

AI Email Assistant

Easy5 tools

Smart email drafting, response suggestions, and inbox management.

LangChainClaudeGmail APINotionCalendar

Workflow Steps

  1. 1

    Classifier Agent prioritizes and categorizes emails

  2. 2

    Summarizer Agent creates quick summaries of long threads

  3. 3

    Draft Agent generates response drafts

  4. 4

    Scheduler Agent suggests optimal send times

  5. 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

Resources