Task Statement 1.1·Domain 1 — 27% of exam
Agentic Loops
Design and implement agentic loops for autonomous task execution
Official Exam Guide Objectives
Task 1.1: Design and implement agentic loops for autonomous task execution.
Knowledge of
- The agentic loop lifecycle: sending requests to Claude, inspecting stop_reason ("tool_use" vs "end_turn"), executing requested tools, and returning results for the next iteration
- How tool results are appended to conversation history so the model can reason about the next action
- The distinction between model-driven decision-making (Claude reasons about which tool to call next based on context) and pre-configured decision trees or tool sequences
Skills in
- Implementing agentic loop control flow that continues when stop_reason is "tool_use" and terminates when stop_reason is "end_turn"
- Adding tool results to conversation context between iterations so the model can incorporate new information into its reasoning
- Avoiding anti-patterns such as parsing natural language signals to determine loop termination, setting arbitrary iteration caps as the primary stopping mechanism, or checking for assistant text content as a completion indicator
What You Need to Know
An agentic loop is the core execution cycle behind every Claude-based agent. It is deterministic control flow that you write in code — not a prompt trick, not a retry wrapper, not a chatbot turn. Claude never runs the loop; it answers one question per pass and your code decides what happens next. Get this lifecycle right and most of Domain 1 follows from it, because orchestration, hooks and session state all attach to points on this cycle. Get it wrong and the agent stops halfway through a task in production while passing every test you wrote.
The Agentic Loop Lifecycle
Four steps, repeated until the model reports that it is finished:
-
Issue the request. Call the Messages API carrying the entire conversation so far — the system prompt, every previous message, and the tool results produced on the last pass. The API is stateless, so anything absent from this payload does not exist as far as the model is concerned.
-
Branch on
stop_reason. The response carries a field stating why generation stopped. Two values drive a basic loop:"tool_use"— one or more tools have been requested. Another pass is needed."end_turn"— the model considers the work complete. Exit here.
-
When
stop_reasonis"tool_use": run each requested tool, add the results to the conversation as a new message, and return to step 1 with that extended history. -
When
stop_reasonis"end_turn": stop looping and hand the final response back to the caller.
One field decides whether the loop goes round again, step 1: messages[] holds the user's task — your code sends the whole array
The append in step 3 is the step people drop, and dropping it fails quietly. Conversation history is the only route by which a tool result reaches the model — there is no side channel and no server-side memory of the call. Skip the append and pass 2 is byte-identical to pass 1, so the model is asked an unchanged question and has no reason to give a changed answer. It will either reissue the same tool call indefinitely, or decide it cannot proceed and stop with the task half-done. Both look like the model behaving badly and both are your control flow.
Model-Driven Decision-Making
Inside the loop, tool selection belongs to the model: it reads the task, weighs the tools it has been described, and chooses. That is model-driven decision-making. Its opposite is a pre-configured decision tree or a fixed tool sequence, where the developer settles the order at design time and the model only supplies arguments.
The exam prefers the model-driven option, and the reason is worth carrying rather than memorising. A hard-coded sequence covers exactly the paths its author imagined; the first request that does not fit one falls through it. A model choosing at runtime handles that request, and chains tools in orders nobody wrote down. What you give up is predictability of order — which is precisely why it is the wrong choice whenever the order is itself the requirement.
That exception is standing rather than occasional: where business logic demands deterministic compliance — financial operations, security checks, regulatory requirements — programmatic enforcement overrides model flexibility, because "almost always in the right order" is not a property a regulator accepts. Task Statement 1.4 covers where that enforcement is implemented.
The Three Anti-Patterns
Three anti-patterns recur for loop termination. All three are worth recognising on sight, because the exam presents them as reasonable-looking code rather than as obvious errors.
Anti-Pattern 1: Parsing natural language signals. Deciding the loop is over because Claude said "I'm done" or "task complete". The model was never constrained to a vocabulary of completion phrases, so no list of phrases detects completion reliably — and the near misses are the dangerous part. "I've finished analysing the first file" is a completion sentence about an unfinished task, and it will end the run with four files unread. Expanding the phrase list does not converge on correctness, because the space of things the model might say is not enumerable. stop_reason exists to delete the interpretation step rather than improve it.
Anti-Pattern 2: Arbitrary iteration caps as the primary stopping mechanism. Making "stop after 10 loops" the mechanism that ends the run. This is wrong in both directions simultaneously: a task genuinely needing 12 iterations is truncated, and a task finishing in 3 keeps circling to the ceiling. The truncation is the worse half, because a cut-off run returns a partial answer with no marker distinguishing it from a complete one. The model is already reporting completion on every pass; a cap discards that report in favour of a number chosen before the task began. Caps remain legitimate as a safety net — an upper bound that stops a runaway agent burning tokens — but never as the mechanism that decides the work is done.
Anti-Pattern 3: Checking for assistant text content as a completion indicator. Using response.content[0].type == "text" to conclude the loop has finished. content is an array that can hold several blocks of different types in one response, and Claude routinely returns explanatory text alongside a tool_use block — "I'll now search for the customer's order history", followed immediately by the call itself. What sits at index [0] reflects what the model chose to say first, which carries no information about whether work remains. Moving the check to [1] does not repair it either: block ordering is a presentational detail, not an interface guarantee.
Common Exam Distractor
The exam frequently presents iteration caps as a plausible fix for premature termination. Reject these answers. A cap is an upper bound, so it can only ever stop a loop sooner — raising or lowering one cannot restart a loop that has already decided to exit. Caps address runaway execution; premature termination is always a stop_reason problem.
Practical Example: The Premature Termination Bug
A developer builds a customer support agent. Simple queries work; complex ones sometimes stop mid-task. Completion is decided by if response.content[0].type == "text".
What happens on a failing request: the customer asks something needing a lookup, and Claude returns a text block explaining its intent ("Let me look up your order") followed by a tool_use block requesting lookup_order. Index [0] holds text, the condition is satisfied, the loop exits, and the customer receives a promise that their order will be looked up — while nothing looks it up. No error is raised anywhere, because from the code's point of view the agent finished normally.
Why it survived testing is the instructive part. Simple queries need no tools, so the model answers them with a single text block and the check happens to be correct. The condition only misfires on responses that contain a tool call — the path that is most awkward to cover in a unit test and most common in production traffic.
The fix is to stop inferring completion from output and read the field that states it: continue while stop_reason == "tool_use", terminate on stop_reason == "end_turn". That condition is indifferent to how many content blocks came back, what types they are, and what order they arrived in.
Text alongside a tool call is not a finish line, step 1: One reply — stop_reason is a field on it, content is an array of blocks
Deep Dive
The complete stop_reason enum
The exam keys on two values. The API reference documents seven, and a production loop has to branch on all of them. Every one of these arrives on a successful HTTP 200 response with valid content — stop reasons are part of the response body, not errors. Errors are 4xx/5xx and carry error details instead.
stop_reason | What it means |
|---|---|
end_turn | The model reached a natural stopping point |
max_tokens | Generation exceeded the requested max_tokens, or the model's own maximum |
stop_sequence | One of your custom stop_sequences was generated (read the response's stop_sequence field to see which) |
tool_use | The model invoked one or more tools |
pause_turn | A long-running turn was paused; you may hand the response back as-is in a subsequent request to let the model continue |
refusal | Streaming classifiers intervened to handle a potential policy violation |
model_context_window_exceeded | The response filled the model's context window |
Anthropic's guidance is blunt: "Make it a habit to check the stop_reason in your response handling logic." The same page draws the line between stop reasons and errors: "stop reasons indicate why generation stopped normally (successful responses), while errors indicate request processing failures."
Sourcesplatform.claude.com › messagesplatform.claude.com › handling-stop-reasonsfetched 2026-07-30
Correct loop control for each value
The documented handling per value maps directly onto branch arms in your loop. Note that only tool_use continues the loop by executing tools; pause_turn continues it by resending unchanged.
| Value | Documented handling |
|---|---|
end_turn | Use the response |
tool_use | Run the tool and return the result |
pause_turn | Add the assistant's response to your messages array and make another API request to let Claude continue |
max_tokens | Raise max_tokens or continue the response |
stop_sequence | Read stop_sequence to see which one fired |
refusal | Read stop_details and retry on a fallback model |
model_context_window_exceeded | Treat the response as truncated |
pause_turn is the one most loops forget. It fires when a server-tool loop reaches its internal iteration limit, and the docs are explicit that "your application should handle pause_turn in any agent loop that uses server tools."
Sourceplatform.claude.com › handling-stop-reasonsfetched 2026-07-30
The canonical loop, and its exit condition
The documented shape is a while loop keyed on stop_reason:
// 1. Send a request with your tools array and the user message.
// 2. Claude responds with stop_reason "tool_use" and one or more tool_use blocks.
// 3. Execute each tool. Format the outputs as tool_result blocks.
// 4. Send a new request containing the original messages, the assistant's
// response, and a user message with the tool_result blocks.
// 5. Repeat from step 2 while stop_reason is "tool_use".
while (response.stop_reason === "tool_use") {
// ...
}The exit condition is stated as the inverse: "the loop exits on any other stop reason (end_turn, max_tokens, stop_sequence, or refusal), which means Claude has either produced a final answer or stopped for another reason that your application should handle." That phrasing matters — exiting the loop is not the same as the task being complete.
Two structural facts underpin the whole design. First, "the model never executes anything on its own. It emits a structured request, your code (or Anthropic's servers) runs the operation, and the result flows back into the conversation." Second, "the model can't run your code, so every tool call is a round trip: the model asks, you execute, you report back, the model continues."
Sourceplatform.claude.com › how-tool-use-worksfetched 2026-07-30
How tool_result blocks are appended — the placement rules
Appending tool results is not "add a message somewhere". There are hard formatting rules, and breaking them produces a 400 rather than degraded behaviour.
- A
tool_useblock carries three fields:id(unique, used to match the result later),name, andinput(conforming to the tool'sinput_schema). - Results go back in a user-role message containing
tool_resultblocks, each withtool_use_idmatching the request'sid, optionalcontent, and optionalis_error. - "Tool result blocks must immediately follow their corresponding tool use blocks in the message history. You cannot include any messages between the assistant's tool use message and the user's tool result message."
- "In the user message containing tool results, the tool_result blocks must come FIRST in the content array. Any text must come AFTER all tool results."
- An empty result is valid: a
tool_resultblock may carry onlytypeandtool_use_id. - On failure, return the error text as
contentwith"is_error": true; Claude "will then incorporate this error into its response to the user." Distinct mechanism, same page: if Claude's own tool request is invalid or missing parameters, "Claude will retry 2-3 times with corrections before apologizing to the user" — that retry loop is about malformed calls, not about your tool failing at execution time.
There is no tool or function role in this API: "user messages include client content and tool_result, while assistant messages contain AI-generated content and tool_use."
Sourceplatform.claude.com › handle-tool-callsfetched 2026-07-30
Parallel tool calls in one iteration
"By default, Claude may call multiple tools in a single response," and Claude 4 and later models do so by default when the request benefits from it. Your loop must therefore treat tool_use as n calls, not one:
- Return "one
tool_resultfor eachtool_useblock, all together in the next user message", matched bytool_use_id, with everytool_resultbefore any text. - Execution order is yours to choose: "you can run the calls concurrently (
Promise.all,asyncio.gather), sequentially in the order they appear, or in any combination". - Even a call you decline to run needs a result: return it "with
is_error: trueand a brief explanation". - Splitting results across separate user messages "teaches" Claude to avoid parallel calls in future turns — the two rules that keep parallelism alive are one user message for all results, and no text before them.
To restrict this, disable_parallel_tool_use: true goes inside the tool_choice object, not at the top level. With tool_choice auto it means at most one tool per response; with any or tool it means exactly one.
Sourceplatform.claude.com › parallel-tool-usefetched 2026-07-30
Iteration caps are a safety bound, not a stop condition
Anthropic's own agent guidance treats caps as control, not completion: it is "common to include stopping conditions (such as a maximum number of iterations) to maintain control", precisely because "the autonomous nature of agents means higher costs, and the potential for compounding errors". The recommendation that goes with it is extensive sandboxed testing plus guardrails — not shorter loops.
The Agent SDK exposes the same idea as a first-class option, maxTurns/max_turns, documented in the SDK reference as "maximum agentic turns (tool-use round trips)". It bounds the loop; it never tells you the task finished. Completion is still stop_reason.
An iteration cap is a fuse, not a finish line, step 1: Three runs of the same agent — each cap is set before the loop starts
Sourcesanthropic.com › building-effective-agentscode.claude.com › typescriptfetched 2026-07-30
Why text alongside tool_use is normal, and why forcing tools backfires
Under the default tool_choice of {"type": "auto"}, "Claude determines on each turn whether to call a tool or respond directly" (tool-use overview), and "Claude often comments on what it's doing or responds naturally to the user before calling tools". The docs add the warning that kills anti-pattern 3 outright: "your code should treat these responses like any other assistant-generated text, and not rely on specific formatting conventions."
The distractor fix — forcing tool_choice: "any" so the model never returns bare text — has a documented side effect: "when you have tool_choice as any or tool, the API prefills the assistant message to force a tool to be used", so the model emits no natural-language response before the tool_use blocks even if asked to. It also cannot be combined with manual extended thinking, where only auto and none are supported (adaptive thinking, including models where thinking is on by default, does support forced tool use), and changing tool_choice invalidates cached message blocks under prompt caching.
Sourcesplatform.claude.com › define-toolsplatform.claude.com › overviewfetched 2026-07-30
Quick Reference
| Fact | Value |
|---|---|
| Exam's two loop-control values | tool_use (continue), end_turn (terminate) |
Full stop_reason enum | end_turn, max_tokens, stop_sequence, tool_use, pause_turn, refusal, model_context_window_exceeded |
| Loop exits on | Any stop reason other than tool_use |
pause_turn handling | Append the assistant response to messages and re-request — do not treat as done |
refusal handling | Read stop_details, retry on a fallback model |
model_context_window_exceeded handling | Treat the response as truncated |
| Stop reason vs error | Stop reasons are in a 200 response body; errors are HTTP 4xx/5xx |
tool_use block fields | id, name, input |
tool_result block fields | tool_use_id, optional content, optional is_error |
Role carrying tool_result | user (there is no tool or function role) |
| Placement rule 1 | tool_result message must immediately follow the tool_use message |
| Placement rule 2 | All tool_result blocks come before any text in that message |
| Parallel calls | One tool_result per tool_use, all in one user message; skipped calls get is_error: true |
| Turning off parallelism | disable_parallel_tool_use: true inside tool_choice, not top-level |
Default tool_choice when tools supplied | auto — Claude may return text, a tool call, or both |
tool_choice: "any"/"tool" side effect | API prefills the assistant message; no natural-language text before tool_use |
| Iteration cap role | Safety bound to maintain control; SDK equivalent is max_turns (tool-use round trips) |
| Invalid tool call behaviour | Claude retries 2–3 times with corrections before apologising |
Exam Traps
Exam Trap
Using response.content[0].type == 'text' to determine loop completion
content is an array, and a single response can hold an explanatory text block and a tool_use block together. Index [0] records what the model said first, not whether work remains — and checking [1] instead is no safer, because block order is not an interface guarantee. Branch on stop_reason.
Exam Trap
Setting arbitrary iteration caps (e.g., 'stop after 10 loops') as the primary stopping mechanism
A cap truncates a task that needed 12 iterations and keeps circling on one that finished in 3, and the truncated run returns a partial answer indistinguishable from a complete one. Keep caps as a runaway safety net; let stop_reason decide completion.
Exam Trap
Parsing natural language phrases like 'I'm done' or 'task complete' to decide loop termination
Nothing constrains the model to a vocabulary of completion phrases, so the phrase list never converges — and "I've finished analysing the first file" ends the run with four files unread. stop_reason removes the interpretation step rather than improving it.
Exam Trap
Forcing tool_choice to 'any' to prevent the agent from returning text
Requiring a tool call on every response removes the model's ability to end its own turn, so the loop loses the only exit condition it had and runs until something external stops it. Let completion be signalled naturally through stop_reason.
Practice Scenario
A developer's agent sometimes terminates prematurely when Claude returns text alongside a tool call. Their loop checks response.content[0].type == 'text' to determine if the agent is finished. Users report incomplete responses on complex queries. What should the developer change?
Build Exercise
Build a Multi-Tool Agent Loop
Difficulty: Intermediate (2/4)
45 minutes
What you'll learn
- How the agentic loop lifecycle works with the Messages API
- Why stop_reason is the authoritative signal for loop control
- How to handle tool_use and end_turn stop_reason values correctly
- How to append tool results to conversation history for multi-turn execution
- When safety iteration caps are appropriate versus inappropriate as stopping mechanisms
- Set up a Claude API client with two tools: a calculator tool (accepts expression, returns result) and a web search stub (accepts query, returns mock results)
Why: Multi-tool setups expose model-driven decision-making — Claude must select the right tool based on context, which is core to agentic architecture.
You should see: Two tool definitions registered with proper JSON Schema input_schema, each with name, description, and parameters.
- Implement the agentic loop that sends requests to Claude and inspects stop_reason after each response
Why: The agentic loop is the core execution pattern — the exam tests whether you use stop_reason (deterministic) versus content-type checks or natural language parsing (unreliable).
You should see: A while loop that calls client.messages.create() and checks response.stop_reason after each iteration.
- Handle the tool_use stop_reason by executing the requested tool, creating a tool result message, and appending it to conversation history
Why: This is the critical handoff in the loop — the exam specifically tests whether you correctly extract tool calls, execute them, and return results in the right message format.
You should see: When Claude requests a tool, your code extracts the tool_use block, runs the corresponding function, and appends both the assistant response and a user message with tool_result to the conversation.
- Handle the end_turn stop_reason by extracting and returning the final response
Why: end_turn is Claude signal that it has completed the task — extracting the final text response correctly closes the loop and returns the result to the user.
You should see: When stop_reason is end_turn, your loop exits and returns the text content from the final response.
- Test with a prompt that requires multiple sequential tool calls (e.g., search for a value then calculate something with it) and verify the loop continues correctly through all iterations
Why: Sequential tool calls test the full loop lifecycle — the agent must complete one tool call, receive the result, reason about it, and decide to call another tool before finally returning.
You should see: At least two tool call iterations before end_turn. The agent searches first, uses the search result in a calculation, then returns the combined answer.
- Add a safety iteration cap of 20 as a maximum bound (not the primary stopping mechanism) and log a warning if it triggers
Why: The exam distinguishes safety caps (acceptable as a fallback) from using caps as the primary stopping mechanism (an anti-pattern). Your cap should never trigger in normal operation.
You should see: A MAX_ITERATIONS constant, a counter that increments each loop, and a warning log if the cap is hit. Normal queries should terminate via stop_reason well before reaching 20.
Sources
- Claude Agent SDK Overview — Anthropic
- Messages API Reference — Anthropic
- Building with Claude API (Skilljar) — Anthropic
- Messages API — request and response reference — Anthropic
- Handling stop reasons — Anthropic
- How tool use works — Anthropic
- Handle tool calls — Anthropic
- Parallel tool use — Anthropic
- Define tools — Anthropic
- Building Effective Agents — Anthropic
Appendix A — Build Exercise Step Hints
Progressive hints revealed by the "Stuck? Get a nudge" control on each step.
Step 1. Set up a Claude API client with two tools: a calculator tool (accepts expression, returns result) and a web search stub (accepts query, returns mock results)
Why: Multi-tool setups expose model-driven decision-making — Claude must select the right tool based on context, which is core to agentic architecture.
You should see: Two tool definitions registered with proper JSON Schema input_schema, each with name, description, and parameters.
Stuck? Get a nudge
Step 2. Implement the agentic loop that sends requests to Claude and inspects stop_reason after each response
Why: The agentic loop is the core execution pattern — the exam tests whether you use stop_reason (deterministic) versus content-type checks or natural language parsing (unreliable).
You should see: A while loop that calls client.messages.create() and checks response.stop_reason after each iteration.
Stuck? Get a nudge
Step 3. Handle the tool_use stop_reason by executing the requested tool, creating a tool result message, and appending it to conversation history
Why: This is the critical handoff in the loop — the exam specifically tests whether you correctly extract tool calls, execute them, and return results in the right message format.
You should see: When Claude requests a tool, your code extracts the tool_use block, runs the corresponding function, and appends both the assistant response and a user message with tool_result to the conversation.
Stuck? Get a nudge
Step 4. Handle the end_turn stop_reason by extracting and returning the final response
Why: end_turn is Claude signal that it has completed the task — extracting the final text response correctly closes the loop and returns the result to the user.
You should see: When stop_reason is end_turn, your loop exits and returns the text content from the final response.
Stuck? Get a nudge
Step 5. Test with a prompt that requires multiple sequential tool calls (e.g., search for a value then calculate something with it) and verify the loop continues correctly through all iterations
Why: Sequential tool calls test the full loop lifecycle — the agent must complete one tool call, receive the result, reason about it, and decide to call another tool before finally returning.
You should see: At least two tool call iterations before end_turn. The agent searches first, uses the search result in a calculation, then returns the combined answer.
Stuck? Get a nudge
Step 6. Add a safety iteration cap of 20 as a maximum bound (not the primary stopping mechanism) and log a warning if it triggers
Why: The exam distinguishes safety caps (acceptable as a fallback) from using caps as the primary stopping mechanism (an anti-pattern). Your cap should never trigger in normal operation.
You should see: A MAX_ITERATIONS constant, a counter that increments each loop, and a warning log if the cap is hit. Normal queries should terminate via stop_reason well before reaching 20.
Stuck? Get a nudge
Appendix B — Interactive Study Prompts
Two prompts to paste into Claude. B1 drills the judgement the exam actually measures; B3 reviews the code you wrote for the Build Exercise above. The exam simulator between them is the interactive quiz on this page.
B1. Concept Check — Discrimination Drill
You are examining me for the Claude Certified Architect – Foundations (CCAR-F) exam, Domain 1: Agentic Architecture & Orchestration (27% of the exam), Task Statement 1.1: Agentic Loops. Use British English throughout.
What this exam actually measures. Not one item on the official exam asks what something is. Every item drops you into a production system that is already misbehaving, offers four defensible engineering responses, and asks which is best. The skill being tested is proportionality: fix the root cause with the cheapest instrument that gives the guarantee the situation demands. So do not quiz me on definitions. Make me choose between options that are both defensible, then attack whatever I chose.
How to run this session.
- One question at a time. Stop and wait. Never answer your own question, and never move on until I have committed.
- Never reveal which option is right before I commit to one.
- Do not praise me. A correct answer earns "Yes" and the next question. If I am right for the wrong reason, say so — that is the failure that costs marks on exam day.
- When I am wrong, quote the exact phrase in my answer that gave it away, correct it in one sentence, and move on. One correction at a time.
- If I write something fluent but empty, name it: "That is a restatement, not a reason."
- Set every scenario inside one of the exam's production contexts: the Customer Support Resolution Agent (Agent SDK, MCP tools
get_customer,lookup_order,process_refund,escalate_to_human), the Multi-Agent Research System (a coordinator delegating to web-search, document-analysis, synthesis and report-generation subagents), or Developer Productivity with Claude (an agent over an unfamiliar codebase usingRead,Write,Bash,Grep,Glob).
Session plan — about twelve questions.
Round 1 — Anchor (1 question). One concrete question to check I have actually read the material. If I cannot answer it, stop the session and tell me to read the lesson before continuing.
Round 2 — Discrimination (5 questions). Each one: describe a symptom in one of the contexts above, with a number or a log observation in it. Offer exactly two responses, both defensible. Ask me to pick one and justify it in a single sentence. Then argue the case for the option I rejected as strongly as you can, and ask whether I am holding or changing my answer. Only after I answer that, tell me which is right and why the other one is the more tempting trap.
Round 3 — Proportionality (2 questions). Take one symptom and run it twice with different stakes: once where the cost of an error is a wasted retry, once where it is an incorrect refund or a corrupted production branch. The right answer must change between the two. If I answer the same way both times without noticing the stakes moved, that is the finding — tell me.
Round 4 — Code review (3 questions). Present a colleague's confident proposal containing one of the trap errors listed below, written the way a teammate would write it in a pull request. Ask me what is wrong with it. Do not signal that anything is wrong.
Round 5 — Verdict. Rate me green, amber or red on each concept below. Name the single weakness most likely to cost me marks, and give me one specific next action: a section of this lesson to re-read, or a step of the Build Exercise to redo. If I am not ready for this task statement, say so plainly.
Concepts in scope
- Agentic loop lifecycle — send a request to Claude via the Messages API including conversation history; inspect
stop_reason; if it istool_use, execute the requested tools, append the results to conversation history and send the updated conversation back; if it isend_turn, the agent is finished. stop_reasonas the control signal — the authoritative, deterministic signal for loop control.tool_usemeans continue;end_turnmeans exit. It replaces all natural-language parsing and heuristic termination.- Tool results in conversation history — results must be appended so the model can reason about the new information on the next iteration. Without them Claude cannot incorporate tool output into its reasoning chain.
- Model-driven decision-making — Claude reasons about which tool to call from context rather than following a pre-configured decision tree, which buys flexibility but is overridden by programmatic enforcement where business logic is critical.
- Safety caps versus stopping mechanisms — an iteration cap is a legitimate bound on runaway cost and an illegitimate way to decide the agent has finished.
Trap errors to plant in Round 4
- Using
response.content[0].type == "text"to decide the loop is complete, when Claude can return text alongsidetool_useblocks in the same response. - Setting an arbitrary iteration cap as the primary stopping mechanism rather than as a safety net.
- Parsing natural-language phrases such as "I'm done" or "task complete" to decide termination.
- Forcing
tool_choiceto"any"to stop the agent returning text, which prevents it from ever signalling completion.
Stay inside the material above. If I raise something outside it, tell me it is out of scope for this task statement and return to the drill. Begin with Round 1.
B2. Exam Simulator
Exam simulator
Question 1 of 10
Scenario · Customer Support Resolution Agent
Your support agent returns an incomplete reply on roughly 8% of multi-step requests: it says "Let me look up your order" and stops there. The loop decides the agent has finished whenever the first content block of a response is text. What change would most effectively address this?
B3. Build Coach — Code Review
The Build Exercise and its hint ladder are already on this page. This prompt is for the one thing the page cannot do: review the code you actually wrote.
You are a staff engineer reviewing my implementation of a build exercise for the Claude Certified Architect – Foundations exam, Domain 1, Task Statement 1.1: Agentic Loops. Use British English throughout.
I am building a multi-tool agent loop: a Claude API client with a calculator tool and a web-search stub, driven by a loop that inspects stop_reason, executes requested tools, appends the results to conversation history, and returns the final text on end_turn.
It has to satisfy all of the following:
- The loop uses
stop_reasonas its sole primary control mechanism. - Tool results are appended to conversation history in the correct message format, with the assistant response preserved alongside them.
- The agent handles a multi-turn chain — search, then calculate using the search result — before finishing.
- A safety iteration cap exists as a bound, and never triggers during normal operation.
- No natural-language parsing and no content-type checking anywhere in the loop control.
How to review.
- Ask me to paste my code. If I have not pasted any, ask for it and nothing else. Do not write the implementation for me, do not offer a reference solution, and do not fill in a step I have skipped.
- Work through the criteria above in order. For each one, quote the line of my code that satisfies it, or say plainly that nothing does.
- Then hunt for the failure modes below. Each is a real production bug, not a style preference.
- Rank everything you find: (1) would fail in production, (2) would lose marks on the exam, (3) style. Give me the first item under (1) and then stop — wait for my fix before giving me the next one.
- If my code satisfies everything, do not congratulate me. Change the requirements — two tools now return
tool_useblocks in the same response, and one of them fails — and make me handle it. - If I ask you to just write it for me, refuse once and give me the smallest nudge that would unblock me instead.
Failure modes to probe
- Loop control keyed on content rather than on
stop_reason, so a response carrying text alongside a tool call ends the loop early. - The assistant turn not being appended before the
tool_resultturn, so the tool result has no call to attach to. - Only the first
tool_useblock handled when a response contains several, leaving the remaining calls unanswered. - A tool error thrown rather than returned to the model, so Claude never learns the call failed and cannot recover.
- The iteration cap doing the work that
stop_reasonshould be doing, which hides the real bug rather than fixing it.
Start by asking me for my code.