Agentic Loops

The agentic loop is the execution cycle behind every Claude agent. Master why stop_reason - not text parsing - is the only reliable signal for when to keep going or stop.

Lesson 1 of 303% of the guide
Prefer to learn by doing?

An agentic loop is the core execution cycle behind every Claude-based agent. It is deterministic control flow that you define in code - not a prompt trick, not a chatbot turn, and not a retry wrapper. Each pass through the loop calls the Messages API, inspects the model's stop_reason, and decides whether to run tools and continue or to return the final answer. Understanding this cycle precisely is the foundation of the entire CCAR-F exam.

The four-step lifecycle

Every iteration of the loop follows the same four steps. The loop repeats until the model signals it is finished:

  1. 1Send the request. Call the Messages API with the full conversation history: the system prompt, prior messages, and any tool results appended on previous iterations.
  2. 2Inspect `stop_reason`. Read the authoritative signal that tells you what to do next. tool_use means Claude wants to call tools; end_turn means Claude is done.
  3. 3Handle `tool_use`. Execute the requested tools, append their results to the conversation history as a new message, then resend the updated conversation.
  4. 4Handle `end_turn`. Extract Claude's final response and present it to the user - the loop terminates.
stop_reason is the only reliable signal

The stop_reason field is the single deterministic, unambiguous control signal for the loop. Never use text parsing, content-type checks, or iteration caps as your primary stopping mechanism. Treat any value other than end_turn as "not finished - investigate why."

A subtle but critical detail: tool results must be appended to the conversation history. If you execute a tool but forget to add its output back into messages, Claude cannot reason about the new information on the next iteration, and the loop stalls or repeats itself. This is one of the most common production bugs.

Beyond tool_use and end_turn

Production loops must handle more than the two common values. Any non-end_turn result means the turn is not cleanly complete:

stop_reasonMeaningHow to handle
tool_useClaude wants to call one or more toolsExecute tools, append results, continue
end_turnClaude finished its responseReturn the final answer, exit loop
pause_turnA long-running server tool is still workingResend to continue the operation
max_tokensThe response hit the token limitContinue or handle truncation
refusalModel declined (normal 200 response)Surface refusal, do not retry blindly
stop_sequenceA custom stop sequence firedHandle per your design

Model-driven vs. pre-configured control

In a model-driven loop, Claude reads the task, weighs the available tools, and selects the appropriate one based on context. This adapts to unforeseen situations and allows flexible tool chaining. A pre-configured decision tree hard-codes the execution sequence - less flexible, but sometimes required for deterministic compliance in financial, security, or regulatory contexts (covered in lesson 1.4). The exam generally favors model-driven approaches for their adaptability.

import anthropic

client = anthropic.Anthropic()
messages = [{"role": "user", "content": "What is 47 * 89, then search for the result?"}]

for _ in range(20):  # safety cap only, NOT the primary control
    resp = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=1024,
        tools=TOOLS,
        messages=messages,
    )
    # The authoritative control signal:
    if resp.stop_reason == "end_turn":
        print(resp.content[-1].text)
        break

    if resp.stop_reason == "tool_use":
        messages.append({"role": "assistant", "content": resp.content})
        tool_results = []
        for block in resp.content:
            if block.type == "tool_use":
                result = run_tool(block.name, block.input)
                tool_results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": result,
                })
        # Tool results MUST be appended back to history:
        messages.append({"role": "user", "content": tool_results})
Content-type checking is a classic trap

Checking response.content[0].type == "text" to decide the agent is done fails constantly - Claude routinely returns explanatory text ("Let me search your order") alongside a tool_use block in the same response. Seeing text first does not mean the agent finished. Inspect stop_reason instead.

Deciding when the loop is done
Don't

Parsing the model's natural language (or the first content block) to guess whether it finished. Text like "Let me search that" ships alongside a tool_use block, so this stops the loop early or never.

if "done" in resp.content[0].text.lower():
    break  # brittle: text != completion
Do

Branch on `stop_reason`, the one deterministic control signal. end_turn means finished; treat every other value as "not done - investigate."

if resp.stop_reason == "end_turn":
    break  # authoritative
Iteration caps are safety nets, not logic

A cap like max 20 iterations is a fine guard against runaway agents, but it must never be your primary termination logic. If a task legitimately needs 12 steps, a cap of 10 truncates real work; if it finishes in 3, extra iterations waste tokens. Let stop_reason end the loop naturally.

One more trap: forcing tool_choice: "any" on every call. It guarantees Claude always calls a tool, which creates an infinite loop when the agent is genuinely finished and should have returned end_turn.

How the exam will try to trick you

The distractors below look right under time pressure — learn the tell.

  1. The trap

    Decide the loop is finished by checking response.content[0].type == "text".

    Correct answer

    Branch on the stop_reason field - continue on tool_use, terminate on end_turn.

    Why: Claude routinely returns explanatory text alongside a tool_use block, so a text-first response does not mean it finished.

  2. The trap

    Use an iteration cap like stop after 10 loops as the primary way to end the loop.

    Correct answer

    Let stop_reason end the loop; keep the cap only as a safety net against runaway agents.

    Why: A fixed cap either truncates legitimate multi-step work or wastes iterations after the task is already done.

  3. The trap

    Parse natural-language phrases like I'm done or task complete to terminate.

    Correct answer

    Rely on the deterministic stop_reason signal instead of the model's prose.

    Why: Natural language is ambiguous; only stop_reason gives an unambiguous machine-readable completion signal.

  4. The trap

    Force tool_choice: "any" so the agent never returns plain text.

    Correct answer

    Let the model signal completion naturally with end_turn.

    Why: Forcing a tool call on every turn means the agent can never finish, producing an infinite loop.

Key takeaways

  • The agentic loop is a four-step cycle: send request, inspect stop_reason, handle tool_use, handle end_turn.
  • stop_reason is the only reliable, deterministic signal for loop control - never parse natural language or check content types.
  • Tool results must be appended to conversation history or Claude cannot reason about them next iteration.
  • Handle all stop_reason values (pause_turn, max_tokens, refusal, stop_sequence) - treat any non-end_turn value as "not finished."
  • Iteration caps are safety nets against runaway agents, never the primary stopping mechanism.
  • Forcing tool_choice: "any" causes infinite loops because the agent can never end its turn.

Frequently asked questions

What is an agentic loop in Claude?+

An agentic loop is the deterministic code-level cycle that runs a Claude agent: it calls the Messages API, checks the stop_reason, executes any requested tools and appends their results to the conversation, then repeats until stop_reason is end_turn.

Why should I use stop_reason instead of checking the response text?+

Because stop_reason is deterministic and unambiguous, while natural language is not. Claude often returns text alongside a tool_use block, so phrases like "task complete" or a text-first response do not reliably indicate the agent has finished.

What happens if I forget to append tool results to the conversation?+

Claude has no way to see the tool output on the next iteration, so it cannot reason about the new information. The loop typically stalls, re-requests the same tool, or produces an incomplete answer.

Practice makes pass

Ready to test what you just learned?

Reading gets you familiar — answering questions gets you certified. Jump into free practice or sit a full timed mock exam, scored 100–1000 just like the real thing.

No sign-up required · Explanation for every answer · Works offline