One loop & Bash is all you need
What You’ll Learn
- How the core agent loop works: send messages, run tools, feed results back
- Why the “write-back” step is the single most important idea in agent design
- How to build a working agent in under 30 lines of Python
The Problem
Without a loop, every tool call requires a human in the middle. The model says “run this test.” You run it. You paste the output. The model says “now fix line 12.” You fix it. You tell the model what happened.
The solution is simple: let the code do the looping.
The Solution
+--------+ +-------+ +---------+
| User | ---> | LLM | ---> | Tool |
| prompt | | | | execute |
+--------+ +---+---+ +----+----+
^ |
| tool_result |
+----------------+
(loop until stop_reason != "tool_use")
The model talks, the harness executes tools, and the results go right back into the conversation. The loop keeps spinning until the model decides it’s done.
How It Works
Step 1. The user’s prompt becomes the first message.
messages.append({"role": "user", "content": query})
Step 2. Send the conversation to the model, along with tool definitions.
response = client.messages.create(
model=MODEL, system=SYSTEM, messages=messages,
tools=TOOLS, max_tokens=8000,
)
Step 3. Add the model’s response to the conversation. Check if it called a tool.
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
return
Step 4. Execute each tool call, collect results, feed them back. Loop.
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})
Put it all together:
def agent_loop(query):
messages = [{"role": "user", "content": query}]
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})
That’s the entire agent in under 30 lines. Everything else in this course layers on top of this loop — without changing its core shape.
What Changed
| 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" |
Try It
cd learn-claude-code
python agents/s01_agent_loop.py
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
Key Takeaway
An agent is just a loop: send messages to the model, execute the tools it asks for, feed the results back, and repeat until it’s done.