(1)Check tool descriptions — are they ambiguous? If the agent can't tell which tool to use, it tries them all. Fix the descriptions.
(2)Check if it's re-calling the same tool — add deduplication.
(3)Check context bloat — after 10 calls, the context is so long the model loses track. Add observation summarization.
(4)Consider a planner step — if the agent is wandering, a plan upfront constrains the path.
(5)Check the system prompt — add explicit guidance like 'you should need at most 5 tool calls for this type of task.'"
The deep dive — diagrams, tradeoff tables, and the follow-up trap
Read the trace before you touch the prompt
Fifteen calls is not one bug. Pull ten traces and count what each call contributed — the shape of the waste names the fix.
The same task, traced twice — twelve of fifteen calls did no work
Five shapes of waste and what each one means
What you see in the trace
Root cause
Fix
Same tool, same arguments, three times
No dedup — the answer is in context but buried
Cache by (tool, sorted(args)); reuse the result
Cycles through four similar-sounding tools
Descriptions overlap; it is guessing between near-ties
Add a “not for X — use Y” clause to each
Calls get vaguer after step 8
Context bloat — raw output crowded out the goal
Summarize observations to 2–3 lines; re-state the goal
Never takes the same path twice
No plan — every step is an independent decision
Plan first, execute it, re-plan only on failure
Only ever stops at the iteration cap
No stop condition the model can reach
State the budget in the prompt and cap it in code
Two guards, both in your code
def step(state, call):
sig = (call.tool, json.dumps(call.args, sort_keys=True))
if sig in state.seen: # never pay twice for one question
return Obs(state.seen[sig], note="identical args — reusing the earlier result")
if state.calls >= HARD_CAP: # a cap the model cannot argue with
return Obs(None, note="tool budget spent — answer from what you have")
state.seen[sig] = execute(call)
state.calls += 1
return summarize(state.seen[sig], max_lines=3)
The note matters as much as the guard — told only “no result”, the model retries with a cosmetic argument change.
REAL SYSTEM
A billing-support agent averaged 14.6 tool calls on a 3-call task. Traces showed 61% were exact duplicates and 22% were get_account vs fetch_customer ping-pong — descriptions differing by one adjective. Dedup, merged descriptions, and a stated 5-call budget took it to 3.4 calls, p50 9.0s → 2.1s, $0.21 → $0.05 per task. Success went up, 68% to 94%: each extra call was another chance to derail, not to succeed.
FOLLOW-UP TRAP
“Just cap it at 3 calls — problem solved?” — A cap alone turns a slow success into a fast failure: the agent hits the wall mid-task and answers from partial data, which is worse because nothing looks broken. Cap and fix the cause — dedup so the budget is not spent on repeats, and make the cap a graceful exit that names what it could not finish.