Skip to content

How to Build Your Own Coding Agent

Build a functional coding agent with Python or Go to automate codebase navigation, surgical file editing, and guarded shell command execution in a loop.

Tuan Tran Van
7 min read
Contents (9 sections)
  1. Step 1 — Build the conversation loop
  2. Step 2 — Define your first tool and handle the tool_use signal (for members)
  3. Step 3 — Complete the file read, write, and edit tools (for members)
  4. Step 4 — Give the agent shell access behind an approval gate (for members)
  5. Step 5 — Run the agent on a real repo and verify the result (for members)
  6. Minimal tool set reference (for members)
  7. Troubleshooting (for members)
  8. Next step (for members)
  9. References (for members)

You will finish with a working coding agent that reads your files, edits them, and runs shell commands on its own, driven by a ReAct (Reason, Act, Observe) loop.

It reads files to see what it is working with, decides what to do next, and runs commands to reach the goal — without you stepping through each action.

You need Python 3.10 or higher, an Anthropic API key in ANTHROPIC_API_KEY, and a terminal. The examples below use Claude Sonnet 5 as the reasoning model.

The complete sample project for this guide is on GitHub: camnangai-public-sources/coding-agent-demo.

A minimal coding agent running in a terminal, assembled from three parts: a language model, a loop, and a set of tools

Step 1 — Build the conversation loop

Pick one — skip the others.

The conversation loop: the history is sent to the model, the model replies, the reply is appended to the history, and it repeats

Python Implementation

python
import anthropic
import sys
 
client = anthropic.Anthropic()
MODEL = "claude-sonnet-5"
 
def run_agent(user_message, conversation_history=None):
    if conversation_history is None:
        conversation_history = []
 
    conversation_history.append({"role": "user", "content": user_message})
 
    while True:
        with client.messages.stream(
            model=MODEL,
            max_tokens=4096,
            system="You are a helpful coding assistant.",
            messages=conversation_history
        ) as stream:
            for event in stream:
                if event.type == "content_block_delta" and event.delta.type == "text_delta":
                    sys.stdout.write(event.delta.text)
                    sys.stdout.flush()
 
            final_message = stream.get_final_message()
            conversation_history.append({"role": "assistant", "content": final_message.content})
 
            if final_message.stop_reason != "tool_use":
                break
            # Tool handling logic follows in Step 2

Go Implementation

go
package main
 
import (
    "context"
    "fmt"
    "github.com/anthropics/anthropic-sdk-go"
)
 
func (a *Agent) Run(ctx context.Context) error {
    conversation := []anthropic.MessageParam{}
    for {
        userInput, ok := a.getUserMessage() // Implementation uses bufio.Scanner
        if !ok { break }
 
        conversation = append(conversation, anthropic.NewUserMessage(anthropic.NewTextBlock(userInput)))
 
        message, err := a.client.Messages.New(ctx, anthropic.MessageNewParams{
            Model:     "claude-sonnet-5",
            MaxTokens: 4096,
            Messages:  conversation,
        })
        if err != nil { return err }
 
        conversation = append(conversation, message.ToParam())
        fmt.Printf("Claude: %s\n", message.Content[0].Text)
    }
    return nil
}

This loop is the heartbeat of the agent. It keeps multi-turn state by carrying the conversation and environment across every interaction.

Share this article