Operational guide

Local-First AI: Route the Cheap Work Home, Rent the Frontier by Exception

Local-First AI: Route the Cheap Work Home, Rent the Frontier by Exception

Open your last frontier-API invoice next to a stopwatch. Somewhere in that bill is a line for renaming a function, summarizing a file, and tagging a support ticket as a refund request — three tasks a 30B model on a GPU you already own answers in tens of milliseconds, on-premise, with the source never crossing your firewall. You paid metered per-token rates for those calls, and you waited on a network round-trip to a datacenter two regions away to get them back. The model did fine work. The placement was wrong.

That is the practitioner’s actual problem, and it isn’t “is the local model good enough.” It’s a routing problem: every task arrives undifferentiated, and someone — or some layer — has to decide whether it’s the kind of work that needs frontier reasoning or the kind that a local model and a few deterministic tools dispatch for free. Get that decision wrong toward the frontier and you overpay, wait, and leak data off-premise on work that never warranted any of it. Get it wrong toward local and you ship a weak answer on a problem that genuinely needed the big model. This is a how-to for getting it right — and instrumenting it so you can see, week over week, that you are.

The tax you pay for frontier-by-default

Reaching for the frontier model on every task feels like the responsible choice — you’re using the best available reasoning. At any real call volume it’s the expensive habit, and it charges you on four meters at once.

Cost. A remote call priced at a fraction of a cent disappears in a demo and becomes the dominant line item once you multiply it across every CI run, batch row, ticket, and commit hook — the overwhelming majority of which never needed frontier reasoning. You’re paying a metered rate for boilerplate.

Latency. A round-trip to a hosted endpoint stacks a network hop, provider-side queueing, and tail variance on top of the model’s own compute. When the task is in a loop someone is waiting on — an editor finishing a rename, a pipeline gate, an interactive agent step — the p99 of an internet hop is the number that bites, and it’s one you don’t own.

Residency. Every frontier call is your prompt leaving the building — source, customer records, pricing logic — crossing into a region you didn’t pick and through retention you can only read about in a policy. It’s a category of exposure that simply cannot occur when the bytes never leave.

Dependency. Frontier-by-default hard-couples you to one provider’s uptime, rate limits, and deprecations; a pricing revision becomes your incident. When the work is portable across endpoints, that coupling loosens into a choice.

None of these are model-quality problems. They are consequences of sending work to the wrong place — and the fix is a routing layer that knows which place is right for each task.

Sort the work before you route it

You can’t route what you haven’t classified. Before any layer touches a request, every task type in your workload belongs in one of three buckets — and the honest surprise is how much falls outside “needs the frontier.”

Task classTools onlyLocal modelFrontier Search, lookup, lint, type-checkyes Code explanation / summarize a fileyes Refactor, rename, boilerplate scaffoldyes Classification / extraction at volumeyes First-pass test generationyes Architecture / design decisionsyes Cross-file integration, ambiguous reasoningyes Security-sensitive reviewyes
A real workload's task mix — sort yours the same way. The frontier column is short on purpose: it's reserved for open-ended reasoning and wide integration, not the high-volume generation that dominates call counts.

The pattern is the lesson. The tasks that generate the most calls — explanation, refactor, extraction, classification, boilerplate, first drafts — cluster in the local column. The tasks that genuinely need the frontier are real but rare: the ambiguous design call, the change that spans a dozen files and has to stay coherent, the review where being subtly wrong is a security incident. Those deserve the best model you can buy. Everything above them is paying frontier rates for work a local model finishes before the network packet would have left.

Two columns matter as much as the local one. The leftmost — tools only — is the cheapest tier and the one teams skip: a large share of “AI tasks” are answered faster and more reliably by ripgrep, the test runner, or a type-checker, which cost nothing and cannot hallucinate. Why ask a model whether a symbol is used when grep knows? The rightmost column is small by design. If it’s filling up, your taxonomy is too generous to the frontier.

Local-first by default, escalate by exception

With the work sorted, routing becomes a short, ordered loop instead of a per-request guess. The order is the discipline: deterministic tools first, then the local model, then — only on a verified failure — the frontier API.

  1. 01Toolsgrep · tests · types — no model, no token
  2. 02Local modelexplain · refactor · extract · first pass
  3. 03Verifytests pass? schema valid? confidence clears bar?
  4. 04Escalateonly on failure · residency gate first
  5. 05Frontierhard reasoning · wide integration · redacted payload
