risheethg/village-microgrid-env
Disaster Relief Coordination
An OpenEnv-compatible benchmark where an LLM agent acts as a disaster relief coordinator — triaging incident reports, dispatching scarce resources across flood-affected zones, and resolving emergencies under hard deadlines and incomplete information.
Why This Environment
Real disaster response coordination is one of the hardest multi-objective reasoning problems humans face: dispatchers must simultaneously triage conflicting casualties, match heterogeneous resource capabilities to terrain conditions, detect misinformation under stress, and make irrevocable allocation decisions with ticking deadlines — all while new information arrives mid-crisis.
No existing OpenEnv benchmark models this concurrent triage → dispatch → monitor loop with terrain physics, dynamic world state, and counterfactual grading. This environment fills that gap directly:
- For RL researchers: A rich, multi-dimensional reward signal with 6+ grading axes — not just task completion, but how the agent allocates, prioritizes, and avoids mistakes.
- For agent evaluators: Deterministic, reproducible episodes that separate good decision-making from lucky rollouts.
- For frontier model testing: The hard task is structurally designed so that even a perfect-knowledge oracle scores ~0.50 — genuine frontier-model difficulty, not artificial trick questions.
Multi-Agent Architecture
The coordinator agent (the LLM) orchestrates three specialist sub-agents through tool-mediated delegation. Each sub-agent encapsulates a domain-specific workflow:
┌──────────────────────┐
│ LLM COORDINATOR │
│ (decision-maker) │
└──────┬───────────────┘
│
┌────────────┼────────────────┐
▼ ▼ ▼
┌─────────────┐ ┌────────────┐ ┌──────────────┐
│ INTAKE AGENT│ │DISPATCH │ │MONITOR AGENT │
│ │ │AGENT │ │ │
│• classify │ │• inventory │ │• check status│
│• urgency │ │• send │ │• close case │
│• verify │ │• reroute │ │• flag false │
└─────────────┘ └────────────┘ └──────────────┘The coordinator decides at each step: Should I triage the new report, dispatch a resource to a critical incident, flag a suspected false alarm, or monitor an ongoing operation? This mirrors how real emergency operations centers work — a single decision-maker delegating to specialized teams while maintaining situational awareness.
Decision Flow Per Step
1. Observe: Read pending reports, zone conditions, resource status, warnings
2. Prioritize: Which action has the highest marginal value right now?
- Critical deadline approaching? → Dispatch immediately
- Unclassified report? → Send to intake agent
- Low-confidence report? → Flag as false alarm
- Stuck resource? → Reroute
- Active assignment aging? → Monitor/close
3. Act: Issue one tool call ({"tool": "...", "args": {...}})
4. World advances: Assignments progress, deadlines tick, conditions change
5. Repeat until max_stepsWhat Makes It Hard
Observation Space
Each step delivers a structured JSON snapshot of the world — no hidden state, no tricks:
Action Space — 12 Tools
Every action is a JSON tool call:
{"tool": "<tool_name>", "args": {"key": "value"}}Coordinator Delegation Tools
Direct Tools
Tasks and Difficulty Progression
Three tasks form a clear difficulty ladder. Complexity scales across every axis simultaneously — more zones, more reports, more deception, stricter deadlines, and deeper grading:
All tasks are generated deterministically from a seed — identical seeds produce identical scenarios.
Resource Fleet
Grading — Multi-Dimensional, Exploit-Resistant
Each episode is scored on [0.0, 1.0]. The grader evaluates multiple complementary dimensions, making it robust against degenerate strategies (dispatching everything wildly fails on resource correctness; ignoring false alarms fails on F1; playing it safe fails on counterfactual).
Task 1 — Flood Response (Easy)
40% resolution + 30% critical + 30% efficiency
Task 2 — Multi-Zone Storm (Medium)
30% resolution + 25% critical + 15% F1 + 15% resource match + 15% counterfactual
Task 3 — Cascade Disaster (Hard)
30% resolution + 25% critical + 15% F1 + 10% resource match + 10% monitoring + 10% counterfactual
Reward Shaping
The per-step reward provides a dense, informative training signal — not just sparse end-of-episode feedback:
- Base: +0.1 per step (minimal, avoids reward gaming)
- Triage rewards: +1.0 classify, +1.0 urgency, +0.5 verify via
call_intake_agent - Dispatch: +2.0 correct resource type, -1.0 wrong type, -1.0 dispatching to false alarm
- False alarm flagging: +1.0 correct flag, -1.5 flagging a real report, -2.0 flagging a critical report
- Temporal urgency multiplier: 2.0x → 1.0x bonus for early action on deadline reports
- Penalties: -0.5 for malformed actions, errors, or repeating the same action twice
Score Calibration
The heuristic baseline is flood-aware and type-matched, so it performs well on medium difficulty. The hard task's score ceiling (~0.50 even for the oracle) reflects genuine structural difficulty — deep flooding, late-arriving reports, and resource scarcity make perfect performance impossible. This leaves meaningful headroom for frontier LLMs to demonstrate planning and reasoning.
Environment Design
Clean software boundaries with no shared mutable state between episodes:
DisasterReliefEnv
├── reset(task, seed) → StepResult # deterministic, clean state
├── step(action) → StepResult # {observation, reward, done, info}
├── grade() → GradeResult # end-of-episode score breakdown
└── close() # cleanup- State management:
WorldStateencapsulates all simulation state.advance_time()runs the simulation tick each step — resource ETAs decrement, deadlines expire, roads unblock, comms restore, fuel depletes. - Observation builder: Stateless function that produces a clean dict from
WorldState— no internal references leak. - Tool registry: All 12 tools registered in
tool_registry.py— adding a tool requires touching one file. - Reward: Pure function over
(state, action_result)— no side effects, fully unit-testable. - Graders: Deterministic, reproducible, score clamped to [0.0, 1.0]. Same seed + same actions = same score.
API Endpoints
Served as a stateful HTTP API (FastAPI, port 7860):
Code Structure
├── app.py # FastAPI server — OpenEnv HTTP API
├── inference.py # Baseline agent: LLM + heuristic fallback
├── openenv.yaml # OpenEnv spec
├── Dockerfile # Python 3.11-slim, non-root, port 7860
├── requirements.txt
└── src/env/
├── environment.py # DisasterReliefEnv — reset / step / grade
├── models.py # Pydantic v2: 7 enums, 4 domain types
├── state.py # WorldState + advance_time() simulation
├── scenarios.py # Deterministic scenario generator (seeded RNG)
├── observation.py # Observation builder (stateless)
├── rewards.py # Per-step reward function (pure, structured)
├── graders.py # Episode graders: F1, counterfactual, resource match
├── tool_registry.py # Tool dispatch table + validation
├── tools_intake.py # classify / urgency / verify
├── tools_dispatch.py # inventory / send / reroute
├── tools_monitor.py # check / close / flag false
└── tools_coordinator.py # Sub-agent delegation wrappersAll domain models use Pydantic v2 with full type annotations. The test suite covers environment logic, grader bounds + idempotency, server endpoints, scenario determinism, stdout compliance, and LLM integration.
Running Locally
Requirements: Python 3.11+, or Docker.
# Install
pip install -r requirements.txt
# Start server
uvicorn app:app --host 0.0.0.0 --port 7860
# Heuristic baseline (no API key needed)
python inference.py --heuristic-only
# With LLM
export API_BASE_URL="https://router.huggingface.co/v1"
export MODEL_NAME="Qwen/Qwen2.5-72B-Instruct"
export HF_TOKEN="your-token-here"
python inference.pyDocker:
docker build -t disaster-relief .
docker run -p 7860:7860 -e HF_TOKEN="your-token" disaster-relief