Home Master Prep STAR Stories Evals & Guardrails Eval Deep Dive Guardrails Deep Dive Core Concepts Intent Recognition 中文

Intent Recognition · Part 1 of 2

Working out what the user actually wants

Intent recognition is the step where an agent decides which of the things it can do is the thing being asked for. Get it wrong and the agent doesn't give a slightly worse answer — it does the wrong thing. This page builds the idea from nothing, then goes all the way to how you measure it.

What it actually is

Also called intent classification. The plainest possible definition: deciding what the user really wants the system to do.

Start with the problem it solves. A person types a sentence. A sentence is just text — a sequence of characters. Software cannot execute a sentence. Software executes functions: get_order_status(order_id), cancel_order(order_id). So something has to stand between the sentence and the function and decide which function this sentence is asking for. That something is intent recognition.

The important part is that it is not keyword matching. Look at three different sentences:

"Can you check where order 123 has got to?"
"Why hasn't my parcel arrived yet?"
"What's the status on this order?"

They share almost no words. One of them doesn't contain the word "order" in the way the others do; one is phrased as a complaint rather than a request. But all three want the same thing to happen, and that thing has a name:

check_order_status

That name is the intent. Once the agent has it, it knows what comes next:

check_order_status
   ↓
Order API → get_order_status(order_id)
The one-sentence version Intent recognition maps natural language onto an executable task category — a label that tells the agent which tool to call, which workflow to run, and what information it still needs to collect.

Note the phrase "executable task category". An intent is not a topic. "Orders" is a topic. cancel_order is a task category, because there is a specific thing the system does when it sees that label. That distinction turns out to drive the entire design, and we come back to it in Start from business actions.

Where it sits in an agent

Intent is one step in a chain. Knowing the chain is how you answer "walk me through your agent" without waffling.

Here is the shape of a typical agent turn. Read it top to bottom — each step depends on the one above it:

User input
   ↓
Understand the request
   ↓
Intent recognition          ← what does the user want?
   ↓
Extract parameters / entities   ← what values does that need?
   ↓
Anything missing?               ← if yes, ask
   ↓
Choose tool / workflow
   ↓
Execute
   ↓
Generate the answer

Worked through one real sentence:

The user says

"Can you check the delivery status of order A123?"

The agent derives

intent = check_delivery_status
order_id = "A123"
→ get_delivery_status(order_id="A123")

Two different things: intent and slots

These get confused constantly, so pin them down separately.

1
Intent — what the user wants done

A single label naming the action. It answers "which of my capabilities is this?"

check_ordercancel_orderchange_addressrequest_refundcreate_ticket

2
Entity, or slot — what that action needs to run

The parameters. An entity is a value mentioned in the text; a slot is a blank the workflow needs filled. Same idea from two directions.

order_idcustomer_idproduct_namedatelocation

Both together, from one sentence — "Cancel the iPhone I ordered yesterday":

Intent
  cancel_order

Slots
  date    = yesterday
  product = iPhone

Notice that the slots here are not yet usable. "Yesterday" is not a date the API accepts, and "iPhone" is not an order ID. Resolving those — turning loose human values into system values — is its own step, and a common source of failures that get blamed on intent recognition when they are really slot-filling failures.

Why an agent needs it at all

Because an agent has many capabilities, and only one of them is correct.

Imagine a customer-service agent holding these tools:

get_order()
cancel_order()
create_return()
check_inventory()
update_address()
create_support_ticket()

The user writes five words: "My thing hasn't arrived yet." Before anything can happen, the agent has to commit to an interpretation:

Is this...
  A. checking the order?
  B. checking the delivery?
  C. asking for a refund?
  D. making a complaint?

There is no neutral option. Whatever it picks determines which tool runs. And here is why this matters more than it first appears:

Wrong intent is not a wrong answer — it's a wrong action If a question-answering system misunderstands you, you get a bad paragraph and you rephrase. If an agent misunderstands you, it may have already cancelled your order. The blast radius is completely different, and it scales with how much power you gave the tools.

