AI Tokenomics · Part 3Stop Sending Every Agent Turn to the Frontier Model
11 min read

Stop Sending Every Agent Turn to the Frontier Model

Most of the 40 turns read a file or run a test, yet all are billed at Frontier prices.

Each square is one turn of the same 40-turn run, shaded by the tier that served it. Watch the plan, the signature change, and the 6 repairs: All Workhorse gets stuck on the repairs, and the router sends only those 8 turns to the Frontier tier, for $1.14 instead of $2.37.Across 40 turns, labels mark the plan, a signature change, and 6 repairs. A cross marks All Frontier, every turn filled: $2.37. A cross marks All Workhorse, every turn hollow: $0.83, stuck on the repair turns drawn in red. A check marks the Router, which fills only the plan, signature change, and repair turns: $1.14. A legend gives Frontier tier turn $0.0592 and Workhorse tier turn $0.0207. plan signature change 6 repairs All Frontier $2.37 All Workhorse $0.83 stuck Router $1.14 Frontier tier turn $0.0592 Workhorse tier turn $0.0207 Each square is one turn of the same 40-turn run, shaded by the tier that served it. Watch the plan, the signature change, and the 6 repairs: All Workhorse gets stuck on the repairs, and the router sends only those 8 turns to the Frontier tier, for $1.14 instead of $2.37.Across 40 turns, labels mark the plan, a signature change, and 6 repairs. A cross marks All Frontier, every turn filled: $2.37. A cross marks All Workhorse, every turn hollow: $0.83, stuck on the repair turns drawn in red. A check marks the Router, which fills only the plan, signature change, and repair turns: $1.14. A legend gives Frontier tier turn $0.0592 and Workhorse tier turn $0.0207. plan signature change 6 repairs All Frontier $2.37 All Workhorse $0.83 stuck Router $1.14 Frontier tier turn $0.0592 Workhorse tier turn $0.0207
Figure 1. Each square is one turn of the same 40-turn run, shaded by the tier that served it. Watch the plan, the signature change, and the 6 repairs: All Workhorse gets stuck on the repairs, and the router sends only those 8 turns to the Frontier tier, for $1.14 instead of $2.37.

I default every agent turn to a Workhorse tier model, escalate to the Frontier tier only on four mechanical signals, and price a 40-turn trajectory at list prices to show what that saves.

Listen to the audio overview(3:15)Fenrir Studio Voice
0:00
3:15

An Agent A program that calls a language model in a loop and lets it use tools, such as search or code. that lands a PR Pull request. A proposed code change that others review before it joins the main code. takes between 30 and 60 Turn One step of an agent. It reads the current state, calls the model once, and acts on the reply. to do it. One of those turns is a plan. A handful are edits that matter. The rest are routine: read a file, run a test, fix an import, and run the test again. Now say every one of those turns goes to a Frontier model The largest, most capable and most expensive tier of models. I keep it for the hardest reasoning. tier model, such as GPT-6 Astra, Claude Fable 5.1 or Gemini 3.1 Pro. Then the bill charges frontier prices for grep, a plain text search.

This is the third part of my AI Tokenomics series. Part one covered the eleven rules and the runtime guards around them. Part two covered the spend cap that does not save you from a loop. This part covers the routing decision inside the loop: which tier gets which turn. A mechanical signal decides that, one that my code can check. Then I show what routing does to the cost of a Trajectory One full agent run on one task, from its first turn to its last. at published List price The public price per token that a vendor publishes, before any discount or contract deal..

The pattern: I send every turn to a Workhorse model A fast, cheaper model tier. It handles routine work, such as reading files and running tests. tier model by default, such as Gemini 3.8 Flash, Claude Haiku 4.5 or GPT-5.6 Luna. I move a turn up to the Frontier tier only on four signals that the code can observe. I cap these Escalation Moving one turn up to the more expensive model tier, because a rule says the cheap one is not enough., and I log every decision with its reason. This is a routing pattern, not a vendor comparison. The arithmetic below works the same way for every pair of models.
PART 01

Anatomy of a 40-turn refactor trajectory

Before I can route turns, I need to know what turns look like. The table shows the shape of a typical refactor run. It is a sketch, not a measurement. In the run, an agent changes a loader's error handling across a small module, and a Pre-commit gate A check that runs the tests on each change. Only exit code 0 lets the change through. runs the tests on each change. The shares are rough. The point is the mix, and most of the mix is cheap, repetitive work.

