- 11 sessions from basic agent loop to autonomous teams - Python MVP implementations for each session - Mental-model-first docs in en/zh/ja - Interactive web platform with step-through visualizations - Incremental architecture: each session adds one mechanism
4.6 KiB
s01: The Agent Loop
The entire secret of AI coding agents is a while loop that feeds tool results back to the model until the model decides to stop.
The Problem
Why can't a language model just answer a coding question? Because coding requires interaction with the real world. The model needs to read files, run tests, check errors, and iterate. A single prompt-response pair cannot do this.
Without the agent loop, you would have to copy-paste outputs back into the model yourself. The user becomes the loop. The agent loop automates this: call the model, execute whatever tools it asks for, feed the results back, repeat until the model says "I'm done."
Consider a simple task: "Create a Python file that prints hello." The model needs to (1) decide to write a file, (2) write it, (3) verify it works. That is three tool calls minimum. Without a loop, each one requires manual human intervention.
The Solution
+----------+ +-------+ +---------+
| User | ---> | LLM | ---> | Tool |
| prompt | | | | execute |
+----------+ +---+---+ +----+----+
^ |
| tool_result |
+---------------+
(loop continues)
The loop terminates when stop_reason != "tool_use".
That single condition is the entire control flow.
How It Works
- The user provides a prompt. It becomes the first message.
history.append({"role": "user", "content": query})
- The messages array is sent to the LLM along with the tool definitions.
response = client.messages.create(
model=MODEL, system=SYSTEM, messages=messages,
tools=TOOLS, max_tokens=8000,
)
- The assistant response is appended to messages.
messages.append({"role": "assistant", "content": response.content})
- We check the stop reason. If the model did not call a tool, the loop ends. This is the only exit condition.
if response.stop_reason != "tool_use":
return
- For each tool_use block in the response, execute the tool (bash in this session) and collect results.
for block in response.content:
if block.type == "tool_use":
output = run_bash(block.input["command"])
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": output,
})
- The results are appended as a user message, and the loop continues.
messages.append({"role": "user", "content": results})
Key Code
The minimum viable agent -- the entire pattern in under 30 lines
(from agents/s01_agent_loop.py, lines 66-86):
def agent_loop(messages: list):
while True:
response = client.messages.create(
model=MODEL, system=SYSTEM, messages=messages,
tools=TOOLS, max_tokens=8000,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
return
results = []
for block in response.content:
if block.type == "tool_use":
output = run_bash(block.input["command"])
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": output,
})
messages.append({"role": "user", "content": results})
What Changed
This is session 1 -- the starting point. There is no prior session.
| Component | Before | After |
|---|---|---|
| Agent loop | (none) | while True + stop_reason |
| Tools | (none) | bash (one tool) |
| Messages | (none) | Accumulating list |
| Control flow | (none) | stop_reason != "tool_use" |
Design Rationale
This loop is the universal foundation of all LLM-based agents. Production implementations add error handling, token counting, streaming, and retry logic, but the fundamental structure is unchanged. The simplicity is the point: one exit condition (stop_reason != "tool_use") controls the entire flow. Everything else in this course -- tools, planning, compression, teams -- layers on top of this loop without modifying it. Understanding this loop means understanding every agent.
Try It
cd learn-claude-code
python agents/s01_agent_loop.py
Example prompts to try:
Create a file called hello.py that prints "Hello, World!"List all Python files in this directoryWhat is the current git branch?Create a directory called test_output and write 3 files in it