CoolFace
Apppublic

mcqueenmater/env-corporate

sourceHugging Faceupdated 4mo agoView on Hugging Face
0likes
App README

πŸ›οΈ Corporate Policy Compliance Environment

An OpenEnv-compliant Reinforcement Learning environment that simulates how enterprise compliance officers audit employee expense claims and corporate action requests against internal policy documents.

![OpenEnv Spec](https://openenv.dev) ![Python](https://python.org) ![HuggingFace](https://huggingface.co/spaces/mcqueenmater/env-corporate) ![License](LICENSE)

πŸ€— Live Space: https://huggingface.co/spaces/mcqueenmater/env-corporate


πŸ“‹ Overview

Every company in the world processes hundreds of expense reports, approval requests, and compliance tickets every day. Today, this requires human auditors who manually read policy documents and make judgement calls. This environment trains an RL agent to do exactly that β€” understand a request, retrieve the relevant policy rule, and make a compliant decision.

This mirrors real production systems used at companies like Ramp, Concur, and SAP β€” but is the open-source RL training environment for this domain. It is grounded in Indian corporate compliance norms: β‚Ή-denominated limits, GST receipt requirements, WFH allowances, and local travel policies (auto-rickshaw, cab, metro).


🎯 Quick Reference: What The Agent Does

The agent plays the role of a corporate compliance officer. Each episode, it receives one employee expense claim and must decide:

  • β€”βœ… Approve β€” claim follows all policy rules
  • β€”βŒ Reject β€” claim violates policy
  • β€”βš οΈ Escalate β€” claim requires senior review (L7+ employees)

The agent can also:

  • β€”πŸ” SearchPolicy β€” look up relevant rules before deciding
  • β€”πŸ“‹ RequestInformation β€” ask for missing documents

πŸ“‹ The 15 Policy Rules (Quick Reference)

#CategoryRule
1MealUnder β‚Ή500 β†’ Approve, no receipt needed
2Mealβ‚Ή500–₹2,000 β†’ receipt required
3MealOver β‚Ή2,000 β†’ receipt + manager note required
4AlcoholAny alcohol on bill β†’ Reject entire claim
5TravelAuto/metro under β‚Ή500 β†’ no receipt needed
6TravelCab after 10 PM β†’ pre-approved with receipt
7TravelCab before 10 PM β†’ manager note required
8FlightL1–L6 must fly economy β†’ business class = Reject
9FlightL7+ may fly business class β†’ Escalate for review
10InternationalOver β‚Ή50,000 β†’ VP approval required
11WFHInternet + electricity capped at β‚Ή1,000/month
12GSTClaims over β‚Ή5,000 β†’ GST invoice required
13DuplicateSame amount + same date = auto Reject
14SeniorityL7+ employees β†’ always Escalate
15PersonalPersonal expenses β†’ always Reject

πŸ† Performance

Current test-set results use 120 held-out claims: 40 easy, 40 medium, and 40 hard.

MethodOverallEasyMediumHard
Rule Baseline0.7170.9090.7130.528
Generic LLM0.7160.8820.7130.553
Trained LLM0.7130.8010.7500.588

The overall score is nearly tied, but the trained model improves on the harder workflow-heavy cases: +0.037 on Medium, +0.035 on Hard, and +0.036 on Medium+Hard average versus the generic LLM. It also uses the right tools more often after training: SearchPolicy rises from 53 to 78 actions, and RequestInformation rises from 29 to 40 actions in eval logs.

Curriculum bands are defined in `app/curriculum_targets.py` and `openenv.yaml`.


Use the Live Space

Visit the running instance: https://huggingface.co/spaces/mcqueenmater/env-corporate

Run locally

bash
git clone https://github.com/VanshGupta18/corporate-compliance-env.git
cd corporate-compliance-env
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt

uvicorn app.server.app:app --host 0.0.0.0 --port 7860
# API docs: http://localhost:7860/docs
# Dashboard: http://localhost:7860/dashboard

openenv validate --url http://localhost:7860 --verbose
pytest tests/ -q

Baseline and inference

bash
python app/baseline.py          # uses COMPLIANCE_API or the default HF Space
python inference.py             # writes inference_run.log + inference_results.json
python -m training.eval_checkpoint --split test
# writes training_run.log, training_results.json, and episodes.jsonl

Train on Google Colab (Unsloth SFT + GRPO)

Notebook: `notebooks/Colab_T4_Training.ipynb` β€” in Colab use File β†’ Open notebook β†’ GitHub and pick your fork, or upload the notebook. Set Runtime β†’ T4 GPU, then set GITHUB_USER in the first code cell.


Docker

bash
docker build -t compliance-env .
docker run -p 7860:7860 compliance-env

See `Dockerfile` and `openenv.yaml` for container and OpenEnv metadata.


πŸ“‘ API Endpoints (Quick Reference)

EndpointMethodDescription
/healthGETServer health check
/wsWebSocketPrimary OpenEnv episode API. Use for stateful reset / step / state loops
/resetPOSTStateless reset smoke check β€’ Body: `{"task_id": "easy\medium\hard"}`
/stepPOSTStateless single action endpoint β€’ Body: {"action": ComplianceAction}. Do not use for multi-step episodes
/stateGETStateless state endpoint for debugging
/tasksGETList all tasks + action schema
/graderPOSTGet final score for completed episode
/baselinePOSTRun baseline agent on all 3 tasks
/docsGETSwagger interactive API documentation
/dashboardGETReact benchmark dashboard
/demoGETMinimal Gradio wrapper

πŸ‘₯ Team

NameRole
Vansh GuptaBackend, Environment Design, Deployment, LLM Agent
SanyaDataset Generation, Policy Rules, Baseline Agent
VedikaQA, Testing, Validation, Bug Fixes

Built for the Meta PyTorch OpenEnv Hackathon 2026.


The agent acts as an AI Compliance Officer. At each step it receives an open "Compliance Ticket" (an expense claim or request) and must:

  1. 1.Understand what the employee is claiming
  2. 2.Search the company policy rulebook if the relevant rule is unknown
  3. 3.Request missing documents from the simulated employee if needed
  4. 4.Resolve the ticket: Approve, Reject, or Escalate

The agent is not handed all information at once. It must earn it β€” mirroring how a real compliance officer navigates incomplete files.


πŸ“¦ Project Structure

meta-openenv/
β”œβ”€β”€ app/
β”‚   β”œβ”€β”€ models.py, client.py, graders.py, baseline.py, dashboard.py
β”‚   β”œβ”€β”€ curriculum_targets.py, policy_snippets.py
β”‚   └── server/               # ComplianceEnv + FastAPI app
β”œβ”€β”€ server/                   # OpenEnv entrypoint (re-exports app.server.app)
β”œβ”€β”€ data/
β”‚   β”œβ”€β”€ policy.md, claims.json, splits/
β”‚   └── generate_dataset.py
β”œβ”€β”€ training/
β”‚   β”œβ”€β”€ prepare_data.py, sft_train.py, grpo_train.py, eval_checkpoint.py
β”‚   β”œβ”€β”€ learning_curve.py, smoke_test.py, training_utils.py
β”‚   └── requirements-training.txt
β”œβ”€β”€ tests/                    # API, graders, curriculum, training smoke
β”œβ”€β”€ scripts/                  # validate-submission.sh, validate_dataset.py
β”œβ”€β”€ inference.py
β”œβ”€β”€ openenv.yaml, Dockerfile, TRAINING.md
└── requirements.txt

Generated at runtime: baseline_run.log, baseline_results.json, inference_run.log, inference_results.json, training_run.log, training_results.json, episodes.jsonl, training/data/, training/logs/, and training/checkpoints/.


🧠 Environment Design

Action Space (ComplianceAction)

The agent takes one of exactly three action types per step:

ActionParametersWhen to Use
SearchPolicyquery: strPolicy rule is unknown β€” search the rulebook
RequestInformationmessage: strDocument is missing from the ticket
ResolveTicketdecision: str, reason: strReady to make final call

Valid decision values: "Approve", "Reject", "Escalate"

Invalid action handling: If the agent sends an unrecognised action_type or missing required fields, the server returns HTTP 400 and applies a -0.1 step penalty. The episode continues.


Observation Space (ComplianceObservation)

At every step, the agent observes:

json
{
  "ticket_id": "EXP-042",
  "employee_name": "Priya Sharma",
  "employee_role": "Junior Engineer",
  "employee_level": "L3",
  "amount": 5000.0,
  "currency": "INR",
  "description": "Client dinner including wine",
  "has_receipt": true,
  "missing_document": "manager_approval",
  "rule_keyword": "entertainment",
  "risk_score": 0.72,
  "env_message": "New ticket received. What is your action?",
  "step_count": 1,
  "max_steps": 8,
  "is_terminal": false
}

Field glossary:

FieldTypeDescription
missing_document`str \null`What document is absent (null if nothing missing)
rule_keywordstrHint for SearchPolicy query (hidden on medium/hard)
risk_scorefloat 0–1Pre-computed risk signal based on amount + role
env_messagestrLatest message from the environment or simulated employee
step_countintSteps taken so far in this episode

State Schema (ComplianceState)

GET /state returns the full mid-episode state:

json
{
  "current_observation": { "...ComplianceObservation fields..." },
  "episode_id": "ep-007",
  "task_id": "hard",
  "steps_taken": 3,
  "actions_history": [
    {"step": 1, "action_type": "SearchPolicy", "query": "entertainment policy"},
    {"step": 2, "action_type": "RequestInformation", "message": "Please share manager approval"}
  ],
  "rewards_history": [0.1, 0.1],
  "cumulative_reward": 0.2,
  "is_done": false
}

Reward Function

Rewards are given at every step β€” not just at the end. This provides a rich training signal over the full trajectory.

EventRewardNotes
Correct ResolveTicket+1.0Full credit for correct final decision
Relevant SearchPolicy+0.15Rule was genuinely unknown at that point
Correct RequestInformation+0.15Document was actually missing
Irrelevant SearchPolicy-0.05Rule was already visible in observation
Asking for info already in ticket-0.2Agent ignored visible context
Wrong ResolveTicket decision-1.0Fatal β€” episode ends immediately
Invalid action format-0.1Malformed action; episode continues
Exceeding max steps-0.5Penalise infinite loops

All rewards are clamped to [-1.0, 1.0] as declared in openenv.yaml.

Episode termination rules:

  • β€”Episode ends immediately on ResolveTicket (correct or wrong)
  • β€”Episode ends if step_count exceeds max_steps for that task
  • β€”Wrong ResolveTicket ends the episode with -1.0 reward
  • β€”All other wrong actions: episode continues, penalty applied

πŸ“Š Tasks

🟒 Task 1 β€” Single-Step Classification (Easy)

Objective: The ticket is fully self-contained. The relevant policy rule is provided directly in the observation. Agent should immediately call ResolveTicket.

  • β€”Max Steps: 3
  • β€”Expected Steps: 1
  • β€”Grader logic:

Easy scoring is component-based: valid ResolveTicket, correct decision, valid reason, and no unnecessary tool calls. A wrong decision stays below the success threshold even if the JSON and reason are valid.

  • β€”Example:
Ticket: "Meal expense β‚Ή800, no receipt attached." Policy shown: "Receipts required for meals above β‚Ή500." Correct action: ResolveTicket(decision="Reject")

🟑 Task 2 β€” Policy Retrieval (Medium)

Objective: Ticket is provided but the policy rule is hidden. Agent must call SearchPolicy with the right keyword first, then resolve.

  • β€”Max Steps: 5
  • β€”Expected Steps: 2
  • β€”Grader logic:

Medium scoring requires a useful SearchPolicy before final decision credit. A lucky correct ResolveTicket without useful search is capped below the success threshold.

  • β€”Example:
Ticket: "Business class flight Mumbai→Delhi, ₹45,000." Agent must search "flight class policy" to find: "Business class permitted only for VP (L7) and above." Employee role: Manager (L5) → Correct: ResolveTicket(decision="Reject")

πŸ”΄ Task 3 β€” Multi-Turn Contextual Decision (Hard)

Objective: Ticket has a missing document. Agent must identify it, call RequestInformation, evaluate the returned document against policy, and resolve β€” all while weighing employee seniority as a risk factor.

  • β€”Max Steps: 8
  • β€”Expected Steps: 3–4
  • β€”Grader logic (multi-component):

Hard scoring requires the intended workflow: useful policy search, correct document request, then a correct final decision. Correct guesses that skip search or document request are capped below the success threshold.

  • β€”Example:
Ticket: "International travel β‚Ή1,20,000 β€” no VP approval note." Agent asks: RequestInformation("Please share VP approval for international travel") Environment returns: "Approval mail from VP Rajesh Mehta attached." Agent verifies β†’ ResolveTicket(decision="Approve", reason="VP approval confirmed")

⚠️ Edge Cases

The dataset includes deliberately tricky cases to test grader robustness:

ScenarioAmountRule ThresholdGround TruthWhy Tricky
Meal just under limitβ‚Ή1,999β‚Ή2,000 receipt ruleApproveOne rupee under β€” no receipt needed
Meal just over limitβ‚Ή2,001β‚Ή2,000 receipt ruleRejectOne rupee over β€” receipt required
Auto-rickshaw, no receiptβ‚Ή450β‚Ή500 local travel thresholdApproveBelow threshold; mode allowed
Cab at 11 PMβ‚Ή1,200Night travel policyApproveLate-night cab is explicitly allowed
WFH internet claimβ‚Ή999β‚Ή1,000/month WFH capApproveUnder cap β€” valid WFH expense
Alcohol in restaurant billβ‚Ή3,500Zero alcohol policyRejectAlcohol line item voids entire claim
VP submitting small claimβ‚Ή500Any amount for L7+EscalateHigh-seniority = always escalate
Duplicate claim same dayβ‚Ή2,200Anti-duplication ruleRejectSame employee, same amount, same day

πŸ—‚οΈ Dataset & Policy

data/policy.md β€” 15 Company Rules

The agent's rulebook covers:

  1. 1.Meals under β‚Ή500 β€” no receipt required
  2. 2.Meals β‚Ή500–₹2,000 β€” receipt required
  3. 3.Meals above β‚Ή2,000 β€” receipt + manager approval
  4. 4.Alcohol is never an approved expense category
  5. 5.Local travel (auto/metro) under β‚Ή500 β€” no receipt needed
  6. 6.Cab rides after 10 PM β€” always approved with receipt
  7. 7.Daytime cab rides β€” require manager note
  8. 8.Domestic flights β€” economy class only for L1–L6
  9. 9.Business class β€” permitted for L7 (VP) and above only
  10. 10.International travel above β‚Ή50,000 β€” VP approval mandatory
  11. 11.WFH internet/electricity allowance β€” max β‚Ή1,000/month
  12. 12.Duplicate claims (same employee, amount, date) β€” auto-reject
  13. 13.Any claim from L7+ employee β€” escalate regardless of amount
  14. 14.GST receipt required for all claims above β‚Ή5,000
  15. 15.Personal shopping, gifts, and entertainment without client present β€” reject

data/claims.json and data/splits/ β€” Curriculum Claims

json
{
  "id": "EXP-001",
  "employee_name": "Ankit Verma",
  "employee_role": "Junior Engineer",
  "employee_level": "L3",
  "description": "Taxi ride at 2:00 PM without manager note",
  "amount": 800,
  "currency": "INR",
  "has_receipt": true,
  "missing_document": "manager_approval",
  "rule_keyword": "daytime cab",
  "risk_score": 0.65,
  "ground_truth_decision": "Reject",
  "ground_truth_reason": "Daytime cab requires manager approval per policy rule 7"
}

ground_truth_* fields are stored in datasets for graders and offline analysis only. They are not populated in agent observations.

Distribution: balanced easy / medium / hard curriculum claims Split: explicit train, validation, and test JSON files under data/splits/

data/generate_dataset.py

Regenerate the full synthetic dataset at any time:

bash
python data/generate_dataset.py \
  --train-per-diff 120 --val-per-diff 40 --test-per-diff 40 --seed 42

Writes data/claims.json and data/splits/{train,validation,test}.json. Then build SFT rows:

bash
python training/prepare_data.py --episodes-per-task 40 --split train

Manual QA report: python scripts/validate_dataset.py (run from repo root).


βš™οΈ API Endpoints

MethodEndpointDescription
WS/wsPrimary stateful OpenEnv session API. Use this for all multi-step episodes
POST/resetStateless reset smoke check. Body: `{"task_id": "easy\medium\hard"}`
POST/stepStateless single action endpoint. Body: {"action": ComplianceAction}
GET/stateStateless debugging endpoint
GET/tasksList all tasks + full action schema
POST/graderGet final score for completed episode
POST/baselineRun baseline agent on all 3 tasks, return scores

OpenEnv creates a fresh environment for each HTTP request. Keep a WebSocket open through ComplianceEnvClient(...).sync() for normal reset() / step() loops.

/tasks response

json
{
  "tasks": ["easy", "medium", "hard"],
  "action_schema": {
    "action_type": "str β€” SearchPolicy | RequestInformation | ResolveTicket",
    "query": "str | null β€” required for SearchPolicy",
    "message": "str | null β€” required for RequestInformation",
    "decision": "str | null β€” Approve | Reject | Escalate",
    "reason": "str | null β€” required for ResolveTicket"
  }
}

πŸ“ˆ Why This Environment Matters

Companies like Ramp, Concur, and SAP spend millions building proprietary AI auditing systems. This is the first open-source RL training environment for corporate policy compliance β€” enabling any researcher or company to train and benchmark agents for enterprise expense auditing without proprietary data.

Because the policy document is a plain policy.md file, any company can drop in their own rulebook β€” making this a general framework, not just a demo. A small SFT + GRPO run already shifts the model toward the behavior compliance teams need: searching policy, asking for missing evidence, and improving on medium/hard tickets even when overall average remains close to the generic LLM.


Built for the Meta Hackathon 2026.

--