0Pricing
Claude Architect · Lekcja

Główna pętla

Od request przez stop_reason i wykonanie narzędzia po dopisanie do historii

Główna pętla to bezpłatna lekcja Claude Architect na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Claude Architect, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Claude Architect zawiera 4 lekcji w sumie.

Części tej lekcji nie zostały jeszcze przetłumaczone i są wyświetlane po angielsku.

Why a Loop at All?

A single call to Claude returns one response. But real agents need to act: look something up, run a tool, then keep going. The agentic loop is the engine that makes this happen.

The model itself is stateless — it keeps no memory between calls. Your code holds the conversation and decides when to keep going and when to stop. Master this loop and you have mastered the foundation every Claude agent is built on.

In this lesson you will trace one full turn: request → stop_reason → tool execution → history append, and repeat.

The Request: Full History Every Turn

Because the model keeps no state, you must send the entire conversation history on every request. The key fields of a Messages API request:

  • model — which Claude model
  • max_tokens — output ceiling
  • system — the persistent instructions
  • messages — the full history (user, assistant, tool results)
  • tools — tool definitions the model may call
  • tool_choice — auto, any, or a forced tool

If you forget to append a turn to messages, the model simply won't see it. The history is the agent's memory.

from anthropic import Anthropic

client = Anthropic()
messages = [{"role": "user", "content": "What is the weather in Paris?"}]

response = client.messages.create(
    model="claude-opus-4-8",
    max_tokens=1024,
    system="You are a helpful travel assistant.",
    tools=tools,
    messages=messages,
)

Inspect the stop_reason

After every response, the first thing you check is stop_reason. It tells you exactly why the model stopped and what to do next:

  • end_turn — the model is done. Stop the loop.
  • tool_use — the model wants a tool run. Execute it, append the result, call again.
  • max_tokens — output was truncated. Raise the limit or stream.
  • stop_sequence — a custom stop string was hit.

The stop_reason is your loop's control signal. Everything the agent does next is driven by this single field — never by reading the text.

response = client.messages.create(
    model="claude-opus-4-8",
    max_tokens=1024,
    tools=tools,
    messages=messages,
)

print(response.stop_reason)  # "tool_use" | "end_turn" | "max_tokens" | ...

tool_use: The Model Asks for Action

When stop_reason is tool_use, the response content contains one or more tool_use blocks. Each block carries:

  • id — a unique id you must echo back
  • name — which tool to run
  • input — the arguments (already parsed for you by the SDK)

The model has decided what to call and with what. It has not run anything — Claude never executes your tools. Running them is your code's job. The model only requests; your harness acts.

for block in response.content:
    if block.type == "tool_use":
        print(block.name)   # "get_weather"
        print(block.input)  # {"city": "Paris"}
        print(block.id)     # "toolu_01A..." -> echo this back

Execute the Tool in Your Code

You map the tool name to a real function and run it with the model's input. This runs entirely on your side — your database, your APIs, your business logic.

This is also where guarantees live. The model decides which tool to call, but deterministic code decides whether it is allowed to run — identity checks, spend limits, permission gates. Decisions are model-driven; hard guarantees stay in code.

def execute_tool(name, tool_input):
    if name == "get_weather":
        return get_weather(**tool_input)
    if name == "lookup_order":
        return lookup_order(**tool_input)
    raise ValueError(f"Unknown tool: {name}")

Append the Assistant Turn AND the Tool Result

Now you grow the history. Two appends happen, in order:

  • First, append the assistant's full response.content — this preserves the tool_use blocks.
  • Then append a user message containing a tool_result block for each call, each with the matching tool_use_id.

Append the whole content, not just the text — dropping the tool_use blocks breaks the pairing and the next call will fail.

messages.append({"role": "assistant", "content": response.content})

tool_results = []
for block in response.content:
    if block.type == "tool_use":
        result = execute_tool(block.name, block.input)
        tool_results.append({
            "type": "tool_result",
            "tool_use_id": block.id,
            "content": str(result),
        })

messages.append({"role": "user", "content": tool_results})

Repeat Until end_turn

With the tool result now in the history, you call the API again. The model sees the result and continues — maybe it answers, maybe it calls another tool. You inspect stop_reason again and do the same thing.

This is the whole loop: request → inspect stop_reason → if tool_use, run tools and append results → repeat until end_turn. The cycle continues for as many tool calls as the task needs, then ends naturally when the model returns end_turn.

while True:
    response = client.messages.create(
        model="claude-opus-4-8",
        max_tokens=1024,
        tools=tools,
        messages=messages,
    )
    if response.stop_reason == "end_turn":
        break
    if response.stop_reason == "tool_use":
        messages.append({"role": "assistant", "content": response.content})
        messages.append({"role": "user", "content": run_tools(response)})

