Support ticket triage
Route each incoming ticket to a team, gauge how upset the customer is, and pull out the order number, in one request. Low-confidence routings go to a triage queue instead of the wrong team.
from simoai import Client, Choice, Extract, Noul, Score
c = Client()
QUESTIONS = {
"team": Choice("Which team should handle this ticket?", {
"billing": "charges, refunds, invoices and payment methods",
"shipping": "delivery, tracking, damaged or missing parcels",
"account": "login, password and account settings",
"tech": "bugs and errors in the website or app",
}),
"mood": Score("How upset is the customer?", ["calm", "annoyed", "angry", "furious"]),
"urgent": Noul("The customer needs an answer today"),
"order": Extract("the order number, exactly as written", max_tokens=12),
}
def triage(ticket: str) -> dict:
r = c.ask({"ticket": ticket}, QUESTIONS)
team = r["team"]
return {
"queue": team.choice if team.probabilities[team.choice] >= 0.8 else "triage",
"priority": "high" if r["urgent"].noul > 0.7 or r["mood"].score >= 2.5 else "normal",
"order": r["order"].value if r["order"].valid else None,
"cost": r.cost_usd,
}
print(triage("I was charged twice for order A-1042 and nobody answers. Fix it today."))
# {'queue': 'billing', 'priority': 'high', 'order': 'A-1042', 'cost': 2.1e-06}curl https://api.temprl.org/v1/ask \
-H "Authorization: Bearer $SIMO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "simo-1-pro",
"state": {"ticket": "I was charged twice for order A-1042 and nobody answers. Fix it today."},
"questions": {
"team": {"type": "choice", "instructions": "Which team should handle this ticket?",
"criteria": {"billing": "charges, refunds, invoices and payment methods",
"shipping": "delivery, tracking, damaged or missing parcels",
"account": "login, password and account settings",
"tech": "bugs and errors in the website or app"}},
"mood": {"type": "score", "instructions": "How upset is the customer?",
"criteria": ["calm", "annoyed", "angry", "furious"]},
"urgent": {"type": "noul", "instructions": "The customer needs an answer today"},
"order": {"type": "extract", "instructions": "the order number, exactly as written",
"kind": "text", "many": false, "max_tokens": 12}
}
}'const QUESTIONS = {
team: {
type: "choice",
instructions: "Which team should handle this ticket?",
criteria: {
billing: "charges, refunds, invoices and payment methods",
shipping: "delivery, tracking, damaged or missing parcels",
account: "login, password and account settings",
tech: "bugs and errors in the website or app",
},
},
mood: { type: "score", instructions: "How upset is the customer?", criteria: ["calm", "annoyed", "angry", "furious"] },
urgent: { type: "noul", instructions: "The customer needs an answer today" },
order: { type: "extract", instructions: "the order number, exactly as written", kind: "text", many: false, max_tokens: 12 },
};
async function triage(ticket) {
const res = await fetch("https://api.temprl.org/v1/ask", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.SIMO_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ model: "simo-1-pro", state: { ticket }, questions: QUESTIONS }),
});
if (!res.ok) throw new Error((await res.json()).error?.message);
const { answers: a } = await res.json();
return {
queue: a.team.probabilities[a.team.choice] >= 0.8 ? a.team.choice : "triage",
priority: a.urgent.noul > 0.7 || a.mood.score >= 2.5 ? "high" : "normal",
order: a.order.valid ? a.order.value : null,
};
}Why it works
- One read, four answers. The ticket is read once; the extra questions are almost free.
- Calibrated routing.
0.8on the chosen team means it is right about 80% of the time, so the triage queue catches the genuinely ambiguous tickets, not a random sample. - Descriptions do the work. Each team's description tells the model what belongs there. Keep them specific.