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

ComponentBeforeAfter
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
  1. Create a file called hello.py that prints "Hello, World!"
  2. List all Python files in this directory
  3. What is the current git branch?
  4. 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.