The client
A fintech where every team runs on data
A large consumer fintech platform. Its sales, logistics and operations teams make decisions from its data every day.
The data was there. Getting a number out of it was the slow part.
The challenge
Every question queued behind someone who writes SQL
A regional sales figure, a count of late deliveries, a list of queues over SLA — each one needed a person who knew both SQL and the schema. Even with an analyst free, going from question to insight took around ten minutes. Without one, the question waited.
The starting point
The people with the questions were not the people who could answer them, so analysts spent their time as translators.
Constraints
What we had to design around
- Data stays homeThe production database sits inside the client’s own boundary. Nothing could be copied out to make the model’s job easier — the agent had to send a query in and get only the result back.
- Safe by constructionA language model writing SQL against live financial data is only safe if a bad query cannot run. Write statements, unbounded scans and tables outside the agreed set had to be impossible, not unlikely.
- House languagePeople ask in the words the business uses, not the column names. The agent had to learn that vocabulary and keep it, so the same term means the same thing next week.
- Keys and auditIn financial services the client holds the keys. Encryption had to use client-managed keys, and every query had to leave a record someone can inspect.
- Where people already areBusiness users would not adopt another dashboard. Questions had to go in, and answers come out, in the chat app they already use every day.
Our approach
Let the model write SQL. Let rules decide if it runs.
Text-to-SQL fails quietly: a plausible query that answers the wrong question, or a correct one that scans a whole table. Better prompting reduces that. It does not remove it.
So we split generation from permission. One ReAct agent does the reasoning — reading the schema, mapping house terms to columns, writing and rewriting the query. A separate, rule-based validator decides what may run, and an executor inside the client’s boundary is the only thing that touches the data. MCP keeps the agent’s view of that data consistent, and a single agent kept the system simple enough to test end to end.
The solution
Five layers, from a chat message to a checked result
- Chat interface — A Telegram bot. The user types a question; the answer comes back as a short explanation, a formatted table, or a link to the full result when it is too large for a message.
- ReAct agent — Built on Agno. It reads the cached schema and the terminology store, reasons about which tables answer the question, writes the SQL, and rewrites it when the validator sends it back with a reason.
- SQL validator — Rule-based and schema-aware. A table allow-list, forbidden clauses and a row-limit policy — checked before anything touches the database, with a clear error fed back to the agent.
- Executor and MCP — Approved queries travel over a private link to an executor on the client side, which runs them under role-based access and row-level security. MCP gives the agent one consistent interface to the data and its metadata.
- Memory and security — A session store holds user instructions and conversation context; domain terms live as vector embeddings. Storage is encrypted with client-managed keys, large results go out as 15-minute signed links, and every query is audit-logged.

Responsible by design
Four rules for an agent near financial data
Validate
No generated SQL runs unchecked.
Every query passes the rule-based validator first. The model writes; the rules decide what is allowed to reach the database.
Contain
The data never leaves the client’s boundary.
The executor runs on the client side. The agent sees schema metadata and the rows it asked for — not a copy of the database.
Restrict
Users see only what they are entitled to.
Role-based access and row-level security apply to every query, so the agent cannot widen anyone’s view of the data by phrasing a question cleverly.
Record
Every question leaves a trail.
Queries are audit-logged and session stores hold metadata, not results. Anyone reviewing the system can see what was asked and what ran.
The first rule carries the others. The agent is free to be creative about how it phrases a query. It has no say in whether that query runs.
Results
What changed for the business teams
- Question to insight cut from 10 minutes to 4 — 60% faster than writing the SQL by hand, and without waiting for an analyst to be free.
- 75% accuracy across 50+ benchmark queries in three domains — sales, logistics and operations — answered from the live schema.
- 90% of agent workflows completed end to end: generate, validate, run and interpret, with no hand-off to a person.
- 80% of stakeholders got accurate results in two tries or fewer, asking in plain language through the chat interface — most of them people who do not write SQL.
- Delivered in five weeks, with client-managed encryption, row-level security and audit logging in place from the start.
Analysts stopped translating routine questions into SQL, and the people asking stopped waiting for them.
Beyond the numbers
What else changes
A question that used to cost an analyst’s attention now costs a message. That changes which questions get asked. The follow-up — “and by week?” — happens in the same conversation instead of a new request.
And because the terminology store grows with use, every term the business teaches the agent makes the next question in that domain easier to answer correctly.
If your analysts spend their day translating questions into SQL
The first step is a benchmark: a few dozen of your real questions, run against your schema. That shows what an agent can answer before anything reaches production.