The Cost and Rate Governor
- Explain why agent loop cost grows faster than the number of steps
- Set four limits: dollars per run, steps per run, calls per minute, spend per day
- Implement a governor that stops a run cleanly
- Choose what happens when a limit is hit
The scare
This one is invented, but I would bet money you will recognise it. A team ships an agent on Friday. Over the weekend a customer's request hits an edge case: a tool keeps returning a slightly odd result, and the agent keeps trying again, slightly differently each time. Nothing crashes. No error alerts. It is technically "working". By Monday morning the API bill has a number on it that makes someone go quiet.
The original Knight Capital story from chapter 8 has the same shape at a horrifying scale: autonomous software, doing exactly what it was told, with no ceiling on how far it could go.
Refresher: why a loop costs more than you think
A chatbot costs one model call per message. An agent runs a loop, and each turn re-sends the whole history so far.
- Turn 1 sends about 2,000 tokens.
- Turn 2 sends 2,000 plus what happened in turn 1.
- Turn 10 sends maybe 6,000.
So ten turns cost far more than ten times turn one. Cost per step goes up as the loop runs, and total cost grows faster than the number of steps. And the model decides when to stop, as chapter 0 pointed out. If it never decides to, nothing else will.
Try it
A stuck agent, thirty steps if left alone. Turn the governor off and see what the loop costs. Then turn it on and move the budget and step cap.
2 steps · spent $0.036
Stopped before step 3: it would exceed $0.05.
Context grows each turn, so the cost of each step rises. A stuck loop is quadratic, not linear.
The pattern
A governor is the part of an old steam engine that stops it running away with itself. Four limits, at four scales:
| Limit | Scope | Protects against |
|---|---|---|
| Budget per run | One task | A single stuck loop |
| Step cap | One task | Loops that never converge |
| Rate limit | Per user or key, per minute | Abuse and bursts |
| Daily spend cap | The whole system | Everything else, as a last net |
Set them before the agent runs, not after the first surprise. This repo's rule says the same: budget limits before any long-running task.
Build it
class BudgetExceeded(Exception):
pass
class Governor:
def __init__(self, max_usd=0.50, max_steps=12):
self.max_usd, self.max_steps = max_usd, max_steps
self.spent, self.steps = 0.0, 0
def before_step(self, est_usd):
if self.steps >= self.max_steps:
raise BudgetExceeded(f"step cap {self.max_steps} reached")
if self.spent + est_usd > self.max_usd:
raise BudgetExceeded(f"next step would exceed ${self.max_usd:.2f}")
def after_step(self, tokens_in, tokens_out, in_price=3e-6, out_price=15e-6):
self.steps += 1
self.spent += tokens_in * in_price + tokens_out * out_price
gov = Governor(max_usd=0.05, max_steps=8)
history_tokens = 2000
try:
while True:
gov.before_step(est_usd=history_tokens * 3e-6)
# ... call the model here ...
gov.after_step(history_tokens, 300)
history_tokens += 400
except BudgetExceeded as e:
print("stopped:", e, f"(spent ${gov.spent:.3f})")
The check happens before each step, using an estimate, so the run stops under budget rather than just over it. Prices vary by model and change often, so read them from config, not from memory. The model-cost comparison in Designing AI Applications shows how wide the gap between models is, which is why sending simple steps to a smaller model is a genuine cost lever.
What happens at the limit
Do not just crash. Choose on purpose:
- Stop and report: "I used my budget. Here is what I found so far."
- Ask to continue: hand it to the Approval Gate for a larger budget
- Downgrade: finish the job on a cheaper model
When it goes wrong
- Only a daily cap. One stuck run can eat the whole day's budget before anyone notices. You need the per-run limit too.
- A limit so loose it never fires. If your average run costs 4 cents, a 50-cent cap protects against a stuck loop but a 20-dollar one protects against nothing.
- Counting only model spend. Tools cost money too: paid APIs, sandboxes, search.
- Silent stops. A run that ends at its limit should say so, and the event should show up in the trace.
- Agent cost grows faster than step count because history is re-sent every turn
- Use four limits: per-run budget, step cap, rate limit, daily cap
- Check before each step using an estimate
- Decide what a limit means: stop and report, ask for more, or downgrade
- Your average run costs 4 cents and the budget is 50 cents. Good limit? What does it protect against, and what would you look at in the trace to tune it?
- Why check the budget before a step instead of after?