Routing Use Cases of Jev in AI Voice Agents

On this page
A voice agent is a long chain of small decisions. Which model answers this turn? Which tool fires? Which team gets the handoff? Each one is a router. This post catalogs every routing job Jev can do. Then it gets hands-on. We decode a real Jev response field by field. We build a reusable router in Python. We run five routers in one call. And we show how to measure whether your routers are right.
Why routing is Jev's core job
Routing has one shape everywhere it appears. You pick one option from a fixed set. That is routing in networks, in call centers, and inside an agent loop. The set is closed and known ahead of time. The job is to pick the right member of it, fast.
That shape is exactly what Jev's Choice question does. An LLM can pick too, but it is a heavy tool for a closed-set pick. It is slower, it costs more, and it can answer with text that is not on your list. A schema-constrained router returns one of your options.
Jev: TypeSafe AI's first System One model, launched September 15, 2026, in early access. It reads a state plus questions and returns typed probabilistic decisions with calibrated confidence.
Jev is not a language model. It does not generate text. You give it a state, such as a transcript or a message. You attach one or more questions. It returns typed decisions. TypeSafe documents three question types, and they map to three routing roles.
Choice: picks one of up to 255 options and returns a probability per option plus a confidence. This is the router.
Noul: returns a yes or no probability. This is the gate.
Score: returns an ordered level with confidence. This is the prioritizer.
Three facts make Jev fit routing. It is fast, at 70 to 500ms per decision. It is cheap, with input at $0.042 per million tokens and output free. And it answers many questions on one state in a single call. You can read the launch note and the TypeSafe quickstart.
The mental model is simple. Use an LLM to talk. Use Jev to decide fast. Because the router is cheap and quick, you can place one at every decision point. Because confidence is calibrated, you can gate the ambiguous ones. So you route confident calls automatically and escalate the rest.
There is a catch. A mis-route is a silent quality drop. Nothing crashes. The wrong model just answers, or the wrong tool fires, and the caller pays for it. So every router must be evaluated. Our pillar on Jev for voice agents sets the wider context. Jev vs an LLM covers when to use which.
Anatomy of a Jev router
The fastest way to understand a router is to read one real response. TypeSafe's quickstart uses a support message and a department router. Here is the state it routes.
"Hi, I've been trying to connect my Stripe account for 3 days and the integration keeps failing. I'm losing sales. Please help ASAP."
Here is the Choice question, exactly as documented. The same docs request also asked a frustration Score and an is_urgent Noul. We show only the router here.
{
"department": {
"type": "choice",
"instructions": "Which team should handle this",
"criteria": {
"billing": "Payment or subscription issues",
"technical": "Bugs or integration problems",
"sales": "Pricing or account questions"
}
}
}The request goes to `POST https://api.typesafe.ai/v1/systemone` with the model `jev-latest`. Here is the documented router output.
{
"department": {
"type": "choice",
"choice": "technical",
"confidence": 0.78,
"probabilities": {
"technical": 0.85,
"sales": 0.0,
"billing": 0.15
}
}
}The whole request reported usage of 392 input tokens and 65 output tokens. Output is free, so only the 392 input tokens bill. At $0.042 per million, that is about $0.000016 per request (illustrative arithmetic).

