DEV Community

Qasim Muhammad
Qasim Muhammad

Posted on • Originally published at cli.nylas.com

Build an AI Email Triage Agent in Python

The average professional receives 121 emails per day. About half is noise — newsletters, automated alerts, marketing. Manually sorting through it burns 2.5 hours daily.

An AI triage agent fixes this. It reads your unread messages, classifies each one into a priority bucket, drafts replies for anything that needs a response, and archives the rest.

The architecture is straightforward: Nylas CLI fetches the email, Python orchestrates the logic, and an LLM handles classification and drafting. If you haven't installed it yet, the getting started guide takes under a minute.

Install Nylas CLI

# macOS / Linux
brew install nylas/nylas-cli/nylas

# Authenticate
nylas auth login
nylas auth whoami
# => Authenticated as you@company.com
Enter fullscreen mode Exit fullscreen mode

For Windows, see the PowerShell email guide which covers the PowerShell install method.

Step 1: Fetch unread emails

import subprocess
import json

def fetch_unread_emails(limit=20):
    result = subprocess.run(
        ["nylas", "email", "list", "--unread", "--limit", str(limit), "--json"],
        capture_output=True, text=True
    )
    if result.returncode != 0:
        return []
    return json.loads(result.stdout)
Enter fullscreen mode Exit fullscreen mode

Each email includes id, subject, from, snippet, and date. The snippet is the first ~200 characters — usually enough for classification. For Gmail-specific details, see List Gmail Emails from the Command Line. For Outlook, see List Outlook Emails from Terminal.

Step 2: Classify with an LLM

Four categories work well in practice:

  • URGENT — respond within 1 hour (production incidents, exec requests)
  • ACTION — respond today (code reviews, meeting follow-ups)
  • FYI — read later, no response needed (team updates)
  • NOISE — archive immediately (newsletters, marketing)
from openai import OpenAI

client = OpenAI()

def classify_email(subject, snippet, sender):
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{
            "role": "system",
            "content": "Classify this email as URGENT, ACTION, FYI, or NOISE. Return only the category."
        }, {
            "role": "user",
            "content": f"From: {sender}\nSubject: {subject}\n\n{snippet}"
        }],
        max_tokens=10
    )
    return response.choices[0].message.content.strip()
Enter fullscreen mode Exit fullscreen mode

With GPT-4o or Claude Sonnet, classification accuracy on these 4 categories typically exceeds 90%. Adding sender-based rules (e.g., always classify noreply@ as NOISE) pushes it above 95%.

Step 3: Draft replies for important emails

def draft_reply(subject, snippet, sender):
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{
            "role": "system",
            "content": "Draft a brief, professional reply to this email. Keep it under 3 sentences."
        }, {
            "role": "user",
            "content": f"From: {sender}\nSubject: {subject}\n\n{snippet}"
        }],
        max_tokens=150
    )
    return response.choices[0].message.content.strip()
Enter fullscreen mode Exit fullscreen mode

For more on AI-powered email drafting, check out the smart compose and draft automation guide.

Step 4: Take action

for email in fetch_unread_emails():
    category = classify_email(
        email["subject"],
        email["snippet"],
        email["from"][0]["email"]
    )

    if category in ("URGENT", "ACTION"):
        reply = draft_reply(email["subject"], email["snippet"], email["from"][0]["email"])
        print(f"[{category}] {email['subject']}")
        print(f"  Draft: {reply}\n")

    elif category == "NOISE":
        # Archive noise
        subprocess.run(["nylas", "email", "archive", email["id"], "--yes"])
Enter fullscreen mode Exit fullscreen mode

Want to send replies automatically instead of printing drafts? See Send Email from the Command Line for the nylas email send syntax.

Step 5: Run it on a schedule

# Add to crontab — run every 15 minutes
*/15 * * * * /usr/bin/python3 /path/to/triage.py >> /var/log/email-triage.log 2>&1
Enter fullscreen mode Exit fullscreen mode

The script only processes unread emails, so re-runs are idempotent. For audit trail requirements, the AI agent audit logs guide covers how to track every action your agent takes.

Use a local LLM for privacy

Don't want email content hitting external APIs? Swap in Ollama:

client = OpenAI(
    base_url="http://localhost:11434/v1",
    api_key="ollama"
)
Enter fullscreen mode Exit fullscreen mode

Llama 3.1 8B handles classification well. Draft quality improves with 70B+ models. Email content never leaves your machine.

Give AI agents direct email access via MCP

Instead of subprocess calls, you can give your AI agent an email address via MCP. One command connects Claude Code, Cursor, or Codex to a real inbox:

nylas mcp install --assistant claude-code
Enter fullscreen mode Exit fullscreen mode

Full MCP setup: Give AI Agents Email Access via MCP

Works with any provider

Nylas CLI handles OAuth for Gmail, Outlook, Exchange, Yahoo, iCloud, and IMAP. Swap providers by changing your grant — the Python code stays identical. For a full comparison of CLI email tools, see Best CLI Email Tools Compared.


Full guide with the complete script, multi-account support, and Anthropic/Ollama examples: Build an AI Email Triage Agent

Related guides:

All guides: cli.nylas.com/guides

Top comments (0)