We are going to build a support ticket triage agent that classifies incoming messages, assigns severity, and routes them to the correct team without a single training example. This is zero-shot learning: the LLM infers the task from a detailed system prompt and a raw ticket body. I will walk through the exact code I run on Oxlo.ai, where flat per-request pricing makes long system prompts economical.
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: Connect to Oxlo.ai and verify the client
I always start by confirming the client can authenticate and reach the API. This avoids debugging network issues later.
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Say 'Connection OK' and nothing else."},
],
)
print(response.choices[0].message.content)
Step 2: Design the zero-shot system prompt
Zero-shot means zero examples. The model must learn the output schema and classification rules from instructions alone. I treat the system prompt as a config file that defines categories, severity levels, and the exact JSON schema the agent must return.
SYSTEM_PROMPT = '''
You are a support ticket triage agent. Your job is to read a raw customer message and return a single JSON object with no markdown formatting.
Classification rules:
- category must be one of: Billing, Technical, Account, General.
- severity must be one of: Low, Medium, High, Critical.
- team must be one of: Finance, Engineering, Customer Success.
- reasoning must be one sentence explaining your choice.
Severity guidance:
- Critical: service down, data loss, security breach.
- High: feature broken, cannot complete core workflow.
- Medium: partial issue, workaround exists.
- Low: general question, feature request, typo.
JSON schema:
{
"category": "string",
"severity": "string",
"team": "string",
"reasoning": "string"
}
Return only the JSON object. Do not include backticks or explanation outside the JSON.
'''
Step 3: Build the triage function
Now I wrap the API call into a reusable function. I use JSON mode to constrain the model to valid JSON, then parse the result with the standard library. I run this on llama-3.3-70b because it follows structured instructions reliably.
import json
def triage_ticket(ticket_body: str) -> dict:
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": ticket_body},
],
response_format={"type": "json_object"},
temperature=0.2,
)
raw = response.choices[0].message.content
return json.loads(raw)
Step 4: Process a batch of tickets
In production, tickets arrive as a list. I loop over them, classify each, and print a routing report. Because Oxlo.ai bills per request, not per token, adding a long system prompt or a verbose ticket history does not inflate the cost per classification.
tickets = [
"I was double charged for my subscription this month. Please refund the extra payment.",
"The API returns a 500 error every time I try to create a webhook. This is blocking our deploy.",
"How do I reset my password? I cannot find the link.",
"We need SAML SSO support for our enterprise account. Is this on the roadmap?",
]
for ticket in tickets:
result = triage_ticket(ticket)
print(f"Ticket: {ticket[:50]}...")
print(f" Route to: {result['team']} | Severity: {result['severity']}")
print(f" Reason: {result['reasoning']}")
print()
Run it
Here is the complete script and the output it produces when I execute 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 support ticket triage agent. Your job is to read a raw customer message and return a single JSON object with no markdown formatting.
Classification rules:
- category must be one of: Billing, Technical, Account, General.
- severity must be one of: Low, Medium, High, Critical.
- team must be one of: Finance, Engineering, Customer Success.
- reasoning must be one sentence explaining your choice.
Severity guidance:
- Critical: service down, data loss, security breach.
- High: feature broken, cannot complete core workflow.
- Medium: partial issue, workaround exists.
- Low: general question, feature request, typo.
JSON schema:
{
"category": "string",
"severity": "string",
"team": "string",
"reasoning": "string"
}
Return only the JSON object. Do not include backticks or explanation outside the JSON.
'''
def triage_ticket(ticket_body: str) -> dict:
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": ticket_body},
],
response_format={"type": "json_object"},
temperature=0.2,
)
raw = response.choices[0].message.content
return json.loads(raw)
tickets = [
"I was double charged for my subscription this month. Please refund the extra payment.",
"The API returns a 500 error every time I try to create a webhook. This is blocking our deploy.",
"How do I reset my password? I cannot find the link.",
"We need SAML SSO support for our enterprise account. Is this on the roadmap?",
]
for ticket in tickets:
result = triage_ticket(ticket)
print(f"Ticket: {ticket[:50]}...")
print(f" Route to: {result['team']} | Severity: {result['severity']}")
print(f" Reason: {result['reasoning']}")
print()
Example output:
Ticket: I was double charged for my subscription this month...
Route to: Finance | Severity: High
Reason: Double charging is a billing error that requires immediate refund processing.
Ticket: The API returns a 500 error every time I try to create...
Route to: Engineering | Severity: Critical
Reason: A recurring 500 error blocking deployment indicates a service outage.
Ticket: How do I reset my password? I cannot find the link...
Route to: Customer Success | Severity: Low
Reason: Password reset is a standard account support question with no urgency.
Ticket: We need SAML SSO support for our enterprise account. Is...
Route to: Engineering | Severity: Low
Reason: Feature requests for roadmap items are not urgent incidents.
Next steps
Swap in kimi-k2.6 or deepseek-v3.2 on Oxlo.ai if you need deeper reasoning for ambiguous edge cases, or add function calling so the agent can open a Jira ticket or Slack channel automatically after classification. If volume grows, the flat per-request pricing on Oxlo.ai keeps long-context triage predictable.
Top comments (0)