Agents and tools · Lesson 4
Decide how the loop ends before you start it
An agent loop is four lines of code. Call the model, run whatever tool it asked for, feed the result back, repeat. The hard part is not the loop. It is the sentence you have not written yet, which says when the loop is over.
Left alone, a loop ends in one of three ways. It finishes the job. It runs out of whatever limit you set. Or it discovers that a tool keeps failing, tries the same call eleven more times, and burns forty dollars finding out that a service is down.
The third one is the default, because nothing in the loop knows the difference between "not done yet" and "not going to work".
Name the finish line in the prompt
A loop with no stated end condition ends when the model decides it feels finished, which correlates with nothing you care about. Say what done looks like, concretely enough that the model can check it:
You are done when you have the order id, the refund amount and the reason, and all three came from a tool result rather than from the customer. Then call
submit_refundonce and stop. Do not call it twice.
The instruction to stop is not redundant. Without something to do at the end, loops keep going, summarising what they did and then checking their work again.
Budget three things separately
One iteration limit is not enough, because the failure modes have different shapes.
Steps. How many times round the loop. A task that needs four steps and takes twelve is not being thorough, it is stuck. Set it near what the task actually needs rather than at fifty.
Money. Track the spend per run and stop at a ceiling. Steps and cost come apart badly the moment one tool returns a large document, and the run that hurts is the one where each step is cheap and the context grows every time.
Wall clock. Somebody is waiting. A loop that gets there in four minutes has already lost, because the person refreshed at ninety seconds and started another one.
Hitting any of the three should produce a specific message. "I could not finish this. Here is what I established, and here is where I stopped" is useful. Silence, or a fabricated summary, is worse than an error.
Repeating a call is the signal to watch
Almost every runaway loop looks the same from the outside: the same tool called with nearly the same arguments, three or four times in a row. It is the cheapest thing to detect and hardly anybody detects it.
Keep a hash of the last few calls. On a repeat, do not simply block it. Tell the model what happened:
You have called
find_orderswith these arguments twice and received the same empty result. It will not return anything different. Either try a different approach or say what you are missing.
That message often produces the good move, which was to ask the customer for their email address. Blocking silently produces a retry with one word changed.
Distinguish a failed step from a failed task
A tool returning an error is not the end of the world, and a loop that abandons everything on the first non-200 is useless in a system where timeouts happen.
Retry the transient ones, twice, with a pause. Do not retry the ones that will never work: a bad argument, a missing permission, an id that does not exist. Those need a different action, and the error message is what tells the model which kind it just hit. This is the same point as writing good tool errors, seen from the loop's side.
Where this doesn't help
Budgets stop a runaway. They do not stop a loop that finishes confidently and wrong, and that is the more expensive failure. A run that stops in four steps with a clean, incorrect answer costs you nothing in tokens and everything in trust. Limits are a safety net, not a quality measure, and no amount of tuning them makes the output better.
Nor does any of this rescue a task that should not be a loop. If the sequence of steps is the same every time, write the sequence. A loop is worth its cost when step three genuinely depends on what step two returned, and most things billed as agents do not meet that test.
The move
Before you run a loop, write down three numbers: maximum steps, maximum spend, maximum seconds. Then add repeat detection on tool calls, and make the message it produces tell the model what it already tried.
Exercise
You are about to put an agent loop in front of customers. It can look up orders, read the returns policy and issue a refund. Write down how it ends: what counts as finished, what stops it early, and what the person waiting sees in each case.
How this gets marked
- 30%Defines finished as a condition the model can check, not as a feeling.
- 30%Bounds more than one dimension. Steps, spend and elapsed time do not fail together.
- 25%Notices the same call being made again and tells the model what it already tried.
- 15%Says what the waiting person is told when a limit is hit, rather than leaving silence.
The lesson is free and stays free. Marking is the part that costs us a model call, so it needs a name to record the score against.