Turn typeWhat happensRough shareTier
PlanRead the task, pick files, split the work into steps1 turnFrontier
Read and navigateOpen a file, list a directory, follow an import30%Workhorse
EditApply a small diff to one file25%Workhorse, unless the diff touches a public export
Run gateRun the test or lint command, read the exit code20%Workhorse
RepairFix whatever the gate complained about15%Workhorse for the first two tries, then Frontier
SummarizeWrite the PR description1 turnWorkhorse

Two things stand out. First, the plan turn is the only one where the model must hold the whole problem in view, and it happens once. Second, the repair loop is where cheap models get stuck. An expensive model earns its price there, but only after the cheap one has failed in a way the gate can see. Everything else is bookkeeping, and bookkeeping does not need a frontier model.

02

The router

The router is one function, and it uses nothing beyond the Python standard library. It takes a turn and the state of the trajectory, and it returns a tier. The default is the Workhorse tier. The router escalates a turn to the Frontier tier on four signals:

  • The turn is a plan or decompose step, where the model splits the task into steps.
  • The pre-commit gate returned Exit code The number a program returns when it ends. 0 means success; any other number, such as 1, means failure. twice in a row for the same file.
  • The diff adds, removes or changes a Public signature The name, parameters and return type of a function or class that other code calls.. The check compares the AST Abstract syntax tree. The structure a parser builds from source code, such as functions, classes and signatures. of the source before and after the edit. It is not a Regex Regular expression. A short pattern that matches text, such as every run of digits in a line. on the diff.
  • The Workhorse tier output failed Schema A formal description of the shape data must have, such as which fields exist and their types. validation.

I cap escalations per trajectory. A file that fails the gate twice in a row is pinned to the Frontier tier for the rest of the run. Pinned means the router keeps the file there, so it does not send the file back down after one good turn. The cap still outranks the pin. Once the cap runs out, a pinned file drops back to the Workhorse tier like every other turn.

Timing matters too. The router routes plan turns and pinned files before any draft exists. It routes every other turn once the Workhorse tier has written its draft, because the signature and schema checks judge that draft. When the router escalates such a turn, the Frontier tier runs it again. Every decision goes into a log with its reason. A router I cannot audit is a router I will not trust when the bill arrives.

router.py
Python
#!/usr/bin/env python3
"""route_turn: pick a tier for one agent turn from mechanical signals only.

Default is the Workhorse tier. The router escalates to the Frontier tier when
  (a) the turn is a plan or decompose step,
  (b) the pre-commit gate returned exit 1 twice in a row for the same file,
  (c) the diff changes a public signature (AST check, not a regex),
  (d) the Workhorse output failed schema validation.
Signal (b) pins the file: later edits and repairs on it go to the Frontier tier.
Plan turns and pinned files route before any draft exists. Other turns route once
the Workhorse draft exists, since (c) and (d) judge it. Escalations re-run on Frontier.
Escalations are capped per trajectory, and the cap wins over a pinned file.
A pinned trajectory sends every turn to the Frontier tier, outside the cap.
Every decision is logged with its reason. Standard library only.
"""
import ast
import json
from dataclasses import dataclass, field

WORKHORSE = "workhorse"
FRONTIER = "frontier"
ESCALATION_CAP = 8        # per trajectory; 20 percent of a 40-turn run
FAILURES_BEFORE_PIN = 2   # after this many gate failures a file stays on Frontier


@dataclass
class State:
    escalations: int = 0
    gate_failures: dict = field(default_factory=dict)   # path -> consecutive exit-1 count
    pinned: set = field(default_factory=set)             # paths pinned to the Frontier tier
    log: list = field(default_factory=list)              # one dict per routed turn
    pinned_trajectory: bool = False                      # every turn on Frontier, no cap


def _public(name: str) -> bool:
    return not name.startswith("_") or (name.startswith("__") and name.endswith("__"))


def public_signatures(source: str) -> dict:
    """Name -> signature for public defs and classes, methods included as Class.method."""
    out = {}

    def walk(body, prefix):
        for node in body:
            if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and _public(node.name):
                out[prefix + node.name] = (type(node).__name__, ast.dump(node.args),
                                           node.returns and ast.dump(node.returns))
            elif isinstance(node, ast.ClassDef) and _public(node.name):
                out[prefix + node.name] = "class"
                walk(node.body, prefix + node.name + ".")

    walk(ast.parse(source).body, "")
    return out


def export_change(turn: dict) -> str:
    """Why a Python edit or repair touches a public signature, or '' if it does not."""
    if turn["kind"] not in ("edit", "repair") or turn.get("before") is None \
            or not str(turn.get("path")).endswith(".py"):
        return ""
    try:
        changed = public_signatures(turn["before"]) != public_signatures(turn.get("after") or "")
    except SyntaxError:
        return "python source does not parse"
    return "diff touches public export" if changed else ""