Concretely:

Misread

"My parcel still hasn't arrived"
→ refund_order

The agent issues a refund nobody asked for.

Correct

"My parcel still hasn't arrived"
→ check_delivery_status

The agent looks up the tracking and explains the delay.

So a large part of any agent architecture is really this chain:

Intent
   ↓
Policy / workflow
   ↓
Tool

Intent picks the policy; the policy picks the tool. Keeping those three separate is what lets you change a business rule without retraining anything.

Start from business actions, not from language

The most common mistake is designing dozens of intents on day one. The second most common is naming them after topics.

The instinct is to look at user messages and group them by what they sound like. That produces topic labels. Do the opposite: write down what the system is able to do, and let the intents fall out of that. For an e-commerce assistant, the capability map might be:

Orders
 ├─ look up an order
 ├─ modify an order
 └─ cancel an order

Delivery
 ├─ check delivery status
 └─ change delivery address

Refunds
 ├─ request a refund
 └─ check refund status

Account
 ├─ update details
 └─ reset password

Now the intents are just names for those leaves:

order_status
cancel_order
modify_order
delivery_status
change_delivery_address
request_refund
refund_status
reset_password

Why topic names fail

Compare two candidate label sets:

Bad — topics

order
product
payment
delivery

Good — actions

check_order_status
cancel_order
modify_order
reorder

The label order is useless to the agent because it does not answer the only question that matters — what do I do now? Four completely different things live inside it: checking, cancelling, modifying, reordering. One of them is read-only and harmless; another is irreversible. Collapsing them into one label throws away exactly the information the next step needs.

The rule A good intent corresponds to one clear business action or workflow — not to a subject area.

Choosing the granularity

The single most useful test in intent design, and it takes one question to apply.

Suppose users ask both "check my order" and "check my delivery". Do you need one intent or two?

check_order

        ...or...

check_order_status
check_delivery_status

The tempting answer is to decide based on how different the sentences sound. That is the wrong axis entirely. The right question is:

The test Does what happens next differ? If two phrasings lead to a different decision, a different workflow, or a different tool, they are different intents. If they converge on the same call, they are one.

Applied:

Split them

check_order_status → Order API
check_delivery_status → Logistics API

Different systems, different failure modes, different answers. Two intents.

Keep them together

Both end at get_order(order_id)

Splitting buys nothing but adds a class the classifier can now get wrong. One intent.

The second case is worth dwelling on, because it explains why more intents is not safer. Every additional label is another opportunity for misclassification, another set of examples to write, another row and column in the confusion matrix. A distinction that changes nothing downstream is pure cost.

What a proper intent definition contains

A name on its own is not a definition. The boundaries are the definition.

Writing refund in a list and moving on is how you end up with a classifier that behaves unpredictably — because nobody, human or model, knows precisely what belongs in it. A complete definition looks like this:

Intent name
  request_refund

Description
  The user is explicitly asking for money back on an
  order they have already paid for.

Include
  "I'd like a refund"
  "Can I return this order?"
  "Cancel it and refund me"

Exclude
  "When will my refund arrive?"
  "Why haven't I received my refund?"

The Include and Exclude sections do the real work. Definitions fail at their edges, not in their middle — nobody misclassifies "I want a refund". The failures live in the neighbouring pair:

request_refund

The user wants a refund to start. This changes data — money moves.

refund_status

A refund already exists; the user wants to know where it is. Read-only.

"Why hasn't my refund arrived yet?" contains the word refund and sounds unhappy, so a careless system files it under request_refund and tries to issue a second refund. It is refund_status. One is a query; the other is a transaction. Writing that boundary down explicitly, with examples on both sides, is the cheapest accuracy improvement available in intent design — and it doubles as your test set.

Three ways to actually build it

Classical classifier, LLM classifier, or no explicit classifier at all. All three are current — the third is where most new systems land.

