Dev.to AI 🤖 Ai 👁 0 📖 3 min read

Using LLM for Intent Detection

I needed a simple way to route incoming support tickets without maintaining a pile of regex rules. An LLM-based intent detector works well for this because it handles varied phrasing and new terminology out of the box. I

I needed a simple way to route incoming support tickets without maintaining a pile of regex rules. An LLM-based intent detector works well for this because it handles varied phrasing and new terminology out of the box. In this tutorial I will walk through the 30-line classifier I shipped using Oxlo.ai.

What you'll need

Step 1: Define the taxonomy and initialize the client

First I define the five intents my support team actually cares about. Then I point the OpenAI SDK at Oxlo.ai. I use llama-3.3-70b here because it sticks to structured output formats reliably.

from openai import OpenAI

INTENTS = [
    "billing",
    "technical_support",
    "account_management",
    "sales_inquiry",
    "general_other"
]

client = OpenAI(
    base_url="https://api.oxlo.ai/v1",
    api_key="YOUR_OXLO_API_KEY"
)

Step 2: Lock down the system prompt

The system prompt is the entire contract. I list the exact intents, give one-line definitions, and mandate a JSON schema so I never have to parse free text.

SYSTEM_PROMPT = """You are an intent classification engine.
Analyze the user message and classify it into exactly one of these intents:
- billing: questions about invoices, payments, refunds, or charges
- technical_support: bugs, errors, integrations, or feature malfunctions
- account_management: password resets, plan changes, user access, or cancellations
- sales_inquiry: pricing questions, demo requests, or upgrade interest
- general_other: anything that does not fit the above

Respond ONLY with a JSON object in this format:
{"intent": "", "confidence": "high|medium|low", "reason": ""}
Do not include markdown formatting or explanation outside the JSON."""

Step 3: Build the classifier function

I wrap the API call in a small function that parses the output and validates the intent against my taxonomy. If the model hallucinates an intent or returns malformed JSON, I fall back to general_other.

import json

def detect_intent(user_message: str, model: str = "llama-3.3-70b"):
    response = client.chat.completions.create(
        model=model,
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": user_message},
        ],
    )
    
    raw = response.choices[0].message.content
    try:
        parsed = json.loads(raw)
        if parsed.get("intent") not in INTENTS:
            return {
                "intent": "general_other",
                "confidence": "low",
                "reason": "Intent returned by model was outside defined taxonomy"
            }
        return parsed
    except json.JSONDecodeError:
        return {
            "intent": "general_other",
            "confidence": "low",
            "reason": "Malformed JSON returned by model"
        }

Step 4: Add a confidence gate and batch test

In production I do not act on medium or low confidence predictions. I route them to a human review queue. I run a batch of representative messages through the detector to verify it behaves correctly.

test_messages = [
    "My invoice last month was double what I expected and I need a refund.",
    "The API returns a 500 error every time I send a list longer than 100 items.",
    "Can I talk to someone about upgrading to the enterprise plan?",
    "I forgot my password and the reset email never arrives.",
    "What is the weather like today?"
]

for msg in test_messages:
    result = detect_intent(msg)
    route = result["intent"] if result["confidence"] == "high" else "human_review_queue"
    print(f"Message: {msg[:50]}...")
    print(f"  Detected: {result['intent']} ({result['confidence']})")
    print(f"  Route: {route}")
    print(f"  Reason: {result['reason']}\n")

Run it

Running the script produces deterministic routing decisions. Here is the output I see:

Message: My invoice last month was double what I expected...
  Detected: billing (high)
  Route: billing
  Reason: User explicitly mentions an invoice and a refund request.

Message: The API returns a 500 error every time I send...
  Detected: technical_support (high)
  Route: technical_support
  Reason: User describes a reproducible server error in the API.

Message: Can I talk to someone about upgrading to...
  Detected: sales_inquiry (high)
  Route: sales_inquiry
  Reason: User expresses interest in an enterprise plan upgrade.

Message: I forgot my password and the reset email...
  Detected: account_management (high)
  Route: account_management
  Reason: User reports a password reset issue and missing email.

Message: What is the weather like today?...
  Detected: general_other (high)
  Route: general_other
  Reason: Message is unrelated to product, billing, or support.

Next steps

To put this into production, wrap the detect_intent function in an async FastAPI endpoint so it can handle concurrent support tickets without blocking. If you start passing long conversation threads or full knowledge-base articles as context to disambiguate intent, switch to a model like kimi-k2.6 or deepseek-v3.2 on Oxlo.ai. Because Oxlo.ai charges per request rather than per token, those long-context classification jobs will not inflate your bill the way they would on token-based providers. See https://oxlo.ai/pricing for plan details.

📰 Read the original article on Dev.to AI

Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.