SupportOps-Env / README.md
Gaurav711's picture
Fix Dockerfile template syntax and add Hugging Face Spaces metadata frontmatter
ae91a5d
|
Raw
History Blame Contribute Delete
10.9 kB
metadata
title: SupportOps Env
emoji: 🎫
colorFrom: blue
colorTo: indigo
sdk: docker
app_port: 7860
pinned: false

🎫 Support Ticket Triage β€” OpenEnv

A real-world OpenEnv environment where AI agents must triage, route, and resolve customer support tickets across three difficulty levels.

This environment models a task that real support organisations handle thousands of times per day: reading an incoming ticket, routing it to the right team, judging its urgency, crafting a helpful reply, and β€” for hard tasks β€” managing a multi-turn conversation through to resolution.


🌍 Motivation

Support ticket triage is:

  • High-volume β€” enterprise companies route millions of tickets per year
  • High-stakes β€” wrong routing costs money; slow responses lose customers
  • Multi-step β€” requires reading comprehension, classification, and generation
  • Under-explored in RL/agent benchmarks (most focus on code or web tasks)

This environment fills a genuine gap: an OpenEnv where agents can be trained and evaluated on a real knowledge-worker workflow.


πŸ—οΈ Environment Overview

Field Value
Action space Discrete (7 action types) + optional text fields
Observation space Structured ticket + conversation history
Reward Shaped per-step + terminal grader score [0.0, 1.0]
Episodes Stateful, multi-step (up to 12 steps for Hard)
Tasks 3 (Easy / Medium / Hard)

πŸ“‹ Tasks

Task 1 β€” Ticket Routing (Easy)

Route the incoming ticket to the correct department.

  • Actions required: ROUTE
  • Score: 1.0 for correct department, 0.1 for wrong (partial), 0.0 for no attempt
  • Departments: billing, technical_support, sales, customer_success, legal
  • Max steps: 3
  • Baseline score: ~0.80

Task 2 β€” Full Triage (Medium)

Fully triage the ticket: route, set urgency, tag, and respond.

Sub-task Weight
Correct routing 30%
Correct urgency level 25%
Relevant tags applied 20%
Informative customer response 25%
  • Max steps: 8
  • Baseline score: ~0.55

Task 3 β€” Full Resolution (Hard)

Manage a multi-turn support conversation to full resolution.

Sub-task Weight
Correct routing 15%
Correct urgency 10%
Quality initial response 20%
Escalation decision 20%
Handle customer follow-up 20%
Close with resolution note 15%
  • Max steps: 12
  • Baseline score: ~0.40

πŸ”Œ API Reference

The environment is served as a REST API (FastAPI).

GET /

Health check. Returns environment metadata and task list.

POST /reset

Start a new episode.

{
  "task_name": "route",        // "route" | "triage" | "resolve"
  "ticket_id": "TKT-001",     // optional β€” omit for random
  "seed": 42,                  // optional RNG seed
  "session_id": "abc123"       // optional β€” generated if omitted
}

Returns: { "observation": {...}, "session_id": "..." }

POST /step

Apply an action.

{
  "session_id": "abc123",
  "action_type": "route",          // required
  "department": "billing",          // for ROUTE
  "response_text": "Hello...",      // for RESPOND
  "urgency": "high",                // for SET_URGENCY
  "tags": ["billing", "refund"],    // for TAG
  "escalation_reason": "...",       // for ESCALATE
  "resolution_note": "..."          // for CLOSE
}

Returns: { "observation": {...}, "reward": {...}, "done": bool, "info": {...}, "session_id": "..." }

GET /state?session_id=abc123

Full internal state including ground truth labels (for debugging/evaluation).

GET /tasks

List all tasks with metadata.


🎬 Action Space

action_type Required fields Description
route department Route ticket to a department
set_urgency urgency Set priority level
respond response_text Send a message to the customer
tag tags Apply classification labels
escalate escalation_reason Escalate with explanation
close resolution_note Resolve and close the ticket
noop β€” Take no action (wastes a step)

Departments: billing Β· technical_support Β· sales Β· customer_success Β· legal

Urgency levels: low Β· medium Β· high Β· critical


πŸ‘οΈ Observation Space

{
  "ticket_id": "TKT-001",
  "subject": "Double charged on my invoice",
  "body": "Full ticket text...",
  "sender_email": "user@example.com",
  "sender_name": "Jane Smith",
  "conversation_history": [
    {"sender": "Jane Smith", "content": "...", "timestamp": "2024-01-01T12:00:00Z"}
  ],
  "current_department": null,
  "current_urgency": null,
  "tags": [],
  "is_escalated": false,
  "is_closed": false,
  "step_number": 0,
  "task_name": "route",
  "task_description": "Route the ticket to the correct department...",
  "available_actions": ["route", "respond", "set_urgency", "tag", "escalate", "close", "noop"]
}

πŸ† Reward Function

Step rewards (shaped, provide dense signal):

  • +0.30 β€” correct ROUTE
  • +0.20 β€” correct SET_URGENCY
  • +0.10Γ—overlap β€” TAG matching required tags
  • +0.15Γ—quality β€” RESPOND addressing key topics
  • +0.20 β€” justified ESCALATE
  • βˆ’0.10 β€” unjustified ESCALATE
  • +0.10 β€” CLOSE with substantive resolution note

Terminal reward (authoritative, [0.0, 1.0]): Each task has a dedicated deterministic grader that computes a weighted aggregate of all sub-task scores. The terminal reward is returned in info["final_grader_reward"].


