Routing LLM Requests Without Rule Contradictions: The Mycroft Router
Simba, a fellow at Humanitarians AI, describes the third Mycroft sprint: a router that sends each request to a cheap, mid or strong model tier using one policy file. The video reports honest results, including that the router never selects the strong tier.
Simba, a fellow at Humanitarians AI, describes the third sprint of Mycroft, a router that decides for every request whether a cheap model can handle it or whether something stronger is needed. The setup is six kinds of task and three cost tiers, with one rule Simba held to throughout: nothing about how one task type is routed should be able to quietly contradict how another is.
One policy file so rules cannot disagree
All task types and their routing rules live in exactly one file, policy.json, not scattered across the router or left in someone's memory of last sprint's decisions. It covers six task types: sentiment, topic, structured extraction, contradiction detection, summarization and a RAG answer.
For each type, the file records the answer format, the allowed labels, the check used to grade the answer, the tier it starts on, the tier it escalates to, and the input length that triggers promotion. Each type also points to a real file in the repo that shows Mycroft does that kind of work, and a test fails if that file disappears.
A router with no hidden state
The router itself is a pure function with no network call, no clock and no model involved in the decision. It refuses anything pinned or unrecognized. Every other request starts on the tier its policy names and is promoted one tier if the input is long. Length is measured in characters rather than tokens, because token counts depend on which model you are asking. Every decision comes back with a one-sentence reason. A break-even rule that would route on cost against accuracy was left out on purpose, because clever was not the goal this sprint.
Twenty-four synthetic fixtures
The bench folder holds three tools: one checks and freezes the fixtures, one reports how much of the policy they cover, and one is used to label them by hand. There are 24 fixtures, four per task type, two easy and two hard, built around fictional companies and frozen on September 10th with a fingerprint of every file. Simba is plain that they are synthetic, and that no real request data is in the repo yet.
The expected tier on each fixture is Simba's own judgment: the cheapest tier trusted to get the answer right. Labels were made before any model touched them, and the labeling tool never shows what the router would pick.
What the comparison showed
Simba's labels put six fixtures on cheap, 11 on mid and seven on strong. The router put eight on cheap, 16 on mid and none on strong. It agreed with the labels on 9 of 24. Simba stresses this is a prediction checked against human judgment, not a measurement of real performance.
The zero is structural. The only paths to the strong tier are a long input or an escalation, and every fixture labeled strong is a short input with a trap in it. Separately, the free checks catch answers in the wrong format, not answers that are simply wrong. A model that misreads sarcasm can still return an allowed label, so the check passes and nothing escalates. Two fixtures exist to test the checks themselves.
Key takeaways
- Keep all routing rules in one file so two rules cannot quietly disagree.
- Make the router a pure function: no network, no clock, no model.
- Measure length in characters, not tokens, since tokens vary by model.
- Label fixtures by hand before seeing what the router would choose.
- The router agreed with human labels on 9 of 24 fixtures, a starting point rather than a benchmark.
- It never routed to the strong tier because short trap inputs have no path there.
- Format checks catch the wrong shape of answer, not a confidently wrong one.
- All fixtures are synthetic, so results do not describe real traffic yet.
Try it yourself
If you route requests across cost tiers, ask whether your cheap check only catches the wrong shape of answer, or whether it would catch a confidently wrong answer that arrives in the right shape. Write one short trap question for your own task type and see whether your routing would send it anywhere stronger.
Chapters
- 0:00The risk of scattered routing rules across LLM cost tiers
- 0:20Locking task types and escalation thresholds in one policy file
- 0:45Building a pure router function without network calls or clock state
- 1:10Testing 24 synthetic fixtures: Human judgment vs. router predictions
- 1:35The structural gap: Why format checks pass confidently wrong answers
Full transcript(auto-generated, with timestamps)
The risk of scattered routing rules across LLM cost tiers
[0:00]Hi, I am Simba, and this one's about the third Mycroft sprint, the router that decides for every request Mycroft gets, whether a cheap model can handle it or whether it needs something stronger. Six kinds of task, three cost tiers, and one rule I wanted to hold myself to. Nothing about how one task type is routed should be able to quietly
Locking task types and escalation thresholds in one policy file
[0:20]Contradict how another one is. The rule, task types and their routing rules live in exactly one file. Not scattered across the router, the prompts in someone's memory of what was decided last sprint. One file, so two rules can never quietly disagree. policy.json logs all six task types, sentiment, topic, structured extraction, contradiction detection, summarization, and a RAG
Building a pure router function without network calls or clock state
[0:47]Answer. For each one it records the answer format, the allowed labels, the check used to grade it, which tier it starts on, which tier it escalates to, and the input length that triggers that promotion. Each type points at a real file in the repo that proves Mycroft actually does that kind of work. And if that file ever disappears, a test catches it. The router itself is a pure function. No
Testing 24 synthetic fixtures: Human judgment vs. router predictions
[1:10]Network call, no clock, no model involved in deciding. It refuses anything pinned or unrecognized. Otherwise, every request starts on the tier its policy says and gets promoted one tier if the input is long, measured in characters, not tokens, because token counts depend on which model you'd even be asking. Every decision comes back with a one-sentence reason attached. One
The structural gap: Why format checks pass confidently wrong answers
[1:36]Thing is deliberately missing, a break-even rule that would route on cost versus accuracy trade-offs instead of just length. That's being left for later, on purpose. It's the clever version, and clever wasn't the goal this sprint. The bench folder holds three tools. fixtures.py checks the fixtures and freezes them. audit.py reports how much of the policy they actually cover. label.py is what I used to label them by hand. 24 fixtures in total, four per task type, two easy and two hard, all built around fictional companies. They got frozen on September 10th with a fingerprint of every file, so if anyone edits one later without meaning to, a test catches that too. Worth saying plainly, these fixtures are synthetic, not real. There's no real request data in this repo right now. 203 of 217 script samples are placeholders. The one data set that looks like sample data literally declares
Itself fake, and the Klarna mock data file is empty. So, these results can tell you something about how models handle these kinds of tasks. They can't yet tell you what Mycroft's real traffic actually looks like. The expected tier on each fixture is my own judgment. The cheapest tier I'd actually trust to get that answer right. I labeled every fixture before any model touched it, and the labeling tool never shows what the router would have picked. That ordering matters. It's the only way the comparison that's coming isn't just me agreeing with myself. Here's what it showed. My labels put six fixtures on cheap, 11 on mid, seven on strong. The router put eight on cheap, 16 on mid, and zero, not one, on strong. Of 24 fixtures, the router agreed with me on nine, and I want to be careful about that number. These are predictions being
Checked against a human's judgment. It isn't a measurement of real performance yet. That zero isn't a coincidence, it's structural. The simple router has exactly two ways to reach the strong tier, a long input or an escalation. There's no path from a short input straight to strong, and every single fixture I labeled as strong is a short input with a trap in it, a sentence that reads simply, but needs real reasoning to get right. As written, the router literally cannot agree with me on any of those seven. That's not a bug to patch quietly. It's the main gap this design has right now. Here's the risk underneath all of this. The free checks catch answers in the wrong format, not answers that are simply wrong. A model that completely misreads sarcasm can still return a label the policy allows. The check passes, and nothing escalates. The router has
No way to know the answer was bad if it was shaped correctly. That's the main design risk heading into the next two sprints. A couple of fixtures exist specifically to test the checks, not the models. In some dash-004, a genuinely correct unit conversion fails the check anyway. In extract-004, a flatly made-up answer passes it. Both are there on purpose. They're the honest edge cases the grading logic itself needs to survive. Two problems got fixed along the way. The labeling tool originally didn't show what each task was actually asking, so it now prints the task itself before every label. And I added a way to redo specific ones. And my very first labeling pass put everything on the cheap tier, so I relabeled those 10 using the same two questions every time. Is there a trap? And is there a reasoning step? Two more problems got found,
But not fixed here. Someone else's merge on August 30th deleted two entries from the run log, and the repo's conformance check can't find Python on Windows. Both are flagged as their own separate tasks. So, one file that can't disagree with itself, a router with no hidden state, and 24 fixtures frozen so nobody can quietly change the answer key. It agrees with human judgment on nine of 24, a starting point, not a benchmark. What's not yet done? Escalation only looks at length. The free checks only catch format, and every fixture behind these numbers is still synthetic, not real traffic. Your turn. If you're routing requests across cost tiers of your own, does your cheap check only catch the wrong shape of answer? Or would it actually catch a confidently wrong answer that happens to come back in the right shape? One file, no contradictions. Simba, for humanitarian's AI.
More from Mycroft Financial AI
3:48Mycroft: An Open Source Educational Experiment Using AI to Invest in AI | Building to Learn
4:18Mycroft: From Reading the Past to Forecasting the Future
5:31Simbaremuteuro explains: Single-Retry Escalation and Cost-Ladder Dynamics
2:04Mycroft: Starting the Lineage Agent
3:15Why "Verified" Status is Just a Claim Until Gates Say No | Mycroft Audit
1:52