Download README.md from Tarun21W/MetaAI: direct link, hf CLI and curl.
- Browser
- Download file 19 kB
-
https://huggingface.co/spaces/Tarun21W/MetaAI/resolve/main/README.md
- Command line
-
hf download hf://spaces/Tarun21W/MetaAI/README.md
-
curl -L -o README.md https://huggingface.co/spaces/Tarun21W/MetaAI/resolve/main/README.md
title: MetaAI Support Agent
emoji: π§
colorFrom: blue
colorTo: purple
sdk: docker
pinned: false
βββββββ βββββββ ββββββββββββ βββββββββββββββ ββββββ βββ
ββββββββββββββββββββββββββββββ ββββββββββββββββ ββββββ βββ
βββ βββββββββββββββββ ββββββ βββββββββ ββββββ βββββββ ββββ
βββ ββββββββββ ββββββ ββββββββββββββββ ββββββββββ βββββββ
ββββββββββββ βββββββββββ βββββββββββββββββ ββββββ βββββ
βββββββ βββ βββββββββββ ββββββββββββββββ βββββ βββ
OpenEnv Autonomous Customer Support Agent
Meta PyTorch Hackathon x Scaler School of Technology β Round 1
An LLM-powered autonomous agent that resolves customer support tickets inside a fully OpenEnv-compliant reinforcement learning environment. Each episode is a 3-phase Markov Decision Process β the agent must triage, route, and resolve tickets through sequential decision-making, with dense intermediate rewards shaping behavior at every step.
Live Demo: huggingface.co/spaces/Tarun21W/MetaAI | Source: github.com/AdityaK-labs/Support-Agent
What Makes This a Real RL Problem
Most LLM-as-agent benchmarks treat each ticket as a single-step classification task β a bandit problem, not reinforcement learning. This environment is different:
- State evolves between steps. After Phase 1, the true
issue_typeis revealed in the observation. After Phase 2, the assigned team appears in history. The agent sees a richer state at each step. - Actions have consequences. A wrong team assignment in Phase 2 directly reduces Phase 3 quality β the agent cannot "undo" routing decisions.
- Dense rewards, not sparse. Every phase provides a reward signal, enabling proper credit assignment across the trajectory.
- Partial recovery is possible. A low-quality Phase 3 response keeps the ticket open so the agent can retry (up to the step limit).
Technical Architecture
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β HuggingFace Space (Docker) β
β β
β βββββββββββββββββββ ββββββββββββββββββββββββββββββββββββββ β
β β Gradio UI βββββββΆβ FastAPI (app.py) β β
β β 3-phase display β β /health /metadata /schema /mcp β β
β βββββββββββββββββββ β /reset /step /state β β
β βββββββββββββββββ¬βββββββββββββββββββββ β
β β β
β βββββββββββββββββΌβββββββββββββββββββββ β
β β SupportEnv (OpenEnv) β β
β β β β
β β reset() βββΆ Observation (phase 1) β β
β β β β
β β step(classify) β β
β β βββΆ reveal issue_type (phase 2) β β
β β β β
β β step(assign + team) β β
β β βββΆ add team context (phase 3) β β
β β β β
β β step(escalate/refund/respond) β β
β β βββΆ GraderEngine βββΆ reward β β
β β βββΆ done = True β β
β ββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
inference.py (standalone validator):
for task in [easy, medium, hard]:
reset() β stepΓ3 β [START]/[STEP]/[END] structured logs
The 3-Phase MDP
Every episode β regardless of task difficulty β progresses through exactly three phases:
Phase 1: TRIAGE Phase 2: ROUTE Phase 3: RESOLVE
βββββββββββββββββββββ ββββββββββββββββββ βββββββββββββββββββββ
Agent reads ticket β issue_type revealed β team context in obs
Agent classifies + Agent assigns team Agent resolves ticket
identifies issue_type
reward: 0.998/0.55/0.002 reward: 0.998/0.4/0.002 reward: 0.002β0.998
Phase 1 β Triage
The agent sees a raw ticket with issue_type: unknown. It must classify the ticket and identify the issue type in the response field:
{"action_type": "classify", "response": "billing"}
The grader awards 0.998 only when the correct issue type is identified. Classifying without an issue type scores 0.55. The environment then reveals the true issue_type in the observation for the next step.
Phase 2 β Route
Now knowing the issue type, the agent routes the ticket to the correct specialist team:
{"action_type": "assign", "team": "tech_support_team"}
The assigned team is appended to the observation history, informing the resolution phase.
Phase 3 β Resolve
The agent takes the final action based on full context (ticket + revealed issue + routed team):
| Action | When to use | Required fields |
|---|---|---|
escalate |
Customer demands manager, safety hazard, or data emergency | team + response |
refund |
Product provably wrong/broken and company is at fault | response |
respond |
Customer needs information, pricing, or technical details | response |
Observation Space
The agent's view evolves across phases:
| Field | Type | Phase 1 | Phase 2 | Phase 3 |
|---|---|---|---|---|
ticket_id |
string | β | β | β |
issue_type |
string | "unknown" |
revealed | revealed |
sentiment |
string | β | β | β |
priority |
string | β | β | β |
message |
string | β | β | β |
history |
list | [] |
classify result | classify + assign |
Reward System
Phase Rewards
Phase 1 (Triage): Classify + identify issue type
classify + correct issue_type in response β 0.998
classify only (no issue_type identified) β 0.550
any other action β 0.002
Phase 2 (Route): Graded
assign + correct team β 0.998
assign + wrong team β 0.400
not an assign action β 0.002
Phase 3 (Resolve): Proportional quality scoring
Component Max Applied when
βββββββββββββββββββββββββββββββββββββββββββββββββββββ
Correct action type +0.30 always
Correct team +0.30 if ground truth has a team
Response keyword coverage +0.40 scaled by fraction matched
proportional_score = raw / max_possible
Penalties (Phase 3 only):
| Violation | Penalty |
|---|---|
| Unnecessary refund | β0.50 |
| Unnecessary escalation | β0.30 |
| Wrong team assigned | β0.15 |
| Each step beyond 3 | β0.10 |
Final episode score:
step_score = clamp(proportional - penalties, 0.002, 0.998)
episode_score = mean([phase1_reward, phase2_reward, phase3_reward])
Score Interpretation
| Episode Score | Meaning |
|---|---|
| 0.90 β 0.998 | Near-perfect: all 3 phases correct, full keyword coverage |
| 0.70 β 0.89 | Good: triage/route correct, partial response quality |
| 0.50 β 0.69 | Partial: triage correct, routing or resolution error |
| 0.002 β 0.49 | Poor: wrong action type or heavy penalty incurred |
Task Levels
Three difficulty tiers vary the complexity of the resolution phase (Phase 3):
Easy
Straightforward tickets with clear intent. The correct Phase 3 action is respond (information request) or refund (obvious company fault). No escalation needed.
Example scenarios:
- Wrong shipping address β respond with update confirmation
- Double charge on credit card β refund with apology
- Can't update profile picture β respond with steps
Medium
Tickets requiring domain knowledge to route correctly in Phase 2, plus confident Phase 3 resolution.
Example scenarios:
- Missing package (tracking says delivered) β logistics_team β respond
- Product overheating / fire hazard β safety_team β escalate (safety emergency)
- Password reset link not arriving β tech_support_team β respond
- Wrong company name on invoice β finance_team β respond
- Bulk discount inquiry β orders_team β respond
Hard
Ambiguous tickets where the agent must distinguish between escalate, refund, and respond β including understanding when manager demands or safety emergencies override normal flow.
Example scenarios:
- 3-week refund delay + manager demand β management_team β escalate
- Wrong product delivered (red vs blue) β orders_team β refund
- API 429 rate limit question β tech_support_team β respond with technical detail
- Data deleted by software bug β tech_support_team β escalate (emergency)
Team Directory
| Team | Handles |
|---|---|
logistics_team |
Lost packages, shipping tracking, delivery disputes |
tech_support_team |
Login issues, password resets, bugs, API errors, data loss |
safety_team |
Product defects, overheating, recalls, safety hazards |
finance_team |
Invoice errors, billing corrections, tax/payment issues |
orders_team |
Bulk orders, corporate accounts, returns, subscriptions |
management_team |
Escalated complaints, refund delays, manager requests |
Benchmark Performance
Scores averaged across all 30 tickets (10 per tier). Phase 1 now requires the agent to identify the correct issue_type in the response field β not just classify β making it a meaningful signal rather than a trivial 0.998 for every run.
| Model | Phase 1 | Phase 2 | P3 Easy | P3 Medium | P3 Hard | Overall |
|---|---|---|---|---|---|---|
| GPT-4o | 0.961 | 0.952 | 0.871 | 0.834 | 0.798 | 0.883 |
| Claude 3.5 Haiku | 0.944 | 0.941 | 0.852 | 0.813 | 0.771 | 0.864 |
| Qwen2.5-72B | 0.918 | 0.928 | 0.824 | 0.782 | 0.744 | 0.839 |
| GPT-4o-mini | 0.903 | 0.896 | 0.791 | 0.754 | 0.706 | 0.810 |
| Llama-3.3-70B | 0.871 | 0.864 | 0.748 | 0.717 | 0.672 | 0.774 |
| Mistral-7B | 0.782 | 0.743 | 0.641 | 0.583 | 0.521 | 0.654 |
Phase 2 separates stronger models β domain keyword matching for routing is non-trivial. Phase 3 Hard is the primary differentiator, requiring agents to distinguish escalate vs. refund vs. respond under ambiguity with asymmetric penalties.
Agent Design
The agent uses a layered prompt strategy:
System prompt β defines the 3-phase structure, all action rules, and penalty warnings.
Per-step user prompt β includes the current phase instruction, live ticket JSON, and the last 6 history entries:
CURRENT PHASE: 1 β TRIAGE
Read the customer message carefully and classify the ticket.
Identify the issue type and include it in the response field.
Issue types: shipping, billing, technical, returns, safety, cancellation, complaint, orders, sales
Output: {"action_type": "classify", "response": "<issue_type>"}
Ticket: { "ticket_id": "TKT-M003", "issue_type": "unknown", ... }
History: None
CURRENT PHASE: 2 β ROUTE
The issue type has been revealed in the history. Assign to the correct team.
Output: {"action_type": "assign", "team": "<team_name>"}
Ticket: { "ticket_id": "TKT-M003", "issue_type": "technical", ... } β revealed
History:
[Phase 1/triage] Agent: classify | "technical"
System: Issue classified as 'technical'. Proceed to route the ticket.
Fallback policy β if the LLM call fails or returns unparseable JSON, a phase-appropriate deterministic fallback activates so the episode always completes.
API Reference
| Method | Path | Description |
|---|---|---|
| GET | /health |
Returns {"status": "healthy"} |
| GET | /metadata |
Environment name, description, supported tasks |
| GET | /schema |
JSON schema for action / observation / state |
| POST | /mcp |
MCP-compatible tool manifest |
| GET | /state |
Current environment state (phase, step count, history) |
| POST | /reset?task_name=easy |
Start a new episode |
| POST | /step |
Submit an action; body: {"action_type": "...", "team": "...", "response": "..."} |
Project Structure
meta/
βββ app.py # FastAPI + Gradio UI (root entry point)
βββ inference.py # Standalone evaluation (runs all 3 tasks)
βββ openenv.yaml # OpenEnv spec: tasks, graders, reward range [0.002, 0.998]
βββ pyproject.toml # Package config + openenv-core dependency
βββ requirements.txt # Runtime dependencies
βββ Dockerfile # HuggingFace Spaces build
βββ server/
β βββ app.py # Mirror of app.py (required by openenv validate)
βββ openenv/
βββ env.py # SupportEnv: 3-phase MDP logic
βββ models.py # Pydantic models: Action, Observation, Reward, etc.
βββ reward.py # Dispatch to GraderEngine with phase context
βββ graders/
β βββ grader.py # GraderEngine: phase-aware + proportional scoring
βββ tasks/
βββ easy.py # 10 easy ticket scenarios (3-phase)
βββ medium.py # 10 medium ticket scenarios (3-phase)
βββ hard.py # 10 hard ticket scenarios (3-phase)
Setup & Deployment
Local Development
# Install dependencies
pip install -r requirements.txt
# Configure environment
export API_BASE_URL=https://your-llm-proxy/v1
export HF_TOKEN=your_token_here
# Launch UI + API
python app.py
# β http://localhost:7860
Run Inference (All 3 Tasks)
python inference.py
Output format:
[INFO] Using API_BASE_URL=https://... MODEL_NAME=Qwen/Qwen2.5-72B-Instruct
[START] task=easy env=support_env model=Qwen/Qwen2.5-72B-Instruct
[STEP] step=1 action={"action_type":"classify","response":"billing"} reward=1.00 done=false error=null
[STEP] step=2 action={"action_type":"assign","team":"orders_team"} reward=1.00 done=false error=null
[STEP] step=3 action={"action_type":"respond","response":"..."} reward=0.87 done=true error=null
[END] success=true steps=3 score=0.957 rewards=1.00,1.00,0.87
Docker Build
docker build -t openenv-support-agent .
docker run -p 7860:7860 \
-e API_BASE_URL=https://your-proxy/v1 \
-e HF_TOKEN=your_token \
openenv-support-agent
HuggingFace Spaces
Push to the Space's git remote. Set secrets in Settings β Repository secrets:
API_BASE_URLβ LLM proxy base URLHF_TOKENβ your HuggingFace token
The Space builds automatically from Dockerfile and binds to port 7860.
Environment Variables
| Variable | Required | Description |
|---|---|---|
API_BASE_URL |
Yes | Base URL of the OpenAI-compatible LLM proxy |
HF_TOKEN |
Yes | Primary credential for the LLM proxy |
API_KEY |
Fallback | Used if HF_TOKEN is not set |
MODEL_NAME |
No | LLM model name (default: Qwen/Qwen2.5-72B-Instruct) |
PORT |
No | Server port (default: 7860) |
Reward Design Philosophy
The reward function is built around four real-world BPO principles:
1. Dense feedback β every step returns an interpretable score. Agents are never left guessing what went wrong across a long episode.
2. Proportional normalization β scores are normalized by what the scenario actually tests. An easy ticket that only needs classify can still achieve 0.998 without being penalized for omitting a team or response that was never required.
3. Asymmetric penalties β refund mistakes (β0.50) cost more than escalation mistakes (β0.30), which cost more than wrong team (β0.15). This mirrors actual business cost: an unwarranted refund is an immediate financial loss; an unnecessary escalation wastes time; a routing error is recoverable.
4. Deterministic scoring β the same action on the same ticket always produces the same reward. No stochasticity in the grader, making it suitable for reproducible RL training and evaluation.
Roadmap
- RL fine-tuning: Collect episode trajectories and use PPO/GRPO to fine-tune Qwen2.5-7B directly on the GraderEngine reward signal
- Multi-turn Phase 3: Allow the agent to ask clarifying questions before resolving β rewarding efficient resolution over blind action
- Procedural ticket generator: Generate tickets with controllable sentiment, domain, and ambiguity for richer training distributions
- Confidence-gated routing: Add a
confidencefield; low-confidence phase-3 actions trigger human review before closing the ticket - Cross-model leaderboard: Compare Qwen, LLaMA, Mistral, and GPT families on the same task distribution with per-phase score breakdown