def route_turn(turn: dict, state: State) -> str:
    """turn = {"n", "kind", "path"?, "before"?, "after"?, "schema_failed"?}."""
    kind, path = turn["kind"], turn.get("path")
    tier, reason = WORKHORSE, "default"
    export = export_change(turn)

    if state.pinned_trajectory:
        tier, reason = FRONTIER, "pinned trajectory"
    elif kind == "plan":
        tier, reason = FRONTIER, "plan step"
    elif kind in ("edit", "repair") and path in state.pinned:
        tier, reason = FRONTIER, "file pinned after repeated repairs"
    elif export:
        tier, reason = FRONTIER, export
    elif turn.get("schema_failed"):
        tier, reason = FRONTIER, "workhorse output failed schema"

    if tier == FRONTIER and not state.pinned_trajectory:
        if state.escalations >= ESCALATION_CAP:
            tier, reason = WORKHORSE, "cap %d reached, stayed on workhorse" % ESCALATION_CAP
        else:
            state.escalations += 1

    state.log.append({"turn": turn["n"], "kind": kind, "tier": tier, "reason": reason})
    return tier


def observe_gate(turn: dict, state: State, exit_code: int) -> None:
    """Feed a gate result back; a second failure in a row pins the file."""
    path = turn.get("path")
    if path is None:          # a whole-suite run names no file to pin
        return
    if exit_code == 0:
        state.gate_failures.pop(path, None)
        return
    state.gate_failures[path] = state.gate_failures.get(path, 0) + 1
    if state.gate_failures[path] >= FAILURES_BEFORE_PIN:
        state.pinned.add(path)


def dump_log(state: State) -> str:
    return "\n".join(json.dumps(entry, separators=(",", ":")) for entry in state.log)

The decision log holds one JSON A common text format for structured data. A parser needs the whole object; half of one fails. object per line. That format is boring on purpose. I can grep it, and I can load it into a spreadsheet. After the run, it tells me why a given turn cost what it cost.

decisions.jsonl
JSON Lines
{"turn":1,"kind":"plan","tier":"frontier","reason":"plan step"}
{"turn":2,"kind":"read","tier":"workhorse","reason":"default"}
{"turn":18,"kind":"edit","tier":"frontier","reason":"diff touches public export"}
{"turn":32,"kind":"repair","tier":"frontier","reason":"file pinned after repeated repairs"}
{"turn":38,"kind":"edit","tier":"workhorse","reason":"cap 8 reached, stayed on workhorse"}

The video below runs the router over a scripted 40-turn trajectory. It prints every decision, then prices the run three ways. The trajectory is a Fixture A fixed, scripted input for a test or demo. It plays out the same way every time., not a recording of a live agent. What the video shows is the router doing what the code says it does.

stop-sending-every-agent-turn-to-the-frontier-model-demoScreen recording
The router over a scripted 40-turn trajectory, then the three totals at list prices
PART 02

Three numbers at list prices

I priced this at published list prices. It is not a report from a live run. The prices are the vendors' list prices as of the catalog's date, 27 September 2026, and they change. The lab at the end of the post computes everything again from your own inputs. So treat the numbers here as the shape of the answer, not the answer itself.

These are the lab's defaults. Each turn sends 32,000 prompt Token A small piece of text, often part of a word. Models read, write and bill text by the token. and gets 2,000 output tokens back. The hit rate in the KV cache The attention keys and values a model saved for tokens it already read. Reusing them skips recomputing that prefix. is 50%, so half of the prompt is already cached. A trajectory has 40 turns, and the router escalates 20% of them. The cost of one turn has three parts. The half of the prompt that misses the cache bills at the uncached input price. The half that hits bills at the cheaper cached input price. The output tokens bill at the output price. Figure 2 adds up those three parts for one turn, then for the whole run.

One turn: 32,000 tokens in, 2,000 out

Step 1: one turn sends a 32,000-token prompt. 16,000 tokens miss the cache and bill at the full input price. 16,000 tokens hit the cache and bill at the cached input price. The 2,000 output tokens bill at the output price. Prompt: 32,000 tokens Output 16,000 uncached 16,000 cached 2,000 output full input price cached input price output price

On the Frontier tier: $0.0592 a turn

