Skip to content

Commit 4e0df12

Browse files
committed
Add Days 21-30: complete Arc 4 (Coding Agents) and Arc 5 (Copilot Studio)
Arc 4 — Coding Agents & Agent Skills (Days 21–25): - Day 21: Building your first coding agent (practical walkthrough) - Day 22: Tool use patterns that work in production - Day 23: Agent memory and context management - Day 24: Multi-step coding agent patterns and failure modes - Day 25: Evaluating coding agent quality Arc 5 — Microsoft Copilot Studio & Multi-Agent Solutions (Days 26–30): - Day 26: Copilot Studio for developers - Day 27: Designing multi-agent workflows in Copilot Studio - Day 28: Connecting Copilot Studio agents to enterprise systems - Day 29: Multi-agent debugging and observability - Day 30: 30 days done — synthesis, lessons, open questions https://claude.ai/code/session_01Nfbjr497RyUJTqFxh2VM8d
1 parent b6172e3 commit 4e0df12

10 files changed

Lines changed: 1702 additions & 0 deletions
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
---
2+
layout: post
3+
title: "Building Your First Coding Agent — A Practical Walkthrough"
4+
date: 2026-04-30
5+
categories: [ai, coding-agents]
6+
tags: [coding-agents, agentic-ai, agent-skills, automation, claude-code]
7+
description: "Stop reading about coding agents and build one. A practical walkthrough of a real, useful coding agent — the decisions made along the way, the things that broke, and what the finished version looks like."
8+
author: akashtalole
9+
---
10+
11+
Yesterday I covered the design principles for coding agents. Today I want to get concrete: let's actually build one.
12+
13+
Not a toy demo. A useful agent that solves a real problem — one I've built and use in my own workflow. I'll walk through the decisions, the things that broke during development, and what the finished version looks like.
14+
15+
---
16+
17+
## The Task: A Test Gap Finder Agent
18+
19+
The agent I'm going to walk through does one thing: given a Python module, it finds functions that have no corresponding tests and reports them with enough context to write those tests.
20+
21+
This is useful because test coverage metrics lie. 80% coverage tells you lines are executed, not that behaviour is tested. The gap finder finds the specific functions with no test coverage at all — the easiest wins.
22+
23+
The agent needs to:
24+
1. Read the source module and identify all functions
25+
2. Read the test files and identify which functions are tested
26+
3. Compare the two and report the gaps
27+
4. For each gap, provide enough context to write a test
28+
29+
Simple enough to build in a day. Real enough to use in production.
30+
31+
---
32+
33+
## Step 1: Define the Tools
34+
35+
Following the design principle from Day 20: define the minimum action space required. For this agent:
36+
37+
```python
38+
tools = [
39+
{
40+
"name": "read_file",
41+
"description": "Read the contents of a file at the given path. Returns the file content as a string. Use this to read source files and test files.",
42+
"input_schema": {
43+
"type": "object",
44+
"properties": {
45+
"path": {
46+
"type": "string",
47+
"description": "Absolute or relative path to the file"
48+
}
49+
},
50+
"required": ["path"]
51+
}
52+
},
53+
{
54+
"name": "list_files",
55+
"description": "List all files in a directory matching a glob pattern. Returns a list of file paths.",
56+
"input_schema": {
57+
"type": "object",
58+
"properties": {
59+
"directory": {"type": "string"},
60+
"pattern": {
61+
"type": "string",
62+
"description": "Glob pattern, e.g. '**/*.py'"
63+
}
64+
},
65+
"required": ["directory", "pattern"]
66+
}
67+
}
68+
]
69+
```
70+
71+
Two tools. Read-only. No write access, no execution. The blast radius of a mistake is zero — the agent can look, not touch.
72+
73+
---
74+
75+
## Step 2: Implement the Tool Handlers
76+
77+
```python
78+
import glob
79+
import os
80+
81+
def handle_tool_call(tool_name, tool_input):
82+
if tool_name == "read_file":
83+
path = tool_input["path"]
84+
if not os.path.exists(path):
85+
return {"error": f"File not found: {path}"}
86+
with open(path, "r") as f:
87+
return {"content": f.read(), "path": path}
88+
89+
elif tool_name == "list_files":
90+
directory = tool_input["directory"]
91+
pattern = tool_input.get("pattern", "**/*.py")
92+
full_pattern = os.path.join(directory, pattern)
93+
files = glob.glob(full_pattern, recursive=True)
94+
return {"files": files, "count": len(files)}
95+
96+
return {"error": f"Unknown tool: {tool_name}"}
97+
```
98+
99+
Notice the structured error returns. When a file isn't found, the agent gets a message it can reason about — not a Python exception that crashes the loop.
100+
101+
---
102+
103+
## Step 3: The Agent Loop
104+
105+
```python
106+
import anthropic
107+
108+
client = anthropic.Anthropic()
109+
110+
def run_agent(source_path, test_directory):
111+
system_prompt = """You are a test coverage analyst. Your job is to identify
112+
functions in a Python source file that have no corresponding tests.
113+
114+
For each function with no tests, report:
115+
- The function name and signature
116+
- What it does (from the docstring or code)
117+
- What test cases would be most valuable
118+
119+
Be specific. Do not report functions that are clearly tested."""
120+
121+
messages = [
122+
{
123+
"role": "user",
124+
"content": f"Find untested functions in {source_path}. Test files are in {test_directory}."
125+
}
126+
]
127+
128+
while True:
129+
response = client.messages.create(
130+
model="claude-opus-4-6",
131+
max_tokens=4096,
132+
system=system_prompt,
133+
tools=tools,
134+
messages=messages
135+
)
136+
137+
# Add assistant response to history
138+
messages.append({"role": "assistant", "content": response.content})
139+
140+
# If no tool calls, we're done
141+
if response.stop_reason == "end_turn":
142+
# Extract final text response
143+
for block in response.content:
144+
if hasattr(block, "text"):
145+
return block.text
146+
break
147+
148+
# Process tool calls
149+
tool_results = []
150+
for block in response.content:
151+
if block.type == "tool_use":
152+
result = handle_tool_call(block.name, block.input)
153+
tool_results.append({
154+
"type": "tool_result",
155+
"tool_use_id": block.id,
156+
"content": str(result)
157+
})
158+
159+
# Add tool results and continue loop
160+
if tool_results:
161+
messages.append({"role": "user", "content": tool_results})
162+
```
163+
164+
The loop is straightforward: send messages, check if done, process tool calls, add results, repeat. This is the core pattern for any tool-using agent with the Anthropic API.
165+
166+
---
167+
168+
## What Broke During Development
169+
170+
**First problem: the agent read too many files.** Without constraints, it tried to read every Python file in the project — which hit rate limits and took forever. Fix: narrow the task in the prompt. "Limit your search to the test directory provided — don't explore the whole project."
171+
172+
**Second problem: false positives.** The agent reported functions as untested when they were tested indirectly through integration tests. Fix: add a note in the system prompt acknowledging this limitation. "Note: you can only detect direct test references, not indirect coverage through integration tests."
173+
174+
**Third problem: vague output.** Early versions reported "function X has no tests" without enough context to act on. Fix: update the prompt to require the structured output format (function name, signature, purpose, suggested test cases).
175+
176+
Each of these was a prompt or tool design fix, not a code fix. That's the pattern: when an agent misbehaves, look at the system prompt and tool descriptions before touching the implementation.
177+
178+
---
179+
180+
## Using It
181+
182+
```python
183+
report = run_agent(
184+
source_path="src/orders/processor.py",
185+
test_directory="tests/"
186+
)
187+
print(report)
188+
```
189+
190+
Output looks like:
191+
192+
```
193+
## Untested Functions Found
194+
195+
### `calculate_shipping_cost(order, destination, express=False)`
196+
**What it does:** Calculates shipping cost based on order weight and destination zone.
197+
**Suggested tests:**
198+
- Standard domestic shipping at different weight thresholds
199+
- Express vs standard rate comparison
200+
- International destination handling
201+
- Edge case: zero-weight order
202+
```
203+
204+
Actionable. Takes ten seconds to run. I run it on every module before closing a sprint.
205+
206+
---
207+
208+
## The Broader Pattern
209+
210+
Every coding agent follows this same structure:
211+
212+
1. **Define the minimum tools** the task requires
213+
2. **Write structured error returns** so the agent can recover
214+
3. **Write a precise system prompt** that constrains scope and output format
215+
4. **Build the observe-reason-act loop** with message history
216+
5. **Test it on real cases, fix the misbehaviours in prompt and tool design**
217+
218+
The implementation is the easy part. The design — especially the system prompt and the tool definitions — is where the quality of the agent lives.
219+
220+
---
221+
222+
*Day 21 of the [30-Day AI Engineering series](/posts/30-day-ai-engineering-blog-plan/). Previous: [What Makes a Good Coding Agent](/posts/what-makes-a-good-coding-agent-design-principles/).*

0 commit comments

Comments
 (0)