← Back to list

AI Agent Engineer in 2026: The 12-Step Roadmap to Building Agents That Actually Work

Loops, graphs, evals, context, memory, tools, and the harness — explained with practical Claude workflows.

Tattva Tarang in Coding Nexus · 2026-08-25 03:34 · 0 claps · 10.8 min read paywalled
#ai #ai-agent #ai-agent-engineering #coding #ai-coding
Open on Medium ↗
Wiki topics: LLM · Large Language Models AGT · AI Agents AI · AI · General 💻 · Programming

AI Agent Engineer in 2026: The 12-Step Roadmap to Building Agents That Actually Work

Loops, graphs, evals, context, memory, tools, and the harness — explained with practical Claude workflows.

AI agents have gotten dramatically better. Frontier coding models went from roughly 30% to more than 80% on SWE-bench Verified in about a year. Coding agents that struggled with relatively simple tasks can now navigate large codebases, use tools, write code, run tests, and recover from mistakes.

And yet, there’s a strange gap.

Only a small percentage of companies say they’ve fully adopted AI agents across their organization.

The problem isn’t simply the model anymore.

It’s the agent system around the model.

An agent can have access to a brilliant model and still fail because:

  • the context window is overloaded
  • tools are poorly described
  • important state disappears
  • loops don’t know when to stop
  • multiple agents are orchestrated inefficiently
  • sessions die before the project is finished
  • nobody has reliable evals
  • the team can’t tell whether a new model is actually better

A demo can work perfectly on your laptop.

Production is where things get interesting.

After working through these problems, there’s a useful way to think about agent engineering:

Context, tools, memory, loops, graphs, harnesses, and evals aren’t seven unrelated skills. They’re seven views of the same engineering problem.

Bad context breaks loops.

Loops without evals can repeatedly produce bad answers.

Graphs can multiply errors just as easily as they multiply throughput.

Memory without a harness disappears when the session ends.

Tools without context management can consume the context window before the actual work starts.

So instead of learning these concepts randomly, it’s better to learn them in dependency order.

Here are the 12 steps.

01. Context — Find Out What the Agent Actually Sees

The first mistake most people make is optimizing the prompt.

That’s only a tiny part of the context.

Andrej Karpathy’s analogy is useful:

The model is the CPU. The context window is the RAM.

Your job isn’t to put everything into that RAM.

Your job is to put the right information there.

For example, in Claude Code, thousands of tokens can already be loaded before you type your first instruction.

That can include:

  • system instructions
  • memory
  • CLAUDE.md
  • skill descriptions
  • environment information
  • MCP tool names
  • other metadata

Your actual prompt might be only a few dozen tokens.

So don’t optimize the 45 tokens while ignoring the thousands already loaded.

Use:

/context

Then inspect what is actually consuming the window.

Follow it with:

/memory

This gives you a much better picture of what Claude is carrying around.

The important metrics are simple:

How much context is being consumed by memory?

How much context is still available?

If your memory files are huge, you’ve probably created an overweight CLAUDE.md.

And that’s a problem.

02. Context — Cut the Noise and Layer the Rest

Once you’ve measured your context, start deleting.

This sounds obvious.

It’s surprisingly difficult.

A lot of instructions in agent projects were written when models were weaker.

As models improve, some of those instructions become unnecessary.

The instinct is usually:

“Let’s add another instruction so the model doesn’t make this mistake.”

After a while, you end up with a 500-line CLAUDE.md containing rules about everything.

That’s backwards.

A better rule is:

Keep only the things the model cannot reasonably infer by reading the repository.

For example:

# payments-api
Subscription billing and invoicing for the web app.
## Gotchas
- All shared types live in src/types.ts.
- Money is stored as integer cents, never floats.
- Webhook retries must remain idempotent.
- db/legacy/ is frozen. Never edit it.
## Deeper guides
- Verification → .claude/skills/verify/
- Releases → .claude/skills/deploy/

Notice what’s missing.

There’s no giant explanation of the directory structure.

No explanation of JavaScript.

No explanation of the test framework.

Claude can inspect the repository and discover those things.

The useful information is the stuff that isn’t obvious.

There’s another important idea here:

Turn instructions into principles

Instead of:

Never write multi-line comments.

Prefer something like:

Write code that matches the surrounding codebase's
comment density, naming conventions, and style.

The first rule forces a decision.

The second gives the model a way to reason about the decision.

And don’t put everything in one file.

