Build an Autonomous Coding Agent · 40 min · Python · Anthropic Python SDK · Pydantic
Objective
The plan schema: structured, not free text
from pydantic import BaseModel
from typing import Literal
class PlanStep(BaseModel):
file_path: str
action: Literal["modify", "create"]
description: str
acceptance_criteria: str # what "done" looks like for this specific step
class ImplementationPlan(BaseModel):
task: str
steps: list[PlanStep]
risk_notes: str # anything the agent flags as risky about this plan
Compare this to just asking the model "make a plan" and parsing whatever prose comes back with regex -- fragile, and you have no reliable way to programmatically check "does step 3 reference a real file?" before you've already started executing steps 1 and 2. A Pydantic schema forces the model's plan into a shape your code can actually inspect and validate.
Getting the model to produce this shape reliably
Ask for the schema explicitly, then validate -- don't just hope
Include the schema (or a description of it) directly in the prompt, ask for JSON matching it, then parse with ImplementationPlan.model_validate_json(response_text). When validation fails (and it will, occasionally — models don't always produce perfectly valid JSON), catch the ValidationError, and either re-prompt with the specific error shown to the model, or fail loudly rather than silently proceeding with a malformed plan.
Validation before execution: catch it here, not mid-run
def validate_plan(plan: ImplementationPlan, repo_path: str) -> list[str]:
problems = []
for step in plan.steps:
full_path = os.path.join(repo_path, step.file_path)
if step.action == "modify" and not os.path.exists(full_path):
problems.append(f"Step targets '{step.file_path}' for modification, but it doesn't exist")
if step.action == "create" and os.path.exists(full_path):
problems.append(f"Step targets '{step.file_path}' for creation, but it already exists")
return problems
This is cheap to run and catches an entire category of failure before Lesson 4's execution engine ever writes a byte: a plan that hallucinates a file path, or confuses "modify" with "create." Running this check costs nothing compared to discovering the same problem 3 steps into execution with 2 files already backed up and half-modified.
Your planning engine generates a plan referencing src/utils/validators.py, but the real file is at src/validators.py (no utils/ subdirectory). validate_plan correctly flags this before execution starts. What should the agent do next?
The safest response to a caught planning error is treating it as caught -- surfacing it back to the model (which can regenerate the plan with the correction) or to the user, not silently guessing a "close enough" path. Auto-correcting via fuzzy matching reintroduces exactly the kind of unverified assumption this validation step exists to eliminate; skipping just that step can leave a multi-file plan in an inconsistent state if later steps depended on it.
What you're building
planning_engine.py: the ImplementationPlan/PlanStep Pydantic schema, a generate_plan function that prompts the model and parses its response into that schema (with retry-on-validation-error), and validate_plan — tested by generating plans for 5 different real tasks against your test repo and confirming validation correctly catches at least one deliberately-broken plan.
Unlock the full lesson
You've read the first 2 sections. The rest of this lesson covers The Problem, Build, Check Yourself, Key Terms & Next — plus a hands-on lab, quiz, and project artifact.
Create a free account — the opening phases of 29 of 35 courses are free, no credit card.