Attempt cheaper tiers first; the frontier API is reached only when the local result fails a real gate. The dashed step is the residency check no escalation skips — nothing sensitive crosses the boundary to a remote endpoint.

The mechanism that makes “escalate by exception” honest is the verify step. Local-first is not an absolutist stance — forcing a small model onto a problem it fails, just to stay pure, ships wrong answers wearing a local badge. So you don’t trust the local output; you test it. Generated code has to make the test suite pass. Extracted data has to validate against its schema. A classification has to clear a confidence threshold. If the gate passes, you’re done — for free, on-premise, in milliseconds. If it fails, that is the signal to escalate, and only then does the frontier API earn the call. The escalation rate stops being a vague worry and becomes a number on a dashboard: a task that keeps escalating is telling you to fix its verifier or swap in a stronger local model, not to give up and route it remote forever.

Make the placement rule executable, not a wiki page the team forgets. Anything unclassified is local by default; crossing the boundary is a denied-by-default action you earn per payload:

RULE RoutePlacement
WHEN data.class in ["source","pii","pricing","internal"]
THEN tier = "local_only"               # never crosses the boundary

WHEN task.class in ["explain","refactor","extract","classify","draft"]
THEN tier = "local_model"              # default home for high-volume work

WHEN local.verify == "fail"
 AND task.class in ["architecture","integration","security_review"]
THEN tier = "frontier"                 # escalation earned, redacted payload only

DECISION: route(tier)  ELSE deny "unclassified_defaults_local"

Deterministic tools before any model

The tier that pays the most is the one with no model in it. Before a single token is generated — local or remote — ask whether the answer already exists in a tool that is exact, instant, and free.

A startling fraction of what gets framed as “an AI task” is really a search, a test, or a static-analysis question wearing AI’s clothes. Is this symbol referenced anywhere? — ripgrep. Does this change break anything? — the test runner. Is this type sound? — the checker. None of these needs a model; all are deterministic, none hallucinate, and each costs effectively nothing. Routing them to even a local model is wasteful; routing them to the frontier is paying metered rates to be told what git grep would have answered for free.

So the loop starts at tools and only then reaches a model: the cheapest correct answer is a tool’s, the next is a verified local one, and the frontier is the last resort, not the first instinct. Teams that invert this — model-first, tools as fallback — pay the most and wait the longest for answers that were never ambiguous.

Guardrails: trust the gate, not the model

The economics only hold if local output is reliably good enough on the work you route to it. The guardrail isn’t a bigger local model; it’s the verifier in front of it, and a clean fallback when it fails.

Pair every local task class with a gate it has to clear. Code generation is gated on the test suite and a type-check; extraction on schema validation; classification and summarization on a confidence threshold and, where it’s cheap, a sampled spot-check. The gate is what lets you route aggressively to local without crossing your fingers — you’re not betting the local model is right, you’re proving it per call and catching the ones it isn’t.

When the gate fails, fall back deliberately. First retry locally with a tighter prompt or more context — many “failures” are a thin prompt, not a weak model. If it fails again, then escalate to the frontier, and log it. That log is the asset: ground truth for which classes your local model handles cleanly and which keep bouncing remote. A class with a near-zero escalation rate can absorb more traffic; a class that escalates constantly wants a better local model, a sharper verifier, or — honestly — a permanent spot in the frontier column. The boundary isn’t set once; the escalation log keeps moving it to where the evidence says it belongs.

Every routed task emits one auditable, replay-safe record — the unit that proves the economics and tunes the loop:

{
  "eventType": "TaskRouted",
  "taskId": "task-2026-06-22-01184",
  "taskClass": "refactor",
  "dataClass": "source",
  "tier": "local_model",
  "verifier": "tests_passed",
  "escalated": false,
  "modelId": "qwen3-coder-30b",
  "latencyMs": 38,
  "costUsd": 0.0,
  "boundaryCrossed": false,
  "correlationId": "9d2c7a18-3b51-4e0a-bf2c-71a4c0d39e55",
  "decidedAt": "2026-06-22T14:08:31Z"
}

The fields that earn their place: the task class and data class on every record, the verifier that justified the tier, an explicit boundaryCrossed flag so a residency question is a query and not an investigation, and costUsd and latencyMs so the case for the split writes itself from the log.

Residency and provider-independence as routing inputs

Cost and latency are dials you tune. Residency and provider-dependency are different in kind — they’re constraints that belong inside the routing rule, because the moment a payload crosses to a remote endpoint, the decision is irreversible.

