← all posts
// agents · agents

Build a coding agent from scratch: the loop is simpler than you think

Coding agents look like magic, and the frameworks around them like rocket science. The core is neither. An agent is a model, a handful of tools, and a loop, and you can write the whole thing in an afternoon. Building the minimal version is the fastest way to understand what Claude Code, Codex, and the rest are doing under the hood, and to debug them when they misbehave.

The entire loop

Here's a coding agent, conceptually complete:

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

while True:
    resp = model(messages, tools=TOOLS)     # model proposes
    messages.append(resp)
    if resp.stop_reason == "end_turn":
        break                               # model is done
    for call in resp.tool_calls:            # model wants to act
        result = run_tool(call.name, call.args)   # your harness executes
        messages.append(tool_result(call.id, result))

That's it. The model reads context, decides whether to act or finish, and when it acts your harness runs the tool and feeds the result back. Loop until done. Everything else is refinement of these few lines.

The tools are the agent's hands

The model can only propose tool calls; your harness executes them. A minimal coding agent needs maybe five:

  • read_file(path) / write_file(path, content) / edit(path, old, new)
  • run(cmd): shell, for tests, build, git
  • search(pattern): grep or glob the tree

The model strings these together: read the file, edit it, run the tests, read the failure, edit again. The toolset defines what the agent can do; the loop just keeps it going.

Where the real work actually is

The loop is trivial. Production-readiness lives in the harness around it, exactly where all the architecture articles on this blog point:

  • Validate tool inputs. The args the model generates are untrusted input. Sanitize before they hit a shell (sandboxing).
  • Sandbox the execution. run(cmd) is a loaded gun. Isolate it; gate the irreversible actions behind a human.
  • Manage context. As the loop runs, the message list grows, and compaction, context editing, caching keep it from exploding in size and cost.
  • Give it an oracle. Point it at your tests so the loop has ground truth to iterate against. That turns guessing into engineering.

The loop is fifty lines and never gets more complicated. The harness (validation, sandbox, context, caching, the test oracle) is where the engineering is, where good agents beat bad ones.

What frameworks add, and when to skip them

Agent frameworks bundle context management, retries, tracing, multi-agent orchestration, and tool ecosystems. At scale, that's real value. But for understanding, and for simple agents, the raw loop is clearer and has fewer surprises. Don't reach for a sixty-abstraction framework before you've felt the loop yourself. You'll debug what you don't understand, and the framework hides exactly the parts (context, tools, the oracle) you most need to see. Start raw; adopt a framework when you hit a wall it's specifically built to solve, not before (the ponytail rule).

Why building it makes you better at using it

Once you've written the loop, the big tools stop being magic. You know a flailing agent is almost always a context or tool or oracle problem, rarely a model problem. You've seen there's nothing else in there. You know why a good CLAUDE.md matters (it's the system prompt), why the cache makes or breaks the cost (the message list is the prefix), and why MCP is useful (it's a standard way to add tools). The fifty-line core is the mental model that makes everything else on this blog click.

#agents#architecture#tutorial