Aditya Guntur commited on
Commit
36068f1
Β·
1 Parent(s): 6c48c5d

fix: restore HF Space YAML frontmatter + update README with full content

Browse files
Files changed (1) hide show
  1. README.md +302 -29
README.md CHANGED
@@ -11,44 +11,317 @@ tags:
11
  - reinforcement-learning
12
  ---
13
 
14
- # PM-Ops: Procedural Project-Management RL Environment
15
 
16
- A multi-app simulated environment (Ticketing, Codebase, Chat) featuring **procedural
17
- organizational drift**, designed for reinforcement learning training.
18
 
19
- ## What makes it RL-shaped
20
 
21
- Every episode samples a fresh OrgConfig -- different label names, priority schemes,
22
- team-to-service mappings, and oncall channel names. Frontier LLMs fail because they
23
- rely on memorized conventions; a trained agent learns to read the runbook first.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
- ## Tasks
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
- | Task | Brief | Max Steps |
28
- |------|-------|-----------|
29
- | triage | Bug report -- correct label, priority, team, channel | 25 |
30
- | incident_routing | Alert -- identify owner via commits, page oncall | 25 |
31
- | release_notes | Compile + post release notes in org style | 40 |
32
- | dep_update | Coordinate dependency update across all owning teams | 40 |
33
 
34
- ## Action space
 
 
 
 
35
 
36
- The agent calls actions like:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
- meta.read_runbook -- learn this org's conventions
39
- ticketing.create_ticket -- file a ticket with label + priority
40
- ticketing.assign_ticket -- assign to correct team
41
- chat.post_message -- notify the right oncall channel
42
- meta.finish -- end episode, trigger scoring
43
 
44
- ## Reward
45
 
46
- Delayed to episode end. Deterministic Python verifier -- no LLM-as-judge.
47
- Partial credit for partial correctness.
 
 
 
48
 
49
- ## Sponsor alignment
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
 
51
- - AI Labs: direct PM-in-the-loop positioning
52
- - Patronus: schema drift -- env tests whether agents track convention changes
53
- - Scale AI: enterprise workflow simulation
54
- - Meta RFC 004: delayed trajectory reward is the native reward mode
 
11
  - reinforcement-learning
12
  ---
13
 
14
+ # PM-Ops πŸ› οΈ
15
 
16
+ > **Can a small model learn to operate inside a company it has never seen before?**
 
17
 
18
+ 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.
19
 
