90% of code at Anthropic is written by Claude agents. Not by engineers typing in a chat window. By autonomous agents running loops, calling tools, and shipping code while the team sleeps.
Follow my Substack to get fresh AI alpha:
This is the exact setup. Step by step. From first API call to a working agent you can point at any task.
This article will cover:
1 - why most "agents" people build are not agents
2 - the 5 parts every working agent needs
3 - how to build each part with Claude, with code
4 - the mistakes that kill agents before they ship
Bookmark this. Every code block below works.
01. Most "AI agents" are not agents
I have built and broken more agents than I can count. Watched them burn tokens all night and produce nothing. Watched them rewrite the same file 30 times. Watched them pass their own test by deleting the test.

Every failure taught me the same lesson: the model is not the problem. The architecture around it is. This guide is everything I learned, compressed into the shortest path I can give you.
Here is what most people build when they say "AI agent":
1while True:2 user_input = input("> ")3 response = call_claude(user_input)4 print(response)
That is a chatbot. It waits for you. It does what you say. It forgets everything between sessions. When you close the tab, it stops.
An agent is a system that works toward a goal without you sitting in front of it. It discovers what needs doing, makes a plan, executes, checks the result, and if it is not done yet - tries again. You set the direction. The agent does the work.
"Claude Code went from zero to $400 million in revenue in a few months. It started as a hackathon project. It still only uses the public API." -
Boris Cherny, Head of Claude Code
Same API you have access to right now. Same models. The difference is the architecture around the model.

02. The 5 parts of a real agent
Every working agent - Claude Code, Devin, Codex, or anything you build yourself - is assembled from five parts. Miss one and it breaks.

03. The API layer
Everything starts here. You call Claude, Claude responds. But the way you call it determines whether you get a chatbot or an agent.

Three things matter: the system prompt, structured output, and temperature.
System prompt is not a greeting. It is your agent's operating manual. Every rule, constraint, and behavior goes here. Without it Claude guesses what you want. With it Claude follows your specification.
1import anthropic23client = anthropic.Anthropic()45response = client.messages.create(6 model="claude-sonnet-4-6",7 max_tokens=4096,8 system="""You are a code review agent.910Rules:11- Read the entire diff before commenting12- Flag only real bugs, not style preferences13- If nothing is wrong, say "LGTM" and stop14- Never suggest changes you haven't tested mentally15- Output format: JSON array of {file, line, issue, fix}""",16 messages=[{"role": "user", "content": diff_content}]17)
Structured output makes your agent's response machine-readable. If Claude returns free text, your code has to parse it. If Claude returns JSON, your code can use it directly.
1# Force JSON output by telling Claude the exact shape2system = """Return ONLY valid JSON. No markdown. No explanation.3Schema:4{5 "status": "pass" | "fail",6 "issues": [{"file": str, "line": int, "issue": str}],7 "summary": str8}"""
Temperature. Set it to 0 for deterministic agents. Set it to 0.3-0.5 for creative work. Default (1.0) adds randomness you almost never want in an agent.
04. Tools
A model without tools can reason but cannot act. It can tell you what file to edit but cannot edit it. It can describe a query but cannot run it.

Claude's tool use lets you define functions the model can call. You describe the function. Claude decides when to call it. You execute it and return the result. Claude uses the result to keep reasoning.
1tools = [{2 "name": "run_sql",3 "description": "Execute a read-only SQL query against the database",4 "input_schema": {5 "type": "object",6 "properties": {7 "query": {8 "type": "string",9 "description": "SQL SELECT query to execute"10 }11 },12 "required": ["query"]13 }14},15{16 "name": "write_file",17 "description": "Write content to a file on disk",18 "input_schema": {19 "type": "object",20 "properties": {21 "path": {"type": "string"},22 "content": {"type": "string"}23 },24 "required": ["path", "content"]25 }26}]
The tool description matters more than you think. Claude reads it to decide when and how to use the tool. A vague description means wrong calls. A precise description means accurate calls.
Start with 3-5 tools. Read file, write file, run command, search, and one domain-specific tool for your use case. That covers 90% of agent tasks.

05. The loop
This is the part that turns a script into an agent. Without a loop, your code calls Claude once and stops. With a loop, your code calls Claude, checks the result, and calls again until the job is done.

Three components:
- Verifier. Something that checks whether the output is good. A test suite, a type checker, a linter, a second Claude call with strict criteria. Without this you have the agent agreeing with itself on repeat.
- State. A record of what happened. What worked, what failed, what to try next. Without state the agent makes the same mistake every pass.
- Stop condition. The goal is met, or a hard limit says "after N tries, stop and report." Without this the loop runs forever and drains your account.
1import json2from pathlib import Path34def run_agent(task: str, max_attempts: int = 5):5 state = {"task": task, "attempts": [], "done": False}67 for i in range(max_attempts):8 # Build context from state9 context = build_prompt(state)1011 # Call Claude with tools12 result = call_claude(context, tools)1314 # Execute any tool calls15 output = execute_tools(result)1617 # Verify the result18 check = verify(output)1920 # Update state21 state["attempts"].append({22 "attempt": i + 1,23 "action": result.summary,24 "passed": check.passed,25 "reason": check.reason26 })2728 if check.passed:29 state["done"] = True30 break3132 # Save state for next run33 Path("state.json").write_text(json.dumps(state, indent=2))34 return state
This is the full skeleton. Every production agent is a variation of this pattern. The details change. The shape does not.
06. Memory
Without memory, every session starts from zero. The agent re-discovers your project structure. Re-learns your conventions. Re-makes the mistakes it made yesterday.

