A router is a decision, not a proxy
A router does not hide the providers behind it, and it should not invent one. You supply the candidates you can really call, with their cost and latency, and the priorities that matter for this task. The answer is one of those candidate ids with a probability, not a paragraph about trade-offs.
Keep the criteria in writing for the same reason you keep classification labels in writing: two runs of the same question should not produce two different policies. If cost decides everything, say so; if quality decides and cost only breaks ties, say that instead.
Semantic routing is classification with another name
A semantic router maps an utterance onto one of a fixed set of routes, usually by comparing embeddings. That is the same job as a classifier with labels, so it inherits the same failure modes: a threshold that looks precise, an embedding model that drifts, and an out-of-scope message that still lands in the nearest bucket.
A typed decision over criteria you wrote keeps the route names and their boundaries explicit, and it returns the probability together with the choice. Neither approach is authorization: a route says which path fits, not that this user may take it.
Compare accepted results, not token prices
A cheaper model that fails a route costs more than the model you avoided, because the work is done again or a wrong answer reaches a customer. Evaluate each route separately on the same held-out examples, and count the errors where they are expensive instead of averaging them away.
Cost and latency per accepted result are the numbers worth tracking, next to the review rate your threshold produces. A published provider benchmark measures their tasks on their data; it is not a measurement of your traffic.
Keep the fallback and the log
An unknown or low-confidence route belongs to a default path or to a person. Code that calls a model should treat a missing candidate as a failure to route, not as a licence to pick the largest model. Record the chosen candidate, the request id and the model version with the outcome, or the decision cannot be audited later.
Retry an uncertain connection with the same request id and unchanged input; a genuinely new task needs a new one. Routing a task to a model is not permission to run an irreversible action — that check stays where it already is.
