Deadman922 commited on
Commit
c0d0ac8
·
verified ·
1 Parent(s): 37b33f5

Delete README.md

Browse files
Files changed (1) hide show
  1. README.md +0 -442
README.md DELETED
@@ -1,442 +0,0 @@
1
- ---
2
- title: SQL Quality Audit Agent Environment
3
- emoji: 🗄️
4
- colorFrom: green
5
- colorTo: blue
6
- sdk: docker
7
- pinned: false
8
- tags:
9
- - openenv
10
- - sql
11
- - data-quality
12
- - rl-training
13
- - agent-evaluation
14
- ---
15
-
16
- <div align="center">
17
-
18
- # 🗄️ SQL Quality Audit Agent Environment
19
-
20
- **The first multi-step RL environment purpose-built for SQL data quality reasoning.**
21
-
22
- *Train agents that think like senior data engineers — not SQL autocomplete engines.*
23
-
24
- [![OpenEnv](https://img.shields.io/badge/OpenEnv-Compatible-22c55e?style=flat-square)](https://huggingface.co/spaces/Deadman922/sql-quality-env)
25
- [![Live Space](https://img.shields.io/badge/HuggingFace-Live%20Demo-f59e0b?style=flat-square)](https://huggingface.co/spaces/Deadman922/sql-quality-env)
26
- [![License: MIT](https://img.shields.io/badge/License-MIT-3b82f6?style=flat-square)](LICENSE)
27
- [![Tasks](https://img.shields.io/badge/Tasks-5%20Graded-8b5cf6?style=flat-square)](#tasks)
28
-
29
- </div>
30
-
31
- ---
32
-
33
- ## The Problem We Are Solving
34
-
35
- Every data team faces the same invisible crisis: production databases that silently degrade. Columns drift from `REAL` to `TEXT`. Foreign keys become orphaned. Duplicate rows inflate join counts by 2×. Schema versioning splits the same business concept across incompatible tables.
36
-
37
- Existing AI benchmarks evaluate SQL *syntax*. None evaluate SQL *reasoning under uncertainty* — the ability to investigate an unknown database, form a hypothesis, apply a targeted fix, and verify the result before committing.
38
-
39
- **This environment changes that.**
40
-
41
- ---
42
-
43
- ## What Makes This Different
44
-
45
- | Benchmark Approach | This Environment |
46
- |---|---|
47
- | Static query → grade output | Multi-step episode with dense reward shaping |
48
- | Clean, predefined schema | Unknown database with planted defects |
49
- | Syntax correctness check | Five-dimension trajectory grading |
50
- | One attempt per task | Configurable attempt budget per task type |
51
- | No process feedback | Per-step rewards for investigation quality |
52
-
53
- The grading system rewards *how* an agent works, not just *what* it submits. An agent that systematically investigates before fixing scores higher than one that submits the correct SQL on the first try — because in production, the correct SQL on the first try is often luck.
54
-
55
- ---
56
-
57
- ## Architecture Overview
58
-
59
- ```
60
- ┌─────────────────────────────────────────────────────────────┐
61
- │ AGENT (inference.py) │
62
- │ LLM → generate_sql() → action_type + SQL/answer │
63
- └──────────────────────┬──────────────────────────────────────┘
64
- │ POST /reset POST /step
65
- ┌──────────────────────▼──────────────────────────────────────┐
66
- │ ENVIRONMENT (FastAPI server) │
67
- │ SQLQualityEnvironment → SQLite in-memory database │
68
- │ │
69
- │ QUERY ──→ read-only SELECT on live db │
70
- │ PROFILE ─→ column statistics (null rate, type dist) │
71
- │ FIX ────→ write SQL on isolated _fresh_test_db() copy │
72
- │ SUBMIT ──→ compute_episode_score() → SQLReward │
73
- └──────────────────────┬──────────────────────────────────────┘
74
- │
75
- ┌──────────────────────▼──────────────────────────────────────┐
76
- │ GRADER (graders.py) │
77
- │ D1 Diagnostic · D2 Hypothesis · D3 Fix · D4 Verify · D5 Gen│
78
- │ Composite score in [0.001, 0.999] → [END] stdout line │
79
- └─────────────────────────────────────────────────────────────┘
80
- ```
81
-
82
- ---
83
-
84
- ## Observation Space
85
-
86
- Every `reset()` and `step()` returns a `SQLObservation` containing everything the agent needs to reason about the task:
87
-
88
- ```json
89
- {
90
- "task_id": "dedup_bug",
91
- "task_description": "Find and repair the duplication bug inflating join result volumes",
92
- "schema_info": [
93
- {
94
- "table_name": "order_items",
95
- "columns": ["id (INTEGER)", "order_id (INTEGER)", "product_name (TEXT)", "quantity (INTEGER)", "price (REAL)"],
96
- "row_count": 320
97
- }
98
- ],
99
- "sample_rows": {
100
- "order_items": [{"id": 1, "order_id": 0, "product_name": "Product 1", "quantity": 2, "price": 19.99}]
101
- },
102
- "episode_id": "bf37e2ee-63e1-434f-9ff3-e0728518a81c",
103
- "attempt_number": 1,
104
- "max_attempts": 5,
105
- "trajectory_history": [],
106
- "available_actions": ["query", "profile", "fix", "submit"],
107
- "investigation_hint": null
108
- }
109
- ```
110
-
111
- | Field | Type | Description |
112
- |---|---|---|
113
- | `task_id` | string | Active task identifier |
114
- | `task_description` | string | Natural language description of the defect to find and fix |
115
- | `schema_info` | list | Table names, typed column names, and row counts |
116
- | `sample_rows` | dict | First 5 rows of each table as JSON records |
117
- | `episode_id` | string | UUID identifying the current episode |
118
- | `trajectory_history` | list | All previous actions and result previews in this episode |
119
- | `available_actions` | list | Valid action types at this point |
120
- | `investigation_hint` | string or null | Actionable feedback after each scored step; null on first step |
121
-
122
- ---
123
-
124
- ## Action Space
125
-
126
- Four actions are available at every step, each with strict database access controls:
127
-
128
- ```
129
- ┌──────────┬──────────────────────────┬─────────────────────────────────────┐
130
- │ Action │ Allowed SQL │ Behavior │
131
- ├──────────┼──────────────────────────┼─────────────────────────────────────┤
132
- │ QUERY │ SELECT, WITH, EXPLAIN, │ Read-only. Runs on live database. │
133
- │ │ PRAGMA │ Returns result preview. No state │
134
- │ │ │ change. Call as many times as needed│
135
- ├──────────┼──────────────────────────┼─────────────────────────────────────┤
136
- │ PROFILE │ None (column name only) │ Returns null rate, distinct count, │
137
- │ │ │ and type distribution for one │
138
- │ │ │ column. Shortcut for inspection. │
139
- ├──────────┼──────────────────────────┼─────────────────────────────────────┤
140
- │ FIX │ UPDATE, INSERT, DELETE, │ Runs on isolated _fresh_test_db() │
141
- │ │ CREATE, ALTER │ copy. Scored but does NOT affect │
142
- │ │ │ subsequent QUERY results. │
143
- ├──────────┼──────────────────────────┼─────────────────────────────────────┤
144
- │ SUBMIT │ None │ Closes the episode. Triggers full │
145
- │ │ │ trajectory scoring. Returns final │
146
- │ │ │ composite reward. │
147
- └──────────┴──────────────────────────┴─────────────────────────────────────┘
148
- ```
149
-
150
- > **Key design decision:** FIX runs on `_fresh_test_db()` — an isolated in-memory copy that is discarded after scoring. The agent cannot verify its fix by re-querying the database afterward. It must reason about correctness from schema and sample data alone. This is precisely what makes D4 self-verification a non-trivial and RL-trainable behavior.
151
-
152
- ---
153
-
154
- ## The Five Tasks
155
-
156
- ### 🔵 `null_audit` — Easy — 1 attempt allowed
157
-
158
- > *"One column in this table has more than 50% missing values. Find it."*
159
-
160
- A `users` table with 8 columns where one randomly-selected column carries exactly a 52% null rate. All other columns have approximately 10% nulls as deliberate distractors. The agent must identify and name the high-null column. No SQL fix is required — this is the only pure-identification task in the benchmark.
161
-
162
- **Why it is non-trivial:** Agents that profile columns one by one will exhaust their step budget. The efficient strategy is to query null rates for all columns in a single statement and immediately submit.
163
-
164
- ```sql
165
- -- Efficient investigation
166
- SELECT
167
- SUM(CASE WHEN email IS NULL THEN 1 ELSE 0 END) * 1.0 / COUNT(*) AS email_null_rate,
168
- SUM(CASE WHEN city IS NULL THEN 1 ELSE 0 END) * 1.0 / COUNT(*) AS city_null_rate
169
- FROM users;
170
- ```
171
-
172
- **Grader:** `server.graders:grade_null_audit`
173
-
174
- ---
175
-
176
- ### 🟡 `type_repair` — Medium — 3 attempts allowed
177
-
178
- > *"Aggregate queries on this table return wrong totals. Something is wrong with the data types."*
179
-
180
- A `transactions` table where the `amount` column is stored as `TEXT` containing numeric strings like `"12.50"`. SQLite aggregate functions silently return incorrect results because the column is treated as text. The fix must convert all 1,000 values to `REAL` without losing any rows. Data preservation is enforced at a 99.5% threshold.
181
-
182
- **Why it is non-trivial:** SQLite does not support `ALTER COLUMN` syntax. Agents must use either `UPDATE ... SET amount = CAST(amount AS REAL)` or a full table recreation pattern. Many agents get stuck on invalid ALTER statements.
183
-
184
- ```sql
185
- -- Correct approach
186
- UPDATE transactions
187
- SET amount = CAST(amount AS REAL)
188
- WHERE typeof(amount) != 'real';
189
- ```
190
-
191
- **Grader:** `server.graders:grade_type_repair`
192
-
193
- ---
194
-
195
- ### 🔴 `dedup_bug` — Hard — 5 attempts allowed
196
-
197
- > *"A JOIN between orders and order_items is returning twice the expected row count. Track down why."*
198
-
199
- An `order_items` table with exactly 320 rows — 160 legitimate records plus 160 exact duplicates, one per `order_id`. When joined against the `orders` table, every order appears twice. The correct clean count after deduplication is 160. The SUBMIT action accepts `root_cause_table` and `offending_key` fields for partial credit independent of fix quality.
200
-
201
- **Why it is non-trivial:** The agent must correctly identify the duplication key (`order_id`), not just delete arbitrary rows. A naive `DELETE` without GROUP BY will produce incorrect results or violate the preservation threshold.
202
-
203
- ```sql
204
- -- Correct approach
205
- DELETE FROM order_items
206
- WHERE rowid NOT IN (
207
- SELECT MIN(rowid) FROM order_items GROUP BY order_id
208
- );
209
- ```
210
-
211
- **Grader:** `server.graders:grade_dedup_bug`
212
-
213
- ---
214
-
215
- ### 🔴 `fk_integrity` — Hard — 4 attempts allowed
216
-
217
- > *"Customer reports indicate missing invoice data. Referential integrity may be broken."*
218
-
219
- An `invoices` table with 1,200 rows where 180 records reference `customer_id` values in the range 10,001–11,000 — IDs that do not exist in the `customers` table. Valid customer IDs are 1–400. These orphan records must be removed while preserving all 1,020 valid invoices intact. The fix is validated against both the orphan count and the preservation ratio.
220
-
221
- ```sql
222
- -- Correct approach
223
- DELETE FROM invoices
224
- WHERE customer_id NOT IN (SELECT id FROM customers);
225
- ```
226
-
227
- **Grader:** `server.graders:grade_fk_integrity`
228
-
229
- ---
230
-
231
- ### 🟡 `schema_drift` — Medium — 4 attempts allowed
232
-
233
- > *"Two upstream data sources emit the same event concept but with completely different schemas. Build a canonical unified table."*
234
-
235
- Two tables exist: `events_v1` (600 rows) with columns `[id, user_id, event_name, amount_text, event_time]` where `amount_text` is TEXT, and `events_v2` (600 rows) with columns `[event_id, account_id, event_type, amount, event_ts, source]` where `amount` is REAL. The agent must create a `canonical_events` table with 1,200 unified rows, correct column names, and consistent `REAL` type for the amount field.
236
-
237
- **Why it is non-trivial:** Requires correctly mapping `id→event_id`, `user_id→user_id`, `event_name→event_type`, `CAST(amount_text AS REAL)→amount`, `event_time→event_time` across both sources. Incorrect column mapping produces wrong schema overlap scores.
238
-
239
- ```sql
240
- -- Correct approach
241
- CREATE TABLE canonical_events AS
242
- SELECT id AS event_id, user_id,
243
- event_name AS event_type,
244
- CAST(amount_text AS REAL) AS amount,
245
- event_time
246
- FROM events_v1
247
- UNION ALL
248
- SELECT event_id,
249
- account_id AS user_id,
250
- event_type,
251
- amount,
252
- event_ts AS event_time
253
- FROM events_v2;
254
- ```
255
-
256
- **Grader:** `server.graders:grade_schema_drift`
257
-
258
- ---
259
-
260
- ## Five-Dimension Scoring
261
-
262
- ```
263
- composite = D1 × 0.20 + D2 × 0.20 + D3 × 0.35 + D4 × 0.10 + D5 × 0.15
264
- ```
265
-
266
- **D1 — Diagnostic Efficiency (20%)**
267
- Measures how quickly the agent identified the defective table and column. Finding the issue in ≤2 queries scores 1.0. Three to four queries scores 0.7. Five to six queries scores 0.4. Never identifying the issue scores 0.0. This dimension directly penalizes shotgun exploration strategies.
268
-
269
- **D2 — Hypothesis Quality (20%)**
270
- Rewards structured investigation. Agents that inspect the table schema before drilling into specific columns score 1.0. Agents that query columns without understanding the table structure first score 0.6. This dimension trains broad-to-narrow reasoning habits.
271
-
272
- **D3 — Fix Correctness (35%)**
273
- The highest-weighted dimension. Evaluated with four severity tiers: PERFECT (issue fully resolved, all data preserved) = 1.0, ACCEPTABLE (issue resolved with minor data impact) = 0.6, DANGEROUS (partial fix that risks data loss) = 0.2, CATASTROPHIC (fix causes data destruction or no improvement) = 0.0. CATASTROPHIC severity overrides the entire composite and caps the final score at 0.05.
274
-
275
- **D4 — Self-Verification (10%)**
276
- A bonus dimension awarded when an agent submits a QUERY action after a FIX to verify the fix worked before calling SUBMIT. This behavior does not emerge from supervised learning on static SQL datasets. It is the primary RL-emergent behavior this environment is designed to cultivate. All zero-shot baseline agents score 0.0 here.
277
-
278
- **D5 — Generalization (15%)**
279
- The fix is silently tested against three schema variants: (1) a base copy of the original data, (2) the same schema with an additional column added, and (3) the same schema with a shifted data distribution. A fix that is brittle to schema changes scores 0.0 even if it perfectly resolves the original task.
280
-
281
- ### CATASTROPHIC Override
282
-
283
- Any fix meeting one of the following conditions is immediately classified as CATASTROPHIC and caps the composite at **0.05** regardless of D1, D2, D4, and D5 scores:
284
-
285
- - The fix contains `DROP TABLE` on the target table
286
- - The fix results in zero rows remaining in the target table
287
- - The fix deletes more than 50% of originally valid records
288
-
289
- This prevents agents from gaming the reward signal through destructive operations.
290
-
291
- ### Score Boundaries
292
-
293
- All scores are clamped to `[0.001, 0.999]`. Exact values of 0.0 and 1.0 are never returned. This design choice prevents RL training instability caused by absorbing boundary states.
294
-
295
- ---
296
-
297
- ## Setup and Installation
298
-
299
- ### Prerequisites
300
-
301
- ```bash
302
- git clone https://huggingface.co/spaces/Deadman922/sql-quality-env
303
- cd sql-quality-env
304
- pip install -r requirements.txt
305
- ```
306
-
307
- ### Environment Variables
308
-
309
- | Variable | Required | Default | Purpose |
310
- |---|---|---|---|
311
- | `API_BASE_URL` | Yes | `https://router.huggingface.co/v1` | LLM API endpoint |
312
- | `MODEL_NAME` | Yes | `meta-llama/Llama-3.3-70B-Instruct` | Model identifier |
313
- | `HF_TOKEN` | Yes | — | HuggingFace API token — no default, must be set explicitly |
314
- | `LOCAL_IMAGE_NAME` | No | — | Docker image name, only required with `from_docker_image()` |
315
-
316
- ```bash
317
- export API_BASE_URL="https://router.huggingface.co/v1"
318
- export MODEL_NAME="meta-llama/Llama-3.3-70B-Instruct"
319
- export HF_TOKEN="hf_your_token_here"
320
- ```
321
-
322
- ### Start the Server
323
-
324
- ```bash
325
- # Python
326
- python -m server.main
327
-
328
- # Docker
329
- docker build -t sql-quality-env .
330
- docker run -p 7860:7860 \
331
- -e API_BASE_URL=$API_BASE_URL \
332
- -e MODEL_NAME=$MODEL_NAME \
333
- -e HF_TOKEN=$HF_TOKEN \
334
- sql-quality-env
335
- ```
336
-
337
- ### Run the Agent
338
-
339
- ```bash
340
- # Single task
341
- python inference.py --task null_audit --url http://localhost:7860
342
-
343
- # Full benchmark run
344
- for task in null_audit type_repair dedup_bug fk_integrity schema_drift; do
345
- echo "--- Running $task ---"
346
- python inference.py --task $task --url http://localhost:7860
347
- done
348
- ```
349
-
350
- ### Verify the Environment
351
-
352
- ```bash
353
- # Health check
354
- curl http://localhost:7860/health
355
-
356
- # List graded tasks
357
- curl http://localhost:7860/tasks
358
-
359
- # Start a manual episode
360
- curl -X POST http://localhost:7860/reset \
361
- -H "Content-Type: application/json" \
362
- -d '{"task_id": "null_audit", "seed": 42}'
363
- ```
364
-
365
- ---
366
-
367
- ## Stdout Log Format
368
-
369
- The inference agent emits exactly three structured line types. Any deviation in format causes evaluation failure at the validator:
370
-
371
- ```
372
- [START] task=null_audit env=sql_quality_env model=meta-llama/Llama-3.3-70B-Instruct
373
- [STEP] step=1 action=query:SELECT COUNT(*)... reward=0.01 done=false error=null
374
- [STEP] step=2 action=submit:email reward=0.99 done=true error=null
375
- [END] success=true steps=2 score=0.999 rewards=0.01,0.99
376
- ```
377
-
378
- | Field | Format | Notes |
379
- |---|---|---|
380
- | `reward` per step | 2 decimal places | `0.01` not `0.0105` |
381
- | `score` in END | 3 decimal places | `0.999` not `1.0` |
382
- | `done`, `success` | lowercase boolean | `true` or `false` never `True` |
383
- | `error` | raw string or `null` | never `None` or empty string |
384
- | debug output | stderr only | never appears in stdout |
385
-
386
- ---
387
-
388
- ## Baseline Scores
389
-
390
- Evaluated with `meta-llama/Llama-3.3-70B-Instruct`, temperature 0.7, max 200 tokens, seed 42, force-submit at step 7:
391
-
392
- | Task | Score | D1 | D2 | D3 | D4 | D5 | Primary Bottleneck |
393
- |---|---|---|---|---|---|---|---|
394
- | `null_audit` | ~0.68 | 1.00 | 0.60 | ~0.70 | 0.00 | 0.00 | Lexical near-misses on column name spelling |
395
- | `type_repair` | ~0.41 | 1.00 | 0.60 | ~0.30 | 0.00 | 0.00 | SQLite ALTER limitation requires table recreation |
396
- | `dedup_bug` | ~0.82 | 1.00 | 0.60 | 1.00 | 0.00 | 1.00 | Reliable once duplicate pattern is identified |
397
- | `fk_integrity` | ~0.82 | 1.00 | 0.60 | 1.00 | 0.00 | 1.00 | Reliable with LEFT JOIN orphan detection |
398
- | `schema_drift` | ~0.50 | 0.00 | 0.00 | 1.00 | 0.00 | 1.00 | No investigation steps before fix |
399
-
400
- D4 (self-verification) scores 0.0 for all zero-shot agents. This is the benchmark's primary training signal. The behavior is RL-emergent and cannot be obtained through imitation learning.
401
-
402
- **Reproducibility configuration:**
403
- - Model: `meta-llama/Llama-3.3-70B-Instruct`
404
- - Temperature: 0.7 · Max tokens: 200 · Top-p: 1.0
405
- - Seed: 42 per task · Max steps: 20 · Force submit at step 7
406
-
407
- ---
408
-
409
- ## API Reference
410
-
411
- | Endpoint | Method | Request Body | Response |
412
- |---|---|---|---|
413
- | `/health` | GET | — | `{"status": "ok", "llm_available": bool, "provider": string}` |
414
- | `/tasks` | GET | — | `{"tasks": [...], "total": 5, "graded": 5}` |
415
- | `/metadata` | GET | — | Environment metadata with full task registry |
416
- | `/reset` | POST | `{"task_id": string, "seed"?: int}` | `SQLObservation` |
417
- | `/step` | POST | `SQLAction` | `{"observation", "reward", "done", "info"}` |
418
- | `/state` | GET | — | `{"task_id", "episode_id", "attempt_number", "cumulative_reward", "done"}` |
419
-
420
- ---
421
-
422
- ## Grader Registry
423
-
424
- | Task | Grader Function | Method |
425
- |---|---|---|
426
- | `null_audit` | `server.graders:grade_null_audit` | String match with lexical partial credit |
427
- | `type_repair` | `server.graders:grade_type_repair` | Schema + row preservation on isolated copy |
428
- | `dedup_bug` | `server.graders:grade_dedup_bug` | Row count + key identification + fix validation |
429
- | `fk_integrity` | `server.graders:grade_fk_integrity` | Orphan elimination + valid record preservation |
430
- | `schema_drift` | `server.graders:grade_schema_drift` | Column overlap ratio + row count alignment |
431
-
432
- ---
433
-
434
- <div align="center">
435
-
436
- *Built for the Meta × PyTorch × HuggingFace OpenEnv Hackathon*
437
-
438
- **One Man Automation · Chennai, India**
439
-
440
- [Live Space](https://huggingface.co/spaces/Deadman922/sql-quality-env) · [Report Issue](https://huggingface.co/spaces/Deadman922/sql-quality-env/discussions)
441
-
442
- </div>