Design Claude tool loops with schemas, observations, and approvals.
A text-only Claude call answers once and stops. Tool use lets Claude request that your code run a function and hand the result back, which is what makes an agent possible. This module frames the whole path before we open the loop.
Tool use lets Claude call functions you control instead of only emitting text.
Why this matters: It matters because the whole agent story starts with declaring a tool and reading a tool_use request.
A plain Claude call works in one shot. You send a prompt, Claude replies, and the turn ends. That is fine for writing or reasoning over text you already provided. It breaks the moment the task needs something Claude cannot produce from its weights: a live price, the contents of a file, the result of running code, or any action in the outside world.
Ask Claude "what is the weather in Paris right now?" and a one-shot call can only guess. It has no way to fetch live data, see what came back, and then answer. closes that gap: you declare functions Claude is allowed to ask for, and Claude can request one, read the result your code returns, and continue.
You give Claude tools as part of the request. Each is a small contract with three parts.
get_weather).Claude never runs the function. It only produces a structured request — the name plus arguments — and hands control back to your code. The between what the model decides and what your application executes is the whole safety story.
A user asks: "Is it raining in Paris right now?" You send Claude the prompt and one tool, get_weather(city).
get_weather, arguments {"city":"Paris"}.That hand-off is the pattern to copy: declare the tool, let Claude ask, run it yourself, return the result.
The first half of the loop: a tool-equipped request produces a tool_use request, not a final answer.
Module 1 ended at the tool_use request. This module completes the cycle: you run the tool, send a tool_result back, and Claude either calls another tool or gives a final answer. Repeating that exchange is the agent loop.
The loop is the four-message exchange that runs a tool and feeds the result back.
Why this matters: It matters because every Claude agent is this loop running until a stop condition fires.
A Claude agent is the tool-use exchange run on repeat. One turn has four moves, and each move feeds the next.
The loop ends when Claude returns a normal text response (stop_reason "end_turn") instead of a tool_use — or when you hit a turn limit you set. The conversation history is the agent's : each tool_result you append is what the next turn observes.
Goal: "What is the weather in Paris, and should I take an umbrella?" One tool is available: get_weather(city).
Notice the hand-off: turn 1's tool_result is exactly what turn 2 reads. Drop that message and Claude would ask for the weather again.
import anthropic client = anthropic.Anthropic() tools = [{ "name": "get_weather", "description": "Get current weather for a city.", "input_schema": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}, }] messages = [{"role": "user", "content": "Weather in Paris?"}] resp = client.messages.create(model="claude-sonnet-4-5", max_tokens=1024, tools=tools, messages=messages) print(resp.stop_reason)
tools=[...]input_schemamessages.createstop_reasonThis is the first half of the loop: send the prompt with the tool, then inspect what Claude decided. The returns a response whose stop_reason tells you whether Claude wants a tool or is done.
stop_reason prints "tool_use". resp.content contains a tool_use block with name "get_weather" and input {"city": "Paris"} — Claude is asking your code to run the tool, not answering yet.
Once you can run the loop, the real decision is how elaborate to make it. This module weighs three patterns, a single tool call, a tool loop, and orchestrator-workers, and pushes you toward the smallest one that works.
Agent patterns range from a single tool call to multi-agent orchestration.
Why this matters: It matters because over-engineering the pattern is the most common and costly mistake.
Decision this forces: Choose the smallest agent pattern for the task: single tool call, tool loop, or orchestrator-workers.
"Agent pattern" is just how much structure wraps the tool-use loop. Three are worth knowing.
Goal: "Summarize today's three top tech headlines."
The lesson: the same goal fits all three, and the cheapest one that works is almost always right.
| Option | Fit | Operational burden | Risk | When to choose | Cost | Complexity |
|---|---|---|---|---|---|---|
| Single tool call | Task needs one lookup or one action, then an answer. | One round trip; trivial to trace and test. | Cannot adapt if the first result is insufficient. | Use when the task is one well-scoped step. | Low | Low |
| Tool loop | Task needs several dependent steps decided on the fly. | Needs a turn budget, dispatch table, and tracing. | Can loop or drift without guards. | Use when steps depend on earlier results and the count is unknown up front. | Medium | Medium |
| Orchestrator-workers | Best for parallel subtasks or distinct specialist roles. | Many loops, message passing, and shared state to debug. | Compounding errors and cost; hard to reproduce. | Use only after a single loop is provably insufficient. | High | High |
With a pattern chosen, this module turns it into running code: a dispatch table, the tool_result hand-off, and a stop condition. It also shows how to verify an AI-generated loop before you trust it, and asks you to fill in the key line.
A concrete tool-use loop in code: tool schema, dispatch, tool_result, stop.
Why this matters: It matters because the gap between the concept and a correct implementation hides bugs.
Before reading on, recall from module 2: after Claude emits a tool_use and your code runs the function, what message must you append, and what happens to the loop if you forget it?
Answer: you append a message carrying the output. Forget it and the next turn re-observes a request with no result, so Claude asks for the same tool again, the classic stuck-loop signature.
def run_agent(client, tools, dispatch, messages, max_turns=8): for _ in range(max_turns): resp = client.messages.create(model="claude-sonnet-4-5", max_tokens=1024, tools=tools, messages=messages) if resp.stop_reason != "tool_use": return resp # final answer -> stop messages.append({"role": "assistant", "content": resp.content}) results = [] for block in resp.content: if block.type == "tool_use": out = dispatch[block.name](**block.input) results.append({"type": "tool_result", "tool_use_id": block.id, "content": str(out)}) # TODO: append the tool_result message so the next turn sees it # messages.append( ??? ) raise RuntimeError("turn budget exhausted")
dispatch[block.name]tool_use_idfor _ in range(max_turns)role: userEverything is wired except the hand-off line. The table maps each tool name to a Python function. The max_turns guard is the that prevents a runaway loop.
messages.append({"role": "user", "content": results}) — the collected tool_result blocks go back as a user-role message. That is the tool_result hand-off; without it the loop stalls and Claude re-requests the same tool.
If you let an AI assistant generate a tool loop, verify these four things before trusting it, AI-written loops look right and fail subtly.
A loop that works on a happy-path demo is exactly the loop that loops forever or burns budget in production. This module names the real failure modes, gives the guard for each, and forces a decision on how much hardening a given deployment needs.
Production failure modes and the guards that catch each one.
Why this matters: It matters because the loop that works in a demo is the loop that loops forever in production.
Decision this forces: Decide the hardening level for the deployment: prototype, production, or high-stakes guards.
Knowing the happy path is not enough. These are the failures that show up only at scale, and the guard for each.
Claude never decides the goal is met and keeps calling tools. Guard: a hard plus a clear stop instruction in the system prompt.
A tool_result is missing or carries a mismatched tool_use_id, so the next turn re-observes old state and repeats the call. Guard: assert every tool_use has a matching before sending.
Claude fills an argument that is wrong, malformed, or dangerous (a destructive query, an out-of-range value). Guard: validate every argument against the input_schema and re-check at the before executing. Never trust model output as a safe instruction.
Every turn appends to the conversation. After many tool calls, early messages, including the original goal, get pushed out and Claude drifts. Guard: summarize or prune old tool_results and re-anchor the goal each turn.
First diagnostic signal: an agent that calls the same tool with the same arguments more than once almost always has a dropped or mismatched tool_result. Check the hand-off before you blame the model's reasoning.
| Option | Fit | Operational burden | Risk | When to choose | Cost | Complexity |
|---|---|---|---|---|---|---|
| Prototype guards | Internal demo or notebook where you watch every run. | Just a turn budget and basic logging. | Fine while a human is in the loop. | Use while exploring, with a human watching each run. | Low | Low |
| Production guards | User-facing agent calling read-only or low-risk tools. | Turn budget, schema validation, error tool_results, tracing. | Lower; failures are caught and logged. | Use when real users depend on the result. | Medium | Medium |
| High-stakes guards | Tools that write data, spend money, or touch production systems. | Adds approval gates, sandboxing, and replayable traces. | Highest blast radius if a guard is missing. | Use whenever a tool can take an irreversible action. | High | High |
Reconstruct the path from memory before reading on: what does a tool definition contain, what are the four moves of one tool-use round trip, which pattern is the right default, and which single missing line stalls the loop?
The answers: a tool is name + description + input_schema; the round trip is request, tool_use, run + tool_result, repeat; the default is a single tool call (escalate only when proven necessary); and the missing tool_result hand-off is what stalls the loop. The habit underneath all of it: keep every tool call observable, validated, and bounded by a stop condition.
Build a working single-tool Claude agent end to end: declare one tool (name, description, input_schema), run the request/tool_use/tool_result/answer loop with a turn budget, and validate the argument before executing. Then extend it: add a second tool so the loop must run two turns, and write down which failure mode from module 5 your guards now cover. Next rung: turn this into a multi-tool loop and study Anthropic's agent-building guidance on when orchestrator-workers is justified.
From memory first: what is the correct order of one complete Claude tool-use round trip?
In a tool loop, Claude requests an action with tool_use, your application performs it and returns tool_result, then Claude continues from that result.
A user asks Claude, “What is the current inventory count for SKU-481, and reorder it if it is below 20.” Why can’t a plain one-shot Claude call reliably complete this task by itself?
A one-shot model call cannot directly fetch live system state or perform external actions unless your application exposes tools and executes them.
Which set contains the essential parts Claude needs in a tool definition in order to decide when and how to call it?
Claude needs a clear tool name, description, and input schema so it can choose the tool and provide valid arguments.
You need to answer a question that may require looking up one customer record, then possibly sending one follow-up email based on the result. What is the smallest suitable agent pattern?
When later actions depend on earlier tool results, a simple tool loop is usually enough without jumping to multi-agent orchestration.
Name two production failure modes for Claude tool loops and one guard you would add for each.
Production hardening means matching likely loop failures to concrete guards such as validation, allowlists, stop limits, retries, and authorization checks.