On this page
- The query ran. The number was wrong.
- What are the risks of text-to-SQL for AI agents?
- Why does valid SQL answer the wrong question?
- Typed collections, not raw tables
- Declare joins and results as contracts
- Scope belongs in the query, at field and record level
- How should AI agents query business data safely?
- Every answer needs a query receipt
- Common anti-patterns
- Ten rules for governed agent queries
- The result: answers that mean what they say
- Frequently asked questions
- How Frozion governs agent queries
- Sources
Text-to-SQL lets a model write queries that run but answer the wrong question: joins that duplicate rows, sums across currencies, results at the wrong grain, or rows outside the user's scope. Governed data access gives agents typed collections, declared join and result contracts, field- and record-level scope, and reviewed queries that leave a receipt each time they run.
The query ran. The number was wrong.
A finance director asks an analytics agent for Q3 revenue by region ahead of a board pack. The agent reads the schema, writes SQL, runs it, and returns a tidy table. EMEA: $14.2 million. The query is syntactically perfect. It executes in under a second. Nobody sees an error.
The real EMEA figure was $10.3 million. The agent joined orders to order lines and then to payments, so every order paid in instalments was counted once per payment. It summed the amount column across euros, pounds and dollars as if they were one currency. It grouped at order-line grain and rolled up, so discounts applied at order level were counted against each line. And because the director could see all regions, nobody noticed that the same query, asked by a regional manager, would also have returned rows outside that manager's scope.
The model didn't hallucinate a table or invent a column. It wrote valid SQL against a real schema. The schema simply didn't carry the business meaning the question depended on.
What are the risks of text-to-SQL for AI agents?
The main risk is wrong answers that look right: valid SQL that duplicates rows, mixes units, aggregates at the wrong grain or reads outside the user's scope, with no error to warn anyone.
Most public discussion of text-to-SQL focuses on whether the generated query matches an expected one. That's a useful benchmark for models. It isn't the question a risk owner asks, which is: did this answer apply our definition of revenue, for the records this person may see, at the grain the decision needs?
| Failure | What happens | Why nobody notices |
|---|---|---|
| Fan-out join | A one-to-many join repeats parent rows, inflating sums | Every row is "real"; the total is just too big |
| Mixed units | Amounts in different currencies or units are added | The column is called amount |
| Wrong grain | A metric defined per order is computed per line, or per day instead of per month | Grouping looks reasonable |
| Wrong definition | Gross used where net was meant; cancelled orders included | The schema doesn't encode the business rule |
| Scope leak | Rows or columns outside the requester's permission are read | The agent's database credential could see them |
| Cost and load | Unbounded scans on production tables | It works fine in testing |
Each of these is a semantics problem, not a syntax problem. A better model reduces the rate. It doesn't remove the class, because the information needed to avoid them isn't in the schema.
Why does valid SQL answer the wrong question?
Because a database schema describes storage, not meaning. It says which columns exist and how tables link, but not which joins are safe, which units can be summed, or what grain a metric is defined at.
Business meaning lives elsewhere: in finance policy, in a data team's dashboards, in the head of whoever built the revenue report. An analyst who has done this for years knows that payments fan out and that currency needs converting at the booking-date rate. A model given only the schema has to guess, and the guesses that produce plausible numbers are the dangerous ones.
Typed collections, not raw tables
The first step is to stop pointing agents at physical tables. Expose business entities instead (customers, orders, invoices, cases) as typed collections with declared fields.
A typed field carries meaning the schema lacks. Amount is a money value with a currency. Status is one of a fixed set of values, and cancelled is excluded from "open" by definition. Region is a scope attribute, checked against the requester. When the agent works against collections like these, a whole category of guessing disappears.
Declare joins and results as contracts
Two contracts prevent most silent errors.
Join contracts state how collections relate and with what cardinality: an order has many lines, a line belongs to one order, an order has zero or more payments. A query that would fan out across two one-to-many relationships before aggregating is flagged before it runs.
Result contracts state what a query returns: the grain (one row per region per month), the units (amounts in reporting currency, converted at a declared rate), the definition (net revenue excludes cancelled and refunded orders), and the shape (field names and types). An agent consuming the result knows exactly what each number means, and so does the person reviewing the decision later.

