Session 5 · Week 3 · Tue 20 Oct 2026 · 75 min

Agents, and how they break.

You've earned the agent, the task cleared all three gates. Now build it so it holds. Tonight: the anatomy, tools as contracts and the ones MCP hands you, planning, memory, durability, single vs multi-agent, and the exact failure mode hiding in each choice.

Ehsan Gazar
Production-Ready Systems with LLMs and Agents · session 5 of 8
Where we're going · 90 minutes

The run sheet.

0–10Anatomy of an agentthe loop, and the stop
10–34Tools as contracts, and MCPthe model's API to the world
34–56Planning, memory, durabilitywhen they help, how they rot
56–72Single vs multi-agentand the cost of coordination
72–84Live: sketch your looptools, memory, stop
84–90Recap + Project 3 previewsecurity next
By the end of tonight

You'll be able to…

1Draw an agent's loop, and name its stopping condition.
2Design a tool as a contract: tight inputs, validated outputs, least privilege, hand-written or MCP.
3Decide when planning and multi-agent earn their cost, and when they don't.
4Make a long run resumable instead of restartable.
5Name the failure mode baked into each pattern before you ship it.
Anatomy

An agent is a loop with four parts.

Model, tools, memory, and a stopping condition. The model reasons, picks a tool, observes the result, and repeats, until it decides it's done or you cut it off. Everything else is detail.

