Most teams still maintain brittle regex-and-keyword pipelines for support ticket triage. I will show you how to replace that entire stack with a single LLM agent that classifies intent, extracts metadata, and routes tickets with one API call to Oxlo.ai.
What you'll need
- Python 3.10 or newer
pip install openai- An Oxlo.ai API key from https://portal.oxlo.ai
- A few sample support tickets to test with
Step 1: Build the legacy baseline
Before we touch the LLM, we need a traditional rules engine to benchmark against. This regex pipeline is representative of what I have seen in production.
import re
LEGACY_RULES = [
(r"refund|money back|charge", "billing", "high", "finance@company.com"),
(r"login|password|account|2fa", "account_access", "high", "security@company.com"),
(r"bug|error|crash|500|timeout", "technical_issue", "critical", "engineering@company.com"),
(r"how do I|tutorial|docs|pricing", "general_inquiry", "low", "support@company.com"),
]
def legacy_triage(text: str):
text_lower = text.lower()
for pattern, category, urgency, assignee in LEGACY_RULES:
if re.search(pattern, text_lower):
return {
"category": category,
"urgency": urgency,
"assignee": assignee,
"method": "legacy"
}
return {
"category": "unknown",
"urgency": "low",
"assignee": "support@company.com",
"method": "legacy"
}
Step 2: Initialize the Oxlo.ai client
Oxlo.ai exposes a fully OpenAI-compatible API, so we can use the official SDK with only a base URL change. I am using llama-3.3-70b because it handles structured instructions reliably, and Oxlo.ai serves it with no cold starts so the first request is as fast as the tenth.
from openai import OpenAI
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key="YOUR_OXLO_API_KEY"
)
Step 3: Define the system prompt
The system prompt replaces the entire rules engine. It encodes the categories, urgency levels, and routing logic in plain English, which means non-engineers can edit it without touching code.
SYSTEM_PROMPT = """You are a support ticket triage agent.
Analyze the user's message and return a JSON object with exactly these keys:
- category: one of billing, account_access, technical_issue, general_inquiry, or other
- urgency: one of critical, high, medium, or low
- assignee: the team email from the mapping below
- reasoning: one sentence explaining your decision
Category to assignee mapping:
- billing -> finance@company.com
- account_access -> security@company.com
- technical_issue -> engineering@company.com
- general_inquiry -> support@company.com
- other -> support@company.com
Return only valid JSON, no markdown fences."""
Step 4: Classify and route with structured output
Now we call Oxlo.ai with JSON mode enabled. Because Oxlo.ai uses request-based pricing, a twenty-word ticket and a two-hundred-word ticket cost the same, which keeps routing overhead predictable. See https://oxlo.ai/pricing for current plan details.
import json
def llm_triage(text: str):
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": text},
],
response_format={"type": "json_object"},
)
content = response.choices[0].message.content
result = json.loads(content)
result["method"] = "llm"
return result
Step 5: Stress-test against ambiguous input
Legacy systems fail on edge cases that lack keywords. We run both classifiers side by side to see where the LLM adds value.
tickets = [
"I forgot my password and I can't log in. Need help ASAP.",
"The dashboard shows a 500 error when I click export.",
"Do you offer refunds if I cancel before the billing cycle ends?",
"My team lead said to ask about enterprise pricing and SOC2 docs.",
"The app is sluggish after the latest update, but no crash.",
]
for ticket in tickets:
print("Ticket:", ticket)
print("Legacy:", legacy_triage(ticket))
print("LLM :", llm_triage(ticket))
print()
Run it
Save everything to triage_agent.py and run:
python triage_agent.py
Expected output:
Ticket: I forgot my password and I can't log in. Need help ASAP.
Legacy: {'category': 'account_access', 'urgency': 'high', 'assignee': 'security@company.com', 'method': 'legacy'}
LLM : {'category': 'account_access', 'urgency': 'high', 'assignee': 'security@company.com', 'reasoning': 'User cannot log in due to forgotten password.', 'method': 'llm'}
Ticket: The app is sluggish after the latest update, but no crash.
Legacy: {'category': 'unknown', 'urgency': 'low', 'assignee': 'support@company.com', 'method': 'legacy'}
LLM : {'category': 'technical_issue', 'urgency': 'medium', 'assignee': 'engineering@company.com', 'reasoning': 'Performance degradation after update indicates a technical issue.', 'method': 'llm'}
The second ticket demonstrates the gap. The legacy engine sees no keyword match and defaults to unknown, while the LLM infers intent from context and routes correctly.
Next steps
Wire this into a FastAPI endpoint and point your Zendesk or Intercom webhook at it so tickets are triaged in real time. If you want to experiment with reasoning models, swap llama-3.3-70b for deepseek-v3.2 or qwen-3-32b on Oxlo.ai without changing any client code.
Top comments (0)