File size: 10,525 Bytes
bb5e238
 
 
 
 
 
 
 
 
 
27cdb3e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
---
title: DevOps RL Agent
emoji: πŸ€–
colorFrom: blue
colorTo: green
sdk: docker
app_file: app.py
pinned: false
---

# πŸ€– DevOps RL Agent

**An AI agent that learns to fix broken Linux/Python environments through reinforcement learning.**

Built with **OpenEnv + TRL (GRPO) + Unsloth** β€” no agent frameworks, no multi-agent systems, just one LLM and one RL loop.

[![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://python.org)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)

---

## 🎯 What It Does

The agent observes broken environments (missing packages, port conflicts, config errors), generates shell commands to fix them, executes those commands in a Docker sandbox, and improves through GRPO training over episodes.

```
=== BEFORE TRAINING (episode 0) ===
Error: ModuleNotFoundError: flask
Step 1: python app.py       β†’ failed (exit 1)
Step 2: sudo pip install    β†’ DANGEROUS COMMAND BLOCKED
Step 3: apt install python  β†’ wrong approach
Result: FAILED (reward: -8.2)

=== AFTER TRAINING (episode 500) ===
Error: ModuleNotFoundError: flask
Step 1: pip install flask   β†’ success
Step 2: python app.py       β†’ Server running on :5000
Result: SOLVED in 2 steps (reward: +12.6)
```

---

## πŸ”„ How the RL Loop Works

This is the core architecture β€” one agent, one environment, one training loop:

```mermaid
graph LR
    A[🧠 LLM Agent] -->|shell command| B[🐳 Docker Sandbox]
    B -->|stdout/stderr/exit_code| C[πŸ—οΈ Environment]
    C -->|observation + reward| A
    C -->|episode data| D[πŸ’Ύ Replay Buffer]
    D -->|training batches| E[πŸ“ˆ GRPO Trainer]
    E -->|updated weights| A
```

### The Loop in Detail

1. **Reset**: Environment loads a random broken scenario (e.g., Flask not installed)
2. **Observe**: Agent receives an error log, command history, and error type classification
3. **Act**: Agent outputs ONE shell command (e.g., `pip install flask`)
4. **Execute**: Command runs in an isolated Docker container with safety checks
5. **Reward**: Multi-signal reward computed (success +10, correct command +3, progress +1, etc.)
6. **Repeat**: Steps 2-5 repeat up to 10 times per episode
7. **Train**: After N episodes, GRPO updates the model using grouped completions

### Error Fingerprinting (Key Differentiator)

Before the agent acts, a rule-based classifier identifies the error type from the terminal output:

| Error Type | Pattern | Example |
|---|---|---|
| `missing_package` | `ModuleNotFoundError` | `No module named 'flask'` |
| `port_conflict` | `Address already in use` | Port 5000 occupied |
| `missing_env` | `KeyError` on env var | `DATABASE_URL` not set |
| `version_conflict` | `ResolutionImpossible` | Package version clash |
| `config_error` | `NameError`, misconfig | Wrong host binding |

This gives the LLM better context and lets us analyze which error categories the agent struggles with.

### Multi-Signal Reward System

The reward engine returns a breakdown dict with 10 independent signals:

| Signal | Value | Purpose |
|---|---|---|
| `success` | +10.0 | Scenario fully solved |
| `correct_command` | +3.0 | Matches optimal fix sequence |
| `progress` | +1.0 | Error log changed (likely improvement) |
| `efficiency_bonus` | +2.0 | Solved in minimal steps |
| `invalid_command` | -2.0 | Command not whitelisted |
| `dangerous_command` | -10.0 | Matches blocklist (rm -rf /, etc.) |
| `no_progress` | -1.0 | Error log unchanged |
| `timeout` | -5.0 | Command exceeded 30s |
| `repeated_command` | -1.5 | Same command twice in episode |
| `step_cost` | -0.2 | Encourages efficiency |

Each column is logged separately during training to detect reward hacking.

### Curriculum Learning

Scenarios unlock progressively based on rolling 50-episode solve rate windows:

- **Level 1** (single-step): Always available β€” `missing_flask`, `missing_numpy`, `wrong_python`
- **Level 2** (two-step): Unlocks at L1 solve rate > 80% β€” `port_conflict`, `missing_env_var`, `broken_requirements`
- **Level 3** (multi-step): Unlocks at L2 solve rate > 80% β€” `corrupt_venv`, `wrong_config`, `db_migration`

---

## πŸ“ Project Structure

```
devops-rl-agent/
β”œβ”€β”€ devops_env/              # OpenEnv-style RL environment
β”‚   β”œβ”€β”€ env.py               # DevOpsEnv (reset/step/reward)
β”‚   └── state_manager.py     # Observation tracking
β”œβ”€β”€ scenarios/               # 9 scenarios across 3 difficulty levels
β”‚   β”œβ”€β”€ registry.py          # ScenarioRegistry
β”‚   β”œβ”€β”€ level1/              # Single-step fixes
β”‚   β”œβ”€β”€ level2/              # Two-step fixes
β”‚   └── level3/              # Multi-step fixes (3-5 steps)
β”œβ”€β”€ executor/                # Docker sandbox execution
β”‚   β”œβ”€β”€ docker_executor.py   # Container management + local fallback
β”‚   └── safety.py            # Command whitelist/blocklist
β”œβ”€β”€ fingerprint/             # Error classification system
β”‚   └── classifier.py        # Rule-based regex classifier
β”œβ”€β”€ rewards/                 # Multi-signal reward computation
β”‚   └── engine.py            # 10 independent reward signals
β”œβ”€β”€ replay/                  # Episode storage (SQLite + SQLAlchemy)
β”‚   β”œβ”€β”€ buffer.py            # ReplayBuffer with batch sampling
β”‚   └── models.py            # ORM models
β”œβ”€β”€ agent/                   # LLM + baseline agents
β”‚   β”œβ”€β”€ baseline_agent.py    # Rule-based (for loop validation)
β”‚   β”œβ”€β”€ devops_agent.py      # LLM agent (Unsloth/HF)
β”‚   └── prompts.py           # System & user prompt templates
β”œβ”€β”€ training/                # GRPO training pipeline
β”‚   β”œβ”€β”€ train_grpo.py        # Training loop + anti-hacking monitor
β”‚   └── curriculum.py        # Rolling-window curriculum scheduler
β”œβ”€β”€ api/                     # FastAPI server (OpenEnv pattern)
β”‚   └── main.py
β”œβ”€β”€ frontend/                # Dashboard (vanilla HTML/CSS/JS)
β”‚   β”œβ”€β”€ index.html
β”‚   β”œβ”€β”€ style.css
β”‚   └── app.js
β”œβ”€β”€ docker/                  # Sandbox container
β”‚   β”œβ”€β”€ Dockerfile.sandbox
β”‚   └── docker-compose.yml
β”œβ”€β”€ tests/                   # Unit tests
β”‚   β”œβ”€β”€ test_env.py
β”‚   β”œβ”€β”€ test_rewards.py      # 100% reward engine coverage
β”‚   └── test_executor.py
β”œβ”€β”€ scripts/
β”‚   └── demo.py              # Before/after training demo
β”œβ”€β”€ requirements.txt
└── README.md
```

---

## πŸš€ Quick Start

### 1. Install Dependencies

```bash
cd devops-rl-agent
pip install -r requirements.txt
```

### 2. Validate the RL Loop (No GPU Required)

Run the baseline agent to confirm environment, executor, and rewards work:

```bash
# Run the demo script
python scripts/demo.py --episodes 50

# Run unit tests
python -m pytest tests/ -v
```

### 3. Start the API Server

```bash
uvicorn api.main:app --reload --port 8000
```

Then visit:
- API docs: [http://localhost:8000/docs](http://localhost:8000/docs)
- Dashboard: [http://localhost:8000/app](http://localhost:8000/app)

### 4. Build the Docker Sandbox (Optional)

```bash
cd docker
docker compose build
docker compose up -d sandbox
```

### 5. Run GRPO Training (Requires GPU)

```bash
python -c "
from training.train_grpo import GRPODevOpsTrainer
trainer = GRPODevOpsTrainer(model_name='unsloth/llama-3.2-3b-instruct')
trainer.train(num_episodes=500)
"
```

## ☁️ Deploy to Hugging Face Spaces

This repository is set up as a Docker Space. To deploy:

1. Create a new Hugging Face Space.
2. Choose `Docker` as the SDK.
3. Push this repository contents to the Space repo.
4. Hugging Face will build the root [Dockerfile](Dockerfile) and start the API on port `7860`.

Useful endpoints after deployment:
- `GET /` health check
- `POST /reset` OpenEnv session reset
- `POST /step` OpenEnv step
- `POST /close` OpenEnv session cleanup
- `POST /episode/run` one-shot episode execution

Notes:
- Runtime DB files are ignored via [.dockerignore](.dockerignore).
- If you want model training inside Spaces, keep `use_grpo=False` unless the Space has a GPU and enough VRAM.

---

## πŸ”’ Safety

The executor enforces strict command safety:

**Whitelisted**: `pip`, `python`, `cat`, `ls`, `grep`, `sed`, `ps`, `kill`, `curl`, `echo`, `mkdir`, `cp`, `mv`, `export`, `source`

**Blocked**: `rm -rf /`, fork bombs, `dd if=`, `mkfs`, `chmod 777 /`, `sudo` + destructive ops

Commands that fail safety checks are immediately blocked, the agent receives a -10.0 penalty, and the episode terminates.

---

## πŸ“Š Success Criteria

After 500 training episodes:

| Metric | Target |
|---|---|
| Level 1 solve rate | > 90% |
| Level 2 solve rate | > 70% |
| Mean steps to solve L1 | ≀ 2 |
| Reward hacking detected | None |
| Model saves/loads correctly | βœ“ |

---

## πŸ”§ API Endpoints

| Method | Path | Description |
|---|---|---|
| `POST` | `/reset` | OpenEnv-style session reset (returns session_id + initial observation) |
| `POST` | `/step` | OpenEnv-style step for a session (`{session_id, action:{command}}`) |
| `POST` | `/close` | OpenEnv-style explicit session cleanup |
| `POST` | `/episode/run` | Run one episode, returns full step log |
| `GET` | `/episode/{id}` | Get stored episode by UUID |
| `GET` | `/stats` | Solve rates, rewards, training progress |
| `GET` | `/replay/{id}` | Step-by-step replay data |
| `POST` | `/train/step` | Trigger training batch |
| `GET` | `/scenarios` | List scenarios with solve rates |
| `GET` | `/recent` | Recent episodes |

---

## πŸ›‘οΈ Anti-Reward Hacking

The training loop includes active monitoring:

1. **Generation inspection** every 50 steps β€” actual agent outputs are printed
2. **Success column tracking** β€” alerts if total reward rises but success rate stays flat
3. **Command repetition detection** β€” flags if one command dominates >50% of actions
4. **Dangerous command counting** β€” terminates if blocklist triggers repeatedly
5. **Per-column reward breakdown** β€” logged separately to catch gaming of individual signals

---

## πŸ—οΈ Tech Stack

- **RL Framework**: TRL (GRPOTrainer) + Unsloth (4-bit LoRA)
- **Base Model**: `unsloth/llama-3.2-3b-instruct`
- **Environment**: OpenEnv-compatible API pattern
- **Execution**: Docker SDK with safety layer
- **Storage**: SQLite via SQLAlchemy
- **API**: FastAPI + Uvicorn
- **Frontend**: Vanilla HTML/CSS/JS (dark theme, glassmorphism)
- **Testing**: pytest (100% reward engine coverage)

---

## πŸ“ License

MIT