1.1 — Agentic Loops
CCAR-F Domain 1 · Task 1.1
What You Need to Know
On its own, Claude answers one request with one response. To act, such as looking up an order, it needs tools: functions in your code that you describe to Claude in the request. Claude does not run a tool you define. When it needs one, its response asks for the call, and your code runs the function and sends the result back. An agent is Claude, its tools and the code that keeps this exchange going.
That code is the agentic loop, the core execution cycle behind every Claude-based agent. It is deterministic control flow, defined in code, not a prompt trick, a retry loop or a chatbot turn. Your code sends a request, runs any tools Claude asks for, and sends the results back until Claude has finished. The stop_reason field in each response says whether to go round again. The other Domain 1 lessons, on orchestration, subagents, hooks and sessions, all build on this loop.
A glazier runs an agent that answers customer messages. When a customer asks for their fitting date, the agent must look up the order and check the fitting diary before it replies. An agent with a broken loop stops halfway through a task like this in production.
The Agentic Loop Lifecycle
The loop has four steps, repeated until Claude has finished.
- Send a request to Claude via the Messages API. It includes the conversation history: system prompt, prior messages, and any tool results from the previous iteration.
- Inspect the stop_reason field in the response. It is the authoritative signal for what happens next. Two of its values matter for agentic loops:
"tool_use": Claude wants to call one or more tools. The loop continues."end_turn": Claude has finished its work. The loop terminates.
- If stop_reason is "tool_use", execute the requested tool(s), append the tool results to the conversation history as a new message, and send the updated conversation back to Claude.
- If stop_reason is "end_turn", the agent has finished. Present the final response to the user.
The glazier's agent gets "tool_use" for the order lookup and again for the diary check, then "end_turn" with the reply.
Step 3 is where loops break. Tool results must be appended to the conversation history. Otherwise the model never sees what the tool returned, so it has nothing new to reason about on the next iteration.
Key Concept: The stop_reason field is the only reliable signal for loop control. It is deterministic and unambiguous. Never use natural language parsing, text content checks, or arbitrary iteration caps as your primary stopping mechanism.
Model-Driven Decision-Making
In an agentic loop, Claude decides which tool to call from the current context. This is model-driven decision-making: the model reads the task, weighs the available tools and picks one. In a pre-configured decision tree or fixed tool sequence, the developer hard-codes which tool runs when.
Model-driven approaches flex. Claude adapts to situations the developer never mapped out, handles edge cases, and chains tools in orders nobody planned.
The exception is business logic that demands deterministic compliance, such as financial operations, security checks and regulatory requirements. There, programmatic enforcement overrides that flexibility. Task Statement 1.4 covers this.
The Three Anti-Patterns
Anti-Pattern 1: Parsing natural language signals. The code checks whether Claude said "I'm done" or "task complete". Natural language is ambiguous. stop_reason exists to remove that ambiguity.
Anti-Pattern 2: Arbitrary iteration caps as the primary stopping mechanism. The code stops after a fixed count. Caps are acceptable as a safety net against runaway agents, never as the primary control.
Anti-Pattern 3: Checking for assistant text content as a completion indicator. The code uses response.content[0].type == "text" to decide the loop is finished. Claude can return text alongside tool_use blocks. Text presence does not tell you whether the agent is finished.
Practical Example: The Premature Termination Bug
The glazier's agent works for simple queries but sometimes stops mid-task on complex ones. Its code checks if response.content[0].type == "text" to determine completion. Claude returns "Let me look up your order" alongside a tool_use block. The code sees text in position [0], concludes the agent is finished and returns the incomplete response.
The fix is to check stop_reason instead of the content type: continue on "tool_use", terminate on "end_turn".
In the exam
Get this lifecycle right and most of Domain 1 falls into place. The keyed answer continues on "tool_use", terminates on "end_turn", and rejects all three anti-patterns. The exam favours model-driven tool selection because it flexes, except where deterministic compliance calls for programmatic enforcement.
Common Exam Distractor: The exam frequently presents iteration caps as a fix for premature termination. Reject these answers. Caps address runaway loops, not premature exits.
Current state note: The Messages API also returns pause_turn, max_tokens, stop_sequence, refusal and model_context_window_exceeded. A production loop must handle them. Treat any value other than end_turn as "not finished, check why".
Exam Traps
- Using
response.content[0].type == 'text'to determine loop completion - Setting arbitrary iteration caps as the primary stopping mechanism
- Parsing natural language phrases like 'I'm done' to decide loop termination
- Leaving tool results out of the conversation history
Check your understanding
Practice Scenario
A customer support agent sometimes exits too early. Its loop decides it is finished whenever response.content[0].type == "text", so it stops even while Claude still has tools to call. What is the fix?
Log in to save your progress.
Sources
- How tool use works, Claude Platform Docs
- Handle tool calls, Claude Platform Docs
- Stop reasons and fallback, Claude Platform Docs
- How the agent loop works, Claude Code Docs
- Building with the Claude API, Anthropic Academy