CoolFace
Apppublic

thrishaldevx/email-triage-env

sourceHugging Facemitupdated 5mo agoView on Hugging Face
0likes
App README

๐Ÿ“ฌ Email Triage OpenEnv

A real-world email inbox triage environment for training and evaluating AI agents. Built on the OpenEnv spec by Meta & Hugging Face.


Why Email Triage?

Email triage is one of the most common, high-value knowledge-worker tasks:

  • โ€”Knowledge workers spend 28% of their workweek managing email (McKinsey)
  • โ€”Enterprise AI assistants must handle priority classification, action routing, and professional reply drafting
  • โ€”Unlike games or coding tasks, email triage tests real-world language understanding under ambiguity

This environment fills a gap in the OpenEnv ecosystem: no existing environment covers natural language inbox management with graded reward signals across all three dimensions.


Environment Overview

PropertyValue
Task typeClassification + Action selection + NLG
EpisodesPer-task inbox of 5โ€“12 emails
Reward typeDense (per-step partial credit)
Tasks3 (easy โ†’ medium โ†’ hard)
Action spaceStructured JSON (label/reply/archive/delete/escalate)
Observation spaceEmail content + inbox state + running score

Tasks

๐ŸŸข easy_triage โ€” Priority Labeling

  • โ€”5 emails with clear, unambiguous priority signals
  • โ€”Agent must assign: urgent | high | medium | low
  • โ€”Max 10 steps
  • โ€”Expected score range: 0.7โ€“1.0 for capable models

๐ŸŸก medium_triage โ€” Priority + Action Selection

  • โ€”8 emails with mixed signals and some ambiguity
  • โ€”Agent must assign priority AND choose: reply | archive | delete | escalate
  • โ€”Max 16 steps
  • โ€”Expected score range: 0.45โ€“0.75 for frontier models

๐Ÿ”ด hard_triage โ€” Full Triage with Reply Drafting

  • โ€”12 emails across all categories
  • โ€”Agent must label priority, choose action, AND draft relevant professional replies for emails that require a response
  • โ€”Reply quality scored on: relevance, professionalism, urgency acknowledgment
  • โ€”Max 30 steps
  • โ€”Expected score range: 0.30โ€“0.60 for frontier models

Action Space

json
{
  "email_id": "e001",
  "action_type": "label",
  "priority": "urgent",
  "reply_body": null
}
FieldTypeRequiredValues
email_idstringโœ…ID of target email
action_typestringโœ…`label \reply \archive \delete \escalate \skip`
prioritystringwhen action_type=label`urgent \high \medium \low`
reply_bodystringwhen action_type=replyFull reply text

Action semantics:

  • โ€”label โ€” assign a priority level to the email
  • โ€”reply โ€” compose and send a reply (requires reply_body)
  • โ€”archive โ€” file the email, no response needed
  • โ€”delete โ€” remove from inbox (spam, newsletters, irrelevant)
  • โ€”escalate โ€” flag as requiring immediate human attention
  • โ€”skip โ€” pass (penalized: โˆ’0.05 reward)

Observation Space

json
{
  "current_email": {
    "id": "e001",
    "from": "cto@enterprise.com",
    "subject": "URGENT: Production database is down",
    "body": "...",
    "timestamp": "2024-03-15T09:02:00Z",
    "has_attachment": false,
    "labels": []
  },
  "inbox_summary": {
    "total": 5,
    "processed": 1,
    "pending": 4,
    "current_index": 1
  },
  "last_action_result": "Action 'label' recorded for email 'e001'. Progress: 1/5.",
  "last_action_error": null,
  "task_name": "easy_triage",
  "step_count": 1,
  "max_steps": 10,
  "score_so_far": 0.4,
  "done": false,
  "reward": 0.4
}

Reward Function

Rewards are dense โ€” the agent receives signal at every step, not just at episode end.

step_reward = (
    priority_accuracy  ร— task_priority_weight  ร— 0.4
  + action_accuracy    ร— task_action_weight    ร— 0.4
  + reply_quality      ร— task_reply_weight     ร— 0.4
  โˆ’ 0.05 ร— (action == "skip")
  โˆ’ 0.10 ร— invalid_action
)

Priority accuracy: Full credit for exact match, partial credit for adjacent levels (e.g., predicting high when true is urgent gets 0.65 credit).

Action accuracy: Full credit for exact match, partial credit for semantically similar choices (e.g., reply when true is escalate gets 0.5 credit).

Reply quality (hard task): Heuristic scorer measuring:

  • โ€”Message length and substance (0.2)
  • โ€”Topic relevance โ€” keyword overlap with subject/body (0.3)
  • โ€”Professional tone โ€” greeting + sign-off (0.2)
  • โ€”Urgency acknowledgment for high-priority emails (0.3)

Task weights:

TaskPriorityActionReply
easy1.00.00.0
medium0.50.50.0
hard0.350.350.30

