The Pre-Call Sort - Routing Sensitive Queries Before They Ever Dispatch

The Pre-Call Sort - Routing Sensitive Queries Before They Ever Dispatch

The problem

Picture a nurse at a clinic network opening the same chat window twice in one shift. The first question is harmless: what's the max daily dose of a common pediatric medication. The second names a patient directly: does patient Marlon Okafor, MRN-000482, have an active penicillin allergy. Both questions go to the same clinical assistant I built to test this, a system I called MedAssist. Both look identical to the system that receives them. Text in, text out, no questions asked.

I built MedAssist's first version with exactly one node in its request graph, and that node does exactly one thing: it calls a general-purpose hosted model, every time, for every query, no exceptions. Nothing in the pipeline was built to tell a question about medication dosing in general apart from a question naming a real patient's identifying details. I seeded a roster of 30 synthetic patients, fake names, fake MRNs in an obviously fabricated MRN-000### pattern, fake SSNs in the 000-00-0000 pattern, and ran a mixed traffic script of 50 queries against the system. Patient-specific queries sailed past the trust boundary to the general hosted endpoint at the same rate as the general ones. I measured this with one number: the percentage of PHI-referencing calls that reached the non-compliant endpoint. It landed above 90%.

Nobody had to misconfigure anything to produce that number. The architecture never had a branch. A single-endpoint pipeline treats every query the same by default, because nothing inside it was ever asked to tell them apart, and a live pipeline that assembles context per request, instead of a batch job that cleans a document once before anyone queries it, has no natural point where that question gets asked unless someone builds one in.

The pattern

The fix is a classifier that runs before any model call dispatches, not a filter that runs on the response after the fact. I added a classify_sensitivity node to MedAssist's graph that inspects each incoming query with a regex-and-lookup check against the synthetic patient roster, tags it as PHI-bearing or general, and sends PHI-tagged queries to a compliance-scoped clinical endpoint while everything else goes to the general hosted model on a conditional edge. The routing decision happens before the call ever leaves the process. By the time a response comes back, the decision that mattered has already been made.

I set the target for this version at zero false negatives against the seeded roster and a leak percentage of 0% for PHI-referencing queries, with a companion check that general queries still route to the general model instead of getting swept into over-routing. Both held on the seeded data. I haven't run this against live traffic wide enough to confirm those numbers hold outside a controlled roster, and I'd rather name that gap plainly than gloss over it.

The shape generalizes past clinical assistants. Anywhere a system pulls from mixed-sensitivity sources on the fly, a classify-then-route step at the front of the pipeline turns "does this leave the boundary" into a decision made once, deliberately, instead of a property that falls out of whatever the rest of the pipeline happens to do. ComplianceGate, an academic classifier-gated multi-tier LLM router built for regulated industries, landed on the same shape independently. An encoder classifies each query for sensitivity before any decoder inference runs, and its authors measured 99.2% classifier accuracy and near-perfect PII recall at 7 milliseconds of overhead. Their own framing states the mechanism almost the way I'd describe MedAssist: queries route to the appropriate endpoint before any model computation begins, so a data-residency violation becomes structurally impossible for correctly tagged traffic.

Design considerations

The catch sits right there in the word classifier. The whole pattern's safety case rests on one component's recall. A false negative here doesn't get caught anywhere downstream, because nothing downstream is looking. The query that should have been tagged just rides along to the general endpoint like any other query would. I've concentrated the risk of misclassification into exactly one decision, made once, at the front of the pipeline. That risk hasn't disappeared. It's moved to a single address, and a single address is easier to audit than a diffuse one scattered across a pipeline, but it's also a single point that fails completely if it fails at all.

That concentration is a real tradeoff, and I'd rather name it directly than file it away as a footnote. A distributed set of weaker checks scattered through a pipeline catches different failures than one strong check at the front does. The front-loaded classifier catches everything correctly if its recall holds, and misses everything a query happens to fall outside of, whatever patterns it was tuned to recognize. I tuned MedAssist's classifier to the roster I seeded. A patient referenced by a nickname, a diagnosis phrased in a way the regex wasn't built to match, an identifier format nobody thought to test: none of those get a second chance further down the pipeline, because the pipeline was designed so nothing further down would need to look.

The calibration question to ask before building this is how much you're willing to bet on one gate holding. If the honest answer is "not fully," the pre-call classifier still earns its place, but it earns it as the first layer of a stack rather than the only layer. I built MedAssist's fix as a single gate on purpose, to show what the gate alone buys and what it doesn't. What it buys is real: a query correctly identified as PHI-bearing structurally never reaches an endpoint that shouldn't see it. What it doesn't buy is protection against the gate itself getting the answer wrong. That gap is exactly where I'd point anyone deciding whether one classifier is enough for a pipeline, or whether it needs a second, differently shaped check standing behind it.