- Home
- For analysts
One person, five databases, an agent as the pair
The AI SQL assistant with governance: ask in your words, get a pipeline you can read
The analyst's day is spread across the tools: the trip data in Postgres, the reference tables in SQLite, the weather feed in MySQL, the exports on S3 — and no one tool sees two of them. The agent changes the drafting; this server changes what the draft becomes: a versioned pipeline over your real schemas, joined in a scratch database, released when you trust it, served as an endpoint when someone else needs the number. No natural-language box — your agent is the interface, and it works through governed tools.
The working loop
Ask in your words, get a pipeline you can read
You describe the question the way you would to a colleague: "rideshare share of trips per borough, split by rainy and dry days". The agent grounds itself first — your real schemas, catalogue statistics, the indexes — then drafts the pipeline, runs it, reads the failure when there is one, and fixes the shape. What lands in front of you is not a chat answer: it is a JSON document of named SQL nodes you can open, read, and argue with. The SQL is yours to audit, because an analysis you cannot read is an analysis you cannot defend.
The governance half is what makes pointing an agent at production acceptable: datasources are registered read-only wherever nothing should ever write, and a write-shaped node is refused at save time and again at run time. The agent explores; it does not mutate.
And the loop is fast in the way that matters: because the agent holds the whole tool surface — schemas, statistics, probe, draft, run, result — the distance between "question" and "checked answer" is one session. You stay the editor; the agent is the desk researcher who never gets bored of re-running the query with the join fixed. The parts of the craft that were never typing — judgment about which numbers matter, skepticism about the convenient answer — stay yours, and the pipeline format is what makes your review of the machine's work possible at all.
One question, five sources
A morning with five databases, as it goes now
-
Ask the question in your words
"Revenue by borough, with the rainy-day split, against last quarter." No tool selection, no export plan — the agent plans the sources.
-
The agent reads before it writes
Schemas, tables, columns and a few real values from each of the five sources — the grounding pass that keeps a plausible query over a misremembered column out of your morning.
-
The pipeline runs where the data lives
One node per source, in each engine's own dialect; every result staged into the run's scratch database; the join as ordinary SQL over them; the scratch database gone when the run ends.
-
You read the SQL, not a vibe
The deliverable is a JSON document of named nodes — the join is right there in the file, so the review is a read, and the question "why this number?" has a line-numbered answer.
-
Release it, and Monday stops asking
When you trust it: release, publish, and the endpoint serves the version you signed — the same answer, on demand, for whoever asks next.
The join
The join you could never do in one tool
The question that breaks single-tool workflows — revenue by borough, where the trips are in Postgres and the zone lookup is in SQLite — is an ordinary pipeline here. Each source node runs as a query at its own engine, in that engine's dialect; each result is staged as a table in a scratch database created for that one run; the join is ordinary SQL over those tables; and the scratch database is dropped when the run ends. Five sources, one analyst, nothing landed anywhere — the worked example runs it on real NYC data, and the lake reads Parquet and Iceberg on S3 in place when the fifth source is a bucket rather than a database.
you › Which rideshare company carried the most trips in each borough last quarter?
- read the datasource facts, columns and stats — hvfhv_zone_day is a census at zone × day × company
- resolved "last quarter" from the data's last day — Q4 2024
- rendered 3 templates, ran the draft: 4 nodes · 763 ms
- draft demo/top_company_by_borough v1 — left for a human to release
| borough | top company | trips | borough trips | share |
|---|---|---|---|---|
| Manhattan | Uber | 17,660,839 | 23,669,163 | 74.62% |
| Brooklyn | Uber | 11,946,362 | 16,057,699 | 74.40% |
| Queens | Uber | 9,786,602 | 12,847,231 | 76.18% |
| Bronx | Uber | 6,078,750 | 7,572,899 | 80.27% |
| Staten Island | Uber | 715,593 | 918,360 | 77.92% |
GET /api/x/demo/top-company-by-borough?anchor_date=2025-01-01 · after release · key scoped to /demo
from the demo workspace — a real result, not a promise
The diagnosis
The probe that says why it is slow
Two agent tools turn "it feels slow" into a diagnosis. Catalogue statistics read one table's row estimates, indexes and per-column bounds from the engine's own catalog — never a scan — so the agent sees that the filter it wrote matches 40 million rows, not 40 thousand.
The SQL probe runs one bounded SELECT — row cap and time box — and captures the EXPLAIN plan before the query, so the plan survives the timeout it explains. The agent fixes the shape from evidence: push the aggregation into the source node, add the predicate the index wants, stop staging a billion rows to count them.
The loop closes without you in the middle: the agent that drafted the query is the one holding the probe's output, so "why is it slow" and "fix it" happen in the same session instead of across a ticket. What comes back is a changed draft and a re-run with numbers attached — diagnosis and fix in one artifact, which you read the way you would read a colleague's work: quickly, because the reasoning is written down.
you › Which rideshare company carried the most trips in each borough last quarter?
- read the datasource facts, columns and stats — hvfhv_zone_day is a census at zone × day × company
- resolved "last quarter" from the data's last day — Q4 2024
- rendered 3 templates, ran the draft: 4 nodes · 763 ms
- draft demo/top_company_by_borough v1 — left for a human to release
| borough | top company | trips | borough trips | share |
|---|---|---|---|---|
| Manhattan | Uber | 17,660,839 | 23,669,163 | 74.62% |
| Brooklyn | Uber | 11,946,362 | 16,057,699 | 74.40% |
| Queens | Uber | 9,786,602 | 12,847,231 | 76.18% |
| Bronx | Uber | 6,078,750 | 7,572,899 | 80.27% |
| Staten Island | Uber | 715,593 | 918,360 | 77.92% |
GET /api/x/demo/top-company-by-borough?anchor_date=2025-01-01 · after release · key scoped to /demo
from the demo workspace — a real result, not a promise
The hand-off
Release when you trust it — and it is an API
When the numbers are the numbers, you release: a person's verb, an immutable version, a diff anyone can read next quarter when the figure is questioned. The draft stays a draft until then — the agent can iterate all day, but the version your team repeats is the one a person signed off.
And the release is the last step of the analyst's job and the first step of everyone else's: publish the endpoint and the finance team's script, the dashboard's refresh, or the PM's curl call all read the same versioned answer — parameters bound, keys scoped — instead of asking you to re-run it every Monday. The question stops being yours to carry, and the answer stops depending on your calendar.
The quieter benefit compounds: because every analysis is a versioned document rather than a session that ended, the person who joins next quarter inherits readable pipelines instead of folklore. The query the team "always runs" has a name, a history, and a diff for every time its definition changed — which is the difference between an analysis and an opinion with a chart.
Asked before
Analyst questions, answered plainly
Do I have to write the SQL myself?
No — that is the agent's job: it reads your real schemas, catalogue statistics and indexes first, drafts the pipeline, runs it and reads the failure when there is one. You read the SQL it left, because a pipeline is a JSON document of named SQL nodes. The authoring tools are docs/mcp-server.md §6.2.
How does the join across five databases work?
Each source node runs as a query at its own engine, in that engine's dialect, and its result is staged as a table in a scratch database created for that one run; the join is ordinary SQL over those tables, and the scratch database is dropped when the run ends. Staging is docs/staging.md §3.
A query is slow — how do I find out why?
Two agent tools answer it: catalogue statistics per table — row estimates, indexes and per-column bounds from the engine's own catalog — and the SQL probe, which runs one bounded SELECT and captures the EXPLAIN plan before the query, so the plan survives the timeout it explains. They are docs/datasources.md §7C and §7D.
How does a query become something my team can rerun?
It already is one: the agent saves its work as a versioned pipeline, you review and release it, and the released version can be published as a GET endpoint with declared parameters — rerun by anyone with the key, served by the version you released. The lifecycle is docs/versioning.md §3.
Give the agent your Monday question
The demo stack is five minutes and four sources; the question you hand the agent first should be the one you answer by hand every week. Compare its pipeline with your spreadsheet, and keep whichever you can defend — only one of them has a diff.