Thirty calls, and nothing came back
Find the current CEO of Acme Corp and their start date.
→ search(‘Acme CEO’) → search(‘Acme Corp CEO’) → search(‘who is the CEO of Acme’) → search(‘Acme leadership’) … [26 more calls] → context window full → ‘I wasn’t able to complete the task.’
A question a human answers in one search. The agent makes thirty calls — each a slightly reworded version of the last — and then, with its context window stuffed full of its own attempts, gives up. And here’s the part that should bother you: the very first search came back with the answer. “Acme Corp CEO: Dana Reeves, since March 2021.” It had it at step 1. It searched twenty-nine more times anyway.
We’ve left Act I. The data layer is fine — retrieval works, the tool returns the right thing. This is a new kind of failure: the system acting over time, and lying to you about whether it’s getting anywhere. Swapping to a bigger model won’t fix a loop; it’ll just loop more eloquently. The fault is in how the agent decides what to do next.
Why it happens
An agent that can’t stop was never asked if it’s done. The loop isn’t stupidity — it’s a control flow with no exit.
A RAG pipeline is a straight line; an agent is a loop: observe, decide, act, repeat. Every turn the scaffold hands the model some context and asks a question, and the shape of that question quietly decides everything. If the only question you ever ask is “what is your next action?”, you have presupposed the answer — there must be a next action. The model, being obliging, always supplies one. It is never offered the move that would end the loop: stop, and answer.
So when an agent spins, don’t ask “why is the model too dumb to stop?” Ask the same first question as always — did it actually obtain the information? If no, you’re back in Act I, on the retrieval side. If yes — it had the answer and still didn’t finish — then the failure is downstream, in how the loop turns having-the-answer into being-done. Localize there.
Where does it break?
A bad answer splits one way: the model either never got the answer-bearing context, or itgot it and still failed. Answer that, and you've halved the search space. Then localize within the failing half.
- ingest
- chunk
- embed
- retrieve
- rerank
- assemble
- prompt
- generate
- post
Descend the pipeline
It had the answer — so we’re on the generation side. Now descend it: read what the agent was actually shown and asked at each step, until one stage owns the loop.
retrieve✓ cleared Did the tool return the answer at all?
Replay step 1.
search('Acme CEO')returns, verbatim: “Acme Corp CEO: Dana Reeves, appointed March 2021.” The information entered the system at the first opportunity. Retrieval is cleared — this is not an Act I failure.generate⚑ suspicious Did the model even register that it already had the answer?
Read the model’s own reasoning at step 2, with the step-1 observation in its context:
“I have found a result mentioning the CEO. To be thorough, I should verify with another search…”
It saw the answer — it isn’t blind. It chose to keep going. That’s the tell: the problem isn’t missing information, it’s that nothing in the loop treats “I have enough” as a reason to stop. So where does the decision to act-again actually come from?
prompt✕ fault here What is the agent asked at each turn — and is ‘finish’ even an option?
Here it is. Pull the action schema the scaffold offers the model every turn:
[ search · open_url · read_file · summarize · web_browse · calculator ] — six tools. There is no ‘finish’ or ‘answer’ action.
The turn prompt reads: “Choose the next tool to call.” Not “Answer the question, or call a tool if you still need to.” The agent is structurally incapable of stopping — its entire action space is verbs that continue the loop. “To be thorough, search again” isn’t the model being cautious; it’s the model picking the only kind of move it’s allowed to make. The fault is the prompt and action schema — a loop with no exit. A behavioural failure that looks like a reasoning failure, and a perfectly capable model spinning inside a cage you built.
Flip the fix
Theory's cheap. Take the same broken system and flip the fix yourself — watch the trace change and the eval scores move.
Find the current CEO of Acme Corp and their start date.
Acme Corp CEO: Dana Reeves, appointed March 2021. (same result, ignored)
Acme Corp CEO: Dana Reeves, appointed March 2021. (same result, ignored)
Acme Corp CEO: Dana Reeves, appointed March 2021. (same result, ignored)
I wasn’t able to complete the task. (context window full after 30 searches)
Make it a standing check
The fix is three small things, none of them a smarter model: add a terminate action (answer) to
the schema; change the turn prompt to “answer now if you can, otherwise act”; and cap steps with a
budget so a genuine dead-end fails fast instead of forever. But fixing this task is a bug fix.
Closing the class is the teardown.
The failure mode is a control loop with no grounded stop condition — and it’s invisible until it times out, because nothing errors; the agent is “working.” So instrument the loop’s behaviour, not just its answers:
- Log and assert tool-calls per task — a histogram with a hard ceiling. The day a prompt tweak sends the median from 2 to 12, this trips, long before a customer waits 90 seconds for nothing.
- A completion-rate eval over real tasks: did the agent return an answer within budget? An agent that loops scores 0 here even when every individual tool call “succeeded.”
And notice the rung you just climbed. In Act I you learned not to trust the symptom, then not to trust your own fix. Here the agent itself told you — “I have found a result… I should keep searching” — that it was making progress, while making none. You’re learning not to trust the system’s account of its own state. Next, in Act III, we add a component whose entire job is to catch mistakes like this — and watch it lie to your face.
- Why does an agent keep searching when it already has the answer?
- Not because the model is dumb — because nothing in the loop treats 'I have enough' as a valid move. If every turn's prompt asks only for the next tool call, the action space contains no way to stop, so the model always picks another tool. The fix is a finish action, not a bigger model.
- How do you design an agent stop condition that doesn't quit early or loop forever?
- Add an explicit terminate action and prompt the model to 'answer now if you can, otherwise act', grounding that decision in the observations it has actually gathered. Pair it with a hard step budget as a backstop, so a genuine dead-end fails fast instead of spinning to a timeout.
- When should an agent ask a human instead of looping?
- When repeated tool calls stop reducing uncertainty — same query, same results, no new information — that's the signal to escalate rather than retry. Detect it by tracking whether new observations actually change the agent's state; if they don't after a few steps, hand off to a person.