Multi-Model Routing: Cheap Draft Models Plus Expensive Verifiers

Multi-Model Routing: Cheap Draft Models Plus Expensive Verifiers

Not every agent turn deserves a frontier model. Route boilerplate and mechanical patches to cheap draft models; reserve expensive verifiers for architecture review, security-sensitive diffs, and flaky-test RCA. You cut spend without lowering the bar on judgments that protect production.

⚡ TL;DR: Classify tasks (draft vs verify); run Haiku/Sonnet-class (or local) drafts; escalate to frontier only on risk signals (IAM, auth, migrations, public API, flaky flakes). Cache shared system prompts. Measure $ / merged PR and revert rate. Companions: prompt caching, code review bots, Cursor rules.

Task taxonomy

export type RouteClass = "draft" | "verify" | "forbid_auto";

export function classify(job: {
  files: string[];
  labels: string[];
  locDelta: number;
}): RouteClass {
  const sensitive = job.files.some(f =>
    /(^|\/)(iam|auth|migration|infra)\//i.test(f) ||
    /\.github\/workflows\//.test(f)
  );
  if (sensitive || job.labels.includes("security")) return "verify";
  if (job.labels.includes("public-api")) return "verify";
  if (job.locDelta > 400) return "verify";
  if (job.files.some(f => f.includes("break-glass"))) return "forbid_auto";
  return "draft";
}

✅ Explicit classifier you can unit test.
❌ “Let the agent decide if it needs a bigger model.”

Two-pass pipeline

DRAFT_MODEL = "anthropic.claude-haiku-4-5-20251001-v1:0"
VERIFY_MODEL = "anthropic.claude-sonnet-4-20250514-v1:0"

def handle(job):
    route = classify(job)
    if route == "forbid_auto":
        return needs_human(job)
    draft = converse(DRAFT_MODEL, build_draft_prompt(job), tool_choice="auto")
    patch = execute_tools(draft)
    if route == "draft" and not risk_signals(patch):
        return patch  # cheap path
    # Expensive verifier: read-only review, may reject
    verdict = converse(VERIFY_MODEL, build_verify_prompt(job, patch), tool_choice={"auto": {}})
    if verdict_rejects(verdict):
        return revise_or_human(job, verdict)
    return patch

Verifier system prompt should focus on: authz bugs, IAM wildcards, missing tests, API breakage — not rewriting style the draft already handled.

Risk signals that force escalation

const RISK = [
  /Resource\s*:\s*"\*"/,
  /Action\s*:\s*"\*"/,
  /ignore.*(auth|tenant)/i,
  /password|secret|AKIA/,
  /DROP TABLE|prisma\s+migrate\s+reset/i,
];

export function riskSignals(diff: string): boolean {
  return RISK.some(r => r.test(diff));
}

Wire the same patterns into AI Code Review Bots so human PRs get equivalent scrutiny.

Economics (illustrative)

# Planning examples — not a vendor quote
# 80% of turns on draft model @ ~0.2–0.4× frontier price
# 20% verify @ full price
# Blended often lands ~0.35–0.55× all-frontier with similar revert rates
# Prompt caching on shared rules/schemas amplifies savings further

Track: tokens by model id, escalations / day, post-merge incidents tagged agent-authored.

Closing checklist

✅ Dos
– ✅ Classify draft vs verify with tested rules
– ✅ Escalate on IAM/auth/migrations/public API/large diffs
– ✅ Keep verifiers read-heavy (review), drafts write-heavy
– ✅ Log model id per turn for cost attribution
– ✅ Combine with prompt caching on shared system text

❌ Don’ts
– ❌ Don’t run security diffs only on the cheap model
– ❌ Don’t hide escalations from developers
– ❌ Don’t use verifier temperature > 0 for CI gates
– ❌ Don’t skip human review on forbid_auto paths
– ❌ Don’t optimize cost without watching revert/incident rates

Related reading

Last updated on September 11, 2026


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply