Why Your Agent Repeats the Same Action
Ever watched your agent call the same tool over and over? The agent repeating actions fix is almost never a bigger model — it's memory and feedback. Here's the teardown.
You are a research assistant. You have access to a `web_search` tool. Use it to find information and answer the user's question accurately. When you have enough information, give a complete answer. Do not repeat tool calls unnecessarily.
Ever sat and watched your agent call the exact same tool, with the exact same arguments, three times in a row — and wondered why it can't see what you can plainly see? You're not alone. It's probably the single most reported agent bug, and the agent repeating actions fix that most people reach for first is the one that works least.
The instinct is to add "don't repeat yourself" to the prompt and hope. That treats a memory problem as a discipline problem. Below is a teardown of a real weak prompt, why it lets the agent spin, and the rewrite that stops it.
Before: The Weak Prompt
Here's the system prompt from an agent that kept re-running the same web search. It's clean, readable, and quietly broken.
You are a research assistant. You have access to a `web_search` tool.
Use it to find information and answer the user's question accurately.
When you have enough information, give a complete answer.
Do not repeat tool calls unnecessarily.What this does: It describes the goal and even asks the agent not to repeat itself — but it gives the model no structured record of what it already tried, and no rule for what to do when a result is unhelpful. The "don't repeat" line is a wish, not a mechanism.
Why It Fails
The prompt fails because of how the loop feeds information back. On each step the model sees the running transcript, but a long transcript of near-identical search results is easy to lose track of. When search result #1 is unhelpful, nothing in the loop marks it as "already tried and useless." So the model, reasoning fresh, lands on the same obvious query again. Same inputs, same output.
There's a deeper issue too. Most loops append raw tool output to the context without any summary of what was learned or ruled out. The model has the data but not the takeaway. It's like a detective handed the same box of evidence every five minutes with no notes — of course they re-examine the same clue.
⚡ Pro tip: If your agent repeats actions, print the model's context exactly as it's sent on the repeated step. Nine times out of ten you'll find the previous failed attempt is technically present but buried, unlabeled, and easy to overlook. The fix is making prior attempts salient, not just present.
The "do not repeat tool calls unnecessarily" instruction actively backfires in one way: it makes teams feel the problem is addressed, so they stop looking for the structural cause. A probabilistic nudge in a prompt cannot guarantee a deterministic property like "never call this twice." The agent repeating actions fix has to live partly in code.
It helps to name the three root causes, because the right fix depends on which one you have. The first is invisible history: the prior attempt is in the context but not salient, so the model doesn't register it. The second is uninformative feedback: the tool returns something ambiguous like an empty list, so the model can't tell "I found nothing" from "I asked wrong," and defaults to asking again. The third is missing progress tracking: the model has no running sense of what's been ruled out, so every step starts from a blank slate. Most stuck agents have at least two of these at once, which is why single-line prompt fixes rarely stick.
You can diagnose which cause dominates in about five minutes. Dump the exact context sent on the repeated step and ask yourself three questions. Can I clearly see the prior attempt and its outcome? Does the tool result actually say whether it succeeded? Is there any summary of what's been tried? Wherever you answer "no" is where your fix goes. This beats guessing, and it stops you from bolting on a prompt line that addresses a problem you don't have.
After: The Improved Prompt
The rewrite pairs a sharper prompt with a loop that maintains an explicit "attempts" ledger and feeds it back every step.
You are a research assistant with a `web_search` tool.
RULES:
- Before searching, check ATTEMPTS below. Never repeat a query that
already appears there.
- If a search returned nothing useful, change your strategy: broaden,
narrow, or try different terms. Do not rephrase trivially.
- After 2 unhelpful searches on a sub-question, state what you could
not find and move on with what you have.
ATTEMPTS SO FAR:
{attempts_ledger}What this does: It moves the record of prior attempts out of the buried transcript and into a labeled, always-visible block the model must consult before acting — converting "please remember" into "here is what you already tried."
The ledger is built in code so it can't be forgotten:
[object Object], ,[object Object],(,[object Object],):
lines = []
,[object Object], h ,[object Object], history:
,[object Object], h.tool == ,[object Object],:
verdict = ,[object Object], ,[object Object], h.was_useful ,[object Object], ,[object Object],
lines.append(,[object Object],)
,[object Object], ,[object Object],.join(lines) ,[object Object], ,[object Object],What this does: It renders every prior search and whether it helped into a compact, readable list that gets injected into the prompt each step, so the model reasons over a clean summary instead of a wall of raw results.
Breaking Down Each Element
Three elements do the real work in this agent repeating actions fix.
The attempts ledger is the core. By surfacing prior queries and their verdicts in a dedicated block, you make "I already tried that" a fact the model reads rather than a memory it has to reconstruct. This is the difference between hoping the model notices and ensuring it sees.
The usefulness verdict is what makes the ledger actionable. "I searched X" is weak; "I searched X and it returned nothing useful" tells the model to change strategy, not just avoid the literal string. You compute
was_usefulThe escalation rule — after two dead ends, report and move on — prevents a subtler loop where the model keeps generating genuinely different queries that all fail. Without a give-up rule, "don't repeat exactly" just turns a tight loop into a slightly wider one.
⚠️ Common mistake: Deduplicating only on exact-match arguments. Agents are clever enough to change one word and technically not "repeat," while doing the same unproductive thing five times. Track semantic intent (the sub-question being answered), not just literal argument strings, or your dedup guard becomes trivial to evade.
⚡ Pro tip: Add a hard code-level guard on top of the prompt: hash each tool call's arguments, and if the same hash appears three times, force the agent into a "summarize and stop" mode. Prompts reduce repetition; code guarantees it ends.
What Makes This Different From Naive Deduplication
It's tempting to think you can solve repetition with a one-line filter: reject any tool call whose arguments exactly match a previous one. That catches the crudest loops, but it fails against the interesting cases in two directions.
It's too strict in some cases. A pagination tool legitimately gets called repeatedly with the same
page_sizeAnd it's too loose in others. As noted, a model that wants to repeat can change one token — a synonym, a reordered clause, an extra space — and slip past exact-match dedup while doing the identical unproductive thing. This is the harder direction, and it's why the attempts ledger tracks the sub-question being answered rather than the raw argument string. Intent-level tracking sees through cosmetic variation; argument-level tracking doesn't.
The ledger approach threads this needle because it records outcomes, not just calls. A repeated call that produced new information stays; a varied call that produced the same dead end gets flagged. You're measuring productivity, which is what you actually care about, instead of literal sameness, which is only a rough proxy.
Variations for Different Contexts
For a coding agent that keeps re-reading the same file, the ledger tracks files already viewed and a one-line note on what each contained, so re-reads only happen when something changed.
For a customer-support agent that keeps re-querying the same order, the ledger records which records were fetched and their key fields, so the model answers from what it has instead of re-fetching. A support engineer at a logistics company told me this single change cut their agent's redundant database calls by roughly 70%.
For a data-analysis agent that reruns the same failing query, the ledger stores the query plus the error, so the model debugs the error rather than blindly resubmitting. A data scientist I worked with used exactly this to stop an agent that had been re-running a query with a typo'd column name a dozen times per session.
For a multi-agent setup where a coordinator delegates to workers, the ledger lives at the coordinator level and tracks which subtask went to which worker and how it turned out. This stops the coordinator from re-delegating an assignment that already failed — the same repetition bug, one level up.
⚡ Pro tip: Keep the ledger compact. If you dump full tool outputs into it, the ledger itself becomes a wall of text the model skims past, recreating the exact problem you were solving. One line per attempt — the query and a short verdict — stays scannable even after twenty steps. When the ledger grows long, summarize older entries rather than dropping them, so the "already ruled out" facts survive.
Save and Reuse This
The attempts-ledger pattern is worth keeping as a template because nearly every agent you build will eventually repeat an action. Once you've written the ledger builder and the paired prompt block, store them somewhere you can pull them into the next project in seconds.
That's exactly what a prompt library like PromptABCD is good for — keeping your ledger prompt, your usefulness-verdict wording, and your escalation rule versioned and instantly reusable, so the agent repeating actions fix is a two-minute paste instead of a rediscovery every time.
Continue Reading
Save the prompts from this post
PromptABCD is a free prompt manager. Paste, organize, and reuse your best AI prompts — no more hunting through chat history.
