AI Agents

How to Build an AI Coding Agent in 2026: A Step-by-Step Guide

AI coding agents have moved beyond autocomplete. Tools like Claude Code, OpenAI Codex CLI, and Cursor don't just suggest code — they read your project…

March 18, 2026·10 min read·1,981 words

AI coding agents have moved beyond autocomplete. Tools like Claude Code, OpenAI Codex CLI, and Cursor don't just suggest code — they read your project, run commands, fix errors, and ship features. In 2026, building your own coding agent is no longer a research project. It's a weekend build.

This guide walks you through the complete architecture: choosing an LLM, implementing tool calling, building the agent loop, sandboxing execution, and adding the context engineering that makes it all work. By the end, you'll have a working coding agent you can extend for your own workflows.

What Is an AI Coding Agent?

A coding agent is an LLM-powered system that can read, write, and execute code autonomously within a loop. Unlike a chatbot that generates text, a coding agent takes action:

  • Reads files from your project
  • Writes or patches source code
  • Runs shell commands (tests, builds, linters)
  • Observes results and self-corrects
  • Repeats until the task is done

The key distinction: a coding agent has tools and a loop. The LLM decides which tool to call, observes the output, reasons about what to do next, and keeps going until it either succeeds or gives up.

This is the same pattern behind Claude Code (Anthropic), Codex CLI (OpenAI), and every serious coding agent shipping today.

The 5 Building Blocks

Before writing any code, understand the five components every coding agent needs:

Component Purpose Example
LLM Reasoning engine Claude Sonnet 4, GPT-5.1, Qwen 3.5
Tool definitions What the agent can do read_file, write_file, run_command
Agent loop Orchestration logic Loop until no tool calls remain
Sandbox Safe execution environment Docker container, Incus, gVisor
Context engineering What the LLM sees System prompt, project files, tool results

Miss any one of these and your agent will either be useless (no tools), dangerous (no sandbox), or confused (bad context). Let's build each one.

Step 1: Choose Your LLM

Your LLM is the brain. For a coding agent, you need a model that excels at three things: code generation, tool calling (structured output), and long-context reasoning.

Top picks for March 2026

  • Claude Sonnet 4 — Best balance of speed, cost, and coding ability. Native tool calling. 200K context.
  • GPT-5.1 — OpenAI's Cookbook literally has a "Build a coding agent with GPT-5.1" recipe. Strong at structured tool use.
  • GPT-5.3-Codex-Spark — Optimized for agentic coding, 1,000+ tok/sec. Powers Codex CLI.
  • Qwen 3.5 (local) — If you want to run locally with Ollama, Qwen 3.5 is the best open-weight option for tool calling.
  • Claude Opus 4 — Maximum reasoning depth for complex multi-file refactors.

For this tutorial we'll use the OpenAI Responses API (works with any GPT-5.x model), but the architecture is identical with Anthropic's Messages API or any provider that supports tool calling.

Quick model setup


from openai import OpenAI

client = OpenAI()  # Uses OPENAI_API_KEY env var
MODEL = "gpt-5.1"

If you prefer Anthropic:


from anthropic import Anthropic

client = Anthropic()  # Uses ANTHROPIC_API_KEY env var
MODEL = "claude-sonnet-4-20250514"

Step 2: Define Your Tools

Tools are the agent's hands. Without them, it's just a chatbot. A minimal coding agent needs three tools:

1. read_file — Read any file in the project

2. write_file — Create or overwrite a file

3. run_command — Execute a shell command

Here's how to define them as OpenAI-compatible tool schemas:


