The Agent Loop Is Just a While Loop Keyed on stop_reason

Claude's tool-use loop is a while loop driven entirely by stop_reason. When the model calls a tool, stop_reason is tool_use — your application executes the tool, appends the result, and calls again. When there's nothing left to call, stop_reason flips to end_turn and the loop ends. In production those aren't the only two values you'll see: max_tokens fires when a response is truncated, pause_turn fires when a server-tool sampling loop hits its own iteration ceiling, and refusal fires when a policy blocks the response — each needs its own branch.

In Chatbots and Callbots, Every Extra Lap Is Wait Time

A batch research agent can loop a few extra times and nobody notices. A callbot user is holding the phone; a chatbot user is watching the screen. Every additional round trip through the loop adds directly to what the user experiences as latency, so the loop's termination logic is a service-quality requirement before it's a cost-control one.

Webhook Timeouts Set the Loop's Real Budget

Each turn of the loop typically calls out to an external webhook or API. Dialogflow CX defaults its webhook timeout to 5 seconds and caps it at 30 seconds — that ceiling effectively decides how many tool calls can fit inside a single turn. If timeouts aren't scoped per tool, one slow call can burn the entire turn's latency budget by itself.

From Design to Operations: Building Guardrails for the Tool-Call Loop

Fix iteration caps, turn latency, and webhook timeouts as numbers before writing code. Anthropic's docs put the server-tool sampling loop's default iteration limit at 10 and recommend capping consecutive pause_turn continuations at 5. Use those as a baseline: for a latency-sensitive real-time channel like a callbot, set a pre-launch target of a 10-call session-wide iteration cap, a 5-second default webhook timeout (15-second ceiling), and a turn-processing p95 under 3 seconds. Non-real-time channels like web chatbots can push the iteration cap to 25.

The first failure pattern is skipping the iteration cap entirely. Anthropic's example code documents max_iterations=50 not as a normal exit condition but explicitly as a safety net. If a session actually reaches that ceiling, treat the occurrence itself as an incident signal — don't just raise the limit and move on.

The second is appending a text block immediately after a tool_result. The documentation notes this pattern trains the model to expect it's the user's turn after every tool call, producing a 2-3 token empty response paired with end_turn. In a callbot that surfaces as dead air; in a chatbot, an empty bubble. The fix is to never add extra text right after tool_result, and if an empty response recurs, re-request with a fresh user message that simply says "please continue."

The third is scoping webhook retries incorrectly. Dialogflow CX's documentation says to retry only non-terminal errors — timeouts and connection failures. Retrying 4xx-class errors that will never succeed just burns the loop's remaining budget on doomed attempts. Apply exponential backoff to retries, and attach an idempotency key to any tool call with side effects — payments, bookings — so a retry can't double-execute it.

Write the stop_reason branches explicitly in code: tool_use executes and continues, pause_turn continues for at most 5 consecutive rounds before handing off to a human, refusal gets one retry on a fallback model before falling back to a safe response, and a tool call truncated by max_tokens gets re-requested for that turn alone with a higher token limit. Without these branches, every failure collapses into "just try again," and root-causing becomes impossible.

Before shipping, deliberately wire in three adversarial tools — one that always errors, one that takes over 5 seconds, one that returns a malformed schema — and run them through scenario tests. Log iteration count, stop_reason, webhook latency, and retry count as standard fields on every turn, and mask any PII that shows up in call or chat content — account numbers, phone numbers — before it hits the log.

Collect the sessions that actually hit the iteration cap or max_continuations on a weekly cadence and sort the causes into three buckets: schema changes, downstream API behavior changes, and prompt regressions. If the average iteration count is trending up week over week, prioritize investigating that before shipping new features. When a tool contract changes, bundle the log-schema update into the same commit so the dashboard doesn't silently break.

The Checklist to Ship With

Chatbot and callbot agent loops quietly accumulate latency and cost without stop_reason-specific branches and an iteration cap. Pin down a 10-call session cap, a 5-second default webhook timeout (15-second ceiling), and a turn p95 under 3 seconds as numbers before launch, then code in three recovery rules — never append text right after tool_result, retry only non-terminal errors, and attach idempotency keys to side-effecting calls — so the loop knows exactly where it's supposed to stop itself.

References

Handling stop reasons — Claude Platform Docs

Webhooks — Dialogflow CX, Google Cloud Documentation