Terminate on stop_reason, Never on Text

Here is the single most important rule of the loop: terminate on stop_reason, never by parsing the text for words like "done" or "finished".

Reading the visible text to decide when to stop is a classic anti-pattern. The model might say "I'm done!" mid-thought, or never say it at all, or say it inside a sentence that isn't actually the end. The stop_reason is a structured, reliable signal; free-text is not.

If you find yourself writing if "done" in response_text, stop — you are building on sand.

# ANTI-PATTERN -- do NOT do this
if "done" in text.lower():
    break

# CORRECT -- structured signal
if response.stop_reason == "end_turn":
    break

Iteration Caps Are a Safety Net, Not the Brake

A robust loop usually adds a maximum iteration count — but understand its role. The cap is a safety net to prevent a runaway loop, not the primary stop mechanism.

The primary, expected way the loop ends is end_turn. The cap only fires in abnormal situations. Treating an arbitrary iteration cap as the main way to stop is an anti-pattern: it cuts off legitimate work and hides the fact that the model never naturally concluded.

MAX_ITERS = 10  # safety net only

for i in range(MAX_ITERS):
    response = client.messages.create(...)
    if response.stop_reason == "end_turn":
        break          # the PRIMARY exit
    # ... run tools, append ...
else:
    log.warning("Hit iteration cap -- investigate, do not treat as normal")

Model-Driven Decisions, Code-Enforced Guarantees

The core loop divides responsibility cleanly:

  • The model decides what to do — which tool, which arguments, when the task is complete (end_turn).
  • Your code enforces guarantees — what is allowed to run, spend limits, identity verification, and the safety-net cap.

Reserve hard-coded control for things that must be guaranteed (a refund over a limit, a destructive action). Let the model drive the flexible, decision-heavy parts. Over-constraining with rigid code makes a brittle agent; under-constraining critical actions makes an unsafe one.

A Full Minimal Loop

Here is the entire core loop in one place. Read it top to bottom — every concept from this lesson is in it: full history each turn, inspect stop_reason, execute tools, append both the assistant turn and the tool results, and exit on end_turn with an iteration cap as a backstop.

This same skeleton scales from a one-tool helper to a complex multi-step agent. The loop never changes; only the tools and the task do.

messages = [{"role": "user", "content": user_query}]

for _ in range(MAX_ITERS):
    response = client.messages.create(
        model="claude-opus-4-8",
        max_tokens=1024,
        tools=tools,
        messages=messages,
    )
    if response.stop_reason == "end_turn":
        break

    messages.append({"role": "assistant", "content": response.content})
    results = []
    for block in response.content:
        if block.type == "tool_use":
            out = execute_tool(block.name, block.input)
            results.append({
                "type": "tool_result",
                "tool_use_id": block.id,
                "content": str(out),
            })
    messages.append({"role": "user", "content": results})

final_text = next(b.text for b in response.content if b.type == "text")

Quick Check: When Does the Loop Stop?

An architect is reviewing a teammate's agent. The loop reads each response's text and breaks when it contains the phrase "task complete". It also has a hard cap of 3 iterations as the main way it ends. Which change best fixes the design?

Recap: The Core Loop

You now own the foundation of every Claude agent:

  • Stateless model — send the full messages history every turn; the history is the memory.
  • Inspect stop_reason first — end_turn stops, tool_use means run a tool, max_tokens means truncated.
  • Claude requests, your code executes — map name + input to a function and run it.
  • Append both turns — the assistant's full content, then a tool_result per call with the matching tool_use_id.
  • Terminate on stop_reason, never on text; the iteration cap is a safety net, not the brake.
  • Model decides, code guarantees.

Internalize this cycle — every advanced pattern in the certification builds directly on it.

Często zadawane pytania

Czy lekcja „Główna pętla” jest bezpłatna?

Tak — pełny tekst „Główna pętla” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Claude Architect, przejdź na CoddyKit PRO. Kurs Claude Architect zawiera 4 lekcji w sumie.

Co nauczysz się w „Główna pętla”?

Od request przez stop_reason i wykonanie narzędzia po dopisanie do historii Ćwiczysz Claude Architect z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Claude Architect?

Nie wymagamy żadnego doświadczenia. Claude Architect w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.

Ile czasu zajmuje lekcja „Główna pętla”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Claude Architect?

Tak. Każda lekcja Claude Architect zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Główna pętla
  2. Kończenie na podstawie stop_reason
  3. Antywzorzec: analizowanie tekstu w celu wykrycia zakończenia
  4. Antywzorzec: arbitralne limity iteracji
← Powrót do Claude Architect