We are building a support ticket intent classifier that reads unstructured customer messages and returns structured JSON with intent labels, extracted entities, and urgency scores. This kind of language understanding pipeline is the backbone of most customer automation stacks. Doing it with an LLM on Oxlo.ai is faster than training a custom model, and the flat per-request pricing means you do not pay extra for long ticket histories.
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: Configure the Oxlo.ai client
I set up the client once and reuse it throughout the script. Oxlo.ai is fully OpenAI SDK compatible, so the only changes are the base URL and the model identifier.
from openai import OpenAI
import json
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key="YOUR_OXLO_API_KEY" # get yours at https://portal.oxlo.ai
)
Step 2: Define the system prompt
The system prompt is the contract. It tells the model exactly what to extract and what schema to return. I keep it strict and include guardrails so the output is predictable.
SYSTEM_PROMPT = """You are a language understanding engine. Analyze the customer support message and return a single JSON object with this exact schema:
{
"intent": "one of: refund_request, technical_issue, billing_question, general_inquiry",
"entities": {
"order_id": "string or null",
"product_name": "string or null",
"email": "string or null"
},
"urgency": "low, medium, or high",
"summary": "a one-sentence summary of the issue"
}
Rules:
- Return only valid JSON. No markdown, no explanation.
- If a value is missing, use null.
- Urgency is high if the customer mentions downtime, data loss, or cannot access a critical feature."""
Step 3: Parse a single ticket with JSON mode
I call Llama 3.3 70B through Oxlo.ai with JSON mode enabled. This forces the model to output valid JSON and saves me from writing brittle regex parsers.
def parse_ticket(text: str) -> dict:
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"},
temperature=0.1,
)
raw = response.choices[0].message.content
return json.loads(raw)
# quick test
ticket = "I was charged twice for Order #998877 and I need a refund ASAP. My email is alice@example.com."
print(parse_ticket(ticket))
Step 4: Add validation and a retry fallback
In production, you will hit malformed outputs or missing keys. I wrap the parser in a small validation layer that retries once with a stricter reminder if the first attempt fails schema validation.
def parse_ticket_safe(text: str) -> dict:
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": text},
]
for attempt in range(2):
try:
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=messages,
response_format={"type": "json_object"},
temperature=0.1,
)
raw = response.choices[0].message.content
data = json.loads(raw)
assert all(k in data for k in ("intent", "entities", "urgency", "summary"))
assert data["intent"] in (
"refund_request", "technical_issue", "billing_question", "general_inquiry"
)
return data
except (json.JSONDecodeError, AssertionError):
if attempt == 0:
messages.append({
"role": "assistant",
"content": "You must return valid JSON matching the schema exactly."
})
messages.append({"role": "user", "content": "Correct your previous output."})
continue
raise ValueError("Failed to parse ticket after retry")
Step 5: Batch process multiple tickets
Most real queues have dozens or hundreds of tickets. Because Oxlo.ai has no cold starts on popular models, I can loop through a list without worrying about warmup latency. The flat per-request pricing also means long ticket threads cost the same as short ones.
tickets = [
"My site is down and I am losing sales. Order #112233. Help now.",
"Can you tell me how to reset my password? No order.",
"I was billed $49.99 twice this month for Pro Plan. Please fix. Email: bob@example.com",
]
results = []
for t in tickets:
result = parse_ticket_safe(t)
results.append(result)
print(result)
# optional: save to NDJSON for downstream ETL
with open("parsed_tickets.jsonl", "w") as f:
for r in results:
f.write(json.dumps(r) + "\n")
Run it
Here is the full script assembled, followed by the output I get when I run it against Oxlo.ai.
from openai import OpenAI
import json
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key="YOUR_OXLO_API_KEY"
)
SYSTEM_PROMPT = """You are a language understanding engine. Analyze the customer support message and return a single JSON object with this exact schema:
{
"intent": "one of: refund_request, technical_issue, billing_question, general_inquiry",
"entities": {
"order_id": "string or null",
"product_name": "string or null",
"email": "string or null"
},
"urgency": "low, medium, or high",
"summary": "a one-sentence summary of the issue"
}
Rules:
- Return only valid JSON. No markdown, no explanation.
- If a value is missing, use null.
- Urgency is high if the customer mentions downtime, data loss, or cannot access a critical feature."""
def parse_ticket_safe(text: str) -> dict:
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": text},
]
for attempt in range(2):
try:
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=messages,
response_format={"type": "json_object"},
temperature=0.1,
)
raw = response.choices[0].message.content
data = json.loads(raw)
assert all(k in data for k in ("intent", "entities", "urgency", "summary"))
assert data["intent"] in (
"refund_request", "technical_issue", "billing_question", "general_inquiry"
)
return data
except (json.JSONDecodeError, AssertionError):
if attempt == 0:
messages.append({
"role": "assistant",
"content": "You must return valid JSON matching the schema exactly."
})
messages.append({"role": "user", "content": "Correct your previous output."})
continue
raise ValueError("Failed to parse ticket after retry")
tickets = [
"My site is down and I am losing sales. Order #112233. Help now.",
"Can you tell me how to reset my password? No order.",
"I was billed $49.99 twice this month for Pro Plan. Please fix. Email: bob@example.com",
]
for t in tickets:
print(parse_ticket_safe(t))
Example output:
{"intent": "technical_issue", "entities": {"order_id": "112233", "product_name": null, "email": null}, "urgency": "high", "summary": "Customer reports site downtime affecting sales."}
{"intent": "general_inquiry", "entities": {"order_id": null, "product_name": null, "email": null}, "urgency": "low", "summary": "Customer asks how to reset their password."}
{"intent": "billing_question", "entities": {"order_id": null, "product_name": "Pro Plan", "email": "bob@example.com"}, "urgency": "medium", "summary": "Customer was double billed for Pro Plan and requests a fix."}
Wrap up
This pipeline is production ready as a starting point. Two concrete next steps: wire the parse_ticket_safe function into a FastAPI endpoint so your support app can call it in real time, or swap the model to qwen-3-32b if you need the same extraction quality across multilingual tickets. You can explore Oxlo.ai pricing at https://oxlo.ai/pricing.
Top comments (0)