Spaces:
Sleeping
Sleeping
File size: 13,874 Bytes
aa8d9a1 36068f1 aa8d9a1 36068f1 aa8d9a1 36068f1 aa8d9a1 36068f1 9c90904 f2699c7 36068f1 aa8d9a1 36068f1 aa8d9a1 36068f1 aa8d9a1 36068f1 aa8d9a1 36068f1 aa8d9a1 36068f1 aa8d9a1 36068f1 aa8d9a1 36068f1 aa8d9a1 36068f1 aa8d9a1 36068f1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 | ---
title: PM-Ops RL Environment
emoji: π
colorFrom: blue
colorTo: purple
sdk: docker
app_port: 8000
pinned: false
tags:
- openenv
- reinforcement-learning
---
# PM-Ops π οΈ
> **Can a small model learn to operate inside a company it has never seen before?**
PM-Ops is a reinforcement learning benchmark and training environment where an LLM agent operates as a product manager inside a fully simulated software organization β navigating ticketing, codebases, and chat β using only the organization's own runbook as its guide.
---
## Links
| Resource | Link |
|---|---|
| π€ **Live Environment** | [TheCrustaceans/Pm-ops β HuggingFace Space](https://huggingface.co/spaces/TheCrustaceans/Pm-ops) |
| π **Blog Post** | [BlogPost.mdx](https://huggingface.co/spaces/TheCrustaceans/Pm-ops/blob/main/BlogPost.md) |
| π₯ **inference image** | [Google Drive](https://drive.google.com/file/d/1JdwYukKrEaMTaOwc1W4Q8bBRjxwGZ2be/view?usp=drive_link) |
| π **train image** | [Google Drive](https://drive.google.com/file/d/1Wb8G0WEPPvAFppBNSVMZ7EMFBPFjsP8n/view?usp=sharing) |
---
## Table of Contents
- [Motivation](#motivation)
- [Environment](#environment)
- [The Three Apps](#the-three-apps)
- [Action Space](#action-space)
- [Observation Schema](#observation-schema)
- [Org Generator](#org-generator)
- [Task Types](#task-types)
- [Difficulty Tiers](#difficulty-tiers)
- [Reward Design](#reward-design)
- [Training Pipeline](#training-pipeline)
- [SFT Warmup](#sft-warmup)
- [GRPO](#grpo)
- [Reward Shaping](#reward-shaping)
- [Results](#results)
- [Project Structure](#project-structure)
- [Setup & Usage](#setup--usage)
---
## Motivation
Frontier models achieve near-perfect scores on standard benchmarks. Yet when deployed inside a real organization, they routinely fail tasks that a junior employee handles on day one.
The reason: **organizational context is not on the internet.**
A model knows that database failures are serious. It does not know that *your* company outsources DB infrastructure to a vendor and that the correct response is an email, not an internal incident ticket. No benchmark measures this gap. PM-Ops does.
**Why system prompts are not the answer:**
| Problem | Detail |
|---|---|
| Attention decay | As conversation grows, the model attends to early context instructions with diminishing weight. Org conventions buried in a long system prompt get effectively ignored. |
| Static snapshots | Channels get renamed. Teams get reorganized. A system prompt written last quarter is already wrong. |
| No gradient | The model is told what to do, not trained on the consequences of ignoring it. There is no learning signal. |
PM-Ops addresses this by making the agent *experience* the consequences of skipping the runbook across thousands of varied organizational configurations during training.
---
## Environment
PM-Ops runs as a WebSocket-based environment server (OpenEnv-compatible) hosted on HuggingFace Spaces. Each episode presents the agent with a freshly generated organization β different team names, channel names, label taxonomies, and priority levels every time.
### The Three Apps
```
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PM-Ops Environment β
β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β Ticketing β β Codebase β β Chat β β
β β (Jira-like) β β (GitHub-like)β β (Slack-like) β β
β β β β β β β β
β β tickets β β repositories β β channels β β
β β projects β β commits β β threads β β
β β teams β β pull requestsβ β DMs β β
β β labels β β file authors β β user profilesβ β
β β priorities β β changed filesβ β β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β
β Ground truth verified against DB β not LLM-judged β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
**Ticketing App:** The source of truth for org state. Agent creates tickets, assigns teams, sets labels and priorities. Correctness is verified by checking the database, not asking another LLM.
**Codebase App:** Pure detective work. No code is written. When a bug report arrives, the agent traces commit history, identifies the responsible change, and finds the author.
**Chat App:** Episode-specific channels. The channel `#oncall-payments` in episode 1 may be `#urgent-billing` in episode 2. The agent must read the runbook to know which one is live β not guess from training data.
### Action Space
```json
{ "action_type": "meta.read_runbook", "args": {} }
{ "action_type": "meta.finish", "args": {} }
{ "action_type": "meta.noop", "args": {} }
{ "action_type": "ticketing.create_ticket", "args": { "summary": "...", "label": "...", "priority": "...", "assignee": "..." } }
{ "action_type": "ticketing.assign_ticket", "args": { "ticket_id": "...", "team": "..." } }
{ "action_type": "chat.list_channels", "args": {} }
{ "action_type": "chat.post_message", "args": { "channel": "...", "text": "..." } }
{ "action_type": "codebase.get_commits", "args": { "repo": "..." } }
```
All actions are emitted as chain-of-thought reasoning followed by a ` ```json ` block. Three-pass extraction handles malformed outputs: code block β raw JSON β regex fallback.
### Observation Schema
Each step returns:
```python
{
"task_brief": str, # the incident/task description
"last_action_result": dict, # {"ok": bool, "data": ..., "error": ...}
"step": int,
"steps_remaining": int,
"reward": float, # always 0.0 until meta.finish
"done": bool,
"token_budget_remaining": int,
}
```
**Delayed reward:** `reward` is always `0.0` until the agent calls `meta.finish`. This forces the agent to commit to a plan and execute it β step-by-step reward hacking is not possible.
### Org Generator
LLM-generated scenarios sound plausible but fail formal verification. PM-Ops uses a **deterministic, parameterized, seedable org generator** instead.
Each org is generated from a seed and produces:
- `label_taxonomy` β org-specific bug/feature labels
- `priority_levels` β org-specific severity scale
- `team_map` β service β owning team
- `oncall_channels` β team β notification channel
- `required_ticket_fields` β what fields must be set for a valid ticket
The same seed always produces the same org. Evaluation is reproducible. Training sees a different org every episode.
### Task Types
| Task | Description | Key Challenge |
|---|---|---|
| **Triage** | Bug report arrives. Create ticket with correct label, priority, assignee. Notify correct channel. | Using org taxonomy, not generic labels. |
| **Incident Routing** | Production incident. Identify affected services, find owning team via codebase + team map, escalate. | Cross-system reasoning: chat β codebase β ticketing. |
| **Release Notes** | Compile and post release notes for resolved tickets in the org's specific format. | Format compliance, not just content. |
| **Dependency Update** | Breaking library change hits multiple services. Notify each team through their own oncall channel, create per-team tickets. | Multi-target coordination without collapsing to single-service logic. |
### Difficulty Tiers
| Tier | Services | Labels | Priorities | Notes |
|---|---|---|---|---|
| **Easy** | 2 | 3 | 2 | No ambiguity. Runbook is complete. |
| **Medium** | 3 | 4 | 4 | Partial runbook. Agent must infer across systems. Multi-owner configs. |
| **Hard** | β₯5 | 5+ | 5+ | Noise channels designed to distract. Outdated/missing docs. Simulated panicking users in general chat β correct behavior is to ignore them. |
The hard tier specifically tests resistance to NLP pressure. A model that responds to "THE SITE IS DOWN PLEASE HELP" in `#general` instead of reading the runbook and acting on verified info fails the episode.
---
## Reward Design
Rewards are **delayed** β revealed only at `meta.finish`.
### Scoring Breakdown
```
Ticket created correctly +0.25
Correct label +0.20
Correct priority +0.20
Correct team assignment +0.20
Correct channel notification +0.15
βββββββββββββββββββββββββββββββββ
Maximum per episode 1.00
```
### Penalties
```
Wrong channel post -0.05 each
(>2 wrong posts β net negative; defeats channel-spray)
Duplicate ticket -0.10 each
Zero valid actions (inaction)-1.00
```
The `-0.05` per wrong channel is calibrated: posting to every channel to guarantee hitting the right one becomes net negative after 2 wrong posts. The agent must read the runbook to know the correct channel.
### Combined Training Reward
```python
if valid_action_count == 0:
combined = -1.0 # never output valid JSON
elif final_score == 0.0:
combined = valid_json_ratio * 0.15 # tried but failed
+ read_runbook_reward * 0.10
- 0.30 # hard penalty for zero completion
else:
combined = final_score * 0.45 # task correctness
+ no_wrong_channels * 0.15 # anti channel-spray
+ valid_json_ratio * 0.15 # format discipline
+ read_runbook_reward * 0.15 # process compliance
+ efficiency * 0.10 # steps saved
```
---
## Training Pipeline
### SFT Warmup
Before GRPO, a short supervised fine-tuning phase runs on baseline agent demonstrations. Without this, the model outputs prose instead of structured JSON actions and the GRPO gradient is zero β the model needs to learn the output format before it can learn the task.
### GRPO
We use **Group Relative Policy Optimization** to train a 7B parameter model directly against PM-Ops episode rewards.
Each training step is a full PM-Ops episode:
1. Model plays through up to 15 turns
2. Environment scores the final state
3. Gradient updates weights based on relative performance across the generation group
No intermediate reward signal is given. The model must learn to plan across multiple steps.
### Reward Shaping
| Component | Weight | Purpose |
|---|---|---|
| `final_score` | 0.45 | Primary correctness from env grader |
| `no_wrong_channels` | 0.15 | Anti-hack: penalise channel spray |
| `valid_json_ratio` | 0.15 | Format discipline |
| `read_runbook` | 0.15 | Process compliance |
| `efficiency` | 0.10 | Steps saved (only when task succeeds) |
`PMOpsGRPOTrainer` subclasses TRL's `GRPOTrainer` and overrides `_calculate_rewards()` to inject pre-computed rewards from the rollout directly, bypassing TRL's broken kwargs flow for multi-turn rollouts.
---
## Results
Evaluated on 13 matched episodes, comparing the heuristic baseline agent against the GRPO-trained model:
| Model | Avg Score (13 eps) |
|---|---|
| Heuristic Baseline | 0.269 |
| GRPO Trained (early) | 0.362 |
| **Improvement** | **+34.6%** |
Training reward trend across 14 steps shows a positive slope of **+0.0024/step** with KL divergence remaining stable and controlled throughout.
---
## Project Structure
```
pm_ops/
βββ server/
β βββ pm_ops_environment.py # OpenEnv Environment subclass
β βββ org_generator.py # Deterministic, seedable org factory
β βββ grader.py # Ground-truth reward computation
β βββ app.py # FastAPI server entry point
βββ inference.py # Heuristic baseline agent + eval runner
βββ pyproject.toml
training/
βββ rollout.py # Multi-turn rollout: build_messages, extract_json_action
βββ rewards.py # Reward functions + weight constants
βββ pm_ops_trainer.py # PMOpsGRPOTrainer (_calculate_rewards override)
βββ prompts.py # SYSTEM_PROMPT, format_observation
βββ dataset.py # Dataset loading + seed parsing
βββ train_v3.ipynb # SFT warmup + GRPO training notebook
```
---
## Setup & Usage
### Run the environment server
```bash
cd pm_ops
pip install -e .
server # starts FastAPI on :7860
```
### Run the baseline agent
```bash
export API_BASE_URL=https://thecrustaceans-pm-ops.hf.space
python pm_ops/inference.py
```
### Connect via OpenEnv client
```python
from openenv.core import GenericEnvClient
env = GenericEnvClient(base_url="https://thecrustaceans-pm-ops.hf.space").sync()
with env:
result = env.reset()
obs = result.observation
result = env.step({
"action_type": "meta.read_runbook",
"args": {}
})
```
### Training
Open `training/train_v3.ipynb` in a Colab instance with a GPU. The notebook handles SFT warmup, GRPO setup, and checkpointing.
---
**Live Environment:** [https://huggingface.co/spaces/TheCrustaceans/Pm-ops](https://huggingface.co/spaces/TheCrustaceans/Pm-ops)
|