Grounded SQL / How it Works
architecture & results
How a question becomes a verified answer
Four stages sit between a plain-English question and SQL you can trust, and every design choice along the way was made for a specific, measurable reason. Here's the full path, and the benchmark numbers that back it up.
the pipeline
Nothing gets executed on faith
The schema is narrowed before generation, and the SQL is proven correct by running it, never by how it looks.
Ask
A question in plain English, plus the target database, typed in, or a schema/.sqlite file you provide yourself.
app.py[INST] {system_prompt}
Database Schema: {schema}
Question: {question} [/INST]
Hybrid retrieval
BM25 (exact column names) and FAISS (semantic meaning) each rank the schema's tables, fused so only relevant tables reach the model.
retrieval/hybrid_linker.pyscore(t) = ฮฃ 1 / (60 + rank_i(t))
summed across the BM25 and FAISS rankings.
Fine-tuned generation
Mistral-7B-Instruct-v0.2 + a QLoRA adapter writes the SQL, served INT8-quantized and cached in Redis by question+schema hash.
serving/model_loader.pytarget: q/k/v/o_proj, gate/up/down_proj
Execution check
The SQL is actually run against SQLite. Correctness is measured by comparing the returned rows, never by how the query looks.
evaluation/executor.pywhy it's built this way
Five decisions that shaped the system
Each one traded simplicity for a measurable gain, not for its own sake.
Execution accuracy over string match
Two SQL queries can look completely different and return identical rows. Only running them tells you if a query is actually correct.
BM25 alongside semantic search
Dense retrieval finds "revenue" โ amount, but misses exact identifiers. BM25 finds customer_id verbatim. Fusing both is what eliminated hallucinated columns entirely.
INT8 over 4-bit for serving
SQL correctness is binary, not a matter of degree. A 3.1% accuracy drop from more aggressive quantization means roughly 32 more wrong queries on the 1,034-query test set, too costly for a two-and-a-half times throughput gain.
Loss masked to SQL tokens only
Training loss is computed only on the generated SQL, not on the schema or question prefix, so the model learns to write SQL, not to predict the schema back.
Tanglish as a documented failure, not a hidden one
The model breaks on Tamil-English code-switched questions, a schema-linking failure on Romanized words. Naming that precisely is more useful than pretending it doesn't happen.
results
Why fine-tuning wins on the 1,034-sample test set
Four systems, scored the same way: execute the generated SQL, compare its rows to the gold query's rows.
LoRA rank ablation training/ablations/
| Config | r | Result |
|---|---|---|
| rank_8.yaml | 8 | underfits, plateaus early |
| config.yaml | 16 | 66.2% exec. accuracy |
| rank_64_v2.yaml | 64 | 70.2% exec. accuracy |
Quantization for serving fp16 โ int8 โ gguf
| Format | Size | p95 | Accuracy |
|---|---|---|---|
| fp16 | 14GB | 1.8s | baseline |
| INT8 | 7GB | 0.8s | −0.8% |
| GGUF Q4_K_M | 4GB | 0.5s | −3.1% |