Implementing AI-Powered User Intent Recognition in Mobile Applications
Intent Recognition classifies what a user wants to do based on text or voice input. In mobile apps, it's the foundation of conversational interfaces, smart search, voice commands, and chatbots. Without accurate intent classification, any NLU logic becomes a fragile set of regexes.
How Intent Recognition differs from general text classification
Generic text classification: "Is this review positive or negative?" Intent Recognition: "Does the user want to place an order, cancel an order, check delivery status, find a product, get help, or say something irrelevant?"
Intent classes are uneven in frequency, may overlap ("I want to return this item" = return_request or complaint?), and users express one intent in dozens of ways. Plus you need to handle out-of-scope queries without pretending "I can do everything."
Technical approaches
Fine-tuned BERT/RoBERTa for production
Fine-tuning a language model on labeled intent data is industry standard. For English apps: distilbert-base-uncased or roberta-base. For other languages, use equivalent models.
from transformers import pipeline, AutoTokenizer, AutoModelForSequenceClassification
# Fine-tuning on custom intents
model = AutoModelForSequenceClassification.from_pretrained(
"distilbert-base-uncased",
num_labels=len(INTENT_LABELS)
)
# Inference
classifier = pipeline(
"text-classification",
model=model,
tokenizer=tokenizer,
return_all_scores=True
)
def classify_intent(text: str) -> IntentResult:
scores = classifier(text)[0]
top = max(scores, key=lambda x: x["score"])
if top["score"] < 0.65:
return IntentResult(intent="out_of_scope", confidence=top["score"])
return IntentResult(intent=top["label"], confidence=top["score"])
The 0.65 threshold is the out-of-scope boundary. Below it—don't guess, honestly say "I didn't understand." This is critical: a wrong intent classification is worse than admitting confusion.
Typical intent set for e-commerce mobile app
| Intent | Example phrases |
|---|---|
search_product |
"find nike shoes," "I want to buy a phone" |
order_status |
"where's my order," "when will it arrive" |
return_request |
"I want to return," "size didn't fit" |
complaint |
"item is broken," "wrong item arrived" |
payment_issue |
"can't pay," "payment failed" |
promo_inquiry |
"any discounts," "promo code" |
out_of_scope |
everything else |
Slot Filling: extracting intent parameters
After classifying intent, extract parameters (slots). For search_product—what exactly, category, attributes. For order_status—order number or timeframe.
Slots are extracted via NER (Named Entity Recognition) or template parsing. For structured domains, regex + custom NER is more reliable than general NER models:
import re
def extract_order_id(text: str) -> Optional[str]:
# formats: #123456, №123456, ORD-123456, order 123456
patterns = [r'#(\d{5,8})', r'№(\d{5,8})', r'ORD-(\d+)', r'order\s+?(\d{5,8})']
for pattern in patterns:
match = re.search(pattern, text, re.IGNORECASE)
if match:
return match.group(1)
return None
def fill_order_status_slots(text: str) -> OrderStatusSlots:
return OrderStatusSlots(
order_id=extract_order_id(text),
period=extract_period(text) # "last week," "in November"
)
Android: integration into conversational interface
class IntentViewModel(private val intentApi: IntentApi) : ViewModel() {
fun processUserInput(text: String) {
viewModelScope.launch {
val result = intentApi.classify(text)
when (result.intent) {
"search_product" -> {
val query = result.slots["product_query"] ?: text
navigateToSearch(query)
}
"order_status" -> {
val orderId = result.slots["order_id"]
if (orderId != null) navigateToOrder(orderId)
else requestOrderId() // ask for clarification
}
"return_request" -> navigateToReturnFlow()
"out_of_scope" -> showFallbackMessage()
else -> handleGenericIntent(result)
}
}
}
}
LLM as alternative
For apps with few intents (< 20) and flexible phrasings—structured output via GPT-4o-mini or Gemini Flash is cheaper and more accurate than fine-tuning. Use a prompt with JSON schema and few-shot examples:
INTENT_SYSTEM_PROMPT = """
Classify user intent. Return JSON: {"intent": "...", "confidence": 0.0-1.0, "slots": {...}}
Valid intents: search_product, order_status, return_request, complaint, out_of_scope
If unclear, use out_of_scope with low confidence.
"""
LLM approach is simpler to update (change prompt, no retraining), but costlier at high traffic.
Process
Define intent taxonomy with product team.
Collect and label training dataset (minimum 100–200 examples per intent).
Train classifier and baseline evaluation on accuracy/F1.
Integrate into mobile conversational interface with out-of-scope handling.
Monitor: out-of-scope ratio, confusion matrix by intent.
Timeline estimates
Fine-tuned BERT classifier with basic slot filling—2–3 weeks. LLM-based Intent Recognition with structured output—3–5 days to implement.