1
A trained classifier

The traditional approach. You collect labelled examples, fine-tune a small language model such as BERT, and it outputs a probability for every intent.

User text
   ↓
classifier
   ↓
refund_status:   0.85
request_refund:  0.10
other:           0.05

Good: fast, cheap per call, stable — the same input gives the same output every time, which makes it easy to test.
Bad: needs training data you may not have, and adding one new intent means retraining and re-validating the whole model.

2
An LLM asked to classify

You give a general-purpose model the list of intents in a prompt and ask for a label back. No training at all.

You are an intent classifier.

Available intents:
  1. check_order_status
  2. cancel_order
  3. request_refund
  4. refund_status
  5. change_address
  6. unknown

Return JSON only.

Input "My refund hasn't arrived yet." and the model returns:

{
  "intent": "refund_status",
  "confidence": 0.94
}

Good: no training data, works from a handful of examples in the prompt, and adding an intent is editing a text file.
Bad: slower and costlier per call, and non-deterministic — which is exactly why the evaluation section below exists.

Worth saying out loudMost "intent routers" in modern agent stacks are literally this: one LLM call with a list and a JSON schema.

3
No classifier — intent is implicit in tool selection

You skip the label entirely. Hand the model the tool definitions and let it pick one directly.

Tools:
  get_order_status
  cancel_order
  request_refund
  get_refund_status
  change_address

User: "My refund still hasn't come through." The model calls get_refund_status(). There was never an intent label — but the decision was made all the same, because choosing a tool is choosing an intent.

User
 ↓
LLM
 ↓
Tool selection
 ↓
Tool execution

Good: one call instead of two, nothing to keep in sync, naturally handles cases you never enumerated.
Bad: the decision is invisible. You cannot easily log "what did it think this was", you cannot apply a policy between intent and tool, and you cannot report intent accuracy to a risk committee.

The point to make in an interview A modern agent may have no module named "intent classifier" anywhere in the codebase and still be doing intent recognition on every turn. The question is never "do we have one" — it's "is the decision explicit enough that we can log it, gate it, and measure it". In a regulated environment that argues for making it explicit even when the model could do it implicitly.

Hierarchical routing: don't classify forty things at once

For anything enterprise-sized, two coarse decisions beat one fine-grained one.

Asking a classifier to choose between forty labels in a single shot is fragile: the labels start overlapping, the examples blur, and accuracy degrades in ways that are hard to attribute. Split the decision into layers instead. The top layer answers only "what kind of thing is this":

                 ┌─ knowledge_query  → retrieval / RAG
                 │
User → Router ───┼─ business_action   → workflow / tool
                 │
                 ├─ conversation      → plain LLM reply
                 │
                 └─ unknown           → ask for clarification

Only inside business_action do you make the fine-grained choice:

business_action
       ↓
order      refund       delivery
 ├ check    ├ request     ├ status
 ├ cancel   └ status      └ change_address
 └ modify

This is called hierarchical intent classification. Three reasons it holds up better:

  • Each decision has fewer options. Four-way then five-way is far more reliable than one twenty-way choice.
  • The dangerous boundary is guarded early. "Is this a question or an action?" is the split that decides whether anything can change state — and it's the easiest one to get right.
  • It localises failures. When accuracy drops you can see which layer moved, instead of staring at one number.

The two intents everyone forgets

Unknown, and ambiguous. Leaving them out is what turns a classifier into a liability.

Unknown / out of scope

A classifier asked to choose between five labels will always choose one of the five. That is what classification means. So if your agent knows only cancel_order, check_order and refund, and a user asks "who is your CEO?", the model does not have the option of saying "none of these" — unless you gave it one.

unknown
out_of_scope
ambiguous

Without those, an off-topic question gets forced into a business intent, and the agent calls a tool for a request that had nothing to do with it. This is the mechanism behind a whole class of embarrassing production incidents, and it is prevented by adding one label and one instruction.

