Routing by intent
One choice question decides which handler, queue or team gets a request. When the model is not sure, the request goes to a fallback instead of the wrong place.
When to use it
- Requests arrive in one place (an inbox, a chat, a form, an agent's input) and need to go to one of several handlers.
- You route with keyword rules today, and they miss requests written in other words.
- A wrong route is costly: the customer waits while the request bounces between teams.
How it works
Ask one choice question with one option per destination, and describe each option in a few words. The answer names the pick, its confidence and the probabilities of every option. Route on the pick when the confidence clears your threshold; otherwise send the request to a fallback: a triage queue, a person, or a larger model.
State
The request
- “I moved last week, can my order go to the new flat?”
Question
Which handler?
- intentchoice
- change_address · 0.94
confidence ≥ 0.70
The change_address handler runs.
confidence < 0.70, or other
Triage, with the top two options as a hint.
The smallest version picks a support queue and sends the unsure messages to triage:
from typesafe_sdk import Choice, TypeSafeClient # Reads TYPESAFE_BASE_URL (https://api.vakyn.com) and TYPESAFE_API_KEY from the environment.client = TypeSafeClient()QUEUES = { "billing": "Charges, invoices and refunds", "shipping": "Delivery, tracking and damaged parcels", "account": "Sign-in, password and profile",} def route(message: str) -> str: result = client.system_one( state=message, questions={"queue": Choice(instructions="Which queue should handle this?", criteria=QUEUES)}, ) answer = result.choices["queue"] if answer.confidence >= 0.7: return answer.choice # sure enough: route automatically return "triage" # unsure: a person decides print(route("I changed my email address and now the password reset link never arrives."))The same idea with handlers instead of queue names. Each option maps to a function; an other option with no description catches the requests that fit none of them.
from typesafe_sdk import Choice, TypeSafeClient # Reads TYPESAFE_BASE_URL (https://api.vakyn.com) and TYPESAFE_API_KEY from the environment.client = TypeSafeClient()THRESHOLD = 0.70 # One option per handler. The descriptions are what the model reads; "other" catches the rest.INTENTS = { "refund": "Wants money back for an order", "change_address": "Wants to change where an order is delivered", "cancel_plan": "Wants to stop a subscription", "bug_report": "Reports something in the app that does not work", "other": None,} def start_refund(msg): ...def update_address(msg): ...def cancel_plan(msg): ...def file_bug(msg): ...def send_to_triage(msg, reason): ... HANDLERS = { "refund": start_refund, "change_address": update_address, "cancel_plan": cancel_plan, "bug_report": file_bug,} def handle(message: str): result = client.system_one( state=message, questions={"intent": Choice(instructions="What does the customer want?", criteria=INTENTS)}, ) intent = result.choices["intent"] if intent.choice in HANDLERS and intent.confidence >= THRESHOLD: return HANDLERS[intent.choice](message) # Unsure, or none of the above: a person picks, with the model's top two as a hint. top_two = sorted(intent.probabilities.items(), key=lambda kv: kv[1], reverse=True)[:2] return send_to_triage(message, reason=top_two)Tips
- Write options that do not overlap. When two options could both be right, the probability splits between them and the confidence drops, so more requests go to the fallback.
- Add an
otheroption. Without one, the model has to pick a destination even for a request that fits none, and the probabilities spread out. - To route on more than one fact, ask them together in the same request: the team as a choice, the urgency as a score. See fan-out.
- For how to set the threshold and what to do below it, see routing by confidence.