How to Route Support Tickets with Jev (Instead of Brittle Keyword Rules)
·2 min read
How to Route Support Tickets with Jev (Instead of Brittle Keyword Rules)
Most support ticket routing systems fail the same way. “Charged twice” goes to billing. “Not working” goes to tech. A ticket that says both flips a coin. Agents pay for every misroute in handle time and customer patience.
Jev ticket routing reads the whole ticket and returns a probability per queue. Mixed tickets show up as mixed distributions — visible, tunable, and safe to hand to humans when unsure.
TL;DR: One choice question with your real queue names (
billing,tech_support,sales). Ifconfidence < 0.7, enqueue for human triage. Log full distributions to catch drift early.
Why keyword routing breaks down
| Failure mode | What happens | Cost |
|---|---|---|
| Overlapping keywords | Ticket matches 2+ queues | Wrong queue SLA |
| New product vocabulary | Unknown words ignored | Permanent misroutes |
| Multi-issue tickets | One dominant keyword wins | Partial resolution |
| Non-English slang | Patterns miss | Silent failures |
Probabilistic ticket classification handles overlap by design: the distribution is the uncertainty.
Queue design that maps to code
Use labels that are exactly your downstream queue keys:
["billing", "tech_support", "sales"]
Avoid UI labels that need a mapping table (“Billing & Payments” → billing). If you must, keep a single mapping layer — not in every worker.
How many queues?
- 3–5 is the sweet spot for choice questions.
- More than ~7 smears probabilities.
- Add one human_only bucket rather than stuffing “other” into existing queues.
What to put in the state
Subject: Charged twice for order #4821
I was charged twice for the same order #4821. Please fix this ASAP.
I already called support once and nobody helped.
Include: subject, body, product name, plan tier, error codes, order IDs.
Exclude: full payment instruments, unnecessary PII, internal notes you do not want the model to see.
The routing request
curl https://openrouter.ai/api/v1/chat/completions \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "typesafe/jev-1.13",
"messages": [{
"role": "user",
"content": {
"state": "<ticket text>",
"questions": [{
"type": "choice",
"text": "Which team should handle this?",
"options": ["billing", "tech_support", "sales"]
}]
}
}]
}'
Example answer:
{
"selected": "billing",
"confidence": 0.94,
"distribution": {
"billing": 0.94,
"tech_support": 0.04,
"sales": 0.02
}
}
Confidence thresholds and human fallback
| Confidence | Action |
|---|---|
| ≥ 0.9 | Auto-assign |
| 0.7 – 0.9 | Auto-assign + notify lead if SLA-sensitive |
| < 0.7 | Human triage queue |
In benchmarks on support-like corpora, a 0.7 floor cuts auto-routes by only 8–12% while nearly eliminating wrong-queue incidents. Low-confidence fall-through is a feature, not a gap in the model.
if (answer.confidence < 0.7) return enqueue('human_triage');
return enqueue(answer.selected);
Production monitoring for ticket routing
- Log selected, confidence, full distribution, latency, ticket id.
- Entropy alerts — rising average entropy often appears weeks before accuracy drops.
- Queue mix dashboards — a sudden spike in
other/human_triageafter a product launch is expected; a slow bleed is not. - Regression set — 50 historical tickets that were hard for agents, re-scored on deploys.
Multi-label and multi-issue tickets
One ticket with billing and tech issues: prefer two questions (primary queue, secondary queue) or a separate “multi-issue” flag question (Noul). Do not force one choice question to average two judgments.
Cost and latency vs LLM routers
| Approach | Latency | Output | Typical cost pattern |
|---|---|---|---|
| Chat LLM router | 1–5s | Prose / JSON to parse | Prompt + completion tokens |
| Jev router | 70–500ms | Typed + distribution | Input only (~$0.042 / 1M tokens) |
For 100k tickets/day, the difference is not rounding error — and agents feel the latency in the tool UI.
FAQ
What about attachments and screenshots?
Jev takes text state. Extract text upstream (OCR, ticket form fields) and pass it in the state.
Can I use this for email and chat too?
Yes — same pattern. See message intent detection.
What if queues are highly specialized (20+)?
Cluster into 4–6 macro-queues first, then a second Jev call (or rules) for sub-queues.