Use layers:

CLAUDE.md
    ↓
Skills
    ↓
Path-specific rules
    ↓
Actual source code

A good rule of thumb is to keep the project-level CLAUDE.md small — roughly under 200 lines.

Everything else should live closer to where it’s needed.

03. Tools & MCP — Make the Agent Discover What It Can Do

Tools are how an agent interacts with the outside world.

Databases.

Browsers.

APIs.

File systems.

Git.

Internal services.

But there’s an important catch:

Tool descriptions are also context.

A badly designed tool can waste context while still being difficult for the model to use.

Instead of writing huge documentation with dozens of examples, design expressive parameters.

For example:

@mcp.tool()
def find_stalled_shipments(hours: int = 24) -> list[dict]:
    """Shipments with no scan event in N hours.
    Use when ops asks what is stuck, or before an escalation review."""
    return query(STALLED_SQL, hours)

And:

@mcp.tool()
def reroute(
    shipment_id: str,
    hub: str,
    reason: str
) -> dict:
    """Reroute a shipment. Writes an audit row.
    Compliance requires a reason for every manual intervention."""
    return post_with_audit(shipment_id, hub, reason)

Look at the descriptions.

They don’t just explain what the tool does.

They explain when to use it.

That’s extremely important for tool discovery.

A useful tool description answers:

“When should I reach for this?”

Not just:

“What does this function do?”

Types can also act as documentation.

For example:

status: Literal[
    "pending",
    "in_progress",
    "completed"
]

That tells the model the lifecycle without needing three paragraphs of instructions.

And don’t dump every MCP schema into the context window.

Tool discovery should be selective.

If the agent can’t find a tool, it doesn’t matter how powerful that tool is.

04. Memory — Decide What Survives the Context Window

Every sufficiently long task eventually hits the context limit.

The question is:

What survives?

Most people let automatic compaction decide.

That’s risky.

Important information can disappear.

A better approach is to put important state on disk.

For example:

project/
├── CLAUDE.md
├── claude-progress.txt
├── plan.md
├── feature_list.json
└── src/

The agent can continuously update:

plan.md

with the current plan.

And:

claude-progress.txt

with what happened during the current session.

The next session can read those files and continue.

This is one of the oldest tricks in computing:

If something matters, persist it.

Don’t rely on the model remembering it.

For example:

# Current Plan
- [x] Add authentication middleware
- [x] Add session validation
- [ ] Add refresh-token rotation
- [ ] Add integration tests

After compaction, the agent doesn’t have to reconstruct everything from memory.

It reads the file.

You should also use context resets deliberately.

If the next task has nothing to do with the previous one:

/clear

is often better than dragging the previous conversation into the next task.

And when compacting:

/compact focus on the authentication bug

gives the model a useful target.

05. Loops — Teach the Agent When to Stop

An agent loop looks simple:

Act
 ↓
Observe
 ↓
Decide
 ↓
Repeat

The difficult part is:

When does it stop?

A model can make two opposite mistakes.

It can stop too early:

“Done!”

when half the work remains.

Or it can keep working forever.

Neither problem is solved by adding:

Please be careful and don't stop until everything is complete.

The stopping condition belongs outside the model’s judgment whenever possible.

For bounded tasks, use deterministic checks:

while not tests_pass():
    fix()

For discovery tasks, a useful pattern is:

Continue until several consecutive rounds produce nothing new.

For example:

const seen = new Set();
const confirmed = [];
let dry = 0;
while (dry < 2) {
  const found = await runFinders();
  const fresh = found.filter(
    item => !seen.has(key(item))
  );
  if (!fresh.length) {
    dry++;
    continue;
  }
  dry = 0;
  fresh.forEach(item => {
    seen.add(key(item));
  });
  confirmed.push(
    ...(await verify(fresh))
  );
}

The important part is this:

!seen.has(key(item))

You deduplicate against everything you’ve already seen.

Not just confirmed results.

Otherwise rejected findings can come back again and again.

Your agent has effectively discovered the same bug 15 times and still thinks it’s making progress.

06. Loops — Add a Verifier

A loop can converge perfectly on the wrong answer.

That’s why agents need verifiers.

The basic idea:

Agent
  ↓
Finding
  ↓
Verifier
  ↓
Accept / Reject

The verifier’s job isn’t to agree.

Its job is to try to kill the answer.

One useful approach is to use multiple perspectives.

For example:

