Skip to content
reaatech

Files · Slack/email triage to priority inbox

86 (1 binary, 729.8 kB total)attempt 1

README.md·8741 B·markdown
markdown
# Slack/Email Triage to Priority Inbox
 
> Automated triage agent that reads inbound Slack messages and Gmail emails, classifies urgency (P0/P1/P2) using AI intent classification, and drafts contextual replies for urgent items.
 
A tutorialized reference solution from [reaatech.com](https://reaatech.com), demonstrating how to build production-grade AI systems with the `@reaatech/*` package family.
 
---
 
## Problem Statement
 
Support teams are overwhelmed by a flood of messages across Slack and email. Urgent issues (outages, security incidents) get buried under routine questions, and every message requires manual reading, classification, and response — wasting hours of agent time each day.
 
This solution automates the triage pipeline: every inbound message is classified by urgency (P0/P1/P2), routed to the right specialist agent, and a contextual AI reply is drafted. P0 alerts are posted to a dedicated Slack channel, ensuring critical issues never go unnoticed.
 
---
 
## Architecture
 
```
┌──────────────────┐     ┌──────────────────────┐     ┌──────────────────┐
│  Slack Events API │────▶│ POST /api/inbound/   │     │  Gmail Poll      │
│  (Webhook)        │     │       slack          │     │  (Cron/Scheduler)│
└──────────────────┘     └──────────┬───────────┘     └────────┬─────────┘
                                    │                          │
                                    ▼                          ▼
                           ┌──────────────────────────────────────┐
                           │          Triage Engine               │
                           │                                      │
                           │  1. Circuit Breaker Gate             │
                           │     └── isAgentReady("classifier")   │
                           │                                      │
                           │  2. Intent Classification            │
                           │     └── @reaatech/agent-mesh-        │
                           │         classifier                   │
                           │                                      │
                           │  3. Confidence Gate                  │
                           │     └── @reaatech/agent-mesh-        │
                           │         confidence                   │
                           │                                      │
                           │  4. Budget Check                     │
                           │     └── @reaatech/agent-budget-      │
                           │         llm-router-plugin            │
                           │                                      │
                           │  5. LLM Draft Generation             │
                           │     └── OpenAI (configurable)        │
                           │                                      │
                           │  6. Circuit Breaker Recording        │
                           │     └── @reaatech/agent-mesh-utils   │
                           └──────────────────────────────────────┘

                          ┌─────────┴──────────┐
                          ▼                    ▼
                   ┌──────────────┐    ┌──────────────┐
                   │ Slack Reply  │    │ Mark Read    │
                   │ (P0 alert)   │    │ (Gmail)      │
                   └──────────────┘    └──────────────┘
```
 
---
 
## Environment Variables
 
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `SLACK_BOT_TOKEN` | Yes | — | Slack bot token for reading messages and posting replies |
| `SLACK_SIGNING_SECRET` | Yes | — | Slack Events API signing secret for webhook verification |
| `GMAIL_CLIENT_EMAIL` | Yes | — | Gmail service account email for JWT auth |
| `GMAIL_PRIVATE_KEY` | Yes | — | Service-account private key (PEM, newlines as `\n`) |
| `GMAIL_USER_EMAIL` | Yes | — | Target Gmail inbox to poll |
| `OPENAI_API_KEY` | Yes | — | API key for the LLM provider |
| `OPENAI_BASE_URL` | No | `https://api.openai.com/v1` | Base URL for OpenAI-compatible endpoint |
| `LLM_MODEL` | No | `gpt-5.2` | Model ID for chat completions |
| `BUDGET_LIMIT_USD` | No | `10.0` | Per-scope USD spend cap before LLM calls are blocked |
| `P0_CHANNEL_ID` | No | — | Slack channel for urgent P0 alerts |
| `P1_CHANNEL_ID` | No | — | Slack channel for high-priority P1 notifications |
| `TRIAGE_POLL_INTERVAL_MS` | No | `60000` | How often the email poller fetches unread messages |
| `LANGFUSE_PUBLIC_KEY` | No | — | Langfuse public key for observability |
| `LANGFUSE_SECRET_KEY` | No | — | Langfuse secret key (pair with public key) |
| `LANGFUSE_BASE_URL` | No | — | Langfuse self-hosted or cloud URL |
 
---
 
## Getting Started
 
```bash
pnpm install
cp .env.example .env
# Fill in all required environment variables
pnpm typecheck       # Verify TypeScript compilation
pnpm lint            # Run ESLint
pnpm test            # Run vitest with coverage
pnpm dev             # Start Next.js dev server
```
 
---
 
## API Endpoints
 
### POST /api/inbound/slack
 
Slack Events API webhook endpoint. Accepts URL verification challenges and message events.
 
**Signature verification:** HMAC-SHA256 using the Slack signing secret.
 
**Request:** Slack Events API payload (`application/json`).
 
**Responses:**
- `200``{ "ok": true }` for message events and URL verification challenges
- `401``{ "error": "invalid signature" }` on HMAC mismatch
 
**Behavior:**
- `url_verification` — returns the challenge token
- `message` — triages the message, posts AI-drafted reply in thread, sends P0 alert
- `unknown` — acknowledges with `{ ok: true }`
 
### POST /api/inbound/email
 
Email poll endpoint intended for cron or scheduler invocation.
 
**Behavior:**
- Fetches all unread Gmail messages
- Triages each message through the TriageEngine
- Marks processed messages as read
- Posts P0 alerts to the configured Slack channel
 
**Success response:**
```json
{ "processed": 5, "p0": 1, "p1": 2, "p2": 2 }
```
 
**Error responses:**
- `502``{ "error": "Gmail fetch failed" }` on Gmail API failure
 
### GET /api/health
 
Health check endpoint.
 
**Response:**
```json
{ "status": "ok", "timestamp": "2026-05-31T...", "uptime": 123.45 }
```
 
---
 
## REAA Packages Used
 
This recipe integrates five `@reaatech/*` packages:
 
| Package | Version | Role |
|---------|---------|------|
| `@reaatech/agent-mesh-classifier` | `1.0.0` | Gemini Flash intent classifier. Detects message language and classifies the user request against registered agents. |
| `@reaatech/agent-mesh-confidence` | `1.0.0` | Confidence-gated routing engine. Evaluates classifier output and decides whether to route, ask for clarification, or fall back. |
| `@reaatech/agent-handoff-routing` | `0.1.0` | Agent registry and capability-based router. Holds the four specialist agents and scores them by skill match, domain match, load, and language. |
| `@reaatech/agent-budget-llm-router-plugin` | `0.1.0` | Budget-aware LLM gating. Checks remaining spend budget before each draft-generation call and blocks when exhausted. |
| `@reaatech/agent-mesh-utils` | `1.0.0` | Circuit breaker with per-agent state. Prevents cascading failures by isolating unhealthy classifier or agent dispatches. |
 
---
 
## Provider Configuration
 
This recipe uses the OpenAI SDK with a configurable `baseURL`. To swap providers:
 
```bash
# OpenAI
OPENAI_BASE_URL=https://api.openai.com/v1
 
# Any OpenAI-compatible endpoint
OPENAI_BASE_URL=https://your-custom-endpoint.com/v1
```
 
The `LLM_MODEL` variable selects which model to use for draft generation.
 
---
 
## Testing
 
```bash
pnpm test
```
 
Runs vitest with coverage reporting. Tests use MSW (Mock Service Worker) for HTTP-level mocking of Slack, Gmail, and OpenAI APIs.
 
Coverage thresholds: 90% on lines, branches, functions, and statements.
 
---
 
## License
 
MIT — see [LICENSE](./LICENSE).