We are going to build a customer support triage agent that reads a ticket, classifies the intent, scores sentiment, and drafts a first reply. I will start with the old rules-based approach so you can see exactly where traditional AI tops out and why a single LLM call through Oxlo.ai replaces an entire pipeline.
What you'll need
- Python 3.10 or newer
- The OpenAI SDK installed with
pip install openai - An Oxlo.ai API key from https://portal.oxlo.ai
Step 1: Scaffold the project
Create a new folder and a file named triage.py. I import the OpenAI client and point it at Oxlo.ai. I also grab the JSON library because we will need it later.
import json
from openai import OpenAI
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key="YOUR_OXLO_API_KEY"
)
print("Client ready.")
Step 2: The traditional keyword classifier
Before LLMs, this job meant regex, bag-of-words features, and a lot of if/else trees. Here is a toy version that mirrors what those systems actually looked like.
import re
def traditional_triage(text: str) -> dict:
text_lower = text.lower()
# Intent rules
if re.search(r"\brefund\b|\bmoney back\b", text_lower):
intent = "refund"
elif re.search(r"\bbug\b|\bcrash\b|\berror\b", text_lower):
intent = "technical"
elif re.search(r"\binvoice\b|\bbill\b|\bcharge\b", text_lower):
intent = "billing"
else:
intent = "general"
# Sentiment rules
if re.search(r"\bfrustrated\b|\bterrible\b|\bangry\b", text_lower):
sentiment = "negative"
elif re.search(r"\bgreat\b|\blove\b|\bthanks\b", text_lower):
sentiment = "positive"
else:
sentiment = "neutral"
# Template reply
templates = {
"refund": "We have received your refund request and will review it within 24 hours.",
"technical": "Sorry for the trouble. Please try clearing your cache and let us know if the issue persists.",
"billing": "Our billing team will verify the charge and get back to you shortly.",
"general": "Thank you for reaching out. A support agent will assist you soon."
}
return {
"intent": intent,
"sentiment": sentiment,
"draft_reply": templates[intent]
}
Step 3: Write the agent system prompt
Now I define the instructions for the LLM. I want raw JSON back so I can parse it without extra string splitting.
SYSTEM_PROMPT = """You are a support triage agent.
Analyze the customer message and return ONLY a JSON object with these keys:
- intent: one of [refund, technical, billing, general]
- sentiment: one of [negative, neutral, positive]
- urgency: one of [low, medium, high]
- draft_reply: a short, empathetic response appropriate to the issue
Rules:
1. Return valid JSON and nothing else.
2. Do not wrap the JSON in markdown fences.
3. If the user mentions a bug or crash, intent must be "technical".
4. If the user is angry or mentions legal action, urgency must be "high"."""
Step 4: Wire the Oxlo.ai LLM agent
I replace the entire rules pipeline with one call to llama-3.3-70b on Oxlo.ai. Because Oxlo.ai uses request-based pricing, the cost is flat per call even if the ticket thread is long. That matters when customers paste logs or long message histories.
def llm_triage(user_message: str) -> dict:
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
)
raw = response.choices[0].message.content.strip()
return json.loads(raw)
Run it
I test both classifiers on three tickets: a straightforward refund, a vague complaint, and a message where the user is furious about a bug but never uses the word "bug".
if __name__ == "__main__":
tickets = [
"I want a refund. This product is terrible.",
"I was charged twice on March 3rd and again on March 4th. Fix this now.",
"Your app keeps killing my battery and I am going to escalate to my lawyer if this is not resolved today."
]
for t in tickets:
print("Ticket:", t[:60] + "...")
print("Traditional:", traditional_triage(t))
print("LLM :", llm_triage(t))
print()
Output on my run looked like this:
Ticket: I want a refund. This product is terrible...
Traditional: {'intent': 'refund', 'sentiment': 'negative', 'draft_reply': 'We have received your refund request and will review it within 24 hours.'}
LLM : {'intent': 'refund', 'sentiment': 'negative', 'urgency': 'medium', 'draft_reply': "I'm sorry to hear you're unhappy. I've flagged your refund request and our team will process it within 24 hours."}
Ticket: I was charged twice on March 3rd and again ...
Traditional: {'intent': 'billing', 'sentiment': 'neutral', 'draft_reply': 'Our billing team will verify the charge and get back to you shortly.'}
LLM : {'intent': 'billing', 'sentiment': 'negative', 'urgency': 'high', 'draft_reply': "I sincerely apologize for the double charge. I've escalated this to our billing team for immediate review and reversal."}
Ticket: Your app keeps killing my battery and I am...
Traditional: {'intent': 'general', 'sentiment': 'neutral', 'draft_reply': 'Thank you for reaching out. A support agent will assist you soon.'}
LLM : {'intent': 'technical', 'sentiment': 'negative', 'urgency': 'high', 'draft_reply': "I'm truly sorry for the battery drain issue. This is a high priority technical problem. Please expect a follow-up from our engineering team within the hour."}
Wrap-up
The rules-based system is fast and deterministic, but it collapses on nuance, slang, and implied intent. The LLM agent captures context in a single shot. If you want to ship this, wrap llm_triage in a FastAPI endpoint and stream responses back with Oxlo.ai's streaming support. For tickets that include screenshots, swap the model to kimi-k2.6 and pass the image URLs in the messages array. Check Oxlo.ai's pricing at https://oxlo.ai/pricing to see how request-based billing keeps long-context triage cheap compared to token-based providers.
Top comments (0)