Implementing Real-Time Intent Classification in Conversational Agents

Implementing Real-Time Intent Classification in Conversational Agents

Quick Answer — Intent Classification for Chatbots

Send the user's message and your list of allowed intents to POST /api/v1/ai/intent-detection. You get back one intent, a confidence score and any extracted entities. Auto-route when confidence is high, ask a clarifying question when it is middling, and fall back to a general chat model or a human when it is low.

Sending every chatbot message straight to a general-purpose language model is slow, hard to control and hard to test. A better design puts a small, structured decision in front: first work out what the user wants, then decide which handler should deal with it. This guide shows how to implement intent classification in a conversational agent: designing your intents, calling the API, setting confidence thresholds, using conversation history and building a routing layer you can test.

Why route by intent instead of prompting for everything

When a user types "I want to change my billing email", your bot has to do something specific: open the account settings flow. An open-ended chat model could produce a friendly paragraph about how to do it, but your application needs a decision it can act on. Intent classification turns free text into exactly that decision: a label such as update_profile that your code can switch on.

Open-ended chat model per message Intent classification first
OutputUnstructured text you must parseLabel + confidence + entities
PredictabilityVaries run to runLimited to the intents you define
TestingHard: judge free-text answersEasy: compare a label with the expected label
CostCharged on every messageUtility endpoint with a free monthly quota; only complex messages go on to chat
Best forOpen-ended conversationRouting, commands, FAQs, ticket triage

The benefit is control. Routine requests such as checking an order, changing a setting or asking for a refund go to deterministic code. Only the messages that genuinely need free-form language generation go on to a chat model.

Design your intents before you write code

The quality of intent routing depends far more on your intent list than on the code. Poorly defined intents overlap, so even a perfect classifier will disagree with your expectations. Follow a few rules:

  • Name intents by what the user wants, not by department. cancel_subscription is better than billing, because the handler for it is specific.
  • Keep the list short. Start with 5 to 10 intents. The API accepts up to 10 allowed intents per request, which is a healthy limit for a first version.
  • Make intents mutually exclusive. If a human could not tell billing_issue from payment_problem, merge them.
  • Plan for the unknown. Include a way to say "none of the above" so odd messages do not get forced into the nearest label.
  • Decide the entities up front. For each intent, list the details your handler needs, such as invoice_number or effective_time.

Calling the Intent Detection API

Post the message together with the intents you allow and the entities to extract for each. Only the text field is required; the others sharpen the result:

request.json JSON
{
  "text": "I want to cancel my Pro subscription starting next week because it is too expensive.",
  "intent_types": ["cancel_subscription", "upgrade_plan", "billing_issue"],
  "intent_entities": {
    "cancel_subscription": ["reason", "effective_time", "plan_type"],
    "billing_issue": ["invoice_number"]
  },
  "allow_fallback": true
}

The response contains the winning intent, how sure the model is, and the entities it found:

response.json JSON
{
  "success": true,
  "request_id": "req_7f3a9c2d1e4b8a6f",
  "data": {
    "result": {
      "intent": "cancel_subscription",
      "confidence": 0.98,
      "entities": {
        "reason": "too expensive",
        "effective_time": "next week",
        "plan_type": "Pro"
      }
    }
  },
  "meta": { "version": "v1", "timestamp": "2026-09-27T12:34:56+00:00" }
}

Setting allow_fallback to true makes the API answer unknown when confidence is low or nothing in your list fits, which is usually safer than a forced best guess. The text field accepts up to 5,000 characters.

Use conversation history, examples and multi-intent

Real conversations are not isolated messages. A user who writes "yes, that one" only makes sense in context, and a single message often contains two requests. The endpoint has options for all three situations:

