DEV Community

shashank ms
shashank ms

Posted on

LLMs for Text Classification and Sentiment Analysis: A Comprehensive Guide

We are going to build a production-ready text classifier that reads customer support tickets, assigns a category, scores sentiment, and flags urgency. It runs entirely on Oxlo.ai's request-based API, so long tickets cost the same flat rate as short ones. This is ideal for teams that need to process large backlogs or threaded conversations without worrying about token costs.

What you'll need

Step 1: Set up the Oxlo.ai client

I start by initializing the OpenAI SDK to point at Oxlo.ai. I use Llama 3.3 70B as the default classifier because it follows instructions tightly and keeps output concise.

from openai import OpenAI
import json
import os

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

CLASSIFIER_MODEL = "llama-3.3-70b"

Step 2: Write the system prompt

The prompt enforces a strict JSON schema. I also ask for a confidence score so I can route low-confidence predictions to a stronger model later.

SYSTEM_PROMPT = """You are a support-ticket classifier. Analyze the user message and return a JSON object with exactly these keys:
- category: one of ["Billing", "Technical", "Feature Request", "Other"]
- sentiment: one of ["Positive", "Neutral", "Negative"]
- urgency: one of ["Low", "Medium", "High", "Critical"]
- confidence: a float between 0.0 and 1.0 representing your certainty
- reasoning: a single sentence explaining your choice

Rules:
- Return only the JSON object, with no markdown formatting.
- If the user mentions downtime or data loss, set urgency to Critical.
- If the user says thank you or praises the product, sentiment is Positive."""

Step 3: Build the core classifier

This function sends the ticket to Oxlo.ai with JSON mode enabled. The temperature is set low to keep outputs deterministic.

def classify_ticket(text: str) -> dict:
    response = client.chat.completions.create(
        model=CLASSIFIER_MODEL,
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": text},
        ],
        response_format={"type": "json_object"},
        temperature=0.1,
        max_tokens=256,
    )
    
    content = response.choices[0].message.content
    return json.loads(content)

Step 4: Process batches safely

Real pipelines handle many tickets at once. I wrap the single-ticket call in a loop that catches parsing errors and marks failed rows so one bad response does not crash the job.

from typing import List

def classify_batch(tickets: List[str]) -> List[dict]:
    results = []
    for text in tickets:
        try:
            result = classify_ticket(text)
            result["input_preview"] = text[:120]
            results.append(result)
        except Exception as e:
            results.append({
                "input_preview": text[:120],
                "error": str(e),
                "category": "Unknown",
                "sentiment": "Neutral",
                "urgency": "Low",
                "confidence": 0.0,
                "reasoning": "Parsing failed"
            })
    return results

Step 5: Add confidence-based fallback

If the fast model is unsure, I send the ticket to DeepSeek V3.2, which excels at reasoning and coding contexts. Because Oxlo.ai pricing is per request, the fallback costs the same flat rate as the first attempt, even if the ticket contains a long stack trace or thread history.

STRONG_MODEL = "deepseek-v3.2"

def classify_with_fallback(text: str, threshold: float = 0.7) -> dict:
    first = classify_ticket(text)
    
    if first.get("confidence", 1.0) < threshold:
        response = client.chat.completions.create(
            model=STRONG_MODEL,
            messages=[
                {"role": "system", "content": SYSTEM_PROMPT},
                {"role": "user", "content": text},
            ],
            response_format={"type": "json_object"},
            temperature=0.1,
            max_tokens=256,
        )
        second = json.loads(response.choices[0].message.content)
        second["model_used"] = STRONG_MODEL
        return second
    
    first["model_used"] = CLASSIFIER_MODEL
    return first

Step 6: Generate a summary report

I add a small reporter so I can scan the output without reading raw JSON.

def print_report(results: List[dict]):
    print(f"Processed {len(results)} tickets\n")
    for r in results:
        tag = r.get("urgency", "Low")
        print(f"[{tag}] {r.get('category', '?'):15} | "
              f"sentiment={r.get('sentiment', '?'):8} | "
              f"confidence={r.get('confidence', 0):.2f} | "
              f"model={r.get('model_used', '?')}")
        print(f"    reasoning: {r.get('reasoning', 'N/A')}\n")

Run it

Here is a small test set. I run each ticket through the fallback classifier and print the report.

if __name__ == "__main__":
    tickets = [
        "I was charged twice last month and I need a refund immediately.",
        "Love the new dashboard, great work team!",
        "The API returns a 500 error every time I try to export my data.",
        "Can you add dark mode to the mobile app? It would really help at night.",
    ]

    classified = [classify_with_fallback(t) for t in tickets]
    print_report(classified)

Example output:

Processed 4 tickets

[High] Billing         | sentiment=Negative | confidence=0.92 | model=llama-3.3-70b
    reasoning: User reports incorrect double charge and requests refund.

[Low]  Feature Request | sentiment=Positive | confidence=0.88 | model=llama-3.3-70b
    reasoning: User praises product and suggests new feature.

[Critical] Technical   | sentiment=Negative | confidence=0.95 | model=llama-3.3-70b
    reasoning: User reports repeated server errors affecting data export.

[Low]  Feature Request | sentiment=Neutral  | confidence=0.76 | model=llama-3.3-70b
    reasoning: User asks for dark mode without expressing strong emotion.

Wrap-up

Two concrete next steps. First, wire the classifier into a webhook so it runs on every new Zendesk or Intercom ticket and tags them automatically. Second, add a vector embedding step using Oxlo.ai's BGE-Large endpoint to cluster similar tickets and detect emerging issues before they become critical. You can explore request-based pricing for high-volume workloads at https://oxlo.ai/pricing.

Top comments (0)