Try this first
Add up the tokens from one eval run. Twenty-five agent runs, each fifteen turns, each resending
a growing transcript.
That is a real number, and most people do not look at it until it is a surprise.
What you pay for
Three rates, and the difference between them is where the money is:
| Relative cost | |
|---|---|
| Ordinary input token | 1× |
| Cache write | ~1.25× |
| Cache read | ~0.1× |
| Output token | ~5× input |
Two consequences follow immediately.
Output is expensive per token, but you buy few of them. A long reply is a few thousand
tokens. Your transcript is fifty thousand, resent every turn.
Cache reads are almost free. This is why Lesson 6.2 is the highest-leverage lesson in the
course. Going from zero cache hits to mostly cache hits cuts an agent’s bill by more than any
other single change.
Measure per run, not per call
Add up across the whole run, because that is what a task actually costs:
class Meter:
def __init__(self):
self.plain = self.written = self.read = self.out = 0
def add(self, usage):
self.plain += usage.input_tokens
self.written += usage.cache_creation_input_tokens
self.read += usage.cache_read_input_tokens
self.out += usage.output_tokens
def cost(self, in_rate=5.0, out_rate=25.0):
"""Dollars per million tokens. Check current rates before trusting this."""
m = 1_000_000
return (
self.plain * in_rate / m
+ self.written * in_rate * 1.25 / m
+ self.read * in_rate * 0.10 / m
+ self.out * out_rate / m
)
def report(self):
total_in = self.plain + self.written + self.read
hit = self.read / total_in if total_in else 0
return (f"in={total_in:,} (cache hits {hit:.0%}) "
f"out={self.out:,} ~${self.cost():.3f}")
Print it at the end of every run. Once the number is visible, you will optimise it without
being told to.
The lever that is not tokens
effort controls how much the model thinks and how hard it works before answering:
# Provider-specific: check your own documentation for the parameter name.
# Anthropic calls it output_config.effort; others have their own equivalent,
# or none at all.
reply = llm.send(messages, TOOLS)
The default is high. Two things are worth knowing:
Lower effort is not simply worse. On routine work, low and medium are strong and much
cheaper. Sweep your eval set across three levels and read the pass rates before assuming you
need the top.
Higher effort can cost less overall. More thinking up front can mean fewer wrong turns, and
on agentic work fewer turns is fewer full transcript resends. The cheapest setting per call is
not always the cheapest setting per task.
That second point is only checkable with an eval. Which is why this lesson comes after 7.2.
Where the money actually goes
In a typical agent session:
| Share | What |
|---|---|
| ~70–90% | Resending the transcript, mostly old tool results |
| ~5–15% | The system prompt and tools, every turn |
| ~5–10% | Output |
So the ranked list of things to do, which is Module 6 in cost order:
- Turn on caching. Biggest win, four lines.
- Return less from tools. Trim, summarise, do not dump.
- Clear old tool results.
- Try a lower effort and check the eval.
- Move bulk reading to a sub-agent on a cheaper model.
Rates change
The multipliers in that table are stable. The dollar figures are not — prices change, models
change, and a hardcoded rate in your code will quietly go wrong.
Keep rates in one place, dated, and check them against current pricing before you make a
decision based on them.
Most of an agent’s bill is resending old tool results. Caching, then smaller tool results,
then clearing — in that order.
Try this before the next lesson
Add the meter and run your eval with and without caching.
Compare the totals. Then run the eval at low, medium, and high effort and put pass rate
next to cost for each. One of those three is probably the right default for your agent, and it
may not be the one you assumed.