Option Use it when Limits
messagesShort replies depend on earlier turnsUp to 5 recent messages, 1,000 characters each
examplesYour product uses unusual wording or two intents are easily confusedUp to 3 few-shot examples
multi_intentUsers often combine requests ("billing problem and cancel")Returns an intents array instead of a single value
min_confidenceYou want weak matches filtered out by the APIA number between 0 and 1
advanced-request.json JSON
{
  "messages": [
    { "role": "user", "content": "I am having billing problems. Also, can I cancel my subscription?" }
  ],
  "intent_types": ["cancel_subscription", "upgrade_plan", "billing_issue"],
  "examples": [
    { "text": "payment declined and cancel my account", "intent": "billing_issue", "entities": {} }
  ],
  "multi_intent": true,
  "min_confidence": 0.5,
  "allow_fallback": true
}

Everything you pass is processed by the AI and counts toward your content size, so send only the history and examples that matter rather than the whole transcript.

Setting confidence thresholds

The confidence score is your safety control. A sensible starting policy for production is:

  • 0.85 and above: auto-route. Execute the matched action immediately.
  • 0.5 to 0.85: confirm. Ask a quick "Did you mean X?" before acting.
  • Below 0.5: fall back. Hand over to a general chat model or a human instead of guessing.
Never auto-act below your threshold
Acting on a 0.4-confidence guess frustrates users more than a clarifying question. Auto-route only on high confidence, and always offer a "something else" path. These numbers are starting points: adjust them after looking at how your real traffic scores, and use stricter thresholds for destructive actions such as cancellations or refunds.

A routing layer in Node.js

The code that uses the result is short. It calls the API, applies the thresholds, and dispatches to a handler:

router.js JavaScript
const INTENTS = ['cancel_subscription', 'upgrade_plan', 'billing_issue', 'order_status'];

async function detectIntent(text, history = []) {
  const res = await fetch('https://rsflowhub.com/api/v1/ai/intent-detection', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'X-API-Key': process.env.RSFLOWHUB_KEY },
    body: JSON.stringify({
      text,
      messages: history.slice(-5),
      intent_types: INTENTS,
      allow_fallback: true,
    }),
    signal: AbortSignal.timeout(30_000),
  });
  if (!res.ok) return { intent: 'unknown', confidence: 0, entities: {} };
  return (await res.json()).data.result;
}

const handlers = {
  cancel_subscription: (entities) => startCancellationFlow(entities),
  upgrade_plan: () => showPlans(),
  billing_issue: (entities) => openBillingTicket(entities),
  order_status: () => lookUpLatestOrder(),
};

export async function route(text, history) {
  const { intent, confidence, entities } = await detectIntent(text, history);

  if (intent === 'unknown' || confidence < 0.5) return escalateToChatOrHuman(text, history);
  if (confidence < 0.85) return askToConfirm(intent);

  return handlers[intent]?.(entities) ?? escalateToChatOrHuman(text, history);
}

Notice what happens when the intent API is unavailable: the function returns unknown with zero confidence, so the user is passed to the fallback rather than seeing an error.

The two-tier routing architecture

  1. Every message goes to intent detection first.
  2. High-confidence routine intents (order status, plan changes, FAQ-style questions) are handled directly by your own code or a FAQ lookup.
  3. Low-confidence, unknown or open-ended messages go to a chat model, with the detected intent included as context.
  4. Sensitive or angry conversations are escalated to a human regardless of intent.

How much traffic stays out of the chat model depends entirely on your product, so measure it. Log the intent, the confidence and the route taken for every message, then look at the share of conversations handled without a model call. That number, not a generic claim, tells you what the routing layer is saving.

Test your intents like any other code

Because the output is a label, intent routing is easy to test. Build a table of 50 to 100 real messages with the intent a human would assign, run them all through detectIntent, and count the matches:

evaluate.js JavaScript
const CASES = [
  ['Where is my parcel?', 'order_status'],
  ['I was charged twice this month', 'billing_issue'],
  ['Stop my subscription at the end of the month', 'cancel_subscription'],
  ['What is the weather like?', 'unknown'],
];

let correct = 0;
for (const [text, expected] of CASES) {
  const { intent, confidence } = await detectIntent(text);
  const ok = intent === expected;
  if (ok) correct++;
  else console.log(`MISS: "${text}" -> ${intent} (${confidence}), expected ${expected}`);
}
console.log(`${correct}/${CASES.length} correct`);

