Home
Home/s01
s0150 lines of code

The Agent Loop

One Loop Is All You Need

Key Insight:

The smallest useful agent is a loop that calls the model, runs tools, and feeds results back.

The core agent loop follows a clear pattern: a while loop calls the LLM, inspects the response for tool calls, executes them, and feeds the results back into the conversation. This pattern forms the foundation of every AI coding agent. The loop itself is framework-agnostic, working with Anthropic's SDK, OpenAI, or any other provider.

Architecture Flow

The Problem: How Does an AI Agent Take Actions?

An LLM alone cannot execute code, read files, or interact with the environment. It can only generate text. To become an agent, the model needs a mechanism to request actions and receive results. The simplest approach is to let the model output structured responses, known as tool calls, intercept them in a loop, execute the requested action, and feed the result back into the conversation. This creates a closed feedback loop that lets the model work iteratively toward a goal.

Why a Simple Loop Wins

The while-true loop represents the minimal viable architecture for an AI agent. There is no state machine, no planner, no task queue. The system prompt tells the model how to behave, and the model's own reasoning determines which tools to call and in what order. This simplicity carries significant advantages: the loop is provably correct, always running until the model signals completion; it is trivially debuggable, with every iteration forming a complete request-response cycle; and it is infinitely extensible, as new capabilities enter through tools rather than modifications to loop logic.

Real Implementation: Effect-Based Concurrency

OpenCode's V2 agent loop (packages/core/src/session/runner/llm.ts) replaces the basic while-true pattern with the Effect TypeScript library. Each turn executes through runTurnAttempt(), which streams the LLM call through the provider SDK, detects tool calls, and settles them in parallel using FiberSet, Effect's structured concurrency primitive. The loop handles multiple exit paths: tool_use continuation, step limits where agent.info.steps bounds the iteration count, context overflow with auto-compaction mid-stream, and interruption signals. Tool calls are not sequential. FiberSet runs all tools concurrently and awaits their collective completion before proceeding to the next LLM call.

Design Decisions

The loop checks `stop_reason != 'tool_use'` instead of checking for an explicit 'done' signal. This means the model naturally signals completion when it has nothing left to execute, with no extra protocol required.

Model selection is a configuration parameter rather than a hardcoded value. The same loop works with any OpenAI-compatible API, making it provider-agnostic from the start.

Comparison: Claude Code

Claude Code uses the same tool-use loop architecture of send, tool_use, execute, append, and repeat. OpenCode distinguishes itself through async Effect and Stream primitives for LLM calls and a formal ToolID schema registry. Where Claude Code uses a flat tool list, OpenCode organizes tools by category (read, write, shell, skill) with structured input and output schemas.

Deep Dive: Design Decisions

Why Bash Alone Is Enough

Bash can read files, write files, run arbitrary programs, pipe data between processes, and manage the filesystem. Any additional tool would be a strict subset of what bash already provides. Adding more tools doesn't unlock new capabilities — it just adds surface area for confusion.

Alternatives: We could have started with a richer toolset, but that would obscure the core insight: an LLM with a shell is already a general-purpose agent.

No Planning Framework — The Model Decides

There is no planner, no task queue, no state machine. The system prompt tells the model how to approach problems, and the model decides what tool to call next based on the conversation so far.

Alternatives: Later versions add explicit planning via Task Categories. But the core loop proves that implicit planning through model reasoning is sufficient for many tasks.

Learn OpenCode — Built with Next.js