Savian: the data was on a dashboard and the person who needed it was in a van
Savian is an agricultural company whose managers need production and attendance data to decide. The data was there and there was a dashboard to look it up, but the person who needs it is out in the field or in a van, with no computer in front of them. Today they ask the way they would ask a colleague, by message or by voice note, and the answer comes back in seconds. This page tells how it is built and the two things we had to take away from the model along the way.
What this page covers
01 · The problem
The data was there and it still did not arrive
Savian works in agriculture. The owners and the managers of its growing estates need production and attendance data every day, for decisions that cannot wait.
It is worth saying up front what the problem was not. That data was not lost and it was not hidden. Savian already had a dashboard where you can look up the same information the agent answers today, built and running before the agent existed.
The problem was the last mile, which is the one almost nobody tells. A dashboard is a desk tool. It is designed for somebody sitting down, with the whole screen in front of them and time to pick filters and read a table.
And where is the person who needs the number? Not in that chair. They are in a van first thing in the morning or in the middle of an estate, with the phone in a pocket and their hands busy. Getting fluent with a dashboard is a job in itself, and it is not the job of somebody working in the field.
So the question waited. It got looked up back at the office, once there was a computer in front of them and the day in the field was over. A number that arrives at the end of the day stops being useful for deciding. It becomes useful for explaining what already happened.
02 · What we removed
The first version let the model write the query
The first version did the thing that looks obvious when you have a language model in front of you. It took the question in plain Spanish, wrote a SQL query with it and ran that query. SQL is the language you use to ask a database for data.
It worked. In a demo it worked very well, which is exactly the problem with letting it write the query.
Why pull something that works? For two separate reasons that are worth keeping apart, because one is visible immediately and the other is not.
Why we pulled it
The first one is security. A model that is allowed to write the query can write any query the language allows. The only thing stopping it is a sentence in its instructions, and an instruction to a language model is a request rather than a guarantee. It gets followed almost every time. That “almost” is the whole difference when there is data from several companies on the other side.
The second one is duller and we saw it before the first. It made mistakes. Queries that ran without an error and returned a number that was not the one somebody had asked for, which is the worst kind of failure because nobody sees it.
That is where the rule that orders everything we have built since comes from. Judgment lives in the code, the interpretation of language lives in the model and knowledge lives in the data. The model does what it is good at, which is understanding what it has been asked. The code does what needs guarantees, which is touching the data.
Matching names also lived inside the model
That was not the only thing we had to take out of there. Work sites and estates have long names that nobody types the same way twice, so the agent corrects what the person writes and matches it to the real name. That correction exists so that nobody has to remember an exact label.
At first the model did that matching too. We handed it the list of work sites and estates that person has access to and asked it which one they meant.
It failed often. And when it did not fail outright it did something worse, matching to the closest name on the list, which is not always the right one. A name that looks alike is not a name that matches. The model does not tell those two things apart well.
Today an approximate matching algorithm does the comparison, what in English is called fuzzy matching. It measures how close two pieces of text are and returns a score, so you can set a floor and discard anything below it. Accuracy went up as soon as it stopped being an opinion.
The shape of the fix is the same as with the query. A task that looked like language turned out to be comparison. And comparing is one of those things code does the same way every time.
03 · How it works today
The model proposes, the code builds
Today the model writes no query at all. It reads the question and returns a form with fixed fields that we defined in advance: the period, the scope, the filters, the metric and the groupings. Nothing else.
The code takes that form, checks that every field carries an allowed value and builds the query itself, with the values passed as parameters and the column names taken from a closed list. No identifier is assembled from text the model wrote.
And that is where the guarantee comes from. A form with five known fields can be checked in full before anything runs. A query written in free text cannot.
Four layers between one company and the one next door
The agent answers managers from several companies in the same group, each one about their own, so keeping them apart is the guarantee that holds the whole system up. Separation is enforced in four places. The model is none of the four.
The information the model can read while it answers contains only the companies of whoever is asking, so the rest do not exist for it and it cannot filter out what it never had.
The name correction we just described searches only inside those same companies. Somebody who writes a name half finished or with a letter out of place lands on the site they meant, if that site is theirs. If it is not, they land nowhere.
After that, the code validates the request against a closed list of allowed values before building anything. And the final query carries an unconditional filter that, if the permission list ever arrived empty, resolves into a condition no row can match. When something fails, the system closes rather than opening.
04 · What it will not do
What the agent refuses to answer
There are questions the system does not answer on purpose. The columns holding hours worked, lateness and absences for named individuals exist in the database and are simply not exposed to the agent.
The refusal does not live in a sentence in its instructions, it lives in what the system can reach. It is not that it decides not to answer, it is that it has nothing to answer with.
05 · Voice
A voice note from the field
With your hands busy and the phone in your pocket, typing is not always comfortable either. The natural way to ask something in the field is to send a voice note, so the agent understands them.
Behind a voice note there are three models and each one does a single thing. The first transcribes the audio into text. The second reads that text, works out what is being asked and composes the answer, with the usual split, because the code still fetches the data. The third turns the answer into speech.
It is the same idea that holds up the rest of the system, splitting the work into pieces that each do one thing and can be checked separately.
A written number is not a spoken number
That chain taught us something that shows up in no demo. The model composing the answer has to write figures and dates out in words, because the one reading them aloud reads what is written.
And what happens when it does not? A text that says “12,539 kilos” does not sound like twelve thousand five hundred and thirty-nine kilos when a synthesizer reads it. Spelled out in words, it does. The same goes for dates, which spoken and written look nothing alike.
It looks like a finishing detail and it decides whether the system gets used or abandoned. An answer that sounds odd does not get questioned, it stops being listened to.
06 · What we watch
What gets measured every week
The most useful measure on this system compares two things that should always agree: the tool the conversation called for and the tool the model actually used. When the tool asked for and the tool used do not match, it almost always means the model answered from memory instead of querying, which is the failure no system error gives away.
The gaps get classified too, one by one. A question that falls outside what the agent covers, one it does cover but has no data for, and one it did not understand are three different problems, with three different fixes and three different owners. Counting them together solves none of them.
Where it stands today and what comes next
The question that used to wait for the office now gets asked from wherever the person is, on a phone, typed or spoken, and the answer comes back in seconds. The dashboard is still there for anyone who wants it. What changed is that you no longer have to reach it.
What is coming next are automatic alerts, the “tell me when this happens” kind, so the system stops waiting for the question and speaks first.
Do you have data nobody looks up because it is too much work to reach?
Tell us your challenge and we reply within one business day. If we don’t see a return, we’ll tell you.