0Pricing
Claude Architect · 강의

에이전트 루프 개요

요청하고, stop_reason을 확인하고, 도구를 실행하고, 반복합니다

에이전트 루프 개요은(는) CoddyKit의 무료 Claude Architect 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Claude Architect 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Claude Architect 강의에는 총 4개의 강의가 포함되어 있습니다.

이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.

What Is the Agentic Loop?

An agent is just a loop around the Claude Messages API. You send a request, Claude either finishes or asks to run a tool, you run it, and you send the results back. Repeat until Claude is done.

The whole pattern is four steps:

  • Request — call the API with your message history.
  • Inspect the stop_reason in the response.
  • Run tools if Claude asked for them, then add the results to the history.
  • Repeat until stop_reason is end_turn.

Master this one loop and you understand the core of every agent.

The Request: Full History Every Turn

The Claude API is stateless. The model keeps no memory between calls, so you must send the full message history every single turn.

A request carries these fields:

  • model — which Claude model to use.
  • max_tokens — the output cap.
  • system — the system prompt.
  • messages — the entire conversation so far.
  • tools — the tools Claude may call.

If you forget to append a turn, Claude simply won't know it happened.

import anthropic

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-opus-4-8",
    max_tokens=1024,
    system="You are a helpful weather assistant.",
    tools=tools,
    messages=messages,  # the FULL history, every turn
)

The Response: Read stop_reason

Every response comes back with a stop_reason. This field — not the text — tells you what to do next. The four values are:

  • end_turn — Claude finished. The loop is over.
  • tool_use — Claude wants a tool run. Execute it and continue.
  • max_tokens — output was truncated by the limit.
  • stop_sequence — a custom stop sequence was hit.

Your loop is really just a decision based on this one field.

if response.stop_reason == "end_turn":
    # Done — return the answer
    ...
elif response.stop_reason == "tool_use":
    # Run the requested tool(s), then loop again
    ...

Defining a Tool

A tool is a function you let Claude call. You describe it with a name, a description, and a JSON Schema for its inputs.

The description is the most important part — Claude reads it to decide when to use the tool. Be clear about its purpose, its inputs, and when it applies.

Claude never runs your tool itself. It only asks for it; your code does the actual work and reports back.

tools = [
    {
        "name": "get_weather",
        "description": "Get the current weather for a city. "
                       "Call this when the user asks about weather "
                       "or temperature.",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": "City name"}
            },
            "required": ["city"],
        },
    }
]

When Claude Asks for a Tool

When stop_reason is tool_use, the response content contains a tool_use block. It carries:

  • an id — used to match the result back to the request,
  • a name — which tool to run,
  • an input — the arguments, already parsed as an object.

Claude can ask for several tools in one response. Run them all before continuing.

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..." -> needed for the result

Sending the Result Back

To continue, you append two things to the history:

  1. The assistant's full response.content (so the tool_use block is preserved).
  2. A new user message holding a tool_result block.

Each tool_result must include the matching tool_use_id. Then you call the API again — that's one turn of the loop.

result = run_tool(block.name, block.input)

messages.append({"role": "assistant", "content": response.content})
messages.append({
    "role": "user",
    "content": [{
        "type": "tool_result",
        "tool_use_id": block.id,   # must match the tool_use id
        "content": result,
    }],
})

The Full Loop in Code

Put it together and the agent is a short while loop:

  • Call the API with the full history.
  • If stop_reason is end_turn, break.
  • Otherwise run the requested tools, append the results, and loop.

The model drives the decisions; your code just runs the tools and carries the history.

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
    messages.append({"role": "assistant", "content": response.content})
    results = [
        {"type": "tool_result", "tool_use_id": b.id,
         "content": run_tool(b.name, b.input)}
        for b in response.content if b.type == "tool_use"
    ]
    messages.append({"role": "user", "content": results})

Terminate on stop_reason, Not Text

Here is the rule that separates a robust agent from a fragile one: always terminate on stop_reason, never by reading the text.

Scanning Claude's output for words like "done", "finished", or "complete" is a classic anti-pattern. The model might say "I'm not done yet" or mention the word "done" in a sentence, and your loop breaks at the wrong moment.

The stop_reason field is the one reliable signal. Trust it.

# WRONG — fragile text parsing
if "done" in response_text.lower():
    break

# RIGHT — structural signal
if response.stop_reason == "end_turn":
    break

Iteration Caps Are a Safety Net

It's wise to add a maximum-iteration counter so a misbehaving loop can't run forever. But understand its role: a cap is a safety net, not the primary way you stop.

The primary stop mechanism is always stop_reason == end_turn. The cap only catches the rare runaway case.

Treating an arbitrary iteration cap as your main stop condition is an anti-pattern — it cuts off legitimate work that just needed one more turn.

MAX_ITERS = 20  # safety net only
for _ in range(MAX_ITERS):
    response = client.messages.create(...)
    if response.stop_reason == "end_turn":
        break          # the REAL stop condition
    # ... run tools, append results ...

Model-Driven Decisions, Coded Guarantees

In the agentic loop, the model decides what to do: which tool to call, with what inputs, and when to stop. That flexibility is the whole point of an agent.

Reserve hard code for the things you must guarantee — for example, a deterministic check that a refund can't exceed a policy limit, or that an action only runs after identity is verified.

Let the model plan; let your code enforce. Don't hard-code the trajectory, and don't ask a prompt to enforce a critical rule.

Handling the Other Stop Reasons

Two stop reasons are easy to forget but matter in production:

  • max_tokens — the output hit the limit and is truncated. Raise max_tokens or stream, then retry; don't treat partial output as final.
  • stop_sequence — a custom stop sequence you configured was matched.

A complete loop branches on all four values. Silently ignoring max_tokens leads to cut-off answers and broken tool calls.

if response.stop_reason == "end_turn":
    finish()
elif response.stop_reason == "tool_use":
    run_tools_and_continue()
elif response.stop_reason == "max_tokens":
    # truncated — raise max_tokens / stream and retry
    handle_truncation()
elif response.stop_reason == "stop_sequence":
    handle_stop_sequence()

Quick Check

Test your understanding of how to terminate the agentic loop.

Recap: The Agentic Loop

You now know the engine behind every agent:

  • Request the API with the full history every turn — the model keeps no state.
  • Inspect stop_reason: end_turn (done), tool_use (run tools), max_tokens (truncated), stop_sequence.
  • On tool_use, run the tools and append each tool_result with its matching tool_use_id.
  • Repeat until end_turn.

Terminate on stop_reason, never by parsing text. Iteration caps are a safety net only. Let the model make decisions; reserve hard code for guarantees.

자주 묻는 질문

“에이전트 루프 개요” 강의는 무료인가요?

네 — “에이전트 루프 개요” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Claude Architect 강의 전체를 잠금 해제할 수 있습니다. Claude Architect 강의에는 총 4개의 강의가 포함되어 있습니다.

“에이전트 루프 개요”에서 뭘 배우나요?

요청하고, stop_reason을 확인하고, 도구를 실행하고, 반복합니다 브라우저에서 직접 실행하는 실습 코드로 Claude Architect을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Claude Architect을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Claude Architect은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“에이전트 루프 개요” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Claude Architect 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Claude Architect 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 시스템을 에이전트답게 만드는 요소
  2. 모델 기반 결정과 하드코딩된 결정
  3. 에이전트를 사용할 때
  4. 에이전트 루프 개요
← Claude Architect(으)로 돌아가기