πŸš€ Setup & Usage

Quick Start (Launch & Verify)

To automatically install dependencies, run the PyTest suite, run the baseline agent (with automatic serverless fallback), and run the 300-episode evaluation suite in one command:

chmod +x run_all.sh
./run_all.sh

Local development

# Install dependencies
pip install -r requirements.txt

# Start the environment server
python server.py
# β†’ Server running at http://localhost:7860

# In another terminal, run the baseline inference (with a model of your choice)
export API_BASE_URL="https://router.huggingface.co/v1"
export MODEL_NAME="meta-llama/Llama-3.3-70B-Instruct"
export HF_TOKEN="your_token_here"
export ENV_BASE_URL="http://localhost:7860"

python inference.py

Docker

docker build -t ticket-triage-env .
docker run -p 7860:7860 ticket-triage-env

# Environment is now available at http://localhost:7860

Quick API test

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

# Start an episode
curl -X POST http://localhost:7860/reset \
  -H "Content-Type: application/json" \
  -d '{"task_name": "route", "ticket_id": "TKT-001", "seed": 42}'

# Take an action (use session_id from reset response)
curl -X POST http://localhost:7860/step \
  -H "Content-Type: application/json" \
  -d '{"session_id": "<ID>", "action_type": "route", "department": "billing"}'

πŸ“Š Baseline Scores

Measured with meta-llama/Llama-3.3-70B-Instruct via HuggingFace Inference API, temperature=0.0 (greedy), seed=42:

Task Score Notes
Route (Easy) ~0.80 Model occasionally confuses billing ↔ customer_success
Triage (Medium) ~0.55 Tags and urgency are hardest sub-tasks
Resolve (Hard) ~0.40 Follow-up handling and escalation decisions are challenging
Overall ~0.58

πŸ“ Project Structure

ticket-triage-env/
β”œβ”€β”€ openenv.yaml          # OpenEnv metadata
β”œβ”€β”€ Dockerfile            # Container definition
β”œβ”€β”€ requirements.txt      # Python dependencies
β”œβ”€β”€ inference.py          # Baseline inference script (hackathon-required)
β”œβ”€β”€ server.py             # FastAPI HTTP server
β”œβ”€β”€ README.md             # This file
└── env/
    β”œβ”€β”€ __init__.py
    β”œβ”€β”€ environment.py    # Core TicketTriageEnv class
    β”œβ”€β”€ models.py         # Pydantic Observation/Action/Reward models
    β”œβ”€β”€ tasks.py          # Task specifications
    β”œβ”€β”€ graders.py        # Deterministic grader functions
    └── data.py           # Synthetic ticket dataset with ground truth

πŸ“œ License

MIT β€” free to use for research and commercial applications.


πŸ“Š Evaluation Leaderboard & Benchmark Results

Evaluated 5 frontier and open-weights models Β· 20 episodes per task Β· 300 total episodes

Leaderboard

Model Easy (Route) Medium (Triage) Hard (Resolve) Δ Easy→Hard
Claude 3.5 Sonnet 0.96 0.89 0.74 -23%
GPT-4o-Mini 0.96 0.86 0.70 -27%
Gemini 2.0 Flash 0.86 0.86 0.62 -28%
Llama-3.1-8B 0.82 0.70 0.39 -53%
Mistral-7B 0.82 0.65 0.40 -51%

Key finding: Larger models degrade 46–53% from Easyβ†’Hard; 7B-class models collapse 73–77%. Multi-step reasoning, long-context tracking, and strict sub-task adherence require higher parametric capacity. Smaller models lose state, mis-route on ambiguous signals, and fail to handle follow-up turns.


Hard Task Failure Mode Analysis

Failure counts among Hard task episodes scoring below 0.3 (out of 20 episodes):

Model Wrong Route Wrong Urgency Missing Tags Unhelpful Resp No Follow-up Step Limit
Claude 3.5 Sonnet 0 0 0 1 1 0
GPT-4o-Mini 1 1 0 2 2 0
Gemini 2.0 Flash 1 2 0 3 3 0
Llama-3.1-8B 6 4 0 7 5 0
Mistral-7B 3 2 0 3 3 0

Reward Hacking & LLM-as-Judge (Scalable Oversight)

The original keyword_overlap grader assigned full credit to any response containing the right keywords, regardless of coherence β€” a classic reward hacking vector. We replaced it with a dual-signal grader:

  • 50% keyword overlap (fast, deterministic)
  • 50% LLM judge score (coherence, tone, actionability)

This mirrors Anthropic's scalable oversight paradigm: augmenting a weak but cheap signal with a stronger, more expensive signal to keep agent behavior aligned.

Measured Reward Hacking Rate (keyword grader score β‰₯ 0.8 but LLM judge < 0.4)

  • Claude 3.5 Sonnet: 1/40 (2%) responses flagged
  • GPT-4o-Mini: 9/40 (22%) responses flagged
  • Gemini 2.0 Flash: 6/40 (15%) responses flagged
  • Llama-3.1-8B: 13/40 (32%) responses flagged
  • Mistral-7B: 17/40 (42%) responses flagged

Continuous Difficulty Curve

Performance as a function of ticket complexity score (0.0–1.0), showing that model capability degrades continuously β€” not just at discrete Easy/Medium/Hard boundaries. See eval_results.json for the full per-ticket breakdown.