The first agent I let run unattended cost eleven dollars to return nothing. Not a wrong answer — nothing. It had spent forty minutes calling the same two tools in alternation, each call adding to a context that was already past the point where the original instructions carried any weight, and it stopped only because the process was killed.
The instinct after that is to add a timeout. A timeout is not a budget, and the difference is most of this post.
#A timeout answers the wrong question
A timeout asks how long the wall clock has been running. Almost nothing you care about is a function of that.
A run that waited overnight for a human approval has burned sixteen hours and zero dollars. A run that has been inside a single model call for four minutes has burned no wall clock worth mentioning and may be about to return a result that costs more than the rest of the day. A run that has made two hundred cheap tool calls in ninety seconds is in more trouble than either. One number cannot separate those, and the one number people reach for separates them worst.
What a budget has to track is work: tokens spent, calls made, attempts consumed, and minutes during which something was actually happening. Those are four different ceilings and they fail in four different ways.
#Enforce it on every state write
The obvious place to check a budget is at a gate, because that is where the run already pauses to be judged. It is also the place a run in trouble will never reach.
The failure is not hypothetical. A run can sit inside one model call for hours — a long tool chain, a retry loop inside a provider SDK, a stream that never closes. If the only budget check happens at the next gate, and the next gate is on the far side of that call, then during the entire period the budget is notionally exceeded nothing is checking it.
So the check moves to every state write. Every time the run records that something happened, it also asks whether it is still allowed to be happening. That is a cheap check on an operation the system was performing anyway, and it turns the budget from a thing that is verified occasionally into an invariant.
The complementary rule: exceeding a budget should exit at the next gate rather than mid-flight. Killing a run in the middle of a write leaves the state inconsistent and the sandbox dirty, and you have traded a spending problem for a cleanup problem. Setting a flag that the next gate honours costs a few more tokens and leaves the system in a state something can reason about.
#The decision at the ceiling
Here is the part that changed how I build these. When a budget is exhausted, what should happen?
The default answer is "fail the run", and it is wrong often enough to be worth replacing. Consider two runs that both present identically as a spent budget:
- One has failed the same assertion five times, each attempt a small variation on the last, with the diff between attempts shrinking. It is one change away.
- One has failed five times in five different places, each fix breaking something the previous fix established. It is going in circles.
The first deserves one more round. The second deserves to stop immediately, and every round it is granted makes the eventual cleanup worse. A fixed policy cannot tell them apart, because the difference is not in the counters — it is in the shape of what happened, which is exactly the kind of thing a model reads well.
So the decision goes to an arbiter with three answers: retry once more with a targeted scope, narrow the objective and continue against the smaller target, or fail and stop. Three outcomes rather than two, because "this is not going to work as specified, but a smaller version would" is the most common real situation and neither of the binary answers expresses it.
#Budget the guessing, not just the spending
There is a second ceiling that is less obvious and catches problems earlier than the token one.
An unattended agent hits ambiguity constantly. The temptation is to have it ask, and the cost of that is a system that interrupts thirty times per run and is therefore worse than doing the work by hand. The alternative I settled on is that flow agents have no question tool at all: an unclear decision is recorded as a question, given a default, rated for impact, and the run continues. Every accumulated question surfaces together at the single human stop. Thirty interruptions become one review.
That works, but it introduces a new way to fail quietly — a run can guess its way to a confident, complete, entirely wrong result. So the assumptions are budgeted too. Exceed a small number of high-impact assumptions and the run halts for a person on that basis alone, before the token budget is anywhere near spent.
The property that makes this good is that the system escalates when it notices it is guessing too much, rather than presenting a large pile of guesses at the end. A run that stops after three consequential assumptions has cost almost nothing and produced the single most useful artefact available: a short list of decisions nobody has made.
#Where the money actually goes
Two things are worth measuring per run, and both are cheaper to add at the start than to retrofit.
The first is spend per phase, so that "this run cost eleven dollars" becomes "reconnaissance cost eleven dollars", which is an actionable sentence. In practice the answer is almost always a phase nobody suspected — usually the one that re-reads a large context on every attempt because compaction was never wired into its loop.
The second is what memory returned, and more importantly what it did not. An agent that failed because a constraint did not fit inside the retrieval budget looks, from the outside, exactly like one that had no constraint to satisfy. Recording what was truncated is the only way to tell those apart, and the difference decides whether you fix the agent or fix the index.
#Which of this applies to one agent
Most of it. The budgets and the loop discipline are a few days of work and they are what stop a single agent from spending real money to return nothing. They are not a platform and they do not need one.
The part to defer is topology. Multi-agent orchestration earns its keep once more than one agent is genuinely involved or the same work runs often enough that its shape matters, and not before — the consistent failure I see is a team building a coordination layer on top of a component that has never reliably finished one task.
The decision between the two is narrower than the debate around it. One question settles it: are the steps knowable before the run starts?
Almost every real system is a graph whose nodes contain loops — the graph carrying the parts you are willing to commit to, the loops carrying the parts you are not. There is more on that choice here.
More on the surrounding disciplines in agentic engineering: harness, loop and graph, and on verifying the machinery itself in testing an agent's control flow without a model.