1.1 Designing Agentic Loops
1.1.1 Why an Agent Needs a Loop at All
Let's start before the jargon. Imagine you ask a colleague, "What's the weather in Paris, and should I pack a coat?" To answer, they can't just think harder — they have to DO something: look up the forecast, read the result, and only then form an opinion. They take an action, observe what comes back, and decide what to do next. If the first website is down, they try another. When they finally know enough, they stop and answer you.
A large language model on its own can't do that. By default it does exactly one thing: you send it text, it sends text back, and the exchange is over. It has no hands — it can't actually call a weather API, read a file, or run a search. It can only describe what it would like to do. That single request-and-reply is powerful for writing or reasoning, but it is a dead end the moment the task requires real actions in the world.
An "agent" is what we call the model once we give it that ability to act — to use tools, see the results, and keep going until the job is done. And the machinery that makes this possible is astonishingly simple: a loop. Your program asks the model what it wants to do, does it, hands back the result, and asks again. Round and round until the model says, "I'm finished." That repeating cycle is the agentic loop, and it is the single most important pattern in this entire domain. Everything else in Domain 1 — orchestration, hooks, handoffs — is built on top of it.
A plain model call answers once and stops. An agent wraps that call in a loop: ask the model, run the tool it wants, feed the result back, repeat until it signals completion.
The one idea to hold onto
An agentic loop turns a model that can only TALK about actions into a system that can actually TAKE them — by calling it over and over, executing what it asks for, and feeding the results back in.
1.1.2 Who Is Actually in Charge — You or the Model?
Here is the question that trips people up, and the exam loves it: in an agentic loop, who decides what happens — your code, or the model? The answer is a deliberate split, and getting it clear now will make everything that follows obvious.
The model decides WHAT to do. On each turn it looks at the conversation so far and chooses, in its own words, which tool it wants to use and with what inputs — "call get_weather with city=Paris." This is called model-driven decision-making: the model reasons about the situation and picks the next action, rather than following a fixed script you wrote in advance. That's exactly what makes agents flexible — they can handle a request you never explicitly planned for, and chain tools together in an order you didn't hard-code.
But the model never actually runs anything. It has no power to execute a tool, and it cannot decide to keep going or stop on its own. Your loop — plain code that you control — does all of that: it reads the model's request, runs the real tool, and decides whether to go around again. Think of it like a driving instructor and a learner. The learner (the model) says "turn left here" and "now speed up." The instructor (your code) has the dual controls — they're the one who can actually hit the brakes, and they decide when the lesson is over. The model proposes; your code disposes.
The model proposes the next action (model-driven decision-making); your loop executes it and decides whether to continue. Keep this split crisp — it explains every rule that follows.
This split has a practical consequence we'll return to in Lesson 1.4: because the model only *proposes*, you can put hard rules in your code that it cannot override. For now, just hold the picture: model chooses the move, your code controls the car.
1.1.3 The Four-Step Lifecycle, Step by Step
Now we can walk through the actual loop. It's four steps, repeated until the model is done. We'll follow our weather example the whole way through so it stays concrete.
- 1.SEND. Your code sends a request to Claude through the Messages API. Crucially, you send the ENTIRE conversation so far — the original question, everything the model has said, and the results of any tools you've run. (Why you resend everything is the subject of the next section.)
- 2.INSPECT. The response comes back with a special field called stop_reason. This one field tells your code what just happened. Your code reads it — it does not read the model's prose to guess. This is the decision point of the whole loop.
- 3.ACT (if the model wants a tool). If stop_reason says "tool_use", the model is asking you to run something — say, get_weather(city=Paris). Your code runs the real function, gets the answer ("12°C, rain"), adds that result onto the conversation, and goes back to step 1.
- 4.FINISH (if the model is done). If stop_reason says "end_turn", the model has everything it needs and has written its final answer. Your code exits the loop and shows the user the reply: "It's 12°C and rainy in Paris — yes, pack a coat."
The four-step lifecycle. Steps 1–2 always happen; step 2 then branches to either step 3 (run a tool and loop) or step 4 (finish). The dashed arrow is what makes it a loop.
Notice that the loop can run many times for a single user question. "What's the weather in Paris and Tokyo?" might go: ask → tool_use(get_weather Paris) → run it → ask again → tool_use(get_weather Tokyo) → run it → ask again → end_turn. Three trips to the model, two tool calls, one final answer. The model decided it needed two lookups; your loop faithfully carried each one out.
1.1.3 — Key Concept
The loop is: SEND the full conversation → INSPECT stop_reason → if "tool_use", run the tool, append the result, and loop again → if "end_turn", stop and answer. The model's stop_reason, not its wording, drives every branch.
1.1.4 Why You Resend the Whole Conversation Every Time
Step 3 said "append the tool result to the conversation, then loop back." That little instruction is where most beginner agents quietly break, so let's slow down and really understand it.
The Messages API is stateless. "Stateless" means it has no memory of your previous requests — each call is a blank slate. The model does not secretly remember that thirty seconds ago it asked for the Paris weather. If you want it to know something, that something must be physically present in the conversation you send THIS time. The API is like a brilliant consultant with total amnesia between meetings: every meeting, you must re-hand them the entire case file, or they'll start from nothing.
So when a tool returns a result, you don't just hold onto it in a variable — you append it to the conversation history as a new message, and then resend the whole history. Now, on the next turn, the model can actually SEE that get_weather returned "12°C, rain" and reason about it: "Rain means a coat is sensible." If you forget to append the result, the model asks for the weather, you fetch it, and then you send back... the same conversation as before, with no weather in it. The model, seeing no answer, asks for the weather AGAIN. You've built an infinite loop of forgetting.
Because the API is stateless, each turn resends a LARGER conversation — the previous messages plus each new tool result appended on. That accumulated history is the model's only memory.
messages = [{"role": "user", "content": "Weather in Paris? Should I pack a coat?"}]
while True:
response = client.messages.create(
model="claude-opus-4-8", max_tokens=1024,
messages=messages, tools=tools, # we resend the FULL history every time
)
if response.stop_reason == "tool_use":
results = execute_tools(response.content) # actually run get_weather
messages.append({"role": "assistant", "content": response.content}) # what the model said
messages.append({"role": "user", "content": results}) # APPEND the result
# ...then the while loop sends this bigger conversation again
else:
return response # end_turn: we're done1.1.4 — Key Concept
Because the Messages API is stateless, the conversation history IS the agent's memory. Every tool result must be appended to that history and resent, or the model can't reason about what it learned — and may loop forever asking for information you already fetched.
1.1.5 stop_reason: The Signal That Runs Everything
We keep saying "read stop_reason, don't read the model's words." Let's understand why that rule is so absolute, because it's the single most tested idea in this lesson.
When the model finishes a turn, it could in principle TELL you it's done in plain English — "I've finished, here's your answer." The temptation is to write code that scans its reply for phrases like that. Resist it completely. Natural language is ambiguous: the model might write "I've finished analysing the first file" while fully intending to keep going with the next one. If your code sees the word "finished" and stops, you've cut the agent off mid-task. Human language was never designed to be a control signal.
That's exactly why every Messages API response includes stop_reason — a small, fixed, machine-readable field whose entire job is to tell your code, unambiguously, why the model stopped. It is deterministic: the same situation always produces the same value. It is the one true signal. The exam centers on two of its values — "tool_use" (the model wants to run a tool, so continue) and "end_turn" (the model is genuinely done, so stop) — but a production-grade agent should recognise all of them, because the others tell you about real situations you must handle gracefully.
| stop_reason value | What it means in plain terms | What your loop should do |
|---|---|---|
| end_turn | The model finished naturally — this is the normal "I'm done" | Exit the loop and show the answer |
| tool_use | The model wants to run a tool before it can continue | Execute the tool, append the result, loop again |
| max_tokens | The reply hit the length limit you set and was cut off | If it was cut off mid tool-call, retry with a higher max_tokens |
| pause_turn | A built-in server tool (e.g. web search) hit its internal step limit | Send the response straight back so the model can continue |
| refusal | The model declined on safety grounds | Stop; on Opus 4.7+ read stop_details.category (cyber/bio) |
| stop_sequence | The model hit a custom stop word you configured | Inspect response.stop_sequence to see which one |
The full stop_reason vocabulary. Learn tool_use and end_turn cold for the exam; recognise the rest so a real agent doesn't choke on them. (Sonnet 4.5+ adds model_context_window_exceeded too.)
A subtle gotcha worth knowing
If you add a text note right after a tool result, the model learns to expect that pattern and may end its turn early with an almost-empty reply. The fix: send the tool result on its own; if a reply still comes back empty, add a fresh "Please continue" user message rather than resending the empty one.
1.1.5 — Key Concept
stop_reason is the only reliable signal for loop control: deterministic, unambiguous, purpose-built. Branch your loop on it — never on the model's prose, the text content type, or a counter.
1.1.6 The Four Anti-Patterns the Exam Loves
Now that you understand WHY stop_reason rules the loop, the classic mistakes almost explain themselves. Each one is a way of trying to control the loop with the wrong signal. The exam tests these relentlessly, usually as "an agent is misbehaving — what's the fix?" The fix is essentially always: use stop_reason.
- •Reading the model's words to decide it's done. ✗ Checking whether the reply contains "I'm finished." ✓ Check stop_reason — language is ambiguous ("finished the first file" ≠ finished everything), which is the exact problem stop_reason was created to solve.
- •Using an iteration cap as the main stop. ✗ "Stop after 10 loops" as the primary rule. This either cuts off a task that needed 12 steps or wastes turns on one that finished in 3. ✓ Stop on end_turn; keep a cap (say 20) only as a safety net so a buggy agent can't run forever.
- •Checking the content type. ✗ Deciding you're done because response.content[0].type == "text". ✓ Use stop_reason — the model can return explanatory text AND a tool_use request in the very same response, so seeing text first means nothing.
- •Forcing the model to always call a tool. ✗ Setting tool_choice: "any" to stop it from replying in text. ✓ Don't — if the model is forced to call a tool every turn, it can never reach end_turn, and you've built an agent that literally cannot stop.
Every termination anti-pattern is a different wrong signal. They all collapse to the same correct answer: drive the loop off stop_reason.
1.1.6 — Exam Trap
When a question describes an agent that stops early, runs forever, or behaves erratically on a multi-tool task, scan the answers for the one that switches control to stop_reason — that is almost always correct. Answers that 'strengthen the prompt', 'add a cap', 'check the text', or 'force tool_choice:any' are the distractors.
1.1.7 Put It Together: Build a Loop Yourself
You now have every piece: why agents need a loop, who controls what, the four-step lifecycle, why history is resent, the role of stop_reason, and the anti-patterns. The fastest way to make it stick is to build a tiny agent and then deliberately break it — because feeling the failure is what cements the rule.
1.1.7 — Build Exercise (45 min)
Build a minimal multi-tool agent. (1) Define two tools — a calculator and a web-search stub — each with a name, description, and input_schema. (2) Write the loop: call messages.create(), then branch on stop_reason. On "tool_use", execute the tool, append BOTH the model's message and the tool result to history, and loop. On "end_turn", return the final answer. (3) Add MAX_ITERATIONS = 20 purely as a safety net. (4) Now break it on purpose: replace the stop_reason check with `if response.content[0].type == "text"` and watch it terminate early when the model narrates before calling a tool. (5) Restore stop_reason and watch it work. You'll never forget the difference.
Everything in the rest of Domain 1 is this loop, scaled up. Multi-agent orchestration (1.2) is a loop that spawns more loops. Hooks (1.5) are code that runs at specific moments inside the loop. Workflow enforcement (1.4) is your code using its control of the loop to guarantee certain steps happen. Master the loop here and the rest of the domain becomes a series of natural extensions.
Where this shows up on the exam
Task Statement 1.1 questions are almost always 'the agent does X wrong — pick the fix.' If you can explain, in your own words, why stop_reason beats every other signal and why tool results must be appended, you can answer them on sight.
Key Takeaways
- ✓A plain model call answers once; an agentic loop wraps it so the model can take actions — your code calls the model, runs the tool it asks for, feeds the result back, and repeats until done.
- ✓Division of labour: the model decides WHAT to do (model-driven decision-making); your code decides WHETHER to continue and actually runs the tools. The model proposes; your code disposes.
- ✓The four-step lifecycle: SEND full history → INSPECT stop_reason → if "tool_use" run the tool, append the result, loop → if "end_turn" stop and answer.
- ✓The Messages API is stateless, so the conversation history IS the agent's memory — every tool result must be appended and resent, or the model can't use what it learned (and may loop forever).
- ✓stop_reason is the only reliable, deterministic loop-control signal; the exam centers on "tool_use" vs "end_turn" but production agents also handle max_tokens, pause_turn, refusal, and stop_sequence.
- ✓Four anti-patterns, all the same mistake (wrong control signal): parsing natural language, using an iteration cap as the primary stop, checking content[0].type, and forcing tool_choice:"any" (which can never end). The fix is always stop_reason.
- ✓The whole of Domain 1 builds on this loop — orchestration spawns loops, hooks run inside the loop, and enforcement uses your control of the loop.
Check Your Understanding
Test what you learned in this lesson.
Q1.An agent on a multi-tool task sometimes stops early when the model writes a sentence of explanation before calling a tool. The loop decides it's finished using response.content[0].type == "text". What's the fix?
Q2.Why must each tool result be appended to the conversation history before the next request?
Q3.Which use of an iteration cap is appropriate in an agentic loop?
Q4.In an agentic loop, which statement best describes the division of responsibility?
Practice This Lesson