We are going to build an ITSM ticket triage agent that ingests raw support emails and chat transcripts, maps symptoms to internal runbooks, and outputs a structured decision: resolve immediately, request more information, or escalate to a specialist queue. This cuts down L1 backlog and gives end users faster answers for repetitive issues like password resets, VPN failures, and access requests.
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 client
I always start by verifying the endpoint with a smoke test. This confirms my Oxlo.ai key and routing are working before I add business logic.
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 'Oxlo.ai client is ready'"},
],
)
print(response.choices[0].message.content)
Step 2: Model the runbook knowledge base
In production I pull this from Confluence or ServiceNow, but a static dictionary is enough to harden the prompt and prove the routing logic. Each entry maps a symptom pattern to a resolution and a default priority.
RUNBOOKS = {
"vpn_disconnect": {
"symptoms": ["vpn drops", "vpn disconnect", "cannot stay connected"],
"resolution": "Switch from Wi-Fi to Ethernet, or enable 'Always On' in the VPN client settings under Preferences > Network.",
"priority": "P3",
"team": "Network Operations"
},
"password_reset": {
"symptoms": ["forgot password", "locked out", "cannot log in"],
"resolution": "Use the self-service portal at https://portal.company.com/reset. If MFA is broken, open a P2 ticket.",
"priority": "P4",
"team": "Identity Management"
},
"access_request": {
"symptoms": ["need access to", "request permission", "grant me"],
"resolution": "Submit the access request form with manager approval. Provisioning takes up to 4 hours.",
"priority": "P4",
"team": "Identity Management"
},
"database_timeout": {
"symptoms": ["database timeout", "query hangs", "sql timeout"],
"resolution": "Escalate immediately. Do not restart services without DBA approval.",
"priority": "P1",
"team": "Database Engineering"
}
}
Step 3: Define the system prompt
This prompt is the core of the agent. It tells the model how to classify the ticket, how to reference the runbook, and what JSON schema to return. Keeping the instructions explicit reduces hallucination.
SYSTEM_PROMPT = """
You are an IT service management triage agent.
Your job is to read a raw user ticket, compare it to the runbooks provided below, and output a strict JSON object.
Runbooks:
- vpn_disconnect (P3, Network Ops): VPN drops or disconnects. Resolution: switch to Ethernet or enable Always On.
- password_reset (P4, Identity): forgot password or locked out. Resolution: self-service portal reset.
- access_request (P4, Identity): need access or permission. Resolution: submit form with manager approval.
- database_timeout (P1, DBA): database timeout or query hangs. Resolution: escalate immediately.
Rules:
1. If the ticket clearly matches a runbook, set action to "resolve" and draft a concise, empathetic user response.
2. If the ticket mentions symptoms that map to P1 or P2, set priority to that level and action to "escalate".
3. If information is missing to choose a runbook, set action to "need_info" and ask one clarifying question.
4. Output ONLY valid JSON with these keys: classification, priority, action, response_to_user, assigned_team.
Do not include markdown formatting or explanation outside the JSON.
"""
Step 4: Build the triage function
The function formats the runbook context and the raw ticket into a single user message, then calls Oxlo.ai. I use llama-3.3-70b here because it handles structured JSON reliably for classification tasks. If you are processing multilingual tickets, qwen-3-32b is a strong alternative on Oxlo.ai. Because Oxlo.ai uses flat per-request pricing, pasting long logs or thread history does not inflate costs the way token-based billing does. See https://oxlo.ai/pricing for current plan details.
import json
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
def triage_ticket(raw_ticket: str) -> dict:
user_message = f"Incoming ticket:\n{raw_ticket}\n\nUse the runbooks to decide the outcome."
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
)
content = response.choices[0].message.content.strip()
if content.startswith("
```json"):
content = content.split("```
json")[1]
if content.endswith("
```
"):
content = content[:-3]
return json.loads(content.strip())
Step 5: Add routing and safety layers
LLMs are good at pattern matching, but I still enforce hard business rules in code. If the model returns P1 or the word "outage" appears in the raw ticket, we override the action to escalate regardless of classification. This layer makes the agent production-safe.
def process_ticket(raw_ticket: str) -> dict:
decision = triage_ticket(raw_ticket)
critical_keywords = ["outage", "down", "production down", "all users"]
if any(k in raw_ticket.lower() for k in critical_keywords):
decision["action"] = "escalate"
decision["priority"] = "P1"
decision["assigned_team"] = "Site Reliability Engineering"
decision["response_to_user"] = "We have escalated this to our SRE team and are investigating now."
print(f"[AUDIT] Ticket classified as {decision['classification']} | Action: {decision['action']} | Team: {decision['assigned_team']}")
return decision
Run it
Here is a realistic ticket and the full execution block. The agent correctly maps the symptom to the VPN runbook and drafts a response.
if __name__ == "__main__":
ticket = (
"Hi, my VPN keeps disconnecting every few minutes when I work from the cafe. "
"It is fine at home. I need to access the staging environment for a demo in 30 minutes. "
"Can someone help ASAP?"
)
result = process_ticket(ticket)
print(json.dumps(result, indent=2))
Expected output:
{
"classification": "vpn_disconnect",
"priority": "P3",
"action": "resolve",
"response_to_user": "Thanks for reaching out. Because the issue only occurs on public Wi-Fi, please switch to a wired Ethernet connection or enable 'Always On' in your VPN client under Preferences > Network. This should stabilize your session for the demo. If the problem persists on a stable network, let us know and we will escalate to Network Operations.",
"assigned_team": "Network Operations"
}
Wrap-up
This agent is already useful as a standalone script, but the real value comes from wiring it into your ITSM platform. A concrete next step is to hook the process_ticket function into the ServiceNow REST API or Jira Service Management webhooks so tickets are created and updated automatically. Another step is to replace the static RUNBOOKS dictionary with a retrieval step using Oxlo.ai's embeddings endpoint (BGE-Large or E5-Large) to search your internal wiki dynamically, which keeps answers current without redeploying code.
Top comments (0)