Ambiguous — the case for not guessing

The user says: "I need to change my order." Change what? Any of these is plausible:

change_address
change_quantity
change_product
cancel_order

The wrong response is to pick the most likely one and act. The right response is to notice that no option is clearly ahead and say so:

intent     = modify_order
confidence = low
   ↓
"Sure — did you want to change the delivery
 address, the quantity, or something else
 about the order?"
What a mature intent layer produces Not a label. A label plus a confidence plus a clarification policy — a written rule about how uncertain is too uncertain to act, which can differ per intent. Read-only lookups can proceed on a weak signal; anything irreversible should demand a strong one.

That asymmetry is the whole idea: the cost of asking one extra question is small and recoverable, and the cost of cancelling the wrong order is neither.

When one message contains two intents

Real users do not send one instruction at a time. This is where intent recognition turns into planning.

"Check my order, and if it hasn't shipped yet, cancel it."

There are two actions in that sentence, and the second is conditional on the result of the first. A system built on the assumption of one message = one intent has no way to represent it; it picks whichever it thinks is dominant and drops the other half silently — which is worse than failing loudly.

{
  "intents": [
    { "intent": "check_order_status" },
    { "intent": "cancel_order",
      "condition": "if not shipped" }
  ]
}

From which the agent builds a small plan:

get_order_status
       ↓
if status != shipped
       ↓
cancel_order
The transition worth naming Once intents carry order and conditions, you have stopped doing intent classification and started doing task planning. Same input, but the output is a graph of steps rather than a label.

You do not have to support this from day one. But you should know where the ceiling of your design is, and be able to say what the system currently does with such a message — which is a question interviewers like, because the honest answer reveals whether you have actually run one of these in production.

Evaluating it, starting with accuracy

Accuracy is where you begin and it is nowhere near where you stop.

The starting point is a test set: inputs paired with the label you believe is correct, called the ground truth.

User inputGround truth
Where is my order?order_status
Cancel my ordercancel_order
My refund hasn't arrivedrefund_status
I want my money backrequest_refund

Run the model and compare:

InputExpectedPredicted
Where is my orderorder_statusorder_status
Cancel ordercancel_ordercancel_order
Refund not arrivedrefund_statusrequest_refund ✗

Accuracy is then the simplest possible summary — how many did it get right, over how many there were:

Accuracy = correct / total

950 / 1000  =  95%

And that number, on its own, is close to meaningless for an agent. The next three sections are why.

Precision, recall, F1

Accuracy averages over everything. These look at one intent at a time — which is where the damage actually is.

The problem with a single accuracy figure is that it hides distribution. If 90% of your traffic is order_status and the model nails it, you can score 95% overall while being hopeless at request_refund — the one that moves money. So measure per intent.

P
Precision — when it says X, how often is it right?

The model predicted request_refund 100 times. 90 of those were genuinely refund requests.

Precision = 90 / 100 = 90%

Low precision means false alarms: the agent acts on requests that were not what it thought. For any intent that changes data, this is the number that hurts.

R
Recall — of all the real Xs, how many did it catch?

There were 120 genuine refund requests in the test set. The model found 90 of them.

Recall = 90 / 120 = 75%

Low recall means misses: real requests routed somewhere else, so the user's problem never gets solved and they escalate to a human.

F
F1 — the two combined into one number
      2 × Precision × Recall
F1 = ────────────────────────
        Precision + Recall

It is deliberately harsh on imbalance: a model with 100% precision and 10% recall scores about 18%, not 55%. Use it to compare model versions in one column; use precision and recall separately to decide whether a specific failure is tolerable.

The trade-off is a business decision, not a technical one You can nearly always buy recall with precision and vice versa by moving a threshold. Which way you lean depends on what the intent does. For cancel_order, favour precision — never act unless certain. For a safety or escalation intent, favour recall — better to over-flag than to miss one.