The misses are the useful part. If two intents are constantly confused, add a few-shot example for the pair or merge them. If out-of-scope messages get forced into an intent, make sure allow_fallback is on and raise your confidence threshold.

Common mistakes to avoid

  • Too many overlapping intents. More labels mean more confusion. Start small and split only when you have evidence.
  • No fallback path. Every router needs somewhere to send messages it does not understand.
  • Trusting confidence blindly. A high score on a wrong label still happens. Add confirmation steps before irreversible actions.
  • Ignoring history. Single words such as "yes" or "the second one" cannot be classified without context. Pass recent messages.
  • Never reviewing misclassifications. Sample low-confidence and escalated messages every week; they reveal missing intents.
  • Sending too much. Intents, entities, examples and history all count as input. Pass only what the decision needs.

Add Intent Routing in One Endpoint

Use the Intent Detection API to route messages, then send open-ended ones to the AI Chat API. One key covers both.

curl -X POST "https://rsflowhub.com/api/v1/ai/intent-detection" \
  -H "X-API-Key: YOUR_FLOWHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"text": "How much does the pro plan cost?", "intent_types": ["pricing_question", "cancel_subscription"]}'
Try Intent Detection →

Key Takeaways

  • Intent classification turns free text into a label your code can act on, with a confidence score and entities.
  • Design short, mutually exclusive intents and plan an unknown path before writing code.
  • Auto-route on high confidence (about 0.85 and up), confirm in the middle, and fall back below about 0.5.
  • Pass recent messages, a few examples and multi_intent when conversations are messy.
  • Two-tier routing keeps routine requests out of the chat model; measure the real share in your own logs.
  • Test intents with a table of real messages and review the misses every week.

Frequently Asked Questions

Mapping a user message to a predefined label such as billing_issue or order_status so the system can route or respond with the right handler. It is the router layer of a conversational agent, and it usually extracts details like order numbers as entities too.

A chat model returns free text, which is hard to act on, hard to test and charged on every message. An intent classifier returns a structured label you can switch on, so routine requests are handled by predictable code and only open-ended messages need a chat model.

A good starting point is 0.85 or higher. Between 0.5 and 0.85, ask a quick clarifying question. Below 0.5, escalate to a chat model or a human. Tune these numbers on your real traffic and use stricter values for actions that are hard to undo.

Set allow_fallback to true so the API returns unknown when nothing fits or confidence is low, then send those messages to the AI Chat API or a human agent with the conversation history attached.

Yes. Set multi_intent to true and the response returns an intents array, each with its own confidence and entities. This suits messages like asking about a billing problem and cancelling in the same sentence.

Start with 5 to 10 clear, mutually exclusive intents. The API accepts up to 10 allowed intents per request, so group rare ones under a general category and split them later if the data shows you need to.

Intent detection is a utility endpoint, so every RSFlowHub plan includes a monthly quota of free utility requests. Credits are only used after the quota is reached.

Reviews & Discussion (5)

5

Based on 3 ratings

5 ★
100%
4 ★
0%
3 ★
0%
2 ★
0%
1 ★
0%
Write a Review or Comment
No rating selected
DA
David Kim Member
2 days ago

The camera motion keywords for video prompts saved me hours of trial and error with Sora. Highly recommended guide!

Reply to David Kim
EL
Elena Rostova Member
2 days ago

This formula structure is gold! Combining subject, environment, rim lighting, and lens parameters completely transformed my Midjourney outputs.

Reply to Elena Rostova
SU
Super Admin Member
2 days ago

Thanks Alex! Adding camera aperture tags like 85mm f/1.4 makes a massive difference in background bokeh.

MA
Marcus Brody Member
2 days ago

We integrated the RSFlowHub AI Chat API to auto-expand user prompts in our mobile app and response latency is under 200ms. Exceptional speed!

Reply to Marcus Brody
SU
Super Admin Member
2 days ago

Awesome to hear Sarah! The single master key architecture makes managing multiple prompt pipelines effortless.

Unified AI Access ⚡

One API Key. Infinite Possibilities.

Get access to OpenAI, Anthropic, Gemini, and more through a single, secure gateway. Start building in minutes with zero setup friction.