Now walk through each field.
choice: the option Jev picked. It is always one of your criteria keys. Here it is `technical`, because "integration keeps failing" matches "Bugs or integration problems."
probabilities: a distribution over every option. Technical gets 0.85 and billing gets 0.15. Sales gets 0.0. The billing mass makes sense. The caller mentions Stripe and lost sales, which sound like payments. Jev saw that pull and still ranked technical first.
confidence: a separate number, 0.78. It is Jev's calibrated estimate of how likely its pick is correct. It is not simply the top probability. Here it sits below 0.85.
So why is this a "route but log" case? The pick is clear. The runner-up trails by 0.70. But confidence is 0.78, not 0.95. Suppose you route automatically above 0.75 (an illustrative threshold). This call clears it by only 0.03. So you act on it, and you tag it for review. If many calls land in that band, you learn where your criteria are blurry. Here, "Stripe" may belong partly to billing.
Calibrated confidence: a confidence score where the stated value tracks real accuracy. Among many decisions at 0.78, roughly 78 percent should be right. That holds on average, not per call, and not automatically on your data.
A reusable router in Python
Every router in the catalog below follows the same logic. Ask a Choice question. Read the pick and confidence. Then route, confirm, or escalate. So write the logic once.
The helper below wraps the TypeSafe SDK's `system_one` call from the quickstart.
import json
import logging
import time
from typesafe_sdk import Choice, TypeSafeClient
log = logging.getLogger("routers")
client = TypeSafeClient() # reads your API key from the environment
DEFAULT_THRESHOLDS = {"route": 0.75, "confirm": 0.50} # illustrative
def route(state, name, instructions, options, thresholds=DEFAULT_THRESHOLDS):
"""Ask one Choice question. Return route, confirm, or escalate."""
question = Choice(instructions=instructions, criteria=options)
started = time.perf_counter()
response = client.system_one(
state=state,
questions={name: question},
)
result = response.answers[name]
latency_ms = round((time.perf_counter() - started) * 1000)
conf = result.confidence
if conf >= thresholds["route"]:
action = "route"
elif conf >= thresholds["confirm"]:
action = "confirm" # ask the caller: "Is this about X?"
else:
action = "escalate" # stronger model or a human
decision = {
"router": name,
"choice": result.choice,
"confidence": conf,
"probabilities": result.probabilities,
"action": action,
"latency_ms": latency_ms,
"review": action != "route" or conf < thresholds["route"] + 0.05,
}
log.info(json.dumps(decision))
return decisionCall it with the documented department router.
decision = route(
state=transcript_text,
name="department",
instructions="Which team should handle this",
options={
"billing": "Payment or subscription issues",
"technical": "Bugs or integration problems",
"sales": "Pricing or account questions",
},
)
# On the docs example: choice=technical, confidence=0.78,
# action=route, review=True (inside the 0.05 margin)Three design choices matter here. First, the log line keeps the full distribution, not just the pick. You need it later to build a confusion matrix. Second, the `review` flag marks near-threshold routes. That is the "route but log" band. Third, thresholds are per router. A guardrail gate needs a stricter bar than a voice picker.
Run several routers in one Jev call
A single voice turn often needs five decisions. Which language? Which intent? Which article grounds the answer? Does this need a human? Is the request risky? Jev answers many questions on one state in one request. So all five share one round trip.
{
"model": "jev-latest",
"state": "Hola, I was charged twice for my plan this month and I want the second charge reversed. If you cannot do it, give me a person.",
"questions": {
"intent": {
"type": "choice",
"instructions": "What does the caller want to do",
"criteria": {
"refund_request": "Wants money back for a charge",
"plan_change": "Wants to upgrade, downgrade, or switch plans",
"cancel": "Wants to end the service",
"other": "None of the above"
}
},
"language": {
"type": "choice",
"instructions": "Which language should the agent reply in",
"criteria": {
"en": "Caller mainly speaks English",
"es": "Caller mainly speaks Spanish"
}
},
"kb_article": {
"type": "choice",
"instructions": "Which article best answers this",
"criteria": {
"kb_duplicate_charge": "Duplicate or double charges and reversals",
"kb_refund_policy": "General refund eligibility and timelines",
"kb_plan_pricing": "Plan prices and what each includes",
"none": "No article fits"
}
},
"needs_human": {
"type": "noul",
"instructions": "Has the caller asked for or clearly needs a human"
},
"risk": {
"type": "noul",
"instructions": "Does fulfilling this require moving money or changing billing"
}
}
}The response below is illustrative. Every field follows the documented shape: Choice answers carry choice, confidence and probabilities, and Noul answers carry a single `noul` probability.
{
"intent": {"type": "choice", "choice": "refund_request", "confidence": 0.91,
"probabilities": {"refund_request": 0.93, "plan_change": 0.03, "cancel": 0.01, "other": 0.03}},
"language": {"type": "choice", "choice": "en", "confidence": 0.72,
"probabilities": {"en": 0.80, "es": 0.20}},
"kb_article": {"type": "choice", "choice": "kb_duplicate_charge", "confidence": 0.88,
"probabilities": {"kb_duplicate_charge": 0.90, "kb_refund_policy": 0.08, "kb_plan_pricing": 0.0, "none": 0.02}},
"needs_human": {"type": "noul", "noul": 0.64},
"risk": {"type": "noul", "noul": 0.95}
}Read it like a router table. Intent routes cleanly. The article routes cleanly. Language sits at 0.72, so the agent confirms: "Would you prefer English or Spanish?" Risk is high, so the refund tool goes behind step-up auth. The human flag is borderline, so the agent offers a transfer instead of forcing one. Five decisions, one request, one latency budget.