The confusion matrix tells you what to fix

Aggregate metrics tell you something is wrong. This tells you which pair of intents is bleeding into which.

A confusion matrix is a grid: rows are what the input truly was, columns are what the model said. The diagonal is correct; everything off it is a specific, named mistake.

True ↓ / Predicted →request_refundrefund_statusorder_status
request_refund9082
refund_status20755
order_status1396

Read the middle row. Of 100 genuine refund_status questions — people asking where their money is — the model called 20 of them request_refund. That is not a vague quality problem. It is one specific leak, in one direction, and it is the dangerous direction: a status question being treated as a transaction request.

It also tells you the fix, which is not "train more" but "define better":

request_refund
  "I want a refund"
  "Please refund this order"

refund_status
  "Where is my refund?"
  "My refund hasn't arrived"
  "How long does a refund take?"
Why this beats a single score Accuracy says 87%. The matrix says "your two refund intents are not distinguishable as written". Only one of those is actionable — and the action costs ten minutes of writing examples, not a retraining run.

Four layers of evaluation

Intent accuracy is layer one of four. The business only cares about layer four.

Getting the label right is not the goal. The goal is that the user's problem gets solved. Those come apart more often than you would expect, so evaluate each stage separately — otherwise a failure at any layer looks identical from the outside.

Layer 1  Intent recognition
Layer 2  Entity / slot extraction
Layer 3  Tool / workflow selection
Layer 4  End-to-end task success

Take one request — "Change the delivery address for order A123 to the Melbourne office" — and check all four:

LayerWhat is checkedPass condition
1 · IntentThe labelchange_delivery_address
2 · SlotsThe parametersorder_id = A123, new_address = Melbourne office
3 · ToolThe call madeupdate_delivery_address()
4 · ResultRealityThe address in the system actually changed

Layer 4 — task success rate, the share of requests that ended with the user's problem solved — is the one to lead with when someone senior asks how good the agent is:

Task success rate = requests completed successfully
                    ─────────────────────────────
                          total requests

And it is genuinely possible to fail layer 1 and pass layer 4. If the model labels a query check_order where your ground truth said check_delivery, but both route to the same lookup and the user gets the right answer, the intent metric records a failure that did not exist. That is a good thing to be able to say: it shows you know which numbers are proxies and which one is the outcome.

The metric that matters most: wrong action rate

For any agent that can change data, this outranks accuracy.

A user asks: "If I cancel my order, do I get a refund?"

That is a question about policy. It is not a request to cancel anything. But it contains the word "cancel", it is about their order, and a model under pressure to pick an action will pick cancel_order. If the agent then executes it, the user has just lost their order by asking a question about losing their order.

False positive action rate The share of turns where the agent took a state-changing action that it should not have taken at all. For agents that can move money, place orders, delete records, or send messages on someone's behalf, this is the number to optimise — ahead of intent accuracy, ahead of F1.

The full metric set for a serious enterprise agent, by layer:

LayerMetricWhat it tells you
IntentAccuracyOverall health, coarse
IntentPrecision / recall / F1Per-intent health
IntentConfusion matrixWhich pairs to fix
RoutingRoute accuracyDid it reach the right branch
ToolTool selection accuracyDid it call the right function
ParameterSlot accuracyWith the right arguments
SafetyWrong action rateDid it do something it shouldn't
AgentTask success rateDid the user's problem get solved
UXClarification rateHow often it has to ask
UXUnnecessary clarification rateHow often it asks when it knew

The last pair is a useful thing to raise unprompted. Clarification is the safety valve for ambiguity — but an agent that asks a question every turn is exhausting, and users abandon it. Tracking unnecessary clarifications separately stops the safe behaviour from quietly becoming the useless behaviour.

Building a test set that isn't flattering

If your test cases are all textbook sentences, your metrics measure nothing.

The temptation is to write the canonical phrasing of each intent and call it a test set:

Cancel my order
Check my order
Request refund

