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
- Python 3.10 or newer
- An Oxlo.ai API key from https://portal.oxlo.ai
- The OpenAI SDK:
pip install openai
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)