| --- |
| title: OpsGuard |
| emoji: π‘οΈ |
| colorFrom: gray |
| colorTo: indigo |
| sdk: docker |
| pinned: true |
| app_port: 8000 |
| base_path: /web |
| tags: |
| - openenv |
| - openenv-hackathon |
| - long-horizon |
| - self-improvement |
| - rl |
| - agent |
| --- |
| |
| # OpsGuard β Train a 3B Model to Run On-Call for an Open-Source Repo |
|
|
| > **Can a 3B model triage 60 issues in a row β while a co-evolved adversary tries to slip spam past it?** |
|
|
| OpsGuard is an OpenEnv environment that trains LLM maintainers against synthetic spammers across 6 difficulty tiers, scoring against the **real maintainer decisions** of `huggingface/peft`. **No simulator. Real GitHub data. Real ground truth.** |
|
|
| ## Hero numbers β rule-based baselines on real GH data |
|
|
| 375 real issues, 1911 comments. Each scenario mixes real issues + synthetic spam at scenario-defined rate. Mean over 3 seeds: |
|
|
| | Scenario (steps Γ spam-rate) | random | label_everything | close_everything | **keyword_triager** | |
| |---|---:|---:|---:|---:| |
| | E0 quiet day (30 Γ 0%) | +3.26 | +0.50 | -1.60 | **+5.70** | |
| | E1 release week (60 Γ 10%) | -3.70 | -3.34 | -5.24 | **+12.71** | |
| | E2 spam wave (120 Γ 40%) | -13.14 | -9.76 | +0.74 | **+23.32** | |
| | E3 coordinated attack (200 Γ 55%) | -14.15 | -23.12 | +12.18 | **+40.58** | |
| |
| Spread grows monotonically: **7 β 18 β 36 β 64 reward points** as horizon and adversary intensity rise. **Greedy strategies fail at long horizons.** No shortcut wins. |
| |
|  |
|  |
| |
| **Trained 3B agent (post-GRPO):** numbers populate after training run lands. Live updates pushed to this README + Hub repo `sai1906/opsguard-grpo`. |
| |
| --- |
| |
| ## The Story β 4 Acts |
| |
| **Act 1 β The Naive Triager.** Baseline (`label_everything`) blindly tags every issue `bug`. On E2, half the queue is synthetic spam β none caught. Reward: -9.76. |
| |
| **Act 2 β The Keyword Spotter.** Hand-coded rules detect spam tokens, route by content keyword, request info on thin reports. Same E2 scenario: **+23.32**. Spam recall jumps 0% β 77%. |
|
|
| **Act 3 β The Adversary Learns.** A second 3B model is DPO-trained as a spammer, mining the maintainer's misses. It paraphrases real issue titles, fabricates urgency, mimics trusted-contributor voice. Keyword spotter's spam recall starts dropping back toward 50% on harder tiers. |
|
|
| **Act 4 β The Co-evolved Maintainer.** The maintainer is GRPO-trained on rollouts against the trained adversary. By round 3, it learns to weigh hidden contributor reputation, query history before deciding, and resist `self_contradiction`-style appeals. Final hero numbers populate post-training. |
|
|
| --- |
|
|
| ## What's in the box |
|
|
| ``` |
| opsguard/ |
| βββ models.py # 9 ActionTypes + Pydantic Action/Observation |
| βββ server/ |
| β βββ opsguard_environment.py # OpenEnv Environment subclass; reset/step/state |
| β βββ app.py # FastAPI + WebSocket via openenv.core.create_app |
| β βββ Dockerfile # Multi-stage uv build, ghcr.io/meta-pytorch/openenv-base |
| βββ world/ |
| β βββ db.py # SQLite query helpers (RepoDB, IssueRow, Contributor) |
| β βββ scenarios.py # E0_quiet_day β E5_hostile_fork_war (30β500 step budgets) |
| β βββ adversary.py # 5 spam patterns Γ 5 tiers (scripted) |
| β βββ trainable_adversary.py # LoRA spammer (round-2 co-evolution); template fallback |
| β βββ grader.py # Composable rubric + multiplicative terminal |
| β βββ curriculum.py # Mastery-based tier unlock |
| βββ eval/ |
| β βββ policies.py # 5 baselines: random, label_everything, close_everything, keyword_triager, memory_aware |
| β βββ harness.py # Rollout + aggregate + markdown summary |
| βββ scripts/ |
| β βββ pull_gh.py # GH REST API issue/PR/comment puller |
| β βββ ingest_to_sqlite.py # JSONL β SQLite, derives truth_action |
| β βββ build_sft_traces.py # Roll rule-based policies, emit (prompt, completion) JSONL |
| β βββ system_prompt.py # SYSTEM_PROMPT, format_observation, parse_action |
| β βββ sft_warmstart.py # Unsloth 4-bit + LoRA SFT (1 epoch) |
| β βββ train_grpo.py # TRL OpenEnv GRPO trainer (vLLM colocate optional) |
| β βββ train_grpo_colab.ipynb # Self-contained Colab notebook |
| β βββ make_plots.py # Reward / spam-recall plots |
| βββ tests/ # Smoke tests (env loop + train integration), all green |
| βββ data/repo.db # 375 real issues + 1911 comments from huggingface/peft |
| βββ openenv.yaml # OpenEnv v1 spec |
| ``` |
|
|
| ## Hackathon themes hit |
|
|
| - **T2 long-horizon** β episodes 30 β 500 steps; multi-issue queues; agent must process backlog while resisting 60-issue spam waves |
| - **T4 self-improvement** β co-evolved adversary (trainable spammer LoRA) + mastery-driven curriculum (E0 β E5 unlocks on rolling reward, kube-sre-gym pattern) |
| - **T3 world modeling touch** β hidden contributor reputation drives reward but is **never** exposed to the agent. Agent infers trust from public stats (PR count, account age) β env penalizes mis-judgement. |
|
|
| ## Action space (9 actions, single per step) |
|
|
| `label` Β· `close_spam` Β· `request_info` Β· `link_duplicate` Β· `assign` Β· `comment` Β· `merge_pr` Β· `query_history` Β· `wait` |
|
|
| JSON contract: |
| ```json |
| {"action_type":"label","target_issue_id":12345,"label":"bug","reasoning":"..."} |
| ``` |
|
|
| ## Reward (composable rubric, hard to game) |
|
|
| Per the OpenEnv hackathon hint *"composable rubrics > monolithic scoring"*, OpsGuard splits reward into 7 components fired conditionally: |
|
|
| | Component | Fires when | Sign | |
| |---|---|---| |
| | `action_match` | agent's action == maintainer's recorded action | +1.0 (correct), -0.5 (wrong), +0.4 (partial) | |
| | `label_overlap` | Jaccard between predicted & true label sets | +0..0.4 | |
| | `spam_resistance` | `close_spam` on synthetic spam | +0.8 | |
| | | engaged with spam (label/merge/assign) | -0.7 | |
| | `reputation_alignment` | closed spam on low-rep author / labeled trusted-author content | Β±0.2 | |
| | `info_request` | first request on under-described issue / redundant | +0.15 / -0.15 | |
| | `repeat_action_penalty` | same action on same issue within 5 steps | -0.15 | |
| | `workflow_efficiency` | per-step cost | -0.02 | |
| | **terminal**: `queue_completion Γ spam_recall_gate` | end of episode | -1..1, multiplicative | |
|
|
| The terminal is multiplicative on purpose β a policy that hits queue completion but ignores spam is gated to 0. Greedy strategies fail. |
|
|
| ## Try it yourself (3 paths) |
|
|
| **1. Run baseline eval (CPU only, ~2 min):** |
| ```bash |
| git clone https://huggingface.co/spaces/sai1906/opsguard |
| cd opsguard |
| pip install -e . |
| python scripts/run_baseline_eval.py --out eval_outputs/baseline |
| cat eval_outputs/baseline/summary.md |
| ``` |
|
|
| **2. Run env locally:** |
| ```bash |
| python -m server.app # β http://localhost:8000 |
| curl http://localhost:8000/state |
| ``` |
|
|
| **3. Train + GRPO (Colab T4 free OR HF Jobs A100 ~1h):** |
| ```bash |
| # Open scripts/train_grpo_colab.ipynb in Colab β Run All |
| # OR via HF Jobs: |
| hf jobs uv run --flavor a100-large \ |
| --with "trl,unsloth,openenv-core,peft,bitsandbytes,vllm,datasets" \ |
| --secrets HF_TOKEN \ |
| -- python scripts/train_grpo.py \ |
| --model unsloth/Qwen2.5-7B-Instruct-bnb-4bit \ |
| --hub-repo sai1906/opsguard-grpo \ |
| --num-steps 200 |
| ``` |
|
|
| ## Live links |
|
|
| - **HF Space (this env):** https://huggingface.co/spaces/sai1906/opsguard |
| - **Colab demo:** [scripts/train_grpo_colab.ipynb](scripts/train_grpo_colab.ipynb) |
| - **Trained LoRA:** https://huggingface.co/sai1906/opsguard-grpo (populates after training) |
| - **Data source:** `huggingface/peft` β 375 issues, 1911 comments, 6-month window |
|
|
| ## Why it matters |
|
|
| Open-source maintainers spend 30β50% of triage time on duplicates, urgency-fabrications, and low-info noise. A 3B model that catches 80% of synthetic spam at 95% precision and routes the rest to the right label is a real productivity win β and it's a problem nobody has trained for in OpenEnv before. |
|
|
| ## References |
|
|
| - OpenEnv (PyTorch + Meta, 2026) β https://github.com/meta-pytorch/OpenEnv |
| - TRL OpenEnv integration β https://huggingface.co/docs/trl/en/openenv |
| - AgentGym-RL ScalingInter-RL (Sep 2025, arXiv 2509.08755) β curriculum design |
| - kube-sre-gym (SF OpenEnv 1st place, Mar 2026) β mastery curriculum + adversarial designer pattern |
| - Multi-Agent Evolve (arXiv 2510.23595) β co-evolution recipe (Proposer/Solver/Judge) |
| - AgentLAB long-horizon attacks (arXiv 2602.16901) β adversarial maintenance pattern catalog |
|
|
| ## Architecture (textual) |
|
|
| ``` |
| ββββββββββββββββββββββββββ |
| β GitHub REST API β |
| β (peft, 6-month pull) β |
| ββββββββββββ¬ββββββββββββββ |
| β pull_gh.py |
| βΌ |
| ββββββββββββββββββββββββββ |
| β data/repo.db (SQLite) β |
| β 375 issues, 1911 cmts β |
| ββββββββββββ¬ββββββββββββββ |
| β |
| ββββββββββββββββββββββΌββββββββββββββββββββββ |
| βΌ βΌ βΌ |
| ββββββββββββββββ βββββββββββββββββββ ββββββββββββββββββββ |
| β Adversary β β Curriculum β β Composable β |
| β - 5 patterns β β - Mastery β β Rubric β |
| β - 5 tiers β β - E0βE5 unlock β β - 7 components β |
| β - LoRA-able β β β β - Multipl. term β |
| ββββββββ¬ββββββββ ββββββββββ¬βββββββββ ββββββββββ¬ββββββββββ |
| β β β |
| ββββββββββββββββββββββΌββββββββββββββββββββββ |
| βΌ |
| ββββββββββββββββββββββββββ |
| β OpsGuardEnvironment β |
| β (OpenEnv subclass) β |
| β reset / step / state β |
| ββββββββββββ¬ββββββββββββββ |
| β FastAPI + WS |
| βΌ |
| ββββββββββββββββββββββββββ |
| β TRL GRPOTrainer β |
| β Qwen2.5-7B + LoRA β |
| β (vLLM colocate) β |
| ββββββββββββββββββββββββββ |
| ``` |
|
|
| ## License |
|
|
| BSD-3 (matching OpenEnv). |
|
|