flowchart LR
  G["Goal"] --> M{"Model
reason + choose"} M -- "call" --> T["Tool"] T --> O["Observation"] O --> MEM[("Memory")] MEM --> M M -- "stop when done
or capped" --> DONE["Result"] classDef m fill:#EEE6FF,stroke:#1F2937,color:#0E1726; classDef ok fill:#D6F5E3,stroke:#1F2937,color:#0E1726; class M m; class DONE ok;

The stopping condition is not optional. An agent without a hard cap is an unbounded bill and an infinite loop waiting to happen.

The most important design surface

A tool is an API you hand the model.

Every tool is a door into your systems that a probabilistic caller can open. Design it like a public API called by a stranger: tight inputs, a clear description, validated outputs, and the least privilege that does the job.

A good tool
narrow scope · typed args · validates its own inputs · does one thing · returns structured, bounded output
a contract
A dangerous tool
run_sql(query) · http_get(url) · shell(cmd) — unbounded power, the model decides how far it reaches
a liability

Prefer refund_order(order_id) with server-side rules over run_sql(...). The tool, not the prompt, is where you enforce what's allowed.

A tool definition, good and bad

The schema is where you enforce the rules.

# GOOD: narrow, typed, server enforces the limits
{ "name": "refund_order",
  "description": "Refund one order the current user owns.",
  "input": { "order_id": "string",
             "reason": "enum[damaged, late, wrong_item]" } }
# server re-checks: user owns order_id, amount <= cap, not already refunded

# DANGEROUS: unbounded power, the model decides how far it reaches
{ "name": "run_sql", "input": { "query": "string" } }  # one injection from a breach

Same capability, two risk profiles. refund_order can only do the one thing, checked server-side. run_sql can do anything, and the model chooses.

How tools break

The model will misuse the door.

1
Wrong arguments
plausible but invalid inputs. Fix: validate every arg in the tool, reject and explain.
2
Wrong tool, wrong time
too many similar tools confuse it. Fix: fewer, well-named tools; clear descriptions.
3
Over-reach
a broad tool does more than intended. Fix: least privilege + approvals on the risky ones.
The tool interface, standardized

MCP is that contract, published.

Everything on the last three slides still holds. MCP standardizes how you hand the contract over: one protocol, so a tool written once is callable by any agent. The architectural shift is quieter and bigger, most tools you ship, you didn't write.

flowchart LR
  AG{"Your agent"} --> C["MCP client"]
  C --> GW["Gateway
auth + allow-list"] GW --> S1["Your server
refund_order"] GW --> S2["Third-party server
you don't run this one"] classDef ok fill:#D6F5E3,stroke:#1F2937,color:#0E1726; classDef un fill:#FEE4E2,stroke:#1F2937,color:#0E1726; class GW,S1 ok; class S2 un;

The gateway is the one place to decide which tools this agent may call. Without it, "just add an MCP server" grants privileges nobody reviewed. Thursday, we attack exactly this.

Planning loops

Plan when the path is unknown. Not by default.

Reason-then-act (observe, decide, repeat) lets an agent handle tasks whose steps aren't knowable up front. But planning multiplies calls and compounds error, one wrong early step and the rest builds on sand. Plan when you must; script when you can.

Planning earns it
open-ended research, multi-hop tasks, recovery from surprises
Planning hurts
a known 3-step job — now it's slower, pricier, and can wander off
The failure mode
error compounding — each step trusts the last; add checkpoints + validation
Planning, drawn

Thought → Action → Observation, on repeat.

flowchart LR
  T["Thought
what next?"] --> A["Action
call a tool"] A --> O["Observation
tool result"] O --> T T -. "goal met or capped" .-> D["Done"] classDef t fill:#EEE6FF,stroke:#1F2937,color:#0E1726; classDef ok fill:#D6F5E3,stroke:#1F2937,color:#0E1726; class T t; class D ok;

Powerful, and where cost hides: every trip round the loop is another model call. The dashed exit, a goal check or a hard step cap, is the whole difference between an agent and a runaway bill.

Memory

Two memories, two failure modes.

flowchart TB
  subgraph SHORT["Short-term · the context window"]
    A["Recent steps + observations"]
  end
  subgraph LONG["Long-term · a store"]
    B["Facts, decisions, past runs"]
  end
  B -. "retrieve on need" .-> A
  A -. "persist what matters" .-> B
  classDef s fill:#DCEBFE,stroke:#1F2937,color:#0E1726;
  classDef l fill:#D6F5E3,stroke:#1F2937,color:#0E1726;
  class A s;
  class B l;
        

Short-term rots (the S3 U-curve, contradictions). Long-term drifts (stale or wrong facts retrieved as truth). Curate both, and never let memory become an unaudited source of authority.

The run that dies at step 30

Long runs get interrupted. Plan the resume.

A 40-step run outlives your deploy window. It will be killed halfway: a timeout, a rate limit, a crash, a Tuesday release. With no durable state you restart from zero, pay for every step twice, and re-fire every side effect you already fired.

1
Checkpoint each step
persist state as you go, so a dead run resumes instead of restarting.
2
Idempotency keys
S5's rule, now load-bearing: a replayed step must not charge the card twice.
3
Know what's replayable
reads replay free. Writes need a key. Money and email need both, plus a log.

This is why the loop and its state are two different things. If the state only lives in the process, the run is only as durable as the process.

Single vs multi-agent

One agent until one agent isn't enough.

Multi-agent (an orchestrator delegating to specialists) buys parallelism and separation of concerns. It charges you coordination overhead, multiplied cost, and new ways to fail. Default to one agent with good tools; split only on a real seam.

flowchart TB
  O{"Orchestrator"}
  O --> A["Researcher
own tools + context"] O --> B["Writer
own tools + context"] O --> C["Checker
own tools + context"] A --> S["Synthesize"] B --> S C --> S classDef o fill:#EEE6FF,stroke:#1F2937,color:#0E1726; class A,B,C o;

Worth it when subtasks are genuinely independent and each needs its own context. Not worth it to make an org chart out of prompts.

The multi-agent tax

More agents, more ways to fail.

1
Coordination cost
agents talking to agents burns tokens and latency before any work is done.
2
Error propagation
one agent's wrong output becomes another's trusted input. Garbage in, confidently out.
3
Cost explosion
every agent has its own loop; multiply the caps or the bill runs away.
The map to keep

Each pattern, and how it breaks.

PatternBuys youBreaks asGuardrail
Tool usereach into systemsmisuse / over-reachtight contract, least privilege
MCP / imported toolsreach without glueprivileges nobody reviewedgateway, allow-list servers
Planning loophandles the unknownerror compoundingcheckpoints, step caps
Memorycontinuityrot / stale driftcurate + expire
Long runsmulti-step workrestart from zero, double side effectscheckpoint + idempotency keys
Multi-agentparallel specialistscoordination + costsplit only on real seams
The through-line

Four rules for an agent that holds.

1
Fewest tools that work
every tool is attack surface and a chance to pick wrong.
2
Tight contracts
validate in and out; the tool enforces the rules, not the prompt.
3
Always bounded
step caps, token budgets, timeouts. No infinite loops, ever.
4
Observable
every step traced, so a run can be replayed and debugged (Week 5).
Your turn · ~10 min

Sketch your agent's loop.

If your system earned an agent, draw its four parts. If it didn't, draw the workflow you chose instead, that's a valid answer too. Post your stopping condition in the chat.

1
Tools
the fewest that do the job
2
Memory
short, long, or none
3
Stop
what ends the loop
4
Break
its worst failure mode
Recap · then Thursday

Loop, tools, memory, stop, and the break in each.

Tools are contracts, including the ones MCP hands you. Plan only when the path is unknown. Curate memory. Checkpoint anything long. Stay single until a real seam forces multi. And every pattern ships with a failure mode, name it before it names you.

Thursday · S8 workshop
Securing agents: prompt injection, tool poisoning, guardrails. Pick + threat-model your architecture. Output: Project 3.
due Sun Aug 9
Bring
your agent loop sketch. Thursday you attack it.
take-home