20
+ ---
21
+
22
+ ## Links
23
+
24
+ | Resource | Link |
25
+ |---|---|
26
+ | πŸ€— **Live Environment** | [TheCrustaceans/Pm-ops β€” HuggingFace Space](https://huggingface.co/spaces/TheCrustaceans/Pm-ops) |
27
+ | πŸ“ **Blog Post** | [BlogPost.mdx](https://huggingface.co/spaces/TheCrustaceans/Pm-ops/blob/main/BlogPost.mdx) |
28
+ | πŸŽ₯ **Demo Video** | [Google Drive](https://drive.google.com/file/d/1JdwYukKrEaMTaOwc1W4Q8bBRjxwGZ2be/view?usp=drive_link) |
29
+ | πŸ“Š **Slides** | [Google Drive](https://drive.google.com/file/d/1Wb8G0WEPPvAFppBNSVMZ7EMFBPFjsP8n/view?usp=sharing) |
30
+
31
+ ---
32
+
33
+ ## Table of Contents
34
+
35
+ - [Motivation](#motivation)
36
+ - [Environment](#environment)
37
+ - [The Three Apps](#the-three-apps)
38
+ - [Action Space](#action-space)
39
+ - [Observation Schema](#observation-schema)
40
+ - [Org Generator](#org-generator)
41
+ - [Task Types](#task-types)
42
+ - [Difficulty Tiers](#difficulty-tiers)
43
+ - [Reward Design](#reward-design)
44
+ - [Training Pipeline](#training-pipeline)
45
+ - [SFT Warmup](#sft-warmup)
46
+ - [GRPO](#grpo)
47
+ - [Reward Shaping](#reward-shaping)
48
+ - [Results](#results)
49
+ - [Project Structure](#project-structure)
50
+ - [Setup & Usage](#setup--usage)
51
+
52
+ ---
53
+
54
+ ## Motivation
55
+
56
+ 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.
57
+
58
+ The reason: **organizational context is not on the internet.**
59
+
60
+ 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.
61
+
62
+ **Why system prompts are not the answer:**
63
+
64
+ | Problem | Detail |
65
+ |---|---|
66
+ | 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. |
67
+ | Static snapshots | Channels get renamed. Teams get reorganized. A system prompt written last quarter is already wrong. |
68
+ | No gradient | The model is told what to do, not trained on the consequences of ignoring it. There is no learning signal. |
69
+
70
+ PM-Ops addresses this by making the agent *experience* the consequences of skipping the runbook across thousands of varied organizational configurations during training.
71
+
72
+ ---
73
+
74
+ ## Environment
75
+
76
+ 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.
77
+
78
+ ### The Three Apps
79
+
80
+ ```
81
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
82
+ β”‚ PM-Ops Environment β”‚
83
+ β”‚ β”‚
84
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
85
+ β”‚ β”‚ Ticketing β”‚ β”‚ Codebase β”‚ β”‚ Chat β”‚ β”‚
86
+ β”‚ β”‚ (Jira-like) β”‚ β”‚ (GitHub-like)β”‚ β”‚ (Slack-like) β”‚ β”‚
87
+ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
88
+ β”‚ β”‚ tickets β”‚ β”‚ repositories β”‚ β”‚ channels β”‚ β”‚
89
+ β”‚ β”‚ projects β”‚ β”‚ commits β”‚ β”‚ threads β”‚ β”‚
90
+ β”‚ β”‚ teams β”‚ β”‚ pull requestsβ”‚ β”‚ DMs β”‚ β”‚
91
+ β”‚ β”‚ labels β”‚ β”‚ file authors β”‚ β”‚ user profilesβ”‚ β”‚
92
+ β”‚ β”‚ priorities β”‚ β”‚ changed filesβ”‚ β”‚ β”‚ β”‚
93
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
94
+ β”‚ β”‚
95
+ β”‚ Ground truth verified against DB β€” not LLM-judged β”‚
96
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€οΏ½οΏ½β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
97
+ ```
98
+
99
+ **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.
100
+
101
+ **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.
102
+
103
+ **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.
104
+
105
+ ### Action Space
106
+
107
+ ```json
108
+ { "action_type": "meta.read_runbook", "args": {} }
109
+ { "action_type": "meta.finish", "args": {} }
110
+ { "action_type": "meta.noop", "args": {} }
111
+ { "action_type": "ticketing.create_ticket", "args": { "summary": "...", "label": "...", "priority": "...", "assignee": "..." } }
112
+ { "action_type": "ticketing.assign_ticket", "args": { "ticket_id": "...", "team": "..." } }
113
+ { "action_type": "chat.list_channels", "args": {} }
114
+ { "action_type": "chat.post_message", "args": { "channel": "...", "text": "..." } }
115
+ { "action_type": "codebase.get_commits", "args": { "repo": "..." } }
116
+ ```
117
+
118
+ 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.
119
+
120
+ ### Observation Schema
121
+
122
+ Each step returns:
123
+
124
+ ```python
125
+ {
126
+ "task_brief": str, # the incident/task description
127
+ "last_action_result": dict, # {"ok": bool, "data": ..., "error": ...}
128
+ "step": int,
129
+ "steps_remaining": int,
130
+ "reward": float, # always 0.0 until meta.finish
131
+ "done": bool,
132
+ "token_budget_remaining": int,
133
+ }
134
+ ```
135
+
136
+ **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.
137
+
138
+ ### Org Generator
139
+
140
+ LLM-generated scenarios sound plausible but fail formal verification. PM-Ops uses a **deterministic, parameterized, seedable org generator** instead.
141
+
142
+ Each org is generated from a seed and produces:
143
+
144
+ - `label_taxonomy` β€” org-specific bug/feature labels
145
+ - `priority_levels` β€” org-specific severity scale
146
+ - `team_map` β€” service β†’ owning team
147
+ - `oncall_channels` β€” team β†’ notification channel
148
+ - `required_ticket_fields` β€” what fields must be set for a valid ticket
149
+
150
+ The same seed always produces the same org. Evaluation is reproducible. Training sees a different org every episode.
151
+
152
+ ### Task Types
153
+
154
+ | Task | Description | Key Challenge |
155
+ |---|---|---|
156
+ | **Triage** | Bug report arrives. Create ticket with correct label, priority, assignee. Notify correct channel. | Using org taxonomy, not generic labels. |
157
+ | **Incident Routing** | Production incident. Identify affected services, find owning team via codebase + team map, escalate. | Cross-system reasoning: chat β†’ codebase β†’ ticketing. |
158
+ | **Release Notes** | Compile and post release notes for resolved tickets in the org's specific format. | Format compliance, not just content. |
159
+ | **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. |
160
+
161
+ ### Difficulty Tiers
162
+
163
+ | Tier | Services | Labels | Priorities | Notes |
164
+ |---|---|---|---|---|
165
+ | **Easy** | 2 | 3 | 2 | No ambiguity. Runbook is complete. |
166
+ | **Medium** | 3 | 4 | 4 | Partial runbook. Agent must infer across systems. Multi-owner configs. |
167
+ | **Hard** | β‰₯5 | 5+ | 5+ | Noise channels designed to distract. Outdated/missing docs. Simulated panicking users in general chat β€” correct behavior is to ignore them. |
168
+
169
+ 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.
170
 
171
+ ---
172
+
173
+ ## Reward Design
174
+
175
+ Rewards are **delayed** β€” revealed only at `meta.finish`.
176
+
177
+ ### Scoring Breakdown
178
+
179
+ ```
180
+ Ticket created correctly +0.25
181
+ Correct label +0.20
182
+ Correct priority +0.20
183
+ Correct team assignment +0.20
184
+ Correct channel notification +0.15
185
+ ─────────────────────────────────
186
+ Maximum per episode 1.00
187
+ ```
188
+
189
+ ### Penalties
190
+
191
+ ```
192
+ Wrong channel post -0.05 each
193
+ (>2 wrong posts β†’ net negative; defeats channel-spray)
194
+ Duplicate ticket -0.10 each
195
+ Zero valid actions (inaction)-1.00
196
+ ```
197
+
198
+ 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.
199
+
200
+ ### Combined Training Reward
201
+
202
+ ```python
203
+ if valid_action_count == 0:
204
+ combined = -1.0 # never output valid JSON
205
+ elif final_score == 0.0:
206
+ combined = valid_json_ratio * 0.15 # tried but failed
207
+ + read_runbook_reward * 0.10
208
+ - 0.30 # hard penalty for zero completion
209
+ else:
210
+ combined = final_score * 0.45 # task correctness
211
+ + no_wrong_channels * 0.15 # anti channel-spray
212
+ + valid_json_ratio * 0.15 # format discipline
213
+ + read_runbook_reward * 0.15 # process compliance
214
+ + efficiency * 0.10 # steps saved
215
+ ```
216
+
217
+ ---
218
+
219
+ ## Training Pipeline
220
+
221
+ ### SFT Warmup
222
+
223
+ 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.
224
+
225
+ ### GRPO
226
+
227
+ We use **Group Relative Policy Optimization** to train a 7B parameter model directly against PM-Ops episode rewards.
228
+
229
+ Each training step is a full PM-Ops episode:
230
+ 1. Model plays through up to 15 turns
231
+ 2. Environment scores the final state
232
+ 3. Gradient updates weights based on relative performance across the generation group
233
+
234
+ No intermediate reward signal is given. The model must learn to plan across multiple steps.
235
+
236
+ ### Reward Shaping
237
+
238
+ | Component | Weight | Purpose |
239
+ |---|---|---|
240
+ | `final_score` | 0.45 | Primary correctness from env grader |
241
+ | `no_wrong_channels` | 0.15 | Anti-hack: penalise channel spray |
242
+ | `valid_json_ratio` | 0.15 | Format discipline |
243
+ | `read_runbook` | 0.15 | Process compliance |
244
+ | `efficiency` | 0.10 | Steps saved (only when task succeeds) |
245
 
246
+ `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.
 
 
 
 
 
247
 
248
+ ---
249
+
250
+ ## Results
251
+
252
+ Evaluated on 13 matched episodes, comparing the heuristic baseline agent against the GRPO-trained model:
253
 
254
+ | Model | Avg Score (13 eps) |
255
+ |---|---|
256
+ | Heuristic Baseline | 0.269 |
257
+ | GRPO Trained (early) | 0.362 |
258
+ | **Improvement** | **+34.6%** |
259
+
260
+ Training reward trend across 14 steps shows a positive slope of **+0.0024/step** with KL divergence remaining stable and controlled throughout.
261
+
262
+ ---
263
+
264
+ ## Project Structure
265
+
266
+ ```
267
+ pm_ops/
268
+ β”œβ”€β”€ server/
269
+ β”‚ β”œβ”€β”€ pm_ops_environment.py # OpenEnv Environment subclass
270
+ β”‚ β”œβ”€β”€ org_generator.py # Deterministic, seedable org factory
271
+ β”‚ β”œβ”€β”€ grader.py # Ground-truth reward computation
272
+ β”‚ └── app.py # FastAPI server entry point
273
+ β”œβ”€β”€ inference.py # Heuristic baseline agent + eval runner
274
+ └── pyproject.toml
275
+
276
+ training/
277
+ β”œβ”€β”€ rollout.py # Multi-turn rollout: build_messages, extract_json_action
278
+ β”œβ”€β”€ rewards.py # Reward functions + weight constants
279
+ β”œβ”€β”€ pm_ops_trainer.py # PMOpsGRPOTrainer (_calculate_rewards override)
280
+ β”œβ”€β”€ prompts.py # SYSTEM_PROMPT, format_observation
281
+ β”œβ”€β”€ dataset.py # Dataset loading + seed parsing
282
+ └── train_v3.ipynb # SFT warmup + GRPO training notebook
283
+ ```
284
+
285
+ ---
286
 
287
+ ## Setup & Usage
 
 
 
 
288
 
289
+ ### Run the environment server
290
 
291
+ ```bash
292
+ cd pm_ops
293
+ pip install -e .
294
+ server # starts FastAPI on :7860
295
+ ```
296
 
297
+ ### Run the baseline agent
298
+
299
+ ```bash
300
+ export API_BASE_URL=https://thecrustaceans-pm-ops.hf.space
301
+ python pm_ops/inference.py
302
+ ```
303
+
304
+ ### Connect via OpenEnv client
305
+
306
+ ```python
307
+ from openenv.core import GenericEnvClient
308
+
309
+ env = GenericEnvClient(base_url="https://thecrustaceans-pm-ops.hf.space").sync()
310
+
311
+ with env:
312
+ result = env.reset()
313
+ obs = result.observation
314
+
315
+ result = env.step({
316
+ "action_type": "meta.read_runbook",
317
+ "args": {}
318
+ })
319
+ ```
320
+
321
+ ### Training
322
+
323
+ Open `training/train_v3.ipynb` in a Colab instance with a GPU. The notebook handles SFT warmup, GRPO setup, and checkpointing.
324
+
325
+ ---
326
 
327
+ **Live Environment:** [https://huggingface.co/spaces/TheCrustaceans/Pm-ops](https://huggingface.co/spaces/TheCrustaceans/Pm-ops)