CoolFace
Apppublic

rushi07ai/email-triage-openenv

sourceHugging Faceupdated 6mo agoView on Hugging Face
0likes
App README

๐Ÿ“ง Email Triage OpenEnv

A real-world OpenEnv-compatible environment where AI agents learn to triage a business support inbox: classify urgency, route to departments, and draft professional replies.

Domain: Customer support email operations โ€” a task millions of humans do daily.

Why Email Triage?

Email triage is genuinely complex for AI agents:

  • โ€”Requires natural language understanding across domains (billing, technical, returns)
  • โ€”Tests priority judgment (what's truly urgent vs. just loud?)
  • โ€”Demands professional communication (Task 3)
  • โ€”Has clear ground-truth labels enabling deterministic, reproducible scoring

Environment Specification

PropertyValue
OpenEnv Version1.0
Action TypeTriageAction (Pydantic model)
Observation TypeEmailObservation (Pydantic model)
RewardDense โ€” partial credit per step (0.0โ€“1.0)
Episode Length10 emails (shuffled from dataset of 10)
Tasks3 (easy โ†’ medium โ†’ hard)
GradersDeterministic keyword + rule-based

Tasks

Task 1 โ€” task_classify (Easy)

Goal: Classify the urgency of each email.

Action fields required:

  • โ€”urgency: urgent | normal | low

Grader: Full credit (1.0) for correct urgency, 0.0 for wrong. Small penalty (-0.05) if unnecessary fields are included.

Expected score: Frontier LLMs ~0.85+, keyword baseline ~0.70

Task 2 โ€” task_route (Medium)

Goal: Classify urgency AND route to the correct department.

Action fields required:

  • โ€”urgency: urgent | normal | low
  • โ€”department: billing | technical | returns | general

Grader: 0.5 for urgency + 0.5 for department. Penalty if response is included.

Expected score: Frontier LLMs ~0.70+, keyword baseline ~0.55

Task 3 โ€” task_respond (Hard)

Goal: Classify urgency, route to department, AND draft a professional customer reply.

Action fields required:

  • โ€”urgency: urgent | normal | low
  • โ€”department: billing | technical | returns | general
  • โ€”response: string โ€” your draft reply to the customer

Grader breakdown (total = 1.0):

  • โ€”Urgency correct โ†’ +0.25
  • โ€”Department correct โ†’ +0.25
  • โ€”Response provided โ†’ +0.10
  • โ€”Response โ‰ฅ50 words โ†’ +0.05
  • โ€”Response โ‰ฅ100 words โ†’ +0.05
  • โ€”โ‰ฅ1 required keyword present โ†’ +0.10
  • โ€”โ‰ฅ3 required keywords present โ†’ +0.10
  • โ€”All required keywords present โ†’ +0.10
  • โ€”Professional closing (Best regards, Sincerely, etc.) โ†’ +0.05
  • โ€”No unfilled placeholders โ†’ +0.05

Expected score: Frontier LLMs ~0.65+, keyword baseline ~0.45

Action Space

python
class TriageAction(BaseModel):
    urgency: Literal["urgent", "normal", "low"]           # required always
    department: Optional[Literal["billing", "technical",
                                 "returns", "general"]]   # required for task 2+3
    response: Optional[str]                               # required for task 3
    reasoning: Optional[str]                              # optional, not scored

Observation Space

python
class EmailObservation(BaseModel):
    email_id: str           # e.g. "E001"
    subject: str            # email subject line
    body: str               # full email body
    sender: str             # sender email address
    timestamp: str          # ISO 8601
    task_id: str            # active task
    task_description: str   # what the agent must do
    step_feedback: str      # human-readable feedback on last action
    reward: float           # reward for last step (0.0 on reset)
    done: bool              # True when episode ends
    score: float            # running cumulative average score

Reward Function Design

Rewards are dense โ€” the agent receives a signal after every email, not just at the end. This enables:

  • โ€”Gradient flow across the entire trajectory
  • โ€”Clear partial credit for multi-component tasks
  • โ€”Interpretable per-step debugging

Penalties discourage providing unnecessary fields (verbosity) and missing the response entirely on Task 3.

API Endpoints

MethodPathDescription
GET/healthHealth check โ€” returns {"status": "healthy"}
POST/resetStart new episode
POST/stepSubmit triage action
GET/stateInternal episode state
GET/tasksAll tasks + action schema
POST/graderScore one action offline
GET/baselineRun rule-based baseline, return scores
GET/docsInteractive Swagger UI

Quick Start

1. Local via Docker

bash
# Build
docker build -t email-triage-openenv .

# Run
docker run -p 7860:7860 email-triage-openenv

# Health check
curl http://localhost:7860/health

2. Local via Python (no Docker)

bash
pip install -r requirements.txt
uvicorn server.app:app --host 0.0.0.0 --port 7860

3. Run an episode with curl

bash
# Start episode
curl -X POST http://localhost:7860/reset \
  -H "Content-Type: application/json" \
  -d '{"task_id": "task_classify", "seed": 42}'

# Submit action
curl -X POST http://localhost:7860/step \
  -H "Content-Type: application/json" \
  -d '{"urgency": "urgent"}'

# Check state
curl http://localhost:7860/state

4. Python client

python
from client import EmailTriageClient

with EmailTriageClient("http://localhost:7860") as c:
    obs = c.reset("task_respond", seed=42)
    
    while not obs.get("done"):
        print(f"Email: {obs['subject']}")
        
        result = c.step(
            urgency="urgent",
            department="billing",
            response="Dear Customer, I apologize for the issue. "
                     "Our billing team will resolve this within 24 hours. "
                     "Best regards, Support Team"
        )
        obs = result["observation"]
        print(f"  Reward: {result['reward']:.3f}")

5. Run the LLM inference baseline

bash
export OPENAI_API_KEY=sk-...
export ENV_BASE_URL=http://localhost:7860
python baseline.py

# Or against your HF Space:
export ENV_BASE_URL=https://YOUR-USERNAME-email-triage-openenv.hf.space
python baseline.py

Baseline Scores

TaskHeuristic BaselineLLM (Qwen2.5-72B)Difficulty
task_classify0.700.82Easy
task_route0.740.78Medium
task_respond0.620.80Hard

Episode length: 20 emails per episode (dataset of 20 realistic support emails).

Project Structure

email-triage-env/
โ”œโ”€โ”€ server/
โ”‚   โ”œโ”€โ”€ __init__.py
โ”‚   โ”œโ”€โ”€ app.py              # FastAPI server (all endpoints)
โ”‚   โ”œโ”€โ”€ environment.py      # Core env: reset() / step() / state()
โ”‚   โ”œโ”€โ”€ models.py           # Pydantic: TriageAction, EmailObservation, TriageState
โ”‚   โ”œโ”€โ”€ email_dataset.py    # 10 realistic emails with ground-truth labels
โ”‚   โ”œโ”€โ”€ graders.py          # Deterministic graders for all 3 tasks
โ”‚   โ””โ”€โ”€ baseline_agent.py   # Keyword heuristic agent (for /baseline endpoint)
โ”œโ”€โ”€ client.py               # Python HTTP client
โ”œโ”€โ”€ baseline.py             # LLM baseline (OpenAI API)
โ”œโ”€โ”€ openenv.yaml            # OpenEnv metadata manifest
โ”œโ”€โ”€ requirements.txt
โ”œโ”€โ”€ Dockerfile
โ””โ”€โ”€ README.md

Running Tests

bash
pip install -r requirements.txt
pytest -q

Deploying to Hugging Face Spaces

bash
hf auth login
hf repos create email-triage-openenv --type space --space-sdk docker
git remote add hf https://huggingface.co/spaces/YOUR_USERNAME/email-triage-openenv
git push -u hf main

The Space will automatically build and run the Dockerfile. Port 7860 is used by default.

License

Apache 2.0