Our CEO recently wrote reaffirming an architectural decision ThoughtSpot made when LLMs first emerged: we do not use LLMs to directly generate SQL.
My team has spent the better part of a year building AgentQL: a capability that doubles down on our decision.
So let me explain what we actually built, why it doesn't just honor that architectural decision but depends on it, and the engineering choices underneath.
The Problem We Were Handed: The Expressibility Gap
Spotter answers most questions through search tokens: a structured, readable representation of intent that a business user can inspect and correct without knowing any query language. Tokens are the right default, and they remain the primary path.
But we kept hitting a wall we came to call the expressibility gap. We saw queries like “Show me customers whose spend this fiscal year declined versus last, ranked by the size of the drop." and "Compare each region's contribution margin against the company average.” Multi-step comparisons, level-of-detail calculations, cohort logic.
Our query engine could answer every one of these and has been able to for years. The token grammar just couldn't state them. And every time we extended the grammar and retrained Spotter to emit it, new analytical features would land, and the gap would reopen.
We needed a richer language for expressing intent, which is how we came to build AgentQL. We did not need–and refused to build–a second execution path.
The Design Decision: SQL-Shaped Intent
We chose SQL syntax for the intent language for two practical engineering reasons:
It’s the most precise, most widely understood notation for analytical questions ever created.
It’s the language LLMs are most heavily trained on. Any bespoke DSL we invented would have been worse on both counts.
But we took SQL's syntax and rejected SQL's execution model. That is the entire design. It's also the first principle behind AgentQL: intent and execution are separate layers.
An AgentQL statement is written against the ThoughtSpot Model: the business names your analysts curated. Not physical tables or warehouse columns.
And it is never, under any circumstance, executed against your database. There is no code path by which it could be. We know, because we built the code paths.
What actually happens: The AgentQL statement is parsed into a query specification, the same internal representation every ThoughtSpot query becomes.
That spec is handed to our deterministic query-generation engine, the same one that produces every query in ThoughtSpot, whether it starts from a Liveboard, a search, or a Spotter conversation. That engine writes the SQL that runs. The LLM's output is input to a compiler, not a query to a database.
We call this SQL-shaped intent.
The distinction from text-to-SQL is not subtle:
|
Text-to-SQL |
ThoughtSpot AgentQL |
|
|---|---|---|
|
What the LLM writes |
The query that runs |
A description of what's wanted |
|
What it references |
Physical tables and columns |
The Model's business names |
|
What executes |
The LLM's output, verbatim |
Deterministic SQL compiled by our proprietary engine |
|
Who enforces security, joins, metrics, calendars |
The LLM (hopefully, per query) |
The engine (always, by construction) |
A concrete example. "Total sales and total returns by product category" might be expressed as:
SELECT "t1"."Product Category",
SUM("t1"."Sales Amount") AS "Total Sales",
SUM("t1"."Return Amount") AS "Total Returns"
FROM "Retail Sales" AS "t1"
GROUP BY "t1"."Product Category"
That statement never runs, and it hides a trap. Behind the Model, sales and returns live in two separate fact tables that share the product dimension: a classic chasm trap.
Hand-written SQL that joins them directly fans out the rows and silently inflates both totals. The compiler knows better, because the Model does: it aggregates each fact at its own grain before combining them, resolves the declared join paths, and injects the querying user's row-level security filters. You can inspect the compiled SQL. The intent took seconds to express; the semantics it inherited took us a decade to engineer.
One consequence worth stating plainly: AgentQL is deliberately not all of SQL. It's a restricted dialect, and the restrictions are the contract. A statement must be fully resolvable against the Model, so the compiler rejects anything it can't deterministically govern, from SELECT * to constructs that would bypass the Model's semantics.
We didn't restrict the dialect because parsing full SQL is hard. We restricted it because every accepted statement is a promise that the compiled query is governed, and we only accept statements we can keep that promise for.
Why We Refused to Make the LLM Do More
It's a fair question: LLMs are getting better fast, so why not just let a frontier LLM write the warehouse SQL and validate it after?
Because we've spent years engineering for what enterprise data actually looks like, and none of it is LLM-friendly. Real data models aren't flat tables. They're multi-star schemas with chasm traps and fan traps, like the sales-and-returns example above: join patterns where the wrong numbers look perfectly plausible.
Competent engineers get these wrong; we've watched it happen. They carry PII that must stay secured for every user on every query. They run different financial calendars per business unit. They define semi-additive measures like inventory and account balances, where a naive SUM across time is simply the wrong answer.
Ask an LLM to re-derive all of that, probabilistically, on every question, and you get exactly two things: uncertainty and a token bill.
Our engine already encodes those semantics. So the engineering answer was obvious, and it became the second principle: keep the LLM's job thin. Understand the question, express it compactly, and stop there. Everything that determines whether the number is right is inherited from the Model by construction.
The LLM cannot forget your row-level security, because it never writes the query that runs. It cannot pick a wrong join path, because the Model's joins are applied by the compiler. It cannot drift from your revenue definition, because there is exactly one, and every query compiles through it.
The failure mode matters just as much, and it's the third principle: a bad query is a compile error, not an incident. When the LLM expresses intent wrong, you get a wrong-but-governed answer or a clear validation error: the statement is rejected at compile time with a reason.
What you never get is an ungoverned query touching your data. In text-to-SQL systems, a hallucination is a live query. In ours, it's a compile error.
These principles buy one more thing, and it's the one our customers care about most: trust. A deterministic compiler means the same question produces the same query specification, the same compiled SQL, and the same numbers, no matter which LLM expressed the intent or how it phrased it. Plenty of text-to-SQL tools will also show you the SQL that ran.
The difference is what reading it buys you. There, you're auditing an LLM's improvisation, query by query: did it pick the right joins, apply the right security, and use the right definition of revenue?
Here, the SQL was compiled from the Model's governed definitions, so those questions are already answered. Verifying an answer means confirming the intent was understood, not re-deriving correctness by hand.
The Consequence: No Charging by Tokens
There's a byproduct of this architecture that shows up on the invoice rather than in the demo. Because the LLM's role is deliberately small, the heavy lifting runs on our deterministic engine, not on metered inference. A question doesn't spin up an agent that burns tokens re-reasoning about your schema.
That's the engineering fact behind a decision the company made at the Spotter 3 launch: no charging by tokens consumed. Our architecture is what made that choice available; the business chose to pass the benefit on.
Nobody wants an experience where every question comes with a meter running in the background. With this architecture, it doesn't have to.
The Same Bet, Doubled Down
Much of the industry is betting that with enough prompting, guardrails, and retries, LLMs will write trustworthy warehouse SQL. We made the opposite bet years before LLMs existed. AgentQL doubles down on it: the LLM should never write the query that executes. LLMs understand intent. A governed engine produces deterministic answers.
For Spotter users, the payoff is immediate: a far wider range of analytical questions answered today, without waiting for the token grammar to grow or for Spotter to be retrained on a new capability. If the engine can answer it, AgentQL can now ask it.
AgentQL gives intent a much richer language. The trust architecture didn't move an inch: it's in the engine, in the Model, and in your hands to verify. This is engineering by design, not by compromise - so you never have to choose between speed and trust.
Start your personalized demo to see how.