Claude agents use three layers of memory:
CLAUDE.md is a markdown file at the root of your project. Claude Code reads it automatically at the start of every session. Your rules, your stack, your conventions. Write it once, read forever.
1# CLAUDE.md23## Project4Task management API. Python 3.12, FastAPI, PostgreSQL.56## Rules7- All responses: {data, error, meta} schema8- Tests required for every new endpoint9- Commit messages: type(scope): description10- Never use print() for logging. Use structlog.1112## Known issues13- Auth middleware expects x-auth-token, not Authorization14- Test suite takes 45s full. Use --filter for iteration.
Skills capture entire workflows. Not just prompts - the full shape: input format, steps, output format, validation rules. First run takes 20 minutes. Replay takes 30 seconds.
Learnings file is a running log of mistakes. The agent writes to it after every session. Next session reads it. Mistakes repeat until they are written down. Then they stop.
1# learnings.md23- Payment API expects idempotency key in header, not body4- PostgreSQL NOTIFY needs explicit LISTEN in connection pool5- Rate limiter counts per-key, not per-IP. Tests need unique keys.
07. The verification gate
The gate is the hardest part to build and the easiest to skip. Most people skip it. That is why most agents break in production.

A verification gate is something that checks the agent's work without the agent grading itself. The model that wrote the code is too generous grading its own homework. You need a second check.
Three patterns that work:
1. Automated tests. The agent writes code. The test suite runs. If tests fail, the agent gets the error output and tries again. This is how Claude Code works internally.
1def verify(output):2 # Run the test suite3 result = subprocess.run(4 ["pytest", "tests/", "-x", "--tb=short"],5 capture_output=True, text=True6 )7 return {8 "passed": result.returncode == 0,9 "reason": result.stdout if result.returncode != 0 else "all tests pass"10 }
2. Type checker / linter. Run mypy, ruff, or tsc --noEmit after every change. Catches entire categories of bugs without writing a single test.
3. Second model as reviewer. Use a separate Claude call with a strict system prompt that only looks for problems. The writer is fast and cheap. The reviewer is slow and strict. That separation is most of the quality.
1# Reviewer prompt - separate from the builder2reviewer_system = """You are a strict code reviewer.3Your ONLY job is to find problems.45Check:6- Does the code match the spec?7- Are there uncaught edge cases?8- Do all tests actually test the right thing?910If everything is correct, respond: {"passed": true}11If anything is wrong, respond: {"passed": false, "issues": [...]}1213Do NOT suggest improvements. Only flag real bugs."""
The writer is fast and cheap. The reviewer is slow and strict. That separation is most of the quality.
08. Putting it all together
Here is a complete agent that takes a GitHub issue URL, reads the issue, writes the code, runs the tests, and opens a PR. Five parts working together.
1import anthropic, subprocess, json2from pathlib import Path34client = anthropic.Anthropic()5CLAUDE_MD = Path("CLAUDE.md").read_text()6LEARNINGS = Path("learnings.md").read_text()78SYSTEM = f"""You are a coding agent.9Read the issue. Write the fix. Run the tests.1011Project context:12{CLAUDE_MD}1314Known issues:15{LEARNINGS}1617Rules:18- Read the full codebase before changing anything19- Write tests for every change20- If tests fail, fix the code, not the tests21- Stop when all tests pass"""2223TOOLS = [24 read_file_tool,25 write_file_tool,26 run_command_tool,27 search_codebase_tool,28]2930def run(issue_text, max_attempts=5):31 messages = [{"role": "user", "content": issue_text}]3233 for attempt in range(max_attempts):34 # Call Claude35 response = client.messages.create(36 model="claude-sonnet-4-6",37 max_tokens=8192,38 system=SYSTEM,39 tools=TOOLS,40 messages=messages41 )4243 # Execute tool calls44 messages = handle_tool_use(response, messages)4546 # Verify: run tests47 test_result = subprocess.run(48 ["pytest", "-x", "--tb=short"],49 capture_output=True, text=True50 )5152 if test_result.returncode == 0:53 print(f"Done in {attempt + 1} attempts")54 return True5556 # Feed failure back into the loop57 messages.append({58 "role": "user",59 "content": f"Tests failed:\n{test_result.stdout}\nFix and retry."60 })6162 return False
That is a working agent. API layer with system prompt and CLAUDE.md. Tools for file operations. A loop with retry. Memory from learnings.md. A verification gate via pytest.
Under 50 lines. Same architecture Claude Code uses internally.
**
09. The 5 mistakes that break every agent
- No verification gate. The agent grades its own homework. It writes code, says "looks good," and moves on. The output looks right and breaks in production.
- No stop condition. The loop runs until your API bill is $200. Without a hard limit the agent retries forever, rewriting the same file 40 times. Always set max_attempts. Always.
- No state file. Same mistake on attempt #1 and attempt #50. The agent does not know what it already tried. It proposes the same broken fix three times in a row because nothing records the failure.
- Too many tools. You give Claude 20 tools and it picks the wrong one. A model with 5 clear tools makes better choices than a model with 20 overlapping ones. Start small. Add tools only when the agent hits a wall.
- Vague system prompt. "Be a good coding assistant" gives you generic output. "All responses must be valid JSON, tests required for every change, never modify files outside /src" gives you an agent that behaves.
Conclusion:
A working agent is not a better prompt. It is a system: API + tools + loop + memory + verification gate. Five parts. Miss one and it breaks.
Most people will read this, bookmark it, and keep using Claude as a chatbot. They will paste one question at a time and copy the response into their codebase by hand.
The ones who build the loop will ship work while they sleep. Same model. Same API. Same price. Different architecture.
The code blocks above all work. Copy them. Run them. Modify them for your use case.
Build one agent this week. Point it at a task you do every day. Let it run.





