Full implementation of the Howl email triage system: - Microsoft Graph API integration with MSAL auth (client-credentials and delegated modes) - Claude LLM classification via tool use for structured output - PostgreSQL database with customers, vendors, whitelist, and email_log tables - Alembic migration for full schema - APScheduler daemon with graceful shutdown - Typer CLI (run, dry-run, status commands) - Business rule classifier with overrides (whitelist protection, low-confidence fallback) - Action executor (move to folders, flag, escalate with webhook) - 35 passing unit tests - README, SETUP, and NEXT_STEPS documentation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
125 lines
4.4 KiB
Markdown
125 lines
4.4 KiB
Markdown
# Howl
|
|
|
|
An M365 email management daemon that uses the Microsoft Graph API and Claude AI to automatically classify and route incoming email based on your customer, vendor, and whitelist database.
|
|
|
|
## How it works
|
|
|
|
```
|
|
Inbox (M365)
|
|
│
|
|
▼
|
|
Graph API polls for unread mail
|
|
│
|
|
▼
|
|
PostgreSQL lookup — is the sender a customer, vendor, or trusted contact?
|
|
│
|
|
▼
|
|
Claude (LLM) classifies the email using sender context + body content
|
|
│
|
|
▼
|
|
Business rules applied (whitelist always wins, low-confidence → hold for review)
|
|
│
|
|
▼
|
|
Action taken: move to folder / flag / escalate with notification
|
|
│
|
|
▼
|
|
email_log table records every decision for audit and retry
|
|
```
|
|
|
|
## Features
|
|
|
|
- Supports both **shared mailboxes** (app-only, no user login) and **personal mailboxes** (delegated device-code auth)
|
|
- **No emails are ever deleted** — only moved, flagged, or escalated
|
|
- **Dry-run mode** — analyze and log without touching any email
|
|
- **Idempotent** — safe to restart; already-processed messages are skipped
|
|
- **Automatic retry** — failed messages are retried on the next poll cycle
|
|
- **Full audit log** — every classification, confidence score, and action is stored in PostgreSQL
|
|
|
|
## Folder routing
|
|
|
|
| Sender type | LLM classification | Action |
|
|
|---|---|---|
|
|
| Customer | Inquiry / correspondence | Move to `Customers` folder |
|
|
| Vendor | Invoice / notification | Move to `Vendors` folder |
|
|
| Whitelist | Trusted contact | Mark read, stay in Inbox |
|
|
| Any | Urgent / escalation | Move to `Escalate` + webhook notification |
|
|
| Any | Uncertain (low confidence) | Move to `Needs Review` |
|
|
| Unknown | Spam / newsletter | Move to `Junk Email` |
|
|
|
|
## Quick start
|
|
|
|
```bash
|
|
# 1. Create and activate a virtual environment
|
|
python3 -m venv .venv && source .venv/bin/activate
|
|
|
|
# 2. Install dependencies
|
|
pip install -e ".[dev]"
|
|
|
|
# 3. Configure environment
|
|
cp .env.example .env
|
|
# Edit .env with your Azure, Anthropic, and PostgreSQL credentials
|
|
|
|
# 4. Run database migrations
|
|
alembic upgrade head
|
|
|
|
# 5. Test with dry-run (no email mutations)
|
|
howl dry-run --limit 10
|
|
|
|
# 6. Start the daemon
|
|
howl run
|
|
```
|
|
|
|
## CLI commands
|
|
|
|
| Command | Description |
|
|
|---|---|
|
|
| `howl run` | Start the daemon (blocking, polls on schedule) |
|
|
| `howl dry-run [--limit N]` | Classify up to N emails, log results, take no action |
|
|
| `howl status [--limit N]` | Display recent email_log entries in a table |
|
|
|
|
## Project structure
|
|
|
|
```
|
|
howl/
|
|
├── howl/
|
|
│ ├── config.py # All configuration (pydantic-settings + .env)
|
|
│ ├── main.py # CLI entry point (Typer)
|
|
│ ├── logging_config.py # structlog setup (JSON or text)
|
|
│ ├── db/
|
|
│ │ ├── engine.py # Async SQLAlchemy engine
|
|
│ │ ├── models.py # ORM models
|
|
│ │ └── queries.py # lookup_sender(), email log helpers
|
|
│ ├── graph/
|
|
│ │ ├── auth.py # MSAL token provider (client-credentials or delegated)
|
|
│ │ └── client.py # Graph API: list, read, move, flag messages
|
|
│ ├── llm/
|
|
│ │ ├── client.py # Anthropic SDK wrapper with retry
|
|
│ │ ├── prompts.py # Prompt builder (system prompt + per-email user turn)
|
|
│ │ └── schemas.py # EmailClassification model + tool definition
|
|
│ ├── pipeline/
|
|
│ │ ├── processor.py # EmailProcessor — orchestrates the full pipeline
|
|
│ │ ├── classifier.py # Business rule overrides on LLM output
|
|
│ │ └── actions.py # ActionExecutor — Graph mutations + webhook
|
|
│ └── daemon/
|
|
│ └── scheduler.py # APScheduler poll loop + graceful shutdown
|
|
├── migrations/ # Alembic database migrations
|
|
├── tests/ # Unit and integration tests
|
|
├── .env.example # Configuration template
|
|
├── SETUP.md # Detailed setup and configuration guide
|
|
└── NEXT_STEPS.md # Roadmap and future improvements
|
|
```
|
|
|
|
## Running tests
|
|
|
|
```bash
|
|
pytest tests/ -q # All tests (no external services needed)
|
|
pytest tests/ -q -m "not integration" # Unit tests only (default)
|
|
pytest tests/ -q -m integration # Requires real M365 + Anthropic credentials
|
|
```
|
|
|
|
## Requirements
|
|
|
|
- Python 3.11+
|
|
- PostgreSQL 14+
|
|
- Microsoft Azure app registration with `Mail.Read` + `Mail.ReadWrite` permissions
|
|
- Anthropic API key
|