Routing past 255 options: the two-stage pattern
A Choice question supports up to 255 options. Some routing jobs are bigger. A support library can hold 1,000 articles. A catalog can hold thousands of SKUs. For those, TypeSafe describes a two-stage system. First score each option independently. Then make an explicit choice among the best few.
Here is the pattern as pseudo-code for 1,000 knowledge-base articles.
from typesafe_sdk import Choice, Score
def route_article(state, articles, batch_size=50, top_k=5):
# Stage 1: score every article's relevance, many per call.
scored = []
for batch in chunks(articles, batch_size): # batch size is yours to tune
questions = {
a.id: Score(instructions=f"How well does '{a.title}: {a.summary}' answer the caller")
for a in batch
}
results = client.system_one(state=state, questions=questions).answers
scored += [(a, results[a.id]) for a in batch]
# Keep the top few candidates by score.
shortlist = top_by_score(scored, k=top_k)
# Stage 2: one explicit Choice among the shortlist plus a way out.
criteria = {a.id: a.summary for a, _ in shortlist}
criteria["none"] = "None of these articles answers the question"
return route(state, "kb_article", "Which article best answers this", criteria)Stage 1 is a ranking job, so Score fits. Stage 2 is a pick, so Choice fits. And the `none` option matters most in stage 2. If no shortlist article fits, the router should say so. Otherwise the agent grounds its answer on the least-wrong article. Many teams also put a cheap retrieval step before stage 1, to cut 1,000 articles to 100. That is a design choice, not a Jev requirement. Check the docs for per-call question limits before you size batches.
Writing good routing criteria
A router is only as good as its option set. Most bad routing comes from blurry criteria, not a weak model. Five rules help.
1. Make options mutually exclusive. "Billing" and "Payments" overlap, so probability splits between them. Merge them or separate them by a clear test.
2. Always include a catch-all. Add `other` or `none`. Without one, an off-topic call is forced into the nearest bucket and routes wrong with a straight face.
3. Write descriptions, not bare labels. The docs example pairs `technical` with "Bugs or integration problems." That description is what lets Jev place a Stripe integration failure correctly.
4. Describe by what the caller says, not your org chart. Callers do not know your team names. Criteria should match how problems sound on the phone.
5. Keep keys stable. Your logs, dashboards, and confusion matrices key on the option name. Renaming `technical` to `tech_support` breaks history.
Here is a weak option set next to a stronger one.
{
"weak": {
"billing": "Billing",
"payments": "Payments",
"support": "Support"
},
"strong": {
"billing": "Charges, refunds, invoices, or subscription changes",
"technical": "Something is broken, failing, or will not connect",
"sales": "Wants pricing, a quote, or to buy more",
"other": "None of the above, or unclear"
}
}The weak set has overlap, no catch-all, and no descriptions. The strong set fixes all three. Test criteria on your ambiguous calls first. The "Stripe integration" call is a good probe. It sounds like billing and is really technical.
Jev routing use cases: the master catalog
The table below lists every routing job Jev can do in a voice agent. It names the primitive, what gets routed, and the mis-route risk. Use it as a map, then read the grouped sections for detail.
Read the primitive column first. Choice is a router, so it appears wherever you pick one of many. Noul is a gate, so it appears wherever the answer is allow or block. Score is a prioritizer, so it appears wherever you rank or grade. Most jobs are Choice, because most routing is a many-way pick.
| Routing job | Primitive | What it routes | Mis-route risk |
|---|---|---|---|
| Model router | Choice | Turn to LLM tier | Wrong tier wastes cost. |
| STT router | Choice | Audio to speech engine | Raises word error rate. |
| TTS / voice router | Choice | Reply to voice engine | Hurts brand or clarity. |
| RAG / knowledge router | Choice | Query to index or source | Grounds on bad data. |
| KB-article router (over 255) | Score then Choice | Query to one article | Cites the wrong answer. |
| Prompt / playbook router | Choice | Context to system prompt | Applies wrong policy. |
| Region / residency router | Choice | Call to regional endpoint | Breaches residency rules. |
| Vendor / version router | Choice | Traffic to vendor or version | Degrades quality. |
| Reasoning-effort router | Score | Turn to reasoning budget | Drops accuracy or adds latency. |
| Intent router | Choice | Utterance to intent or flow | Derails the whole call. |
| Is-a-tool-needed gate | Noul | Turn to tool call or not | Fires a needless action. |
| Tool router | Choice | Turn to specific tool | Returns wrong data. |
| Language / locale router | Choice | Speech to language or locale | Garbles replies. |
| Voice / persona router | Choice | Segment to persona | Feels wrong to caller. |
| Response-strategy router | Choice | Turn to answer, clarify, confirm, defer | Stalls the call. |
| Reply-bank router | Choice | Turn to canned reply | A wrong line misinforms. |
| Personalization router | Choice | Caller to segment treatment | Gives a bad offer. |
| Disambiguation router | Choice | Ambiguous input to one meaning | Acts on wrong request. |
| Step-up auth router | Noul | Request to auth check or not | Exposes a sensitive action. |
| Fraud / risk router | Score | Call to risk level | Lets fraud through. |
| Disclosure / consent router | Noul | Turn to disclosure or not | Creates a compliance gap. |
| Guardrail gate | Noul | Action to block or allow | Runs a banned action. |
| Escalate-or-not gate | Noul | Turn to human or not | Frustrates the caller. |
| Skill / department router | Choice | Handoff to team or skill | Forces a repeat transfer. |
| Callback / hold router | Choice | Wait to callback, hold, voicemail | Wastes caller time. |
| De-escalation router | Choice | Upset caller to recovery path | Escalates anger. |
| Agent router | Choice | Task to sub-agent | Handles it poorly. |
| Fallback / recovery router | Choice | Failure to recovery path | Leaves a dead end. |
| Post-call action router | Choice | Call to CRM, ticket, follow-up | Corrupts records. |
| QA-sampling router | Score | Call to human review or not | Hides real defects. |
Pipeline and infrastructure routing
These routers pick the plumbing before or during a turn. They are invisible to the caller and easy to get wrong. They rarely show up in a demo. They surface when the bill or the quality slips in production.
The model router sends each turn to the cheapest LLM that can still handle it. A ZIP code lookup does not need a frontier model. A tangled complaint might. The savings compound across thousands of calls. LangChain ships this as ModelRouterMiddleware in `langchain_typesafe.experimental.middleware`. See our model routing deep dive for the full pattern.
The STT router picks the speech-to-text engine for the current audio context. A noisy phone line may need a different engine than a clean VoIP call. The TTS / voice router picks the synthesis engine for the reply. A short confirmation and a long explanation may not want the same voice.
The RAG / knowledge-base router chooses which index or source to query. A billing question hits billing docs, not the product wiki. See retrieval-augmented generation for the pattern it sits in front of. When the target is one article out of hundreds, use the two-stage pattern above.
The prompt / playbook router swaps the system prompt to match the situation. A returning VIP and a first-time caller may need different scripts. The region / data-residency router sends the call to the right regional endpoint. The vendor / version router shifts traffic during a migration or canary. The reasoning-effort router uses Score to set how much thinking budget a turn deserves.
Conversation and runtime routing
These routers shape the live dialogue. They fire on nearly every turn, so speed matters most here. A slow router adds delay the caller can hear. This is where a 70-to-500ms decision pays off.
The intent router maps the caller's utterance to an intent or flow. It is the most common router in any agent. A misread derails the whole call, because every later step trusts the intent. "I want to cancel" and "I want to change my plan" lead to different flows. Our routing and escalation post covers intent in depth.
Tool use needs two routers. The is-a-tool-needed gate is a Noul that decides whether any tool should fire. Many turns need no tool, just an answer. The tool router is a Choice that picks which tool. Splitting the gate from the pick keeps each decision easy to evaluate. Our tool calling post and the tool calling guide go deeper.
The language / locale router detects the caller's language and locale. It sets the reply language and how numbers and dates read aloud. The voice / persona router matches persona to segment. The response-strategy router picks the dialogue act: answer, clarify, confirm, or defer. The reply-bank router picks a pre-written reply with no LLM call at all. That is the cheapest turn you can serve. The personalization router selects the treatment for a segment. The disambiguation router resolves ambiguous input to one meaning before the agent acts.
Safety and compliance routing
Safety routers decide whether something risky may proceed. Most are Noul gates, because the answer is yes or no. A silent failure is most expensive here. A wrong pick can breach policy or expose a customer.
The step-up auth router decides when a request needs stronger verification, such as a balance transfer. The fraud / risk router is a Score that rates how risky a call looks. Score fits because risk is a spectrum. The disclosure / consent router decides when a required disclosure must play. A missed disclosure can turn a routine call into a regulatory problem.
The guardrail gate blocks or allows a risky action before it runs. LangChain's AutoModeMiddleware uses Jev this way to gate risky tool calls. The gate returns a typed probability, not free text a caller can argue with. Our guardrails and jailbreak post and the PII handling guide cover this layer.
Human-handoff routing
Handoff routers decide when and where a human takes over. A bad transfer makes the caller repeat everything. A missed escalation traps a frustrated caller with the bot. A needless one wastes a human on a call the agent could close.
The escalate-or-not gate is a Noul that decides whether this turn needs a human. The skill-based / department router picks which team gets the handoff. The docs example above is exactly this router. It is the job an automatic call distributor does with skills-based routing. The callback / hold / voicemail router chooses how to handle a wait. The de-escalation router picks the recovery path for an upset caller. See the escalation guide for the full playbook.
Multi-agent and meta routing
These routers coordinate agents and decide what happens after a call. They sit above the single turn and shape the whole session.
The agent router picks which sub-agent handles a task. The fallback / recovery router picks what to do on low confidence or failure. It can retry, rephrase, hand off, or apologize and close. This is the safety net for every other router. In the Python helper above, it is what runs on `escalate`.
The post-call action router decides which CRM update, ticket, or follow-up a call needs. A wrong write quietly corrupts records for weeks. The QA-sampling router is a Score that decides which calls go to human review. Random sampling wastes reviewers on easy calls. Our posts on call monitoring and voice agent metrics cover this layer.
How to add a Jev router to any decision in a voice agent
Every router follows the same recipe. Learn it once and reuse it everywhere.
1. Identify the decision and its fixed option set. Write down the exact options. Routing needs a closed set. Open-ended text is a job for an LLM.
2. Write criteria as descriptions. Make options exclusive and add a catch-all. Use the rules above.
3. Send the state plus a Choice question to Jev. Jev returns a pick, a probability per option, and a confidence.
4. Set thresholds per decision. Higher-stakes routers get higher bars. A guardrail gate should be stricter than a voice picker.
5. Route, confirm, or escalate. Act on confident picks. Confirm the middle band with the caller. Escalate the rest.
6. Batch questions per turn. Put every router for that turn in one request.
7. Log the full distribution. Keep the pick, confidence, and probabilities for every call.
8. Evaluate routing accuracy on real calls. Calibration is not correctness on your data, so measure it.
How to evaluate a router
A router can look fine in logs and still be wrong. Every decision returns a valid option. Nothing errors. So you need labeled calls. Take a sample of real calls, have humans label the correct route, and compare. Three views matter.
Accuracy per class
Overall accuracy hides weak classes. A router can be 90 percent right overall and 60 percent right on its rarest class. Report recall per option. That tells you which callers the router fails.
The confusion matrix
A confusion matrix shows where errors go. Rows are the true route. Columns are the router's pick. Here is an illustrative matrix for the department router on 200 labeled calls.
| True route | Picked technical | Picked billing | Picked sales | Recall |
|---|---|---|---|---|
| technical (100) | 88 | 9 | 3 | 88.0% |
| billing (60) | 6 | 50 | 4 | 83.3% |
| sales (40) | 2 | 5 | 33 | 82.5% |
The largest error cell is technical sent to billing. That echoes the Stripe example. Integration failures that mention payments leak into billing. The fix is usually criteria, not thresholds. Add "payment integration failures go to technical" to the technical description.
Misroute cost weighting
Not every misroute costs the same. Sending a technical caller to billing costs one transfer. Sending a billing caller to sales can cost trust, because a caller with a charge problem hears a pitch. So weight each cell by cost. With illustrative weights of 1 per transfer and 2 for any call sent to sales by mistake, the matrix above scores 36 cost units across 29 errors.
from collections import Counter
# Each row: (true_label, predicted_label, confidence) from your decision log.
COST = {("billing", "sales"): 2, ("technical", "sales"): 2} # illustrative
def evaluate(rows):
pairs = Counter((t, p) for t, p, _ in rows)
labels = sorted({t for t, _, _ in rows})
for t in labels:
total = sum(n for (tt, _), n in pairs.items() if tt == t)
print(f"{t}: recall {pairs[(t, t)] / total:.1%}")
cost = sum(n * COST.get((t, p), 1) for (t, p), n in pairs.items() if t != p)
print(f"weighted misroute cost: {cost}")
# Calibration check: accuracy inside each confidence band.
for lo in (0.5, 0.6, 0.7, 0.8, 0.9):
band = [(t, p) for t, p, c in rows if lo <= c < lo + 0.1]
if band:
acc = sum(t == p for t, p in band) / len(band)
print(f"confidence {lo:.1f}-{lo + 0.1:.1f}: accuracy {acc:.1%} (n={len(band)})")The last loop is the calibration check. If calls at 0.7 to 0.8 confidence are right only 55 percent of the time, your thresholds are too loose for your traffic. Calibrated in general does not mean calibrated on your accents and your edge cases.
Evaluate every router: the Evalgent angle
Here is the honest limit. Jev decides and classifies; it does not generate. It works on transcript or text state, not raw audio. So a bad transcription can feed a bad decision, even when the router is right about the text it saw. Its confidence is calibrated in general, not on any single call. And it is in early access. None of that removes the core risk. A mis-route is a silent quality drop that no error log shows.
That is why every router needs an independent check. A vendor grading its own router is not evidence. The accuracy that matters is the accuracy on your traffic. Evalgent is the neutral, third-party evaluator that measures routing accuracy on your real calls. We build the labeled set. We report per-class recall, the confusion matrix, and cost-weighted misroutes for each router. We check whether the model router escalates when it should. We check whether the guardrail gate blocks what it must. Low latency means nothing if the pick is wrong.
We also flag drift. A router that was accurate last month can degrade as callers, products, and criteria change. A router you cannot measure is a router you cannot trust. See our take on independent voice AI evaluation and the broader voice agent evaluation approach.
Jev can't abstain: always include an escape option
Jev always returns one of the options you define. It has no built-in "I don't know." When a call transcript holds no evidence for any option, such as a two-word turn, a dropped call or a caller talking about something else, Jev still picks the least-wrong option, and that label looks just like a confident answer. TypeSafe's own Choice docs recommend adding an "other" or "none of the above" option when the list may not cover every input.
Each routing job in this guide has the same failure mode. A tool router with no `no_tool_needed` option will pick a tool, and a model router with no `default_tier` option will pick a tier, even when the turn carries no signal.
Four rules keep this from hurting you on live calls:
- Add an explicit escape option such as `not_enough_information`, `not_applicable` or `none_of_the_above` to every Choice question where evidence can be missing. Describe it as concretely as the real options, because Jev reads only the descriptions, not your option keys.
- Ask whether the evidence exists first. A Noul like "does the caller state a reason for calling?" before the Choice question separates "no evidence" from "evidence for option B".
- Check the distribution, not just the label. In one independent edge-case test, a forced choice with no valid option still returned a pick at 0.52 probability but only 0.04 confidence (jev-exploration). The numbers flagged the problem; the label did not. Set your human-review cutoff from your own labeled calls, because an independent study on data unlike Jev's training found its confidence can run high (jev-ood-calibration).
- Track the escape-option rate. If it climbs, your transcripts, STT or option set have drifted away from what callers actually say.
Frequently asked questions
What are the routing use cases of Jev in a voice agent?
Jev routing use cases span the whole stack. Pipeline routers pick the model, STT, TTS, index, and prompt. Runtime routers pick intent, tool, language, and persona. Safety gates cover auth, fraud, and guardrails. Handoff routers pick escalation and department. Meta routers handle agents, fallback, and QA sampling. See the 30-row catalog above.
Which Jev question type do I use for routing?
Use Choice for routing. It picks one option from a fixed set of up to 255 and returns a probability per option plus confidence. Use Noul for yes or no gates, such as blocking a risky action. Use Score for ordered priorities, such as risk level. For more than 255 options, score candidates first, then choose.
How does Jev route to the right model in a voice agent?
Jev reads the turn as a state and answers a Choice question naming your model tiers. It returns the chosen tier plus calibrated confidence in 70 to 500ms. Confident turns go to a cheap model. Low-confidence turns escalate to a stronger one. LangChain packages this as ModelRouterMiddleware.
Can Jev route which tool a voice agent calls?
Yes. Tool use needs two Jev questions. A Noul gate decides whether any tool is needed on this turn. A Choice question then picks which tool from your registered set. The pick is always one of your listed options. Risky tools can also sit behind a separate Noul gate, as in LangChain's AutoModeMiddleware.
What is the difference between the Choice, Noul, and Score questions?
Choice picks one of up to 255 options with a probability each, so it is the router. Noul returns a yes or no probability, so it gates allow-or-block decisions. Score returns an ordered level with confidence, so it prioritizes. Examples include risk scoring, ranking articles, or picking which calls need review.
How fast and cheap is a Jev routing decision?
TypeSafe reports 70 to 500ms per request. Input costs $0.042 per million tokens and output is free. The documented department example used 392 input tokens, or roughly $0.000016 (illustrative arithmetic). Many questions share one request, so several routers cost about one call.
What happens if Jev mis-routes a decision?
A mis-route is a silent quality drop. Nothing crashes. The wrong model, tool, or team just handles the turn, and the caller pays for it. Calibrated confidence lets you confirm or escalate the ambiguous cases. But calibration is not a per-call guarantee, so every router must be evaluated on real calls.
How do you evaluate routing accuracy on real calls?
Label a sample of your real calls with the correct route and compare the router's picks. Report recall per class, a confusion matrix, and cost-weighted misroutes. Check accuracy inside each confidence band. Evalgent, an independent evaluator, runs this on your traffic and flags drift over time.
The bottom line
Routing is pick-one-of-N with calibrated confidence, which is exactly Jev's Choice question. Every router can fail silently, so Evalgent measures routing accuracy on your real calls.
Ready to see which of your routers are quietly failing? Book a demo and we will grade them against your real calls.
Related Articles

Why AI voice agents fail in production: 9 failure layers, how to detect each, and how to prevent them
Voice agents fail in production across 9 layers, from 8 kHz audio to silent tool errors. Symptoms, root causes, alerts, and fixes in one master table.
Read more
Voice agent regression testing: how to detect regressions from model updates you don't control
Voice agent regression testing for changes you don't control: pin each layer, run a nightly pinned-vs-latest canary, and use paired stats to detect drops.
Read more