Final episode score = weighted average across all emails (0.0โ€“1.0).


Setup & Usage

Prerequisites

  • โ€”Python 3.10+
  • โ€”Docker
  • โ€”pip install openenv-core

Local Development

bash
# Clone and install
git clone https://huggingface.co/spaces/your-org/email-triage-env
cd email-triage-env
pip install -e .

# Run server locally
PYTHONPATH=. uvicorn email_triage_env.server.app:app --host 0.0.0.0 --port 7860

# In another terminal โ€” quick smoke test
curl -s -X POST http://localhost:7860/reset \
  -H "Content-Type: application/json" \
  -d '{}' | python3 -m json.tool

Docker

bash
# Build (from repo root)
docker build -f email_triage_env/server/Dockerfile -t email-triage-env .

# Run easy task
docker run -p 7860:7860 -e EMAIL_TRIAGE_TASK=easy_triage email-triage-env

# Run hard task
docker run -p 7860:7860 -e EMAIL_TRIAGE_TASK=hard_triage email-triage-env

# Test the server
curl -s http://localhost:7860/health
curl -s -X POST http://localhost:7860/reset -H "Content-Type: application/json" -d '{}'

Running the Baseline Inference Script

bash
export HF_TOKEN=hf_your_token
export API_BASE_URL=https://router.huggingface.co/v1
export MODEL_NAME=Qwen/Qwen2.5-72B-Instruct
export IMAGE_NAME=email-triage-env   # local Docker image
export EMAIL_TRIAGE_TASK=easy_triage,medium_triage,hard_triage

python inference.py

Expected output:

[START] task=easy_triage env=email-triage-env model=Qwen/Qwen2.5-72B-Instruct
[STEP] step=1 action=label(e001,priority=urgent) reward=0.40 done=false error=null
[STEP] step=2 action=label(e010,priority=low) reward=0.40 done=false error=null
...
[END] success=true steps=5 score=0.820 rewards=0.40,0.40,0.40,0.40,0.00

OpenEnv Validation

bash
openenv validate

Deploy to Hugging Face Spaces

bash
# Install HF CLI
pip install huggingface_hub

# Login
huggingface-cli login

# Push
openenv push --repo-id your-org/email-triage-env

Baseline Scores

Tested with Qwen/Qwen2.5-72B-Instruct via HuggingFace router:

TaskScoreNotes
easy_triage~0.82Strong on clear signals, occasional priority confusion
medium_triage~0.61Action selection harder; reply/escalate confusion
hard_triage~0.47Reply quality limits score; urgency acknowledgment weak

Frontier model ceiling (GPT-4o, Claude 3.5):

TaskScore
easy_triage~0.95
medium_triage~0.78
hard_triage~0.65

The hard task remains genuinely challenging: drafting relevant, professional replies that reference specific email context requires deep language understanding beyond surface-level classification.


Email Corpus

The environment includes 15 realistic email scenarios across 9 categories:

CategoryExamples
IncidentProduction DB down, security alert
LegalContract deadline, NDAs
SalesEnterprise renewal, pricing request
SupportBug reports, how-to questions
HRBenefits enrollment, expense reports
BusinessPartnership proposals, press requests
BillingAWS invoices, subscription notices
NotificationsGitHub PR approvals, LinkedIn pings
NewsletterMarketing, social media digests

Each email has ground-truth labels for: true_priority, true_category, true_action, and requires_response.


Project Structure

email-triage-env/
โ”œโ”€โ”€ __init__.py          # EmailTriageEnv, EmailAction, EmailObservation
โ”œโ”€โ”€ models.py            # Pydantic model exports
โ”œโ”€โ”€ client.py            # WebSocket EnvClient
โ”œโ”€โ”€ inference.py         # โ† Baseline agent (root level, per OpenEnv spec)
โ”œโ”€โ”€ openenv.yaml         # Environment manifest
โ”œโ”€โ”€ pyproject.toml       # Package config
โ”œโ”€โ”€ README.md
โ””โ”€โ”€ server/
    โ”œโ”€โ”€ __init__.py
    โ”œโ”€โ”€ email_data.py    # 15 email scenarios + 3 task configs
    โ”œโ”€โ”€ environment.py   # EmailTriageEnvironment core logic
    โ”œโ”€โ”€ app.py           # FastAPI via openenv create_app()
    โ”œโ”€โ”€ requirements.txt
    โ””โ”€โ”€ Dockerfile

Environment Variables

VariableDefaultDescription
EMAIL_TRIAGE_TASKeasy_triageActive task name
ENABLE_WEB_INTERFACEfalseEnable Gradio UI at /web
PORT7860Server port
API_BASE_URLHF routerLLM endpoint for inference
MODEL_NAMEQwen2.5-72BModel for inference script
HF_TOKENโ€”HuggingFace API key
IMAGE_NAMEโ€”Docker image for client

License

MIT โ€” contributions welcome.