How to Answer
"I'd diagnose in this order:
- (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.'"
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.
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.
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.
“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.