Those will pass, and they tell you nothing, because no real user talks like that. One intent needs many shapes. For cancel_order alone:

Cancel my order.
I don't want this anymore.
Can you stop my order?
I ordered the wrong item.
Please cancel order #1234.
I changed my mind.
Can you undo my purchase?

Then deliberately include the categories that break things:

  • Typos and sloppy input — "cancle my ordr"
  • Spoken register — fragments, no punctuation, filler words
  • Long rambling messages where the actual request is one clause in the middle
  • Vague phrasing that genuinely should trigger clarification
  • Multiple intents in one message
  • Context-dependent messages — "yes, do that" only means something given the previous turn
  • Negation — the single highest-value category, below

The negation case deserves its own example, because it defeats every keyword-based system and a surprising number of model-based ones:

"I don't want to cancel my order,
 I just want to change the address."

The word cancel is right there. A keyword rule fires cancel_order and destroys the order. The correct label is change_delivery_address. Put a handful of these in your test set on day one — they are the cases that separate a system that looks like it works from one that does.

Where test cases should come from Three sources, in order of value: real production transcripts, every incident or complaint you have had (each becomes a permanent case), and only then hand-written variations. Made-up examples inherit the imagination of whoever wrote them, which is exactly the blind spot you are trying to cover.

The whole thing as one line

If you remember nothing else from this page, remember the chain.

User request
   ↓
What does the user want?
   ↓
Intent
   ↓
What information does that need?
   ↓
Slots
   ↓
What should the system do?
   ↓
Workflow / tool
   ↓
Did it actually solve the problem?
   ↓
Task success

Which is why "intent recognition" is a slightly misleading name for the discipline. It is not an isolated classification problem. It is the first link in:

Intent → parameters → routing → tool → task completion And the engineering definition worth quoting: intent recognition is the act of mapping a user's natural language onto the next decision the system can actually execute.

Everything in this page follows from that. Design intents around executable actions, split them only when execution differs, define their boundaries by their neighbours, keep an escape hatch for unknown and ambiguous, and measure the chain end to end rather than only the first link.

Glossary

Every term used above, defined plainly.

Intent
A label naming what the user wants the system to do, chosen from a fixed list the system knows how to act on.
Intent classification
The act of picking that label from the user's text. Same thing as intent recognition.
Entity
A specific value mentioned in the text — an order number, a date, a company name.
Slot
A blank in a workflow that must be filled before it can run. Entities are what fill slots.
Slot filling
Collecting the missing values, including by asking the user for them.
Router
The component that reads the intent and sends the request down one branch of the system rather than another.
Hierarchical intent classification
Deciding coarsely first (question vs action), then finely within that category. More reliable than one large flat choice.
Confidence
A number the classifier attaches to its own answer, expressing how sure it is. Used to decide whether to act or ask.
Clarification policy
The written rule for how uncertain is too uncertain to act, usually different per intent depending on reversibility.
Out of scope
A deliberate label for requests the system is not meant to handle, so they are not forced into a business intent.
Ground truth
The label a human decided is correct, used as the reference when scoring predictions.
Accuracy
Share of test cases labelled correctly. Coarse, and misleading when traffic is unevenly distributed.
Precision
Of everything the model called intent X, the share that genuinely was X. Low precision means false alarms.
Recall
Of everything that genuinely was intent X, the share the model found. Low recall means misses.
F1 score
Precision and recall combined into one number, weighted so that being bad at either drags it down.
Confusion matrix
A grid of true label against predicted label, showing exactly which intents are being mistaken for which.
Tool selection accuracy
How often the agent called the right function — a step closer to the outcome than intent accuracy.
Task success rate
Share of requests that ended with the user's problem actually solved. The outcome metric.
Wrong action rate
How often the agent took a state-changing action it should not have taken. The safety metric.
Task planning
Turning a request into an ordered, possibly conditional sequence of steps, rather than a single label.