# The Intelligent Model Router — adapts and adjusts in real time

**What it is:** a router that sits in front of every AI call and, *in real time*, sends each task to the model (or panel of models) most likely to produce the best result for the least cost — and **gets smarter every time**, because it records what actually won and biases future routing toward it. It's not a static `if code → model A` table. It's a feedback loop: route → observe the outcome → update → route better next time.

**Why it beats a fixed default or a one-shot router:** task difficulty, model strengths, latency, and cost all vary per request, and model quality drifts as vendors ship updates. A static router is wrong the day a model changes. An *adaptive* router measures reality and follows it.

---

## The two-layer decision

Every task passes through two layers:

### Layer 1 — REAL-TIME CLASSIFY (per call, instant)
A fast, cheap model (or a regex pre-filter) reads the task and emits:
```
{
  "kind":       "code | debug | strategy | writing | analysis | research | mechanical | chat",
  "difficulty": "trivial | moderate | hard",
  "needs":      ["grounding"?, "long-context"?, "tool-use"?, "math"?],
  "stakes":     "low | high"
}
```

### Layer 2 — ADAPTIVE ROUTE (uses memory)
Given that signature, pick the route by combining **need** with **learned performance**:
```
route(sig):
  candidates = models that satisfy sig.needs        # capability filter first
  if sig.difficulty == "trivial" or sig.kind in {mechanical, chat}:
      return cheapest(candidates)                    # never overspend on easy work
  # otherwise rank candidates by a learned score, then decide solo vs panel:
  ranked = candidates sorted by score(model, sig.kind)   # see "the score" below
  if sig.difficulty == "hard" or sig.stakes == "high":
      return PANEL(top 2-4 ranked)                   # deliberate + cross-verify + judge
  return SOLO(ranked[0])                              # one strong model, fast
```

---

## The score (this is the "adapts in real time" part)

Keep a small running table: for each `(model, kind)`, store `n` (uses), `wins` (times it produced the chosen/best answer), and `avg_quality` (0-10 from your judge or your own rating).

```
score(model, kind):
  s = win_rate(model, kind) * 0.6  +  avg_quality(model, kind)/10 * 0.4
  # exploration bonus so a new/under-tried model still gets sampled (UCB-style):
  s += sqrt( 2 * ln(total_uses_for_kind) / max(1, n(model, kind)) ) * EXPLORE   # EXPLORE ~0.15
  # tie-breakers you care about:
  s -= cost_per_call(model)  * COST_WEIGHT
  s -= p95_latency(model)    * LATENCY_WEIGHT
  return s
```

- **Exploit**: most traffic goes to the model with the best track record for that kind.
- **Explore**: the `sqrt(...)` term guarantees you keep sampling less-tried models, so the router *discovers* a newly-improved model instead of being stuck on yesterday's winner.
- **Cost / latency**: bias toward cheap+fast when quality is close.

## The feedback loop (close it, or it's not adaptive)

After every routed call:
```
observe(model, kind, quality):
  n += 1
  if quality >= win_threshold: wins += 1
  avg_quality = running_mean(avg_quality, quality)
  persist()       # write the table to disk so it survives restarts
```
Where does `quality` come from? Best → worst:
1. A **judge model** scores the output 0-10 (blind, on a rubric).
2. A **downstream signal** (tests passed, the email got a reply, the user kept the answer).
3. Your **thumbs up/down**.
Any of these works; even a coarse signal makes the router converge.

## Calibrate it once a week (the offline benchmark)
Run a fixed set of representative tasks through every candidate model + the panel, judge them blind A/B, and seed/refresh the table. This bootstraps the router so it's smart on day one instead of learning from scratch, and it catches model drift (a vendor update that made a model better — or worse).

---

## Cost & safety rails (so it never blows up)
- **Capability filter first**: never route a task to a model that can't do it (no-vision model on a screenshot), regardless of score.
- **Cheap floor**: trivial/mechanical/chat always go to the cheapest capable model — the table never overrides this.
- **Per-model timeout**: a slow/cold model drops from a panel instead of stalling it.
- **Budget cap**: if a call is metered, the router respects a hard ceiling and downgrades to a free/cheaper model when near it.
- **Graceful degrade**: any model error → fall to the next-ranked; the router never throws.

## Why this is the endgame
A fixed router is a guess frozen in time. This one is a guess that *corrects itself against reality* every call — exploiting what works, exploring what might, and re-calibrating as the models themselves change. Point it at any roster of models (any vendors, any sizes) and it converges on the best cost-adjusted quality for *your* actual workload.

---
*The Intelligent Model Router — route by what wins, not by what you assumed. Measure, adapt, repeat.*
