A single user question takes minutes instead of seconds, then times out or returns nothing useful. The trace shows the model calling the same tool again and again — often with byte-identical arguments — collecting the same unhelpful result each time. On a hosted API the first sign is the bill: one conversation burned forty calls' worth of tokens, because every retry re-sends the whole growing history.
Any tool loop written as while True: call the model, execute, repeat — the
natural first shape of module 3.8's run_turn. The trigger is a result the model
cannot act on: an empty search, an opaque error like "invalid input", a tool that
returns nothing on success, or a question whose answer no available tool can
provide. Small local models loop sooner, but every model loops eventually. Linked
module: 3.8 (Tool use / function calling).
The model retries because, from where it stands, retrying is reasonable: it needed information, the call produced none, and no message told it why or what to do instead. It cannot see the loop from inside — each turn looks like the first attempt, and an unbounded harness happily obliges forever. Read the trace and classify the cause. Identical arguments each time means the result gave the model nothing to change — usually an opaque error or empty result with no explanation. Slightly varying arguments means the model is groping for a value that does not exist, which is a capability gap: no tool can answer this question. Either way, the missing piece is on your side of the line — the harness set no budget, and the tool's failure messages carry no information.
Three changes to the harness, all in code. First, a hard cap: bound the loop
(for _ in range(5) instead of while True) and on hitting it return an honest
"could not complete this after 5 tool calls" rather than silence. Second,
repeated-call detection: keep a set of (tool_name, serialized_args) seen this
turn; on a repeat, do not execute — return "you already called this with these
exact arguments and got the same result; try something different or say what is
missing" as the tool result. Third, informative failures: every error or empty
result must say why and what would help — "no orders found for 10-08; ids look
like 'ORD-####'" lets the model change course, where "invalid input" guarantees a
retry. Re-run the looping conversation; it should end in a corrected call or an
honest refusal within the cap.
No unbounded loops, ever — a step cap and a token or rupee budget per conversation go in on day one, sized generously and alarmed when hit. Review traces weekly for near-loops (two identical calls) even when the cap saved you, because they mark tools whose results or errors need better wording. When a question genuinely has no matching tool, the right behaviour is a stated limitation, so put that rule in the system prompt and test it with questions your tools cannot answer.
Q: "Your agent burned its token budget calling the same search twelve times. What went wrong and how do you stop it?" Strong answer: explain that each retry looks like a first attempt to the model, blame the harness before the model — no cap, no repeat detection, uninformative results — and give all three fixes with the reason errors must explain themselves. Bonus for the cost detail: every retry re-sends the full history, so a loop's cost grows with each turn, not linearly.