Area 03 // AI AgentsLesson 60 / 14015 min read
Stop conditions, errors and cost control
Agents need clear rules for when to stop (task done, step or token budget used, time up, stuck in a loop), sensible handling of tool and model errors (retry, back off, report), and cost controls so a confused agent can't run up a large bill.
Think of it like this
Analogy
You send a teenager to the shops with £20 and a list. Sensible rules: come back when you've got everything (done), or after an hour (time limit), or when the £20 is spent (budget). If a shop is shut, try once more later, then try another shop (retry and fallback). If you find yourself walking round the same aisle for the third time, stop and phone home (loop detection). And either way, tell me what you managed to buy.
The problem it solves
The agent loop runs until something stops it. Without good stop conditions, a confused agent can loop for an hour, call the same failing tool hundreds of times, or quietly spend hundreds of pounds on tokens. Without error handling, one flaky API call crashes a long task. And when an agent does stop early, users often get nothing useful.
How it works: stop conditions
- Success: the model gives a final answer or calls a
finishtool. The cleanest stop condition. - Step limit: a hard maximum number of loop turns (e.g. 10–50 depending on the task). Always set one.
- Budgets: maximum tokens or estimated cost per task (a token budget), and a wall-clock time limit.
- Loop detection: the same tool with the same arguments several times, or no new information over several steps, means stuck.
- Unrecoverable errors: authentication failures, permission denied, invalid configuration.
- Human stop: a cancel button the user can press at any time.
- Stop gracefully: return what was done, the best partial answer, and what's missing or failed.
How it works: error handling and retries
- Tool errors become tool results marked as errors with a useful message, so the model can fix arguments or change approach (tool calling).
- Transient failures (timeouts, 429 rate limits, 5xx errors) are retried by the harness with exponential backoff and jitter (wait 1 s, 2 s, 4 s…), a few times at most (retries with backoff).
- Model API errors are retried similarly; persistent ones can fall back to another model (fallbacks).
- Actions must be safe to retry: use idempotency keys so a retried "send payment" doesn't pay twice.
- Check the model's stop reason: a reply cut off by
max_tokensor a refusal isn't a normal answer; handle it explicitly. - Count errors: too many in a row is itself a stop condition.
How it works: cost and loop control
- Know where the money goes: every turn resends the whole context, so long histories and big tool results dominate cost (agent loop).
- Set per-task budgets and stop (or ask the user) when exceeded.
- Keep context lean: concise tool results, clear old results, compaction (context engineering).
- Use prompt caching for the stable system prompt and tool definitions.
- Use cheaper models for simple sub-steps, summaries and checks; reserve strong models for planning and hard reasoning.
- Lower effort where deep reasoning isn't needed (reasoning models).
- Monitor and alert on cost per task and runaway runs (cost per request).
See it in code
- Needs
- Nothing: plain Python 3.
A loop guard that stops on steps, tokens, time or repeated actions, and a retry helper with exponential backoff.
import time
class LoopGuard:
"""Stop conditions for an agent loop: steps, tokens, time, and repeated actions."""
def __init__(self, max_steps=8, max_tokens=20_000, max_seconds=60):
self.max_steps, self.max_tokens, self.max_seconds = max_steps, max_tokens, max_seconds
self.steps = self.tokens = 0
self.start = time.monotonic()
self.seen = []
def check(self, action: str, tokens_used: int) -> str | None:
self.steps += 1; self.tokens += tokens_used
if self.steps > self.max_steps: return "max steps"
if self.tokens > self.max_tokens: return "token budget"
if time.monotonic() - self.start > self.max_seconds: return "time budget"
if self.seen[-2:] == [action, action]: return "same action 3 times in a row"
self.seen.append(action)
return None
def call_tool_with_retry(fn, attempts=3, base_delay=0.01):
for i in range(attempts):
try:
return fn(i)
except TimeoutError:
time.sleep(base_delay * 2 ** i) # exponential backoff between tries
return "error: tool unavailable, tell the user"
guard = LoopGuard()
actions = ["search('refund')", "search('refund')", "search('refund')"]
for a in actions:
reason = guard.check(a, tokens_used=1_500)
print(f"{a:18} -> {'STOP: ' + reason if reason else 'continue'}")
flaky = lambda i: (_ for _ in ()).throw(TimeoutError()) if i < 2 else "ok on try 3"
print("flaky tool:", call_tool_with_retry(flaky))search('refund') -> continue
search('refund') -> continue
search('refund') -> STOP: same action 3 times in a row
flaky tool: ok on try 3Real backoff starts around 0.5–1 s and adds random jitter; the tiny delay here just keeps the example fast.
A real example
Example
The weekend agent bill
A startup ran a research agent on a schedule. A website it relied on changed its layout, the scraping tool started returning empty results, and the agent kept rephrasing the same search, about 400 times per task, all weekend. Monday's bill was roughly 60× normal.
Fixes: a step limit (30), a per-task token budget, loop detection on repeated tool calls with empty results, an error counter that stops after five consecutive tool failures, a daily spend alert, and a "partial result" message to the user explaining which sources failed.
The easy way
Start here
Add four things to every agent from day one: a step limit, a token budget, a finish tool, and try/except around tools that returns the error to the model. Plus a provider spend limit on your account.
When you grow: the production setup
Production harnesses add time budgets, loop and no-progress detection, per-tenant and per-feature cost budgets, retry policies per tool with idempotency, model fallbacks, cancellation from the UI, alerts on cost and error spikes, and dashboards of stop reasons ("why did tasks end?"), which point straight at problems.
Trade-offs
What you gain
- Bounded cost and time for every task.
- Resilience to flaky tools and APIs.
- Users always get a result or a clear explanation.
What it costs you
- Limits set too low cut off legitimate long tasks.
- Retries add latency and can repeat side effects without idempotency.
- Loop detection can produce false positives.
When not to use it
- Never skip stop conditions entirely; tune their values per task type instead.
Common mistakes
- No step limit "because the model will stop when it's done".
- Retrying non-idempotent actions (payments, emails) without protection.
- Retrying immediately in a tight loop instead of backing off.
- Ending silently with no partial result.
- Only finding out about runaway costs from the invoice.
Interview questions
Try answering out loud first, then open the model answer.
What stop conditions should a production agent have?
Model answer
Successful completion (final answer or finish tool), a maximum step count, token or cost budget, wall-clock time limit, loop or no-progress detection (repeated identical actions, no new information), consecutive error limits, unrecoverable errors such as auth failures, and user cancellation. On early stop it returns progress, partial results and what's missing.
How should an agent handle tool and API failures?
Model answer
Distinguish error types: validation and business errors go back to the model as error results so it can adapt; transient errors (timeouts, 429, 5xx) are retried in the harness with exponential backoff and jitter and a cap; persistent model failures fall back to another model or provider; non-idempotent actions use idempotency keys before any retry; repeated failures trigger a stop with a clear message and an alert.
How do you control the cost of agents?
Model answer
Bound each task (steps, tokens, time), keep context small (concise tool outputs, clearing old results, compaction), cache the stable prompt prefix, use cheaper models or lower effort for simple sub-steps, avoid unnecessary multi-agent fan-out, route only complex requests to agents, and monitor cost per task with alerts and per-tenant budgets.
Quiz
Pick an answer to see whether it’s right and why.
0 of 5 answered
Key terms
- Stop condition
- A rule that ends an agent loop, such as a final answer, a maximum number of steps, a token or time budget, or an error.
- Token budget
- A limit on how many tokens a request, step or whole task may use, to control cost, latency and context size.
- Exponential backoff
- Retrying a failed request after waits that double each time (1s, 2s, 4s…), usually with random jitter, so a struggling service can recover.
- Loop detection
- Spotting that an agent is stuck, for example repeating the same action or making no progress, and stopping it.
- Agent
- A system where a model decides, in a loop, which tools to call and what to do next until a task is done.
Before and after
Progress is saved in this browser only.