B2B Support Triage OpenEnv Benchmark - Complete Project Overview
1) What this project is
This project is a complete OpenEnv simulation environment designed for real-world B2B SaaS support triage.
Instead of a toy game, it models a workflow support engineers actually perform:
- classify an incoming ticket
- set urgency/priority
- route to the correct team queue
- define SLA expectation
- (hard task) provide policy-compliant customer communication
It exposes the standard OpenEnv contract:
reset(...)step(action)state()
with typed Pydantic models and deterministic graders.
2) Why this environment is useful
Practical utility
This environment can evaluate agent quality on an enterprise operation where correctness, consistency, and policy compliance matter.
Deterministic benchmarking
All tasks are fixture-driven and deterministic, so model comparisons are reproducible.
Multi-step reasoning
Agents must make a sequence of structured decisions, not just one-shot classification.
3) High-level architecture
Main components
- Environment runtime
- File:
server/support_triage_environment.py - Implements environment state machine and reward shaping.
- Typed schemas
- File:
models.py - Defines action, observation, state, and reward-breakdown models.
- Task fixtures
- File:
fixtures/tasks.json - Defines easy/medium/hard scenarios, allowed values, answer keys, and policy hints.
- Grader logic
- File:
graders.py - Computes normalized score in
[0,1]using weighted criteria per task.
- OpenEnv API server wiring
- File:
server/app.py - Binds environment + typed models into OpenEnv HTTP/WebSocket endpoints.
- Inference baseline
- File:
inference.py - Runs all tasks using OpenAI client and emits required structured logs.
- Client adapter
- File:
client.py - Converts typed Python actions/observations to/from wire payloads.
4) OpenEnv contract and endpoint behavior
Manifest
- File:
openenv.yaml - Declares:
name: b2b_support_triage_envruntime: fastapiapp: server.app:appport: 8000
Runtime endpoints
POST /resetPOST /stepGET /stateGET /healthGET /schema
Environment lifecycle
reset(task_id=..., seed=...)
- loads selected task fixture
- clears previous episode state
- returns initial observation with visible ticket + dynamic plan hints
step(action)
- validates action semantics and allowed values
- updates decision state
- computes score delta and shaped reward
- checks done conditions (
submitor max steps)
state()
- returns current internal state object (episode info, step count, cumulative reward, history)
5) Data model design (typed)
5.1 Action model
File: models.py
B2BSupportTriageAction fields:
action_type: enumclassifyset_priorityroutedraft_replysubmit
ticket_id: required for non-submitpayload: action-specific content
payload supports:
categorypriorityroute_queuesla_minutesescalatereply_text
Validation rules
- non-submit requires
ticket_id - required fields per action:
classify -> categoryset_priority -> priorityroute -> route_queue + sla_minutesdraft_reply -> reply_textsubmit -> none
This enforces structured behavior and prevents ambiguous free-text action control.
5.2 Observation model
B2BSupportTriageObservation includes:
task_id,step_index,max_stepsvisible_ticketcurrent_plan(next actions + policy hints)applied_decisionslast_action_errorprogress_score(grader score in[0,1])reward_breakdown:correctness_deltapolicy_bonusrepeat_penaltyinvalid_penaltyterminal_bonus
- inherited OpenEnv fields
reward,done,metadata
5.3 State model
B2BSupportTriageState tracks:
- episode metadata (
episode_id,step_count,task_id,seed) max_stepscumulative_rewardapplied_decisionsaction_historycompletion_flags
6) Task system (easy -> medium -> hard)
Defined in fixtures/tasks.json.
6.1 Easy
Goal:
- correct category + priority for one billing ticket
Hidden answer key includes:
category=billingpriority=medium- plus routing defaults
6.2 Medium
Goal:
- category + priority + queue routing + SLA
Hidden answer key includes:
category=billingpriority=highroute_queue=billing-l2sla_minutes=120
6.3 Hard
Goal:
- security incident handling with escalation + compliant customer response
Hidden answer key includes:
category=securitypriority=urgentroute_queue=security-incident-responsesla_minutes=120escalate=true- required reply phrases
7) Grading mechanics
File: graders.py
Each task has deterministic weighted scoring.
7.1 Easy weighting
- category: 0.6
- priority: 0.4
7.2 Medium weighting
- category: 0.35
- priority: 0.25
- route_queue: 0.25
- sla_minutes: 0.15
7.3 Hard weighting
- category: 0.15
- priority: 0.15
- route_queue: 0.15
- sla_minutes: 0.10
- escalate: 0.20
- reply_policy phrase coverage: 0.25
The result is clamped and normalized to [0,1].
Determinism
The grader is pure:
- no network calls
- no random operations
- same input decisions always yield same score
8) Reward shaping logic
File: server/support_triage_environment.py
Reward is step-wise, dense enough for learning signals, and punishes bad behavior.
Positive components
correctness_delta: only when progress score increasespolicy_bonus: bonus for substantive policy-style draft repliesterminal_bonus:0.2 * final_scorewhen episode ends
Penalty components
invalid_penalty: invalid ticket/value/actionrepeat_penalty:- repeated identical action
- contradiction penalties (changing already-set value)
- no-progress streak penalties
Episode boundaries
- done on
submit - done on max step limit
- steps after done receive penalty (
episode_already_done)
This discourages looping and rewards incremental correctness.
9) Inference baseline design
File: inference.py
9.1 Runtime setup
Reads:
HF_TOKEN(required)API_BASE_URL(default set)MODEL_NAME(default set)LOCAL_IMAGE_NAME(default set)
9.2 Task execution
- runs tasks in fixed order: easy, medium, hard
- fixed seeds per task for reproducibility
9.3 Action selection strategy
- requests JSON action from model (OpenAI client call)
- validates/coerces action
- falls back to deterministic policy if output invalid or misaligned
This keeps baseline robust and reproducible while still exercising LLM calls.
9.4 Required stdout format
Per task episode:
[START] ...- multiple
[STEP] ... [END] ...
Then aggregate score summary line.
10) Deployment and packaging
10.1 Containerization
- Dockerfile:
server/Dockerfile - builds runnable image exposing uvicorn on port 8000
- includes healthcheck on
/health
10.2 Python packaging
pyproject.tomldefines project metadata and dependencies- installs environment package editable in image
10.3 OpenEnv compatibility
openenv validate -vpasses- simulation API surface is compliant
11) Testing strategy
Files under tests/ verify behavior:
test_environment.py- reset clean state
- invalid ticket penalty
- hard task can reach full score
- max-step termination behavior
test_graders.py- perfect answer gives 1.0
- weighted partial score check
- deterministic hard-task reply scoring
test_inference_logging.py[START]/[STEP]/[END]formatting checks
This coverage ensures both task semantics and evaluation fidelity.
12) How to use the project locally
12.1 End-to-end helper
./run_all_checks.sh
Runs tests, validation, docker build/run smoke checks, and optional inference.
12.2 Inference helper (prompts for token if missing)
./run_inference.sh
12.3 Manual core commands
pytest -q
openenv validate -v
docker build -t b2b_support_triage_env-env:latest -f server/Dockerfile .
docker run --rm -p 8000:8000 b2b_support_triage_env-env:latest
Detailed runbook: local_setup.md.
13) Usability summary for your hackathon goal
What this project gives you:
- clear real-world domain
- structured and typed agent action space
- deterministic multi-level tasks
- strong reward shaping (partial progress + anti-loop penalties)
- reproducible baseline script with required logs
- Docker + OpenEnv validation support
This aligns well with your round requirements and helps reduce submission-time surprises.
14) Current limitations and next improvements
Current limitations
- single-ticket episode scope (per task) rather than multi-ticket queue backlog
- deterministic phrase matching for hard reply policy (simple lexical strategy)
- baseline includes deterministic fallback policy (useful for reliability, less pure model-only behavior)
Next upgrades (optional)
- multi-ticket routing episodes with queue-level capacity constraints
- richer policy engine with explicit rule graph and violation tags
- adversarial or noisy customer phrasing variants per seed
- benchmark report exporter with per-criterion analytics
15) Security and publication posture
- fixtures are synthetic
- no hardcoded credentials
- token usage is environment-variable based only
- suitable for external publication, assuming you do not commit local secret files (
.env, shell history, etc.)