CertSafari
    Anthropic Claude Certified Architect – Foundations (CCAR-F)· Lessons

    Domain 1 · Lesson 1/30

    Agentic loop lifecycle: stop_reason control flow and tool results

    Design and implement agentic loops for autonomous task execution

    14 min read
    3.86% of exam
    5 sources
    Published 4 Oct 2026
    Docs as of 30 Sep 2026

    What you will be able to do

    • Describe the five steps of a client-side agentic loop, from the first request to the final answer
    • Write loop control flow that keeps going on stop_reason "tool_use" and stops on "end_turn" and the other exit values
    • Add tool_use and tool_result blocks to the conversation history correctly so Claude can use each result on the next iteration
    • Explain why Claude, not a pre-configured sequence of tools, chooses the next action in an agentic loop, and tell a workflow from an agent
    • Avoid the three termination anti-patterns: parsing natural language, treating an iteration cap as the primary stop, and treating assistant text as a completion signal

    Key concept

    stop_reason-driven loop — An agentic loop is a while loop controlled by one field in each API response. When stop_reason is "tool_use", you run the requested tools, add the results to the history and send another request. Any other value ends the loop.

    1.The request, execute, return cycle

    Tool use is a contract between your application and Claude. You declare which operations exist and what shape their inputs take, and Claude decides when to call them. Claude never runs anything itself. It sends back a structured request, your code performs the operation, and the result goes back into the conversation. Claude never sees your implementation. It sees only the schema you defined and the result you return.

    Claude can't run your code, so every call to a client-executed tool is a round trip, and your application has to drive it. The agentic loop is that round trip, repeated until the task is done. The documentation gives it a standard five-step shape:

    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 and 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 for as long as stop_reason is "tool_use".

    The Agent SDK describes the same cycle from the inside. Claude receives the prompt along with the system prompt, tool definitions and conversation history. It evaluates the current state and decides what to do next. The SDK runs the tools Claude requested, and the cycle starts again. The SDK counts each full cycle as one turn.

    Checkpoint 1 of 7· Put it in order

    Put the steps of one client-side agentic loop iteration in order

    1. 1.Send a new request with the original messages, the assistant's response, and a user message of tool_result blocks
    2. 2.Repeat while stop_reason is still "tool_use"
    3. 3.Send a request with the tools array and the user message
    4. 4.Receive a response with stop_reason "tool_use" and one or more tool_use blocks
    5. 5.Execute each tool and format the outputs as tool_result blocks

    Sources12

    2.Branching on stop_reason

    A single field decides which way the loop goes. Every Messages API response includes a stop_reason that says why Claude stopped generating. The loop's control flow depends on that field and nothing else in the response.

    The rule is short. If stop_reason is "tool_use", run the tools and continue the conversation. On any other value, leave the loop: either Claude has given its final answer, or it stopped for a reason your application has to deal with separately. "end_turn" is the normal exit and means Claude finished its response naturally. The other values are exits too, but they don't mean the work succeeded.

    stop_reason values and what an agentic loop does with each
    stop_reasonWhen it occursWhat to do
    tool_useClaude is calling a toolRun the tool, return the result, iterate again
    end_turnClaude finished its response naturallyUse the response; the loop is done
    max_tokensThe response reached your max_tokens limitRaise max_tokens or continue the response
    pause_turnA server-tool loop reached its iteration limitSend the assistant content back to continue
    refusalClaude declined to respondRead stop_details and retry on a fallback model

    A simple request shows what this means in practice. The loop doesn't need a tool call before it can end. The Agent SDK says Claude keeps calling tools and processing the results until it produces a response with no tool calls. If a question can be answered directly, that happens on the very first request. A first-iteration "end_turn" with only text means the task is finished. It isn't a bug.

    The documentation recommends checking stop_reason explicitly in your response-handling code and giving each value its own branch:

    Dispatching on every stop_reason value; end_turn falls through to the default case that returns the textpython
    def handle_response(response):
        match response.stop_reason:
            case "tool_use":
                return handle_tool_use(response)
            case "max_tokens":
                return handle_truncation(response)
            case "model_context_window_exceeded":
                return handle_context_limit(response)
            case "pause_turn":
                return handle_pause(response)
            case "refusal":
                return handle_refusal(response)
            case _:
                # Handle end_turn and other cases
                return next(
                    (block.text for block in response.content if block.type == "text"),
                    "",
                )

    Checkpoint 2 of 7· Fill the gap

    Which stop_reason value belongs in the blank so that this code runs the requested tools?

    if response.stop_reason == " ? ":
        # Extract and execute the tool
        for block in response.content:
            if block.type == "tool_use":
                result = execute_tool(block.name, block.input)
                # Return result to Claude for final response

    Checkpoint 3 of 7· Exam question

    An architect is building a client-side agent loop against the Messages API for a data-cleanup task. The first response has stop_reason set to "tool_use" and contains a tool_use block requesting a file-listing tool. What should the loop do next to correctly continue the agentic execution?

    Sources312

    3.Adding tool results to the conversation history

    Running the tool is only half of each iteration. On every request, Claude sees the prompt, system prompt, tool definitions and the conversation history you send. If the result isn't in that history, Claude can't use it to decide what to do next.

    Each tool_use block has three fields: an id, a name, and an input that matches the tool's input_schema. The id lets you pair each result with its request. Your tool_result block repeats that id in its tool_use_id field, and it goes inside a message with the user role:

    A tool_result returned in a user-role message, paired with its tool_use block by tool_use_idjson
    {
      "role": "user",
      "content": [
        {
          "type": "tool_result",
          "tool_use_id": "toolu_01A09q90qw90lq917835lq9",
          "content": "15 degrees"
        }
      ]
    }

    The next request must contain three things: the original messages, the assistant's complete response including its tool_use blocks, and this user message. The continuation message needs one tool_result for every tool_use block in the response. If one is missing, the API rejects the request with an error saying tool_use ids were found without tool_result blocks immediately after them. Once the result is in the history, Claude uses it to continue answering the original prompt.

    Failures go back to Claude in the same way. If the tool throws an error, for example a network failure, return the error message as the content and set "is_error": true. Claude can then explain the failure to the user, or try the call again with the missing information filled in.

    One detail of message structure matters a lot. Don't put a text block after the tool_result blocks. The documentation warns that this teaches Claude to expect user input after every tool use. Claude may then end its turn early with an empty "end_turn" response.

    Checkpoint 4 of 7· Check yourself

    After Claude returns stop_reason "tool_use", which history update lets it use the result on the next request?

    Checkpoint 5 of 7· Exam question

    A junior engineer proposes capping an autonomous research agent at exactly 5 tool-call iterations and treating that cap as the loop's primary stopping mechanism, regardless of stop_reason. What is the main architectural problem with relying on a fixed iteration cap this way?

    Sources234

    4.Model-driven decisions versus pre-configured sequences

    Nothing in the loop you have just seen says which tool runs next. Your code defines the available operations and their schemas, and Claude determines when and how to call them. On each iteration Claude evaluates the current state, which includes every tool_result already in the history, and decides how to proceed: call another tool, call several, or give a final answer. The result of one call can change what it does next.

    This is what separates an agent from a workflow. Anthropic draws the line this way: in a workflow, LLMs and tools are orchestrated through predefined code paths. In an agent, the LLM dynamically directs its own process and tool usage. A hard-coded sequence such as "always call search, then fetch, then summarise", or a decision tree your code walks regardless of what the results say, is a workflow. The loop only counts as agentic when the choice of the next action comes from Claude's reasoning over the history.

    The two are not ranked. Workflows offer predictability and consistency for well-defined tasks. Agents are the better option when flexibility and model-driven decision-making are needed. Your loop's job in the agentic case is to stay mechanical: run what Claude asked for, return the results, and let stop_reason say when it is finished.

    Checkpoint 6 of 7· Match them up

    Match each term to its description

    Tap a term, then the definition that fits it.

    Sources125

    5.Termination anti-patterns

    Because stop_reason is the loop's control signal, three tempting shortcuts are wrong.

    1. Parsing natural language to decide when to stop. Scanning Claude's prose for phrases such as "I'm done" or "task complete" is fragile. The tool-use documentation gives the general rule: if you're writing a regex to extract a decision from model output, that decision should have been a tool call. For termination, the structured signal already exists, and it is stop_reason.

    2. Making an iteration cap the primary stopping mechanism. The SDK loop runs until Claude produces a response with no tool calls. A limit such as max_turns or max_iterations is a safeguard, and the documentation's own example uses it to prevent runaway sessions. Hitting the cap is not a normal finish. The result subtype becomes error_max_turns, which means the turn limit was hit before the task finished, and no result field is available. If your loop routinely ends this way, the cap is doing the job that stop_reason should do.

    3. Treating assistant text as a completion indicator. A response can carry text, tool calls, or both. Claude often writes a sentence such as "I'll check the weather" in the same response as a tool_use block, and that response has stop_reason "tool_use". If you stop because text is present, you abandon the task mid-step. In the other direction, a response with no text can still be a legitimate end_turn. Branch on stop_reason, not on whether text exists.

    Checkpoint 7 of 7· Check yourself

    A response contains a text block followed by a tool_use block, and its stop_reason is "tool_use". What should the loop do?

    Sources12

    Exam traps

    Each one states something that sounds right. Open it to see what is actually true.

    1. 1.A task must go through at least one tool call before an "end_turn" response counts as a valid finish.Why is that wrong?

      The loop ends at the first response with no tool calls, and that can be the very first response. A text-only "end_turn" on iteration one means the task is complete.

      Covered in Branching on stop_reason

    2. 2.Adding the assistant's text to the history is enough to keep the loop going, and the tool_use and tool_result blocks can be left out.Why is that wrong?

      The next request must include the assistant's full response and a user message with the tool_result blocks. Without them Claude has no record of the result and may ask for the same call again.

      Covered in Adding tool results to the conversation history

    3. 3.A loop that always calls the same tools in a fixed order is an agent, because it uses tool_use and tool_result blocks.Why is that wrong?

      Using the tool API does not make a loop agentic. When code fixes the path, it is a workflow; an agent is one where the LLM dynamically directs its own process and tool usage.

      Covered in Model-driven decisions versus pre-configured sequences

    4. 4.Setting max_turns is the correct way to decide when an agentic loop is finished.Why is that wrong?

      The loop finishes when Claude returns a response with no tool calls. A turn cap prevents runaway sessions, and reaching it is reported as the error_max_turns result, not as success.

      Covered in Termination anti-patterns

    Sources

    Every claim above is drawn from one of these pages, quoted as it was written on the date shown.

    1. 1.
      “The model never executes anything on its own.”
      ↩︎ The request, execute, return cycle
      “Claude never sees your implementation; it only sees the schema you provided and the result you returned.”
      ↩︎ The request, execute, return cycle
      “Client-executed tools (both user-defined and Anthropic-schema) require your application to drive a loop.”
      ↩︎ The request, execute, return cycle
      “In practice this reads as: while stop_reason == "tool_use", execute the tools and continue the conversation.”
      ↩︎ Branching on stop_reason
      “Claude determines when and how to call them.”
      ↩︎ Model-driven decisions versus pre-configured sequences
      “if you're writing a regex to extract a decision from model output, that decision should have been a tool call”
      ↩︎ Termination anti-patterns
      “In practice this reads as: while stop_reason == "tool_use", execute the tools and continue the conversation.”
      ↩︎ Key concept
      “Send a new request containing the original messages, the assistant's response, and a user message with the tool_result blocks.”
      ↩︎ Exam trap 2
      “Send a new request containing the original messages, the assistant's response, and a user message with the tool_result blocks.”
      ↩︎ Checkpoint
      “The loop exits on any other stop reason ("end_turn", "max_tokens", "stop_sequence", or "refusal")”
      ↩︎ Prediction
    2. 2.
      “Steps 2 and 3 repeat as a cycle. Each full cycle is one turn.”
      ↩︎ The request, execute, return cycle
      “Claude continues calling tools and processing results until it produces a response with no tool calls.”
      ↩︎ Branching on stop_reason
      “Claude receives your prompt, along with the system prompt, tool definitions, and conversation history.”
      ↩︎ Adding tool results to the conversation history
      “Claude evaluates the current state and determines how to proceed.”
      ↩︎ Model-driven decisions versus pre-configured sequences
      “Prevent runaway sessions”
      ↩︎ Termination anti-patterns
      “Hit the maxTurns limit before finishing”
      ↩︎ Termination anti-patterns
      “It may respond with text, request one or more tool calls, or both.”
      ↩︎ Termination anti-patterns
      “Claude continues calling tools and processing results until it produces a response with no tool calls.”
      ↩︎ Exam trap 1
      “Hit the maxTurns limit before finishing”
      ↩︎ Exam trap 4
    3. 3.
      “Every Messages API response includes a stop_reason field that tells you why Claude stopped generating.”
      ↩︎ Branching on stop_reason
      “The continuation is a user message of tool_result blocks, one for every tool_use block in the response”
      ↩︎ Adding tool results to the conversation history
      “tool_use ids were found without tool_result blocks immediately after”
      ↩︎ Adding tool results to the conversation history
      “Never add text blocks immediately after tool results: This teaches Claude to expect user input after every tool use.”
      ↩︎ Adding tool results to the conversation history
    4. 4.
      “After receiving the tool result, Claude will use that information to continue generating a response to the original user prompt.”
      ↩︎ Adding tool results to the conversation history
      “you can return the error message in the content along with "is_error": true”
      ↩︎ Adding tool results to the conversation history
      “id: A unique identifier for this particular tool use block. This will be used to match up the tool results later.”
      ↩︎ Checkpoint
    5. 5.
      “Workflows are systems where LLMs and tools are orchestrated through predefined code paths.”
      ↩︎ Model-driven decisions versus pre-configured sequences
      “systems where LLMs dynamically direct their own processes and tool usage”
      ↩︎ Model-driven decisions versus pre-configured sequences
      “Workflows are systems where LLMs and tools are orchestrated through predefined code paths.”
      ↩︎ Exam trap 3

    Continue to page 2 of 2

    Model-driven agent loops and termination anti-patterns

    Spotted a mistake, or was something unclear? Tell us.