Tag data with its boundary class at ingestion and make crossing an explicit, denied-by-default action. Source, customer PII, pricing logic, regulated records — these route to local_only and are never eligible for the frontier tier, full stop. The only payloads allowed across are ones proven to carry nothing sensitive, often after redaction. That flips the burden the right way: you earn the right to escalate a payload by showing it’s safe to send, not by remembering to hold the dangerous ones back. Off-premise crossings of sensitive data are a metric with a target of zero, and the event log makes that target auditable rather than aspirational — a wall the routing layer enforces, not a clause a contract promises.

Provider-independence falls out of the same design almost for free. When the bulk of your work runs through a local model behind an OpenAI-compatible interface, the frontier API becomes one swappable endpoint behind your router, not the substrate your system stands on. A price change, a rate-limit cut, or a deprecation degrades into a config change at the router rather than a re-architecture — leaving you the leverage to negotiate, multi-source, or move, because the dependency is a setting, not a foundation.

Both of these move on the same instruments as cost and latency once the router owns placement:

  • Share of tasks served locally10–30%80–95%Cost / control
  • Blended cost per task1.0× baseline0.1–0.3×Cost
  • In-loop latency (p99)300–1200 ms10–60 msLatency
  • Sensitive payloads sent off-premiseuntracked0 / periodResidency
  • Frontier-provider lock-inhard couplingswappable endpointDependency
Sample planning ranges for executive discussion — calibrate against your own task mix, model sizes, and amortized hardware. Hold the escalation rate, the blended cost delta, the p99, and the off-premise crossing count in every review.

The figures aren’t the point; the coupling is. One routing discipline moves all four at once. You don’t choose between saving money, cutting latency, and keeping data home — the same decision that sends a refactor to your GPU is the decision that doesn’t pay metered rates, doesn’t wait on a hop, and doesn’t put your source in someone else’s region.

Standing up the router

This is buildable now with parts you already have. The reference shape is a transparent layer in front of your model calls — point your tooling’s base URL at a local router, and route from there:

client → ANTHROPIC_BASE_URL → local router/proxy
  ├─ search / test / typecheck   → deterministic tools     (no model)
  ├─ explain / refactor / extract → local coder model       (own GPU)
  ├─ embeddings / retrieval      → local vector store
  └─ verify fails on a hard call → frontier API (escalation)

The local model tier is unexciting in the best way: a 30B-class coder model — qwen3-coder:30b runs here at roughly 100 tokens/second on a single GPU — behind an OpenAI-compatible endpoint, serving the explain/refactor/extract/classify/first-pass column at a marginal cost that rounds to electricity. The router reads the task class and data class, runs the tools tier, attempts local, checks the verifier, and escalates only on failure through the residency gate. Phase it so each step pays before you build the next:

  1. Weeks 1–4 — prove it on one task classPick your highest-volume local-eligible class (refactor or classification). Stand up the local model behind a verifier, route just that class to it, tag its data class. Baseline blended cost, p99, and escalation rate; ship the routing event so every call is logged.
  2. Months 2–6 — widen the local columnAdd task classes as their verifiers prove out, watching the escalation log to set each one's confidence bar. Move the deterministic-tools tier in front of everything. Drive the off-premise crossing count to zero and keep it there.
  3. Ongoing — tune the boundaryWeekly: escalation rate by class, p99, boundary crossings (target zero). Monthly: the cost-mix review and the worst-escalating classes to re-route or re-verify. Quarterly: refresh the task taxonomy and the local-model roster as better small models land.

The compounding is in the instrument, not the model. The first task class pays for the router, the verifier framework, the event contract, and the boundary policy. The second reuses every piece of that and pays only for its own gate. By the fourth or fifth, each new class ships against a layer that already knows how to keep it home, cheap, and inside the wall — and the frontier bill shrinks to the handful of genuinely hard calls that always deserved it.

What we covered

The goal was never to run everything on your own hardware. It’s to stop renting frontier reasoning for work a local model finishes before the request would have cleared your router — and to spend that scarce, expensive endpoint only on the few calls where its reasoning is the thing you’re actually paying for.


References: NIST AI RMF · NIST Privacy Framework · NIST CSF 2.0 · GDPR (data residency / minimization) · CISA Secure by Design · OpenAI-compatible inference APIs.

Let’s talk shop

Let’s build the system your operations actually need.

Custom software, intelligent workflows, and governed AI — designed around how your team really runs, not a template.

Direct founder access · fixed-scope pilots · measurable outcomes