Step 2: on Gemini 3.1 Pro the turn costs $0.0320 uncached plus $0.0032 cached plus $0.0240 output, $0.0592 in all. 40 turns at that price cost $2.37. Frontier tier Gemini 3.1 Pro $0.0592 $0.0320 uncached + $0.0032 cached + $0.0240 output 40 turns × $0.0592 = $2.37

On the Workhorse tier: 2.9x cheaper

Step 3: on Gemini 3.8 Flash the same turn costs $0.0120 uncached plus $0.0012 cached plus $0.0075 output, $0.0207 in all, 2.9x cheaper than the Frontier bar above it. 40 turns cost $0.83. Frontier $0.0592 Workhorse tier Gemini 3.8 Flash 2.9x cheaper a turn $0.0207 $0.0120 uncached + $0.0012 cached + $0.0075 output 40 turns × $0.0207 = $0.83

The router: 8 Frontier turns, 32 Workhorse

Step 4: the router run from Figure 1. The plan, the signature change and 6 repairs are 8 Frontier turns at $0.0592, $0.4736. The other 32 are Workhorse turns at $0.0207, $0.6624. The run costs $1.14, against $2.37 all Frontier. plan signature change 6 repairs Router 8 Frontier turns × $0.0592 = $0.4736 32 Workhorse turns × $0.0207 = $0.6624 $1.14 All Frontier: $2.37
1 of 4
Figure 2. Four steps: one turn of the default run, then the whole run, priced at list prices with Gemini 3.1 Pro as the Frontier tier and Gemini 3.8 Flash as the Workhorse tier. Each turn bills three parts: the uncached half of the prompt, the cached half, and the output. On the Frontier tier, 40 turns cost $2.37. The router of Figure 1 sends 8 of the 40 turns to the Frontier tier, for $1.14.

Take Gemini 3.1 Pro as the Frontier tier. It is still a Preview model, gemini-3.1-pro-preview. Take Gemini 3.8 Flash as the Workhorse tier. A Frontier tier turn costs $0.0592, and a Workhorse tier turn costs $0.0207. From there:

PolicyPer turnPer 40-turn trajectoryPer 1,000 trajectories
All Frontier$0.0592$2.37$2,368
All Workhorse$0.0207$0.83$828
Router, 20% escalation$0.0284$1.14$1,136

The router costs 52% less than all-Frontier, and it still sends one turn in five to the expensive tier. It costs about $300 more per thousand trajectories than all-Workhorse. That gap is what the escalations buy: a frontier model on the plan, on the public-signature edit, and on the repair loop that the cheap model could not close. One detail: the table prices an escalated turn at the Frontier rate only. A turn that the signature or schema check escalated also paid for the Workhorse draft it rejected. In this run that is one extra Workhorse turn, about $0.02.

The Gemini numbers carry an expiry date. Google lists the Flash rates as promotional through 31 December 2026. From 1 January 2027 they double, to $1.50 input, $0.15 cached and $7.50 output per million tokens. A Workhorse turn then costs $0.0414. The price ratio between the two tiers falls from 2.9x to 1.4x. The same routed run then costs $1.80 against $2.37 for all-Frontier, a saving of 24%.

Next, the same arithmetic with the other two pairs. What changes is the price ratio between the tiers. The ratio is the whole story. So for each pair I give the ratio and the three trajectory costs, and nothing else.

PairFrontier to Workhorse ratioAll FrontierAll WorkhorseRouter, 20%
Gemini 3.1 Pro / Gemini 3.8 Flash2.9x$2.37$0.83$1.14
Claude Fable 5.1 / Claude Haiku 4.59.6x$10.56$1.10$3.00
GPT-6 Astra / GPT-5.6 Luna46.6x$11.04$0.24$2.40

Read down the last column, not across the rows. The wider the price gap between a vendor's two tiers, the more a 20% escalation rate costs compared with all-Workhorse. The same wide gap makes it save more compared with all-Frontier. At a 2.9x ratio the router saves 52%. At a 46.6x ratio it saves 78%, and the 20% of turns that escalate make up 92% of the routed bill. That last number is why I keep the escalation rate honest. I pay for every point of escalation that no signal justifies, and I pay at the wide end of the ratio.

Per 1,000 trajectories: $2,368 against $1,136 for the Gemini pair, $10,560 against $2,995 for the Anthropic pair, $11,040 against $2,397 for the OpenAI pair. Change the model pair, the cache hit rate or the escalation rate in the lab, and these numbers move together.
04

Where it breaks

Three failure modes follow. The router or the lab already has a fix for each one.