tools = [
    {
        "type": "function",
        "function": {
            "name": "read_file",
            "description": "Read the contents of a file at the given path",
            "parameters": {
                "type": "object",
                "properties": {
                    "path": {
                        "type": "string",
                        "description": "Relative path to the file"
                    }
                },
                "required": ["path"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "write_file",
            "description": "Write content to a file, creating it if needed",
            "parameters": {
                "type": "object",
                "properties": {
                    "path": {
                        "type": "string",
                        "description": "Relative path to the file"
                    },
                    "content": {
                        "type": "string",
                        "description": "Full file content to write"
                    }
                },
                "required": ["path", "content"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "run_command",
            "description": "Run a shell command and return stdout/stderr",
            "parameters": {
                "type": "object",
                "properties": {
                    "command": {
                        "type": "string",
                        "description": "Shell command to execute"
                    }
                },
                "required": ["command"]
            }
        }
    }
]

Implementing tool handlers


import subprocess
from pathlib import Path

WORKDIR = Path("./workspace")

def handle_tool_call(name: str, args: dict) -> str:
    if name == "read_file":
        path = WORKDIR / args["path"]
        if not path.resolve().is_relative_to(WORKDIR.resolve()):
            return "Error: path traversal blocked"
        return path.read_text() if path.exists() else "Error: file not found"

    elif name == "write_file":
        path = WORKDIR / args["path"]
        if not path.resolve().is_relative_to(WORKDIR.resolve()):
            return "Error: path traversal blocked"
        path.parent.mkdir(parents=True, exist_ok=True)
        path.write_text(args["content"])
        return f"Written {len(args['content'])} bytes to {args['path']}"

    elif name == "run_command":
        result = subprocess.run(
            args["command"], shell=True, capture_output=True,
            text=True, timeout=30, cwd=WORKDIR
        )
        output = result.stdout + result.stderr
        return output[:5000]  # Truncate to avoid context bloat

    return "Error: unknown tool"

Notice the path traversal check on file operations — this is your first line of defense. The agent should never escape its workspace directory.

> Pro tip: Production agents like Claude Code and Codex CLI add dozens more tools — search_files, patch_file (surgical edits), web_fetch, list_directory, and more. Start with three, then expand based on what your agent needs. For how context size affects tool design, see our context engineering guide.

Step 3: Build the Agent Loop

The agent loop is the core of your coding agent. It's deceptively simple:

1. Send the conversation (system prompt + messages) to the LLM

2. If the response contains tool calls → execute them, append results, go to 1

3. If no tool calls → return the response to the user

This is the exact pattern Claude Code, Codex CLI, and every serious coding agent uses. Here it is in code:


import json

def agent_loop(user_message: str, system_prompt: str) -> str:
    messages = [
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": user_message}
    ]

    max_iterations = 20  # Safety limit

    for i in range(max_iterations):
        response = client.chat.completions.create(
            model=MODEL,
            messages=messages,
            tools=tools,
            tool_choice="auto"
        )

        msg = response.choices[0].message
        messages.append(msg)

        # No tool calls = agent is done
        if not msg.tool_calls:
            return msg.content

        # Execute each tool call
        for tool_call in msg.tool_calls:
            args = json.loads(tool_call.function.arguments)
            result = handle_tool_call(tool_call.function.name, args)

            messages.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": result
            })

    return "Agent hit iteration limit — task may be incomplete."

Why a loop instead of a single call?

A single LLM call can generate code, but it can't verify it works. The loop lets the agent:

  • Write code, then run tests to check it
  • See a test failure, then fix the bug
  • Read existing files to understand context before making changes
  • Iterate through multiple files in a logical order

This is what separates a coding agent from a code generator. The agent observes, reasons, and acts — repeatedly.

Step 4: Sandbox Your Agent

Here's the uncomfortable truth: your agent runs arbitrary shell commands. Without sandboxing, a confused or hallucinating LLM could rm -rf /, leak environment variables, or make network requests to arbitrary endpoints.

Every production coding agent sandboxes execution. Here's how the big ones do it:

Agent Sandbox approach
Codex CLI Network-disabled Docker container per task
Claude Code Permission tiers (read/write/execute) with user approval
Cursor IDE-integrated with undo, file-scoped changes

The simplest production-grade approach is a Docker container:


# Dockerfile.sandbox
FROM python:3.12-slim

RUN useradd -m agent
WORKDIR /workspace
USER agent

# No network access, limited memory
# Controlled via docker run flags

Run your agent inside it:


docker run --rm \
  --network none \
  --memory 512m \
  --cpus 1 \
  -v $(pwd)/workspace:/workspace \
  coding-agent-sandbox \
  python agent.py "Add unit tests for auth.py"

Key flags:

  • --network none — No internet access. The agent can't exfiltrate data or download malicious packages.
  • --memory 512m — Prevents runaway processes from eating all RAM.
  • -v — Mount only the project directory, nothing else.

Minimum sandboxing checklist

Even without Docker, enforce these rules:

  • [ ] Path containment — All file operations stay within the workspace
  • [ ] Command allowlisting — Block dangerous commands (rm -rf /, curl | sh, etc.)
  • [ ] Timeout enforcement — Kill commands that run longer than 30 seconds
  • [ ] No secrets injection — Never put API keys in the agent's environment. Let the scripts the agent calls read secrets at runtime, as recommended by sandboxing best practices.
  • [ ] Output truncation — Cap tool output at 5-10KB to prevent context window overflow

Step 5: Engineer Your Context

Context engineering is what separates a decent coding agent from a great one. The LLM only knows what you put in its context window — and with tool results piling up, you'll burn through tokens fast.

This is where context engineering becomes critical. If you haven't read our complete guide, the short version: curate what the model sees, not just what you prompt it with.

The system prompt

Your system prompt defines the agent's behavior. Here's a battle-tested starting point:


SYSTEM_PROMPT = """You are a coding agent. You help users by reading, writing,
and executing code in their project.

Rules:
- Always read relevant files before making changes
- Run tests after writing code to verify correctness
- Make minimal, focused changes — don't rewrite entire files
- If a command fails, read the error and try to fix it
- If you're stuck after 3 attempts, explain the issue and stop

Available tools: read_file, write_file, run_command

Working directory: /workspace
"""

Context management strategies

As your agent loops, the message history grows. Here's how to keep it manageable:

1. Truncate tool output — Cap at 5KB per tool result. Nobody needs 50KB of test output.

2. Summarize periodically — After 10+ iterations, inject a summary of progress so far and trim old messages.

3. Use prompt caching — If your provider supports it (Anthropic and OpenAI both do), prompt caching can cut costs by 90% on the static portions of your context.

4. Load files on demand — Don't dump the entire project into context. Let the agent read_file what it needs.

> Real-world example: The OpenAI Cookbook's GPT-5.1 coding agent recipe demonstrates this pattern: the agent scaffolds a project, refines it through patches, executes commands, and even fetches external documentation — all within a managed context window.

Step 6: Put It All Together

Here's the complete minimal coding agent in under 100 lines:


#!/usr/bin/env python3
"""Minimal AI coding agent — reads, writes, and executes code."""

import json
import subprocess
from pathlib import Path
from openai import OpenAI

client = OpenAI()
MODEL = "gpt-5.1"
WORKDIR = Path("./workspace")
MAX_ITERATIONS = 20

SYSTEM_PROMPT = """You are a coding agent. Read files, write code, run commands.
Always read before writing. Run tests after changes. Stop after 3 failed attempts."""

tools = [
    {"type": "function", "function": {
        "name": "read_file",
        "description": "Read a file",
        "parameters": {"type": "object", "properties": {
            "path": {"type": "string"}}, "required": ["path"]}}},
    {"type": "function", "function": {
        "name": "write_file",
        "description": "Write a file",
        "parameters": {"type": "object", "properties": {
            "path": {"type": "string"},
            "content": {"type": "string"}}, "required": ["path", "content"]}}},
    {"type": "function", "function": {
        "name": "run_command",
        "description": "Run a shell command",
        "parameters": {"type": "object", "properties": {
            "command": {"type": "string"}}, "required": ["command"]}}}
]

def handle_tool(name, args):
    if name == "read_file":
        p = WORKDIR / args["path"]
        return p.read_text() if p.exists() else "File not found"
    elif name == "write_file":
        p = WORKDIR / args["path"]
        p.parent.mkdir(parents=True, exist_ok=True)
        p.write_text(args["content"])
        return f"OK — wrote {p}"
    elif name == "run_command":
        r = subprocess.run(args["command"], shell=True,
            capture_output=True, text=True, timeout=30, cwd=WORKDIR)
        return (r.stdout + r.stderr)[:5000]
    return "Unknown tool"

def run(task):
    msgs = [{"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": task}]
    for _ in range(MAX_ITERATIONS):
        resp = client.chat.completions.create(
            model=MODEL, messages=msgs, tools=tools)
        msg = resp.choices[0].message
        msgs.append(msg)
        if not msg.tool_calls:
            return msg.content
        for tc in msg.tool_calls:
            result = handle_tool(tc.function.name,
                json.loads(tc.function.arguments))
            msgs.append({"role": "tool",
                "tool_call_id": tc.id, "content": result})
    return "Hit iteration limit."

if __name__ == "__main__":
    import sys
    print(run(" ".join(sys.argv[1:])))

Save this as agent.py, create a workspace/ directory, and run:


python agent.py "Create a Python Flask app with a /health endpoint and write tests for it"

The agent will create the files, write tests, and run them — all autonomously.

Beyond the Basics: What Production Agents Add

The agent above works, but production tools go further. Here's what Claude Code, Codex CLI, and similar agents add on top:

Patch-based editing

Instead of rewriting entire files, production agents use surgical edits — replacing specific lines or applying diffs. This is more token-efficient and less error-prone. The OpenAI Cookbook recipe uses a code_editor tool with apply_patch commands for this.

Multi-file awareness

Real projects span hundreds of files. Production agents add search_files (grep/ripgrep), list_directory, and find_references tools so the LLM can navigate without reading everything.

Permission tiers

Claude Code classifies operations by risk:

  • Read — Always allowed (reading files, listing directories)
  • Write — Requires approval on first use
  • Execute — Always requires approval unless in auto-accept mode

This graduated trust model lets developers work fast while staying safe.

AGENTS.md and project configuration

Both Codex CLI and Claude Code read project-level configuration files (AGENTS.md, CLAUDE.md) that tell the agent about coding standards, test commands, and project structure. This is context engineering in action — the agent gets project-specific knowledge before it starts.

Prompt caching for cost control

A 20-iteration agent loop with GPT-5.1 can easily hit 100K+ tokens. Prompt caching caches the system prompt and tool definitions across calls, cutting per-iteration costs dramatically.

Common Mistakes and How to Avoid Them

Mistake Why it happens Fix
Agent rewrites files instead of patching No patch tool available Add a surgical edit tool
Context window blowup Uncapped tool output Truncate all outputs to 5KB
Agent loops forever No stop condition Set max iterations + "give up" instructions
Agent deletes critical files No sandbox Docker + path containment
Agent ignores test failures System prompt doesn't enforce testing Add explicit "run tests after changes" rule

FAQ

What LLM is best for building a coding agent?

As of March 2026, Claude Sonnet 4 and GPT-5.1 are the strongest options for coding agents. Both have excellent tool calling support and strong code generation. For local/private setups, Qwen 3.5 running on Ollama is the best open-weight alternative, though it trails the commercial models on complex multi-file tasks. Choosing between Qwen versions? See our Qwen 3.5 vs 2.5 comparison — 3.5 has better tool calling, but upgrading isn't always worth it if your prompts are tuned for 2.5. Choosing between Qwen versions? See our Qwen 3.5 vs 2.5 comparison — 3.5 has better tool calling, but upgrading isn't always worth it if your prompts are tuned for 2.5.

How is a coding agent different from GitHub Copilot?

Copilot (and similar autocomplete tools) suggest code inline as you type. A coding agent runs autonomously — it reads your project, writes code, executes commands, checks results, and iterates. Copilot assists; an agent acts. This distinction matters — autonomous coding agents are already reshaping engineering teams as companies rethink how many developers they need. For a full comparison, see our Claude Code vs Cursor vs Copilot breakdown.

Do I need Docker to sandbox my coding agent?

Not strictly, but it's the most reliable approach. Without Docker, you need manual path containment, command filtering, timeout enforcement, and network restrictions — all of which Docker handles with a single docker run command. Alternatives include Incus containers, gVisor, and cloud sandbox services like E2B or Daytona.

How much does it cost to run an AI coding agent?

Costs vary by model and task complexity. A typical 15-iteration agent loop with GPT-5.1 uses roughly 50-80K tokens, costing $0.15-0.40 per task. With prompt caching, you can cut that by 50-90% on the cached portion. Local models via Ollama cost nothing per token but require capable hardware — see our best hardware for local LLMs guide.

Can I build a coding agent with open-source models?

Yes. Qwen 3.5 and similar open-weight models support tool calling and work well for coding tasks. The architecture in this guide is model-agnostic — swap the API client and model name, and everything else stays the same. Performance will be lower than commercial models on complex tasks, but for straightforward code generation and testing, open models are viable.

What is the OpenAI Cookbook coding agent recipe?

The OpenAI Cookbook published a reference implementation showing how to build a coding agent with GPT-5.1 using the Responses API. It covers project scaffolding, code patching, command execution, and fetching external documentation — all within a managed agent loop. It's the closest thing to an official "how to build Codex CLI" tutorial.

Frequently Asked Questions

What LLM is best for building a coding agent?
As of March 2026, Claude Sonnet 4 and GPT-5.1 are the strongest options for coding agents. Both have excellent tool calling support and strong code generation. For local/private setups, Qwen 3.5 running on Ollama is the best open-weight alternative, though it trails the commercial models on complex multi-file tasks. Choosing between Qwen versions? See our Qwen 3.5 vs 2.5 comparison — 3.5 has better tool calling, but upgrading isn't always worth it if your prompts are tuned for 2.5. Choosing between Qwen versions? See our Qwen 3.5 vs 2.5 comparison — 3.5 has better tool calling, but upgrading isn't always worth it if your prompts are tuned for 2.5.
How is a coding agent different from GitHub Copilot?
Copilot (and similar autocomplete tools) suggest code inline as you type. A coding agent runs autonomously — it reads your project, writes code, executes commands, checks results, and iterates. Copilot assists; an agent acts. This distinction matters — autonomous coding agents are already reshaping engineering teams as companies rethink how many developers they need. For a full comparison, see our Claude Code vs Cursor vs Copilot breakdown.
Do I need Docker to sandbox my coding agent?
Not strictly, but it's the most reliable approach. Without Docker, you need manual path containment, command filtering, timeout enforcement, and network restrictions — all of which Docker handles with a single docker run command. Alternatives include Incus containers, gVisor, and cloud sandbox services like E2B or Daytona.
How much does it cost to run an AI coding agent?
Costs vary by model and task complexity. A typical 15-iteration agent loop with GPT-5.1 uses roughly 50-80K tokens, costing $0.15-0.40 per task. With prompt caching, you can cut that by 50-90% on the cached portion. Local models via Ollama cost nothing per token but require capable hardware — see our best hardware for local LLMs guide.
Can I build a coding agent with open-source models?
Yes. Qwen 3.5 and similar open-weight models support tool calling and work well for coding tasks. The architecture in this guide is model-agnostic — swap the API client and model name, and everything else stays the same. Performance will be lower than commercial models on complex tasks, but for straightforward code generation and testing, open models are viable.
What is the OpenAI Cookbook coding agent recipe?
The OpenAI Cookbook published a reference implementation showing how to build a coding agent with GPT-5.1 using the Responses API. It covers project scaffolding, code patching, command execution, and fetching external documentation — all within a managed agent loop. It's the closest thing to an official "how to build Codex CLI" tutorial.

🔧 Tools in This Article

All tools →

Related Guides

All guides →