sqrl-9b
sqrl-9b is a 9B-parameter agentic text-to-SQL model built on
Qwen/Qwen3.5-9B. It answers natural-language
questions over SQLite databases by optionally
probing the database first — running
read-only exploration queries to check value formats, join paths, and filters — before
committing to a final SQL answer. It is the larger sibling of
feyninc/sqrl-4b, trained with the same recipe.
Training stages:
- SFT distillation — fine-tuned on ~10.2k execution-verified agentic trajectories
sampled from a much stronger RL-trained 35B teacher (Qwen3.6-35B-A3B, 70.3% BIRD-dev EX)
on cleaned BIRD-train + Spider-train questions. Only trajectories whose final SQL
execution-matched the gold answer were kept.
- CISPO RL — online reinforcement learning (group-relative advantage, group size 8)
with execution-match reward on the same cleaned BIRD+Spider question pool, with
async dynamic sampling. Best checkpoint at step 60.
Results
All BIRD-dev numbers measured on the full 1534-question set at temperature 0.7 with the
agentic harness (execution-clustered majority vote = sample 8 candidates, group them by
identical execution result set, return the largest cluster's query).
| Benchmark | Metric | Score |
|---|
| BIRD-dev | pass@1 (avg over 8 samples) | 66.6% EX |
| BIRD-dev | vote-of-8 (execution-clustered) | 69.8% EX |
Family comparison on identical seeds (pass@1 / vote@8):
sqrl-4b 64.6 / 68.8 ·
sqrl-9b 66.6 / 69.8 ·
sqrl-35b-a3b 68.7 / 70.6.
Notes from the measurement campaign:
- sqrl-9b vote-of-8 (69.8%) is the family's cost/performance sweet spot — within
0.8 pts of anything the whole family achieves under any sampling/voting scheme,
including the 35B. vote-of-4 captures ~85% of the voting gain at half the cost.
- Vote unanimity is a strong free confidence signal: when all 8 samples agree (~60% of
questions) the answer is right ~87% of the time; narrow/tied votes are right ~19-43% —
useful for routing or escalation.
- The pre-RL SFT checkpoint scores 64.0% pass@1 on the same harness; base Qwen3.5-9B
scores ~56%.
How it works — the agentic protocol
The model emits exactly one action block per turn, after a brief reasoning summary:
<sql> SELECT ... </sql> — a read-only exploration query. Execute it against the
database and feed the result back as a user turn wrapped in <observation> tags.
The model continues (up to a step budget).
<answer> SELECT ... </answer> — the final SQL. Execute and return.
Many questions are answered directly (zero exploration turns); the model learned to
explore only when seeing real data would change its answer (value-format checks,
ambiguous joins).
Usage
Serve with vLLM
1vllm serve feyninc/sqrl-9b --served-model-name sqrl-9b \
2 --data-parallel-size 4 --gpu-memory-utilization 0.90 --max-model-len 32768
Important: do not enable a reasoning parser (e.g. --reasoning-parser qwen3).
The model's post-</think> content carries the action protocol; stripping/rerouting
the think block breaks it. Parse message.content — if it contains a </think>,
take everything after it.
Recommended sampling: temperature 0.7, top_p 0.95 (matches training); greedy also works.
Prompt format
System prompt (fill {schema}, {evidence}, {max_steps}):
1<role>
2You are an expert data analyst, fluent in SQL, with a meticulous eye for
3matching a question's intent to the exact tables, columns, and stored value formats of
4a database.
5</role>
6
7<task>
8Translate the user's natural-language question into a SQL query that answers
9it, using the database schema in <schema> and any domain hints in <evidence>. You can
10run read-only queries against the database to inspect it before giving your final
11answer.
12</task>
13
14<database_engine>
15SQLite
16</database_engine>
17
18<schema>
19{schema}
20</schema>
21
22<evidence>
23{evidence}
24</evidence>
25
26<protocol>
27Think through the problem internally first. Then, in your response, write a
28BRIEF summary of your reasoning — 2-4 sentences stating which tables and columns are
29relevant, the joins and filters, and any exact value-format detail. Be decisive: state
30the plan once, do not second-guess or restate. End your message with EXACTLY ONE action
31block (and nothing after it):
32
33 <sql> a read-only query to inspect the database </sql>
34 Use this when uncertain and you want to see real data before answering — e.g.
35 confirm a value's exact stored format, check a filter actually matches rows,
36 sanity-check an intermediate result, or verify a join. You will see the result
37 rows and then continue.
38
39 <answer> your final SQL query </answer>
40 Use this once you are confident. It is executed and scored, and the task ends.
41
42Examples:
43 I'll check the exact county value before filtering, since the format may vary.
44 <sql> SELECT DISTINCT `County Name` FROM frpm LIMIT 10 </sql>
45
46 The schools are in the frpm table; I'll select `School Name` and filter `County Name`
47 to 'Alameda'. The schema is clear, so I can answer directly.
48 <answer> SELECT `School Name` FROM frpm WHERE `County Name` = 'Alameda' </answer>
49</protocol>
50
51<rules>
52- Use only the tables and columns defined in <schema>.
53- Quote identifiers containing spaces or special characters with backticks.
54- Return exactly the columns the question asks for — no more, no fewer.
55- Use the hints in <evidence> to resolve ambiguous terms and value encodings.
56- If you already know the correct query, go straight to <answer> — investigating is
57 optional; only run <sql> when seeing the data would actually change your answer.
58- The action block must be the LAST thing in your message; do not discuss the tags
59 themselves in your reasoning.
60- You have at most {max_steps} <sql> steps; after that you must give <answer>.
61</rules>
First user turn:
1Question: {question}
2
3Reason about it, then give <sql> to investigate or <answer> to finish.
After executing an exploration <sql>, feed the result back as a user message:
1<observation>
2{tab-separated result rows, or the error message}
3</observation>
4Continue with <sql> or <answer>.
{schema} is a readable dump of the SQLite schema (CREATE-TABLE-like listing of
tables/columns/types). {evidence} is the BIRD external-knowledge hint, or
(none provided). max_steps=5 was used in training. When the step budget is
exhausted, send You must finish now. Give your <answer>. as a user turn.
Minimal driver loop
1import re
2from openai import OpenAI
3
4client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")
5
6messages = [{"role": "system", "content": system_prompt},
7 {"role": "user", "content": user0}]
8for step in range(6):
9 r = client.chat.completions.create(model="sqrl-9b", messages=messages,
10 temperature=0.7, top_p=0.95, max_tokens=8192)
11 content = r.choices[0].message.content
12 if "</think>" in content: # strip inline think scratchpad
13 content = content.split("</think>")[-1].strip()
14 messages.append({"role": "assistant", "content": content})
15 ans = re.findall(r"<answer>(.*?)</answer>", content, re.S)
16 if ans:
17 final_sql = ans[-1].strip(); break
18 sql = re.findall(r"<sql>(.*?)</sql>", content, re.S)
19 if not sql:
20 break
21 obs = run_readonly(sql[-1].strip()) # your sqlite executor
22 messages.append({"role": "user",
23 "content": f"<observation>\n{obs}\n</observation>\n"
24 "Continue with <sql> or <answer>."})
Checkpoint notes
- This repo contains the full merged HF checkpoint (LoRA rank 32 merged into the
base). Qwen3.5-9B has untied embeddings, so the merge is direct: the trained head
lands in
lm_head, embed_tokens is untouched. Loads with standard transformers /
vLLM — no custom code beyond trust_remote_code if your transformers version
requires it for Qwen3.5.
- The vision tower is inherited from the base model unchanged. The model is usable as a
standard VL checkpoint, but SQL training was text-only.
- Text-to-SQL training data: BIRD-train and Spider-train, execution-filtered and
semantically cleaned (3-model LLM-judge panel). BIRD-dev and Spider-test were never
trained on.
Intended use & limitations
Built for SQLite text-to-SQL with schema + optional evidence in context. Works best
with the exact prompt protocol above (it was both SFT'd and RL'd under it). Not tuned
for other SQL dialects; identifier quoting follows SQLite backtick conventions. As with
all text-to-SQL models, execute generated SQL read-only and validate before acting on
results.