MetaAI / README.md
Tarun
Claude Sonnet 4.6
Update README to reflect all session changes
a89bbe8
|
Raw History Blame Contribute Delete
19 kB
metadata
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_type is 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 URL
  • HF_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 confidence field; 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