Verifier 1 → correctness
Verifier 2 → security
Verifier 3 → reproducibility

Then:

if votes >= 2:
    accept()
else:
    reject()

Imagine the agent finds six potential issues.

You could run:

6 findings
×
3 independent verifiers
=
18 verification tasks

in parallel.

You might get:

missing auth on /invoices/:id
3/3 → accepted
race in webhook retry
2/3 → accepted
unsafe regex in validator
0/3 → rejected
N+1 query in dashboard
1/3 → rejected

That’s powerful because the first agent doesn’t get the final word.

And there’s a deeper lesson here.

A verifier is essentially an inline eval.

You’re defining what “good” means and testing it immediately.

That same thinking becomes extremely valuable later when you build your full evaluation system.

07. Graphs — Stop Running Everything Sequentially

A lot of agents look like this:

Task 1
  ↓
Task 2
  ↓
Task 3
  ↓
Task 4

But what if Task 2 and Task 3 don’t depend on Task 1?

You’re wasting time.

Represent your workflow as a graph.

A node is work.

An edge means:

“This output is required by that node.”

If no data crosses between two nodes, they probably don’t need an edge.

One of the most useful patterns is the diamond:

┌── Agent A ──┐
             │             │
Input ───────┼── Agent B ──┼── Reduce ── Synthesize
             │             │
             └── Agent C ──┘

Fan out.

Do independent work in parallel.

Reduce with normal code.

Then let one agent synthesize the result.

This distinction matters:

Use agents for judgment. Use code for plumbing.

Don’t spend an expensive model call doing something a Set can do.

For example:

const unique = [...new Set(results)];

That’s not an AI problem.

It’s a programming problem.

Modern coding-agent workflows can coordinate very large numbers of subagents.

The important point isn’t the exact number.

It’s the architecture.

Once your work can be split into independent units, parallelism becomes one of the biggest performance improvements available.

08. Graphs — Understand the Cost of Your Topology

Parallelism isn’t automatically better.

You need to understand what your graph actually does.

Compare:

parallel()

with:

pipeline()

A barrier-style workflow might look like:

A ─┐
B ─┼── wait ── C
C ─┘

Everything waits for the slowest task.

A pipeline behaves more like:

A → B → C
    A → B → C
        A → B → C

Items can move independently.

For many workflows, pipelines are faster because work doesn’t have to wait unnecessarily.

Use a barrier only when you genuinely need all results.

For example:

Collect everything
      ↓
Global deduplication
      ↓
Synthesis

You really do need the entire collection there.

There’s another major optimization:

Use different models for different nodes

Don’t automatically run every subagent on your most expensive model.

Imagine 100 nodes doing:

Extract the invoice number.

You don’t necessarily need your most capable model for that.

Use a cheaper model for repetitive work.

Then use the stronger model for:

Review the findings.
Resolve conflicts.
Make the final decision.

The architecture becomes:

100 cheap agents
       ↓
      merge
       ↓
1 strong agent

That can dramatically reduce cost while preserving quality where judgment matters.

09. Harness — Build for Session Death

Long-running agent projects have an interesting problem.

The next session doesn’t remember the previous engineer.

Think of it like a software team working shifts.

Engineer A works for six hours.

Engineer B arrives.

Engineer B has never seen the project.

What happens?

If your agent system relies on conversation history, everything starts falling apart.

A strong harness solves this before the coding starts.

For a large project, have an initializer create:

init.sh
claude-progress.txt
feature_list.json

And create a git checkpoint.

The feature list should contain actual requirements.

For example:

{
  "category": "functional",
  "description": "New chat button creates a fresh conversation",
  "steps": [
    "Navigate to main interface",
    "Click the New Chat button",
    "Verify a new conversation is created",
    "Check that chat area shows welcome state"
  ],
  "passes": false
}

Initially:

"passes": false

Everything starts failing.

The agent is only allowed to change:

"passes": true

when the feature actually works.

Why JSON?

Because a structured format makes it harder for an agent to casually rewrite the specification itself.

Otherwise the model might “solve” the problem by changing the requirement.

That’s not engineering.

That’s moving the goalposts.

10. Harness — One Meaningful Increment Per Session

Once the harness exists, give each session a clear contract.

Something like:

1. Get oriented.
2. Pick one feature.
3. Implement it.
4. Verify it like a user.
5. Leave the repository clean.

The orientation step should be mechanical.

For example:

