Are you TokenMaxxing hard enough? Find out in less than a minute →

Some of Your Agent's Tasks Don't Need an Agent

Some of Your Agent's Tasks Don't Need an Agent

Look at a trace of your agent doing routine work and you will find stretches where nothing is actually being decided. The agent calls get_user, then get_orders, then get_invoice, formats the three results into a report, and writes it to disk. Same three calls, same order, same formatting, every single run. There is no branch that depends on the model’s judgment. There is no ambiguity for reasoning to resolve. The model is being invoked to walk a fixed path, and you are paying input and output tokens on every step of that walk.

That is the cheapest kind of waste to describe and the easiest to overlook, because from inside the loop it feels like the agent is working. It is working. It is just doing a job that a plain function could do without a model at all.

The part of the loop that never branches

An agent earns its cost when the next action depends on something the model has to figure out. Read this error and decide whether to retry or escalate. Look at these search results and pick the relevant one. Interpret an ambiguous instruction. Those are real reasoning steps, and a model is the right tool.

A lot of agent runtime is not that. It is glue. The prompt says “pull the account summary,” and pulling the account summary means the same three API calls in the same order followed by the same string formatting, no matter what the account contains. The model reads the instruction, emits the tool calls, waits, reads the results, emits the next tool call, and finally emits the formatted output. Every one of those turns is a paid round trip to reproduce a sequence that was fixed before the run started.

Here is the shape, before and after:

before (agent loop, paid every run):
  turn 1  model → tool_call get_user(id)
  turn 2  model → tool_call get_orders(id)
  turn 3  model → tool_call get_invoice(id)
  turn 4  model → format + write report.md

after (deterministic script, $0 per run):
  report = format(get_user(id), get_orders(id), get_invoice(id))
  write("report.md", report)

The right column costs nothing per run and does not drift. The left column costs four model turns and can, on a bad day, decide to format the report differently or skip a call. You are paying a premium for nondeterminism you did not want in that stretch of the workflow.

What tj optimize script looks for

The reason this hides is that you cannot eyeball it. You would have to read hundreds of traces and notice, by hand, that a particular run of tool calls repeats with the same signature and never forks. That is a clustering problem, so we treat it as one.

Install and point it at your captured runs:

pipx install tokenjam   # or: pip install tokenjam
tj onboard
tj optimize script

The analyzer walks your recorded traces and builds a signature for each contiguous run of tool calls: which tools, in what order, with what argument shape. Then it clusters those signatures across all your runs. Two things make a cluster a candidate:

Near-identical across runs: the signatures in the cluster agree with each other roughly 90% or more of the time. The sequence is stable, not incidental.

Zero-branch: no step in the sequence depends on model output that varies. The path does not fork based on what the model decided; it forks, at most, on plain input values a script could switch on itself.

A cluster that clears both bars is one where the model is being paid to be a very expensive for loop. The report lists those clusters with how often each fires and how many model turns each one costs you, so the ones worth extracting sort to the top.

Why this lever is different from the others

Most cost work makes a model call cheaper. Route the call to a smaller model. Compress the prompt so the call carries fewer input tokens. Cache the stable prefix so you pay the read rate instead of full price. All of those keep the call and shrink it. I have written about that whole family in where your agent bill actually goes.

Script does something else. It removes the call. When a stretch of your loop was never doing reasoning, the cheapest version of that call is no call at all. A cascaded model saves you a multiple on that turn. Deleting the turn saves you the whole thing. That is why, per item, this is the largest saving available when it applies: you are not paying a smaller amount for the work, you are paying nothing, because the work moved to code that runs for free.

It applies less often than the other levers. That is the honest trade. Not every agent has a deterministic spine you can carve off. But when the analyzer finds a cluster that fires on every run and never branches, the recovered amount is the full cost of those turns, forever, not a fraction of it.

Who this is for

This lever is aimed at people who own their agent code: SDK and API builders running self-built, multi-provider agents where you can actually go edit the loop and move a sequence into a helper function. If you can open the file that emits the tool calls, this is a real, runnable analyzer and a real refactor you control.

For Claude Code specifically, it is not part of the story, and that is deliberate. Native hooks and Anthropic’s own tooling already cover a lot of this deterministic-glue ground inside that runtime, so there is less loose sequence to carve off and less benefit to a separate analyzer. On a self-built agent, the glue is yours, uninstrumented, and paid for on every run. That is where clustering the signatures earns its keep.

The mental model is the one from agent token economics: every token should be buying a decision you could not have made in advance. When a run of tool calls was decided in advance, a model is the wrong thing to spend on it. Finding those runs is a measurement problem, and once you have the traces, it is one you can solve.

Common questions

How is this different from just routing that part to a cheaper model?
Routing keeps the model call and pays a smaller amount for it. Script removes the call. If a stretch of your loop never branches on model output, the correct price for it is zero, because a plain function reproduces it exactly. Cheaper-model routing is the right move for the reasoning steps that remain; it is the wrong move for a step that was never reasoning.
Will tj optimize script rewrite my agent for me?
No. It clusters your tool-call signatures and flags the near-identical, zero-branch clusters as candidates. It hands you a targeting list. You write the script and decide what to lift out of the model loop. Treat the output as 'here are the deterministic stretches worth extracting,' not as a patch.
What counts as 'near-identical' and 'zero-branch'?
Near-identical means the tool-call signatures in a cluster agree with each other roughly 90% or more across runs: same tools, same order, same argument shape. Zero-branch means no step forks on model output that varies from run to run. A cluster that clears both is one where the model is walking a path that was fixed before the run began.
Why isn't this part of the Claude Code story?
Because Claude Code's native hooks and Anthropic's own tooling already handle a lot of the deterministic-glue work inside that runtime, so there is less loose sequence to extract. This lever pays off for self-built SDK and API agents, where the glue is your own code, running uninstrumented, and billed on every run.
I only have a handful of traces. Is that enough to run it?
You can run it on a small set, but clustering gets more confident with more runs, because a sequence only looks stable once you have seen it repeat. If a cluster fires on nearly every run you have, that is already a strong signal. If your volume is low, capture a few days of real traffic first, then run tj optimize script against the fuller set.
How much can I actually save from this?
It depends on how much of your loop is deterministic glue, which varies a lot by agent. When it applies, the saving is the full cost of the extracted turns rather than a fraction, so per candidate it is the biggest lever here. When your agent is mostly genuine reasoning with little rote sequence, it will find little, and that is a fair answer too.

Get TokenJam updates