June 24, 2026

Intent First: How a Bot Decides What You're Actually Asking

The most expensive mistake in a data bot is made in the first half-second, before a single table is fetched: treating every message the same way. A user says "thanks, that's perfect" and the bot dutifully tries to turn it into SQL. Someone asks to compare two departments and the bot runs a single lookup that can't express a comparison. A question about "last month" gets handed to the database with the words "last month" still in it. None of these are model failures. They're the absence of a first step.

That first step is understanding the question before answering it — and it breaks into three cheap, decisive moves: classify the intent, extract the entities, resolve the time. Do them up front and everything downstream gets simpler, because every later stage is now specialized for a question it already understands.

Intent: pick the road before you drive

Most pipelines have at most a crude fork — "is this a data question, yes or no?" That's not enough resolution. Real questions come in kinds, and each kind wants a different strategy:

  • Aggregate ("how many…", "what's the average…") → a counting/grouping path.
  • Select / list ("show me the…", "which employees…") → a retrieval path with sensible row limits.
  • Comparison ("compare A and B", "what's the difference…") → decompose into parallel sub-questions, then join the results.
  • Explain ("how did you get that?", "what does this query do?") → describe the reasoning or the data lineage; no new query needed.
  • Schema / meta ("what tables do you have?", "what's in the employee record?") → answer about structure, not data.
  • Chitchat ("hi", "thanks") → a simple response that never touches the database.

Classifying into one of these is the single highest-leverage branch point in the whole system, and it's cheap — a fast, inexpensive model does it in one call. The payoff compounds: a comparison gets the decomposition it needs, an explain request stops wastefully re-querying, and chitchat stops triggering a database round-trip. Each route is simpler to build and more reliable to run because it only has to handle one shape of question.

It's also the natural home for a guardrail. The same first pass that spots "chitchat vs. data query" is where you catch the out-of-domain or adversarial input — the prompt injection, the "ignore your instructions," the request that has no business reaching your data. Reject it here, at the door, before any machinery spins up.

Entities: extract and normalize what the question is about

Once you know the kind of question, pull out the things it's about. This is named-entity recognition, tuned to your domain, and it does more than tag words — it normalizes them and points at the data.

Take "mame zamestnance holkky?" — a misspelled, colloquial way to ask whether there are female employees. A good extraction step produces something structured:

  • zamestnance → type PERSON, normalized "employee," suggests tables for people.
  • holkky → type GENDER, normalized "female," with a concrete filter value, suggests the gender lookup.

Three things just happened that pay off enormously later. The typo and the slang were absorbed — "holkky" became a clean, canonical concept. The filter value was resolved — "female" is now tied to an actual coded value, not left for the SQL step to guess. And each entity nominated its own candidate tables, so by the time retrieval runs, it already has a head start. Front-loading this turns a vague sentence into a typed, normalized, table-aware request before a single row is touched.

Time: resolve "last month" to a real range

Relative time is a quiet wrecking ball. "Last quarter," "this year," "the past month" — these are trivial for a human and treacherous for a model, because the answer depends entirely on today's date, and language models are unreliable at date arithmetic. Hand "last month" straight to SQL and you're gambling on the model inventing the right boundaries.

So you resolve it deterministically, once, before generation. "Last quarter" becomes an explicit range between two dates. "This year" becomes a concrete year filter. The bot then generates SQL against a real, unambiguous interval — and an entire category of subtle, hard-to-spot date errors simply stops happening. This is the same instinct as resolving a coded value in a separate focused step rather than letting the generator guess: anything that can be pinned down exactly should be pinned down outside the creative step.

Why this belongs at the very front

It's tempting to fold all this into the SQL-generation prompt and let one big model sort it out. Don't. Each of these moves removes ambiguity that would otherwise compound downstream — and they remove it cheaply, mostly with fast models doing narrow, well-shaped tasks.

Think of it as the first fixed stage of the pipeline, the one that earns its place by making every stage after it easier. Intent picks the strategy, so the rest of the system isn't a one-size-fits-all path pretending a comparison and a greeting are the same problem. Entities seed retrieval and pre-resolve filters, so the generator starts with candidates and clean values instead of raw text. Temporal resolution deletes a class of errors before they can be made. The bot spends a little intelligence up front to understand the question — and saves a great deal of it, and a lot of wrong answers, on everything that follows.

A bot that answers before it understands is just a fast way to be confidently wrong. Understanding first is what makes everything after it worth trusting.


Is your bot running every question down the same path? An intent-and-entity layer is usually the cheapest accuracy win available — and the foundation everything else builds on. Let's scope what your bot should understand before it answers.