The problem
A customer support assistant receives a user inquiry requiring two pieces of information: the current order status and the customer's payment history. The model responds with stop_reason: "tool_use" requesting two parallel tool invocations in the same turn: get_order_status and get_payment_history. The engineering team building the agent harness faces a design choice: execute both tools and return a single aggregated text summary, or execute each tool independently and return distinct tool_result blocks strictly mapped to their initiating tool_use_id.
The first approach seems cleaner on the surface — it produces a human-readable synthesis. But it violates the protocol contract: if one of the tool calls fails or returns an error, the model has no programmatic way to determine which invocation failed because individual execution provenance was erased. The second approach is slightly more verbose, but it is the only architectural pattern that preserves sufficient metadata for the agent loop to continue deterministically.
A tool loop is not "letting the model run wild." It is controlled delegation, where the execution harness is responsible for enforcing protocol integrity at every iteration.
The tool execution lifecycle without shortcuts
The tool use lifecycle follows a strict sequence:
- The model completes a turn with
stop_reason: "tool_use", emitting one or moretool_usecontent blocks, each bearing a uniqueid. - The host harness executes the corresponding tool function for each block.
- The harness constructs matching
tool_resultcontent blocks — one pertool_use_id— and injects them into the subsequent user turn in the conversation history. - The model continues generation with the returned tool outputs available in context — either requesting subsequent tools or concluding the turn with
stop_reason: "end_turn".
The most frequent production failure mode: when a model requests multiple independent tools concurrently in a single turn, they can be executed in parallel (assuming no inter-tool dependencies), but results must preserve strict association with their originating tool_use_id. Collapsing results into free text or dropping IDs causes loops to lose conversational state and hallucinate previous inquiries.
Where architectural decisions fail
A recurring distractor pattern in scenario questions: when an operation is fully deterministic (such as computing a cryptographic hash or formatting a date string), developers often delegate it to a model tool call, or worse, spawn an entire subagent. If an operation has an exact, unambiguous algorithmic implementation, execute it directly in conventional application code within the harness. Never burn latency and token costs on an LLM call — let alone a multi-agent orchestration layer — for something a pure function solves instantly.
The rule of thumb: reserve tool loops (and especially subagents) for steps requiring subjective judgment, ambiguity resolution, or external system I/O. Everything else is code.
Another common vulnerability is omitting explicit termination criteria. An agent loop that repeatedly requests tools because each intermediate completion seems "incomplete" drains operational budgets and induces timeouts. The harness must enforce a strict iteration ceiling; once reached, it returns an explicit "task uncompleted" state rather than letting the loop run until request timeouts crash the process.
Errors, ceilings, and cancellations are loop primitives
In production systems, a robust tool execution harness treats operational anomalies as first-class states:
- Tool execution errors: The
tool_resultmust explicitly communicate the failure (such as returning anis_error: trueflag and error payload), rather than masking the issue by returning an empty string. - User cancellation: When an in-flight operation is cancelled, the harness must clean up the state gracefully without leaving unfulfilled
tool_useblocks in the message history, which corrupts subsequent API requests. - Iteration and timeout ceilings: Explicit limits must enforce deterministic circuit breaking.
Put it into practice
In this lesson's lab, you will implement a Python tool execution harness: it processes concurrent tool invocations, maps results strictly by tool_use_id, identifies deterministic operations that should run in native code, and enforces iteration limits. Test assertions verify that invocation-to-result mapping remains resilient even when execution completion order differs from request order.