Part 2: Recapping the coding agent
Before adding skills, we need the base agent shape from the prerequisite workshop: a model, a system prompt, tools, and memory. This session keeps that foundation and adds skills and commands around it.
The four pieces of an agent
An agent has four pieces:
- Model - the LLM that reasons and decides.
- System prompt - instructions that define behavior and role.
- Tools - functions the model can call to interact with the world.
- Memory - message history from previous turns.
The loop is:
- User gives input.
- Agent sees the input, prompt, tools, and memory.
- Agent either responds or asks to call a tool.
- If the agent calls a tool, your code runs it.
- The tool result is added to memory.
- The loop continues until the agent returns a final answer.
The agent decides which tools to use and in what order. We are not writing a fixed sequence such as "always read, then write, then test". We expose useful actions and let the model choose.
A small tool-use example
A coding request might flow like this:
User: "Fix the bug in calculator.py"
LLM: "I'll read the file first."
Tool: read_file("calculator.py")
LLM: "Found the issue. I'll fix it."
Tool: write_file("calculator.py", fixed_code)
LLM: "Let me verify the fix works."
Tool: execute_bash("pytest")
LLM: "Fixed. Added a check for division by zero."
The model is doing the planning, but the Python code still controls which tools exist and how they run.
The coding-agent tools
The prerequisite agent has five generic coding tools. We reuse them because skills and commands are behavior layers, not replacements for file and shell access.
class AgentTools:
def read_file(self, filepath: str) -> str:
"""Read the contents of a file."""
def write_file(self, filepath: str, content: str) -> None:
"""Write content to a file."""
def see_file_tree(self, root_dir: str = ".") -> list[str]:
"""List all files in a directory."""
def execute_bash_command(
self,
command: str,
cwd: str = None,
) -> tuple[str, str, int]:
"""Run a shell command."""
def search_in_files(
self,
pattern: str,
root_dir: str = ".",
) -> list[tuple[str, int, str]]:
"""Search for text in files."""
These tools let the agent read a project, make changes, run tests, and search for relevant code. That is enough for a general-purpose coding agent in the notebook.
Download tools.py into the project folder:
wget https://raw.githubusercontent.com/alexeygrigorev/workshops/refs/heads/main/coding-agent/tools.py
The downloaded file is intentionally generic. It does not know about skills, commands, OpenCode, or Claude Code. Keeping those pieces separate lets us add skills without touching the file operations.
The fuller prototype tools
The prototype/ folder contains a more OpenCode-shaped tool set. It uses
similar ideas but with names closer to the OpenCode tool registry:
read_file(filepath, offset=0, limit=0)write_file(filepath, content)edit_file(filepath, old_string, new_string, replace_all=False)glob_files(pattern, path=".")grep_files(pattern, path=".", glob_pattern=<recursive pattern>)bash_command(command, cwd=None, timeout=30)
The extra edit_file and glob_files tools are useful in a more complete
coding agent. For the notebook, the five-tool version is enough because the
workshop focus is skill loading and command loading.
Continue with Part 3: Building the base runner to turn these tools into a working ToyAIKit runner.