A minimal Python implementation of a code-editing AI agent, ported from Thorsten Ball's Go tutorial "How to Build an Agent" [https://ampcode.com/how-to-build-an-agent]
This project proves that building an impressive AI agent doesn't require complex frameworks or thousands of lines of code. It's fundamentally an LLM, a loop, and enough tokens - everything else is engineering polish.
The agent provides Claude AI with three essential tools:
- read_file: Read the contents of any file
- list_files: List files and directories
- edit_file: Edit files using string replacement
- Terminal-based chat interface with Claude AI
- File system access for Claude through tools
- Conversation history maintained across interactions
- Tool execution with visual feedback
- Error handling and graceful degradation
- Support for creating new files and editing existing ones
- Python 3.13+ (as specified in pyproject.toml)
- UV package manager (recommended) or pip
- Anthropic API key
-
Clone or download this repository
-
Install dependencies using UV:
uv syncOr install manually with pip:
pip install anthropic python-dotenv- Set up your environment variables:
# Create a .env file
echo "ANTHROPIC_API_KEY=your_api_key_here" > .envOr export directly:
export ANTHROPIC_API_KEY="your_api_key_here"Run the agent:
python main.pyYou'll see a prompt like:
Chat with Claude (use 'ctrl-c' to quit)
You:
Explore the current directory:
You: What files do you see in this directory?
Claude: I'll help you see what's in the current directory. Let me list the files and directories for you.
tool: list_files({})
Claude: I can see several files and directories in the current directory:
[... detailed file listing ...]
Read a file:
You: What's in main.py?
Claude: I'll check the main.py file to see what's in it.
tool: read_file({"path":"main.py"})
Claude: Based on my review, main.py implements a Claude AI assistant agent...
Create a new file:
You: Create a hello world script in Python
Claude: I'll create a hello world script in Python for you.
tool: edit_file({"path":"hello.py","old_str":"","new_str":"print(\"Hello, World!\")"})
Claude: I've created a simple hello.py script that prints "Hello, World!" to the console.
Edit an existing file:
You: Change the hello world script to say "Hello, Claude!"
Claude: I'll modify the hello.py script to say "Hello, Claude!" instead.
tool: read_file({"path":"hello.py"})
tool: edit_file({"path":"hello.py","old_str":"print(\"Hello, World!\")","new_str":"print(\"Hello, Claude!\")"})
Claude: I've successfully updated the hello.py script...
Agent Class: The main orchestrator that manages the conversation loop and tool execution.
ToolDefinition Class: Defines the structure for tools with name, description, input schema, and execution function.
Tool Functions:
read_file(): Reads file contents with error handlinglist_files(): Recursively lists files and directoriesedit_file(): Performs string replacement edits or creates new files
- User inputs message
- Agent sends conversation history + available tools to Claude
- Claude responds with either text or tool use requests
- If tools are requested, agent executes them and sends results back
- Claude provides final response incorporating tool results
- Loop continues
When Claude wants to use a tool, it responds with structured data like:
{
"type": "tool_use",
"id": "tool_123",
"name": "read_file",
"input": {"path": "example.txt"}
}The agent:
- Finds the matching tool definition
- Executes the tool function with the provided input
- Returns a tool result with the output
- Sends the result back to Claude for processing
The edit_file tool uses simple string replacement rather than AST manipulation or line-based editing. This approach works surprisingly well because:
- Claude understands exactly what text to replace
- It can make precise, targeted changes
- No need for complex parsing or code understanding
- Works with any text file format
Each tool includes a JSON schema that tells Claude:
- What parameters it accepts
- What each parameter does
- Whether parameters are required
- Expected data types
The implementation includes robust error handling:
- File not found errors
- Permission errors
- Invalid tool parameters
- Network connectivity issues
- Graceful degradation when tools fail
The terminal interface uses ANSI color codes:
- Blue for user input
- Yellow for Claude responses
- Green for tool execution
- Clear visual separation between conversation turns
This Python port includes several adaptations:
Simplified Tool Definitions: Python's dynamic typing eliminates the need for complex generic schema generation.
Pathlib Usage: Modern Python path handling instead of string manipulation.
Exception Handling: Pythonic try/except blocks rather than explicit error returns.
Dictionary-Based Messages: Claude's Python SDK uses simple dictionaries for messages instead of specialized types.
UTF-8 Encoding: Explicit encoding handling for cross-platform compatibility.
Adding new tools is straightforward:
- Define a tool function:
def my_tool(input_data: Dict[str, Any]) -> str:
# Tool implementation
return "result"- Create a tool definition:
MY_TOOL_DEFINITION = ToolDefinition(
name="my_tool",
description="What this tool does and when to use it",
input_schema={
"type": "object",
"properties": {
"param": {"type": "string", "description": "Parameter description"}
},
"required": ["param"]
},
function=my_tool
)- Add to the tools list:
tools = [READ_FILE_DEFINITION, LIST_FILES_DEFINITION, EDIT_FILE_DEFINITION, MY_TOOL_DEFINITION]Based on "How to Build an Agent" by Thorsten Ball (April 15, 2025). The original Go implementation and tutorial provided the foundation for this Python port.