pwd
cat claude-progress.txt
cat feature_list.json
git log --oneline -20
./init.sh

Then check whether the application actually works.

This is important.

Imagine the previous session broke authentication.

The new session starts implementing payments without checking.

Now you have a payment feature built on a broken application.

The agent should first establish:

Does the existing system still work?

Then choose the highest-priority unfinished feature.

And don’t rely only on unit tests.

If the requirement says:

“A user can create a new conversation.”

Then test it like a user:

Open browser
    ↓
Click New Chat
    ↓
Enter message
    ↓
Submit
    ↓
Verify new conversation

Browser automation and actual end-to-end interaction catch bugs that source-code inspection often misses.

The harness is what turns:

“Build this huge application.”

into:

“Every session, make one measurable improvement.”

That’s much easier for an agent to handle.

11. Evals — Replace “It Feels Better” With a Number

Eventually every serious agent team reaches the same problem.

Someone says:

“The new version feels worse.”

And nobody can prove it.

That’s where evals come in.

You don’t need 10,000 tasks to start.

Start with 20–50 real failures.

Get them from:

  • bug reports
  • support tickets
  • manually tested failures
  • production incidents
  • failed agent runs

Each task needs an unambiguous success condition.

For example:

task:
  id: "fix-auth-bypass_1"
desc: "Fix authentication bypass when password is empty"
  graders:
    - type: deterministic_tests
      required:
        - test_empty_pw_rejected.py
    - type: llm_rubric
      rubric: prompts/code_quality.md
    - type: static_analysis
      commands:
        - ruff
        - mypy
        - bandit
    - type: state_check
      expect:
        security_logs:
          event_type: auth_blocked

Now you aren’t asking:

“Did the agent say it fixed the bug?”

You’re checking the actual result.

That’s a huge distinction.

Use multiple types of graders.

1. Code-based graders

Fast and objective.

pytest
ruff
mypy

2. Model-based graders

Useful for things tests can’t easily measure.

For example:

Does the implementation follow the project's conventions?

3. Human graders

Still the gold standard.

Use humans to calibrate the automated graders rather than manually grading everything forever.

And whenever possible:

Grade the outcome, not the agent’s path.

Don’t require:

Tool A → Tool B → Tool C

if another valid sequence produces the correct result.

Agents can solve problems in ways you didn’t anticipate.

That’s part of their value.

12. Evals — Make Sure Your Number Is Actually Honest

An eval suite can be wrong.

That’s a dangerous problem because a bad eval can make a good model look bad.

Suppose an agent gets:

0% pass rate

You might conclude:

“The model is terrible.”

But perhaps the grader is broken.

Maybe the expected answer is:

96.124991

while the agent returns:

96.12

Maybe both answers are perfectly acceptable.

This is why you need to inspect transcripts.

When an eval fails, ask:

Did the agent fail?
OR
Did the evaluator fail?

Both happen.

There’s another problem:

Saturation

If your eval is already at:

100%

it’s difficult to measure improvement.

You need a suite with enough difficulty to create room for the model to improve.

Then track different metrics.

pass@k

Probability that at least one of k attempts succeeds.

pass^k

Probability that all k attempts succeed.

Suppose an agent has a 75% chance of succeeding on one attempt.

Three independent attempts all succeeding is:

0.75 × 0.75 × 0.75
= 0.421875

So only about:

42%

For customer-facing systems, that distinction matters.

You don’t want:

“It usually works if we retry three times.”

You want:

“It works reliably.”

That’s why consistency metrics matter just as much as capability metrics.

And finally, automate everything.

Put your eval suite into CI.

Then every model upgrade becomes:

New model
   ↓
Run suite
   ↓
Compare baseline
   ↓
Inspect regressions
   ↓
Ship or reject

Instead of:

New model
   ↓
Everyone argues about whether it feels better

메타데이터
post_id
cc282f0872db
slug
ai-agent-engineer-in-2026-the-12-step-roadmap-to-building-agents-that-actually-work-cc282f0872db
url
https://medium.com/coding-nexus/ai-agent-engineer-in-2026-the-12-step-roadmap-to-building-agents-that-actually-work-cc282f0872db
canonical_url
https://medium.com/coding-nexus/ai-agent-engineer-in-2026-the-12-step-roadmap-to-building-agents-that-actually-work-cc282f0872db
author_url
https://medium.com/@tarangtattva2
status
ok
fetched_at
2026-08-30 20:15:14