Scope belongs in the query, at field and record level
The agent's database credential is usually broader than any one user. If the query runs with that credential, the agent can see everything, and every answer is a potential disclosure. OWASP's guidance on excessive agency is direct about this: give an LLM-driven tool only the permissions the task needs.
Apply the requester's authority inside every query, at two levels:
- Record level. Only rows the person may see: their tenant, legal entity, region, accounts or cases.
- Field level. Only columns they may see: margins, salaries and personal data are masked or omitted for people without access.
The scope comes from the person the agent acts for, not from the agent's own service account, and it's checked when the query runs, not when the agent was configured. See taxonomy-scoped authorization for how scope can follow business structure such as regions and product lines.
How should AI agents query business data safely?
Give agents reviewed, named queries to call as tools, rather than asking them to write SQL. Teams author each query in SQL or visually, review it, and publish it with its contracts and scope.
This doesn't mean giving up flexibility. A query can take parameters (region, period, account), and a library of reviewed queries covers most operational questions. The agent's job shifts from writing SQL to choosing the right query and filling in parameters, which models do far more reliably.
| Approach | Who writes the query | Meaning | Scope | Reviewable |
|---|---|---|---|---|
| Free-form text-to-SQL | The model, per request | Guessed from schema | Agent's credential | After the fact, if logged |
| Text-to-SQL with a semantic layer | The model, constrained | Partly declared | Often still the agent's credential | Partially |
| Governed queries | People, in SQL or visually | Declared in contracts | Requester's, per run | Before any agent uses it |
Where an agent does need an ad hoc question answered, treat the generated query as a proposal: check it against join and result contracts, apply scope, and route anything novel and consequential to a person before its result drives an action.
Every answer needs a query receipt
When an agent's number feeds a decision, you'll eventually be asked where it came from. A query receipt answers that in one record:
- which query and version ran, with which parameters;
- on whose behalf, with what scope applied;
- against which data, at what time or snapshot;
- what it returned, including row count and totals.
That turns "the agent said EMEA was $10.3 million" into evidence you can check and reproduce. It also links into the wider decision record. See audit trails versus evidence.

Common anti-patterns
| Anti-pattern | Why it fails | What to do instead |
|---|---|---|
| Point the agent at raw production tables | The schema has no business meaning | Expose typed collections with declared fields |
| Trust SQL because it executes | Fan-out, mixed units and wrong grain never error | Check against join and result contracts |
| Run agent queries with a shared service credential | Every user gets the agent's full view | Apply the requester's record and field scope per run |
| Put "only show permitted data" in the prompt | Instructions aren't access control | Enforce scope inside the query |
| Let each agent invent its own revenue definition | The same question gets different answers | Publish one reviewed query per business metric |
| Log the SQL text only | You can't tell what data or scope it ran against | Record a full query receipt |
| Let ad hoc generated queries drive actions directly | Novel, unreviewed logic decides consequential outcomes | Treat them as proposals; review before use |
Ten rules for governed agent queries
- Don't give agents raw tables; give them typed collections.
- Declare joins with cardinality, and flag fan-out before aggregation.
- Declare each result's grain, units, definition and shape.
- Store money with its currency and convert at a declared rate.
- Apply the requester's record- and field-level scope inside every query.
- Use the delegating person's authority, not the agent's service account.
- Author queries in SQL or visually, and review them before agents use them.
- Let agents choose queries and parameters, not write production SQL.
- Treat any generated query as a proposal until it's checked.
- Record a receipt for every run: query, version, parameters, scope, data and result.
The result: answers that mean what they say
Text-to-SQL made it easy for anyone to ask a database a question. For agents that act on the answer, that ease is the problem: the question gets answered, just not quite the one that was asked. Governed queries move business meaning out of the model's guesswork and into contracts people can read and review.
The agent still does the useful part: understanding the request, picking the right query, explaining the result. The definition of revenue, the safe joins, the scope and the record of what ran stay where they belong, with the people accountable for them.
Frequently asked questions
What are the risks of text-to-SQL for AI agents?
Wrong answers that look right, plus access risk. Generated SQL can duplicate rows through joins, sum mixed currencies, aggregate at the wrong grain, read columns or records outside the user's scope, or run expensive queries against production. None of these raise an error.
Is text-to-SQL accurate enough for business decisions?
It works for exploration, not for decisions on its own. Accuracy benchmarks measure whether generated SQL matches an expected query, not whether a business definition such as net revenue was applied correctly. For decisions, agents should call reviewed queries with declared meaning, grain and scope.
What is governed data access for AI agents?
It's an approach where agents query business data only through typed collections and reviewed queries, with the requester's field- and record-level scope applied inside the query, declared result contracts, and a receipt recording what ran, for whom and what it returned.
How does this work for agents we already run?
Route the agent's data requests through a governed runtime over MCP or HTTPS. Instead of sending SQL, the agent calls reviewed queries as tools; scope is applied for the person it acts for, and every result carries a query receipt.
How Frozion governs agent queries
In Frozion, agents query typed operational collections through Collections & Query Builder. Join and result contracts declare how entities relate and what each result means, and every run leaves a query receipt. Teams author queries in SQL or in the Visual Query Builder, and review them before any agent uses them. Authorization combines roles, conditions, taxonomy scope, purpose and record-level checks, so each query runs with the authority of the person the agent acts for. Any workflow that runs a query can be exposed as an MCP tool for agents you already run.
Sources
- OWASP Top 10 for LLM Applications 2025Excessive agency, sensitive information disclosure and improper output handling.
- OWASP LLM06:2025 Excessive AgencyLimit what an LLM-driven tool can do to what the task needs.
- OWASP Top 10 for Agentic Applications (2026)ASI02 tool misuse and ASI03 identity and privilege abuse.
- NIST SP 800-162, Guide to Attribute Based Access ControlPer-request access decisions from subject, object and environment attributes.
- NIST SP 800-53 Rev. 5AC-3 access enforcement, AC-6 least privilege, AU audit records.
- W3C PROV-ONeutral model for recording what produced a result.
- EY survey on AI governance, September 202672% worry they can't trace the lineage behind critical AI decisions.
More in Data & context
Context assembly 2
Retrieval & data access 3
State, memory & features 2
Other topics