Cascade thrash. The Workhorse tier fails the gate, so the router escalates. The Frontier tier fixes the file. For the next edit on the same file, the router drops back to the Workhorse tier, and the gate fails again. With no cap, this loop pays for both tiers on every cycle. So the router caps escalations per trajectory. It also pins a file to the Frontier tier after the file's second gate failure in a row. The third attempt on a hard file goes to the Frontier tier and stays there until the cap runs out.

KV-cache prefix loss on a tier switch. The cached input price assumes that the start of the prompt, its prefix, is already stored on the model that serves the turn. After a tier switch, the other model has never seen that prefix. So the first turn after a switch is a cold prompt, billed at that tier's uncached rate. At the lab defaults, a cold Frontier tier turn on Gemini 3.1 Pro costs $0.0880 instead of $0.0592. The lab's cold-cache preset sets the hit rate to zero, so you can see the worst case for a router that switches often. If your escalations come in clusters, the cost sits between the warm and cold numbers. If they alternate turn by turn, you are close to cold.

Trajectories that should never be routed. Some runs deserve the Frontier tier from the first turn. One is greenfield design: new code, with no gate to fail yet. Another is a security-sensitive change, where the failure is a wrong edit that passes the tests. A third is a refactor across several repositories, where the plan must hold across contexts the Workhorse tier will not see. For these I pin the whole trajectory, and the router has a mode for that. Set pinned_trajectory on the state, and every turn returns Frontier with the reason "pinned trajectory", outside the cap.

The rule under all three: the trigger is a mechanical signal that the code can observe, such as exit codes, AST diffs, schema validators and turn types. The trigger is never the model's own confidence. Ask a model whether it needs a bigger model, and it answers in whichever direction its training rewarded. I cannot audit that.

Latency moves the same way as cost. A Workhorse tier turn returns faster, in wall-clock time, than a Frontier tier turn on the same prompt. How much faster depends on your region, your prompt size and the hour. Measure it on your own traffic and enter the numbers into the lab, rather than taking a figure from me.

PART 03

Lab: your prices, your trajectory

The lab below computes the three numbers again from your own settings. It opens on the model pair, the escalation rate and the cache hit rate. Under More settings you can also change the prompt size, the output size and the turn count, pick each tier's model yourself, and enter your own latency. It uses the catalog list prices for the models you pick. Four presets match the sections above. all-frontier is the baseline. router-20 is the run I walk through above. router-5 is what a tighter set of signals buys. cold-cache is the worst case after a tier switch, with the hit rate at zero.

Interactive Lab · tokenomics-arbitrageFrontier vs Workhorse Routing Cost Engine

Compare three ways to run one agent trajectory at published per-token list prices: every turn on the Frontier tier, every turn on the Workhorse tier, or a router that keeps the Workhorse tier by default and escalates a chosen share of turns. Pick one of three same-vendor model pairs, then move the escalation share and the KV-cache hit rate. More settings holds one model per tier, custom rates, token sizes, turn count and latency.

One reading of the same idea comes from the research side. Pro-Router: Token-Aware Progressive Model Routing (Aug 2026) by Gui and co-authors frames routing as a progressive decision made with the token budget in view, rather than a one-shot classifier. That is the same instinct as my repair-count and cap rules. Their router learns the signal; mine hard-codes it. For a pre-commit loop I would rather have the hard-coded one, because I can read it.

06

What to change this week

  • Flip the default. Route every turn to the Workhorse tier, and make the plan turn the only escalation. Watch the gate pass rate for a day before you add the other three signals.
  • Write the decision log before you write the router. Log one JSON line per turn, with the tier and the reason. If the reason field is ever empty or says "model asked for it", that is the bug.
  • Put the escalation cap in the same config file as your spend cap. They are the same control at two different layers. The spend cap is the one that fires when the escalation cap is set wrong.

First published 14 Sep 2026 · last revised 9 Oct 2026 · 12 revisions

Hand this to your agent

You

Read this essay by Ibrahim Ulukaya, https://ulukaya.dev/posts/stop-sending-every-agent-turn-to-the-frontier-model, and look at my project. Which agent turns go to the Frontier model when a Workhorse tier default could handle them, and what mechanical signals should trigger escalation? Name the file and line for each finding.

Cited by
  1. Instrument: Frontier vs Workhorse Routing Cost Engine

    Compare three ways to run one agent trajectory at published per-token list prices: every turn on the Frontier tier, every turn on the Workhorse tier, or a router that keeps the Workhorse tier by default and escalates a chosen share of turns. Pick one of three same-vendor model pairs, then move the escalation share and the KV-cache hit rate. More settings holds one model per tier, custom rates, token sizes, turn count and latency.