How AI Agents WorkCourse map

Chapter 20 · Thinking like a designer

Architecture exercises

Ten design problems. Write your own design for each one, then compare it with one reasonable answer.

How to use these exercises

Each exercise describes a real job, such as a weekly report on competitors, and asks a few design questions. Write your answer in the box, then open the reveal and compare. The reveal shows one reasonable design, not the only one.

Each tool has an effect class from chapter 2, because the class decides how much checking a call needs. A read-only tool changes nothing. A reversible write changes something you can undo, such as a draft. "External" means the action leaves your system or cannot be undone, such as an email or a payment.

In the diagrams, each step says who does it. Steps marked (code) are deterministic: they run the same way every time. Steps marked (model) need judgment. The colors repeat this: violet for the model, blue for your code, green for tools and outside systems, amber for stored data, and rose for a person.

Start from the least agentic design that works (chapter 0): if the steps never change, code should run them. Some jobs below need no agent at all. When you finish, grade your design with the rubric at the end.

Competitor report every Monday

A product team tracks eight competitors. Every Monday by 7:00 they want a report on what changed last week across about 40 pages, such as pricing pages and release notes, plus news. Every claim must link to its source.

  1. What tools does it need?
  2. What state should persist?
  3. Does it need memory?
  4. Should it use subagents?
  5. Does it need scheduling?
  6. What should happen if one website fails?
  7. How should success be evaluated?
Reveal one reasonable architecture

A deterministic workflow is enough, because the sources, the schedule, and the report format are known in advance. Code runs the steps in order, and the model only reads changed pages and writes the report.

Monday 06:00 (code)
  |
  v
For each source, in parallel (code):
  fetch --> compare with last week's copy
     |                |
     | failed         | changed
     v                v
  mark "not checked"  extract facts and quotes (model)
     |                |
     +-------+--------+
             v
Choose what matters, write the report (model)
  |
  v
Check every quote against its page (code)
  |
  v
Post once for this week (code)

Tools

  • fetch_page(url) Read-only Code limits how often it calls each site.
  • search_news(company, since) Read-only It finds recent news.
  • save_copy(source, text) Reversible write It keeps each page for next week's comparison.
  • post_report(week, text) External It posts once per week.
Design decisions
StateA database keeps each page's last copy and a record per week: the facts, the draft, and whether it was posted.
MemoryAlmost none: a short guide from the team says which changes matter.
SubagentsNo, because a loop in code already gives each source its own model call.
SchedulingYes, it runs weekly. The week (for example 2026-W43) is the run's id, so a second trigger that week does nothing.
If one site failsCode retries three times, then lists the page as "not checked" in the report, so a failure never looks like "no change".
ApprovalsPosting is an external effect, because a wrong report misleads people once it is out. So a person approves each report until the evaluation shows few errors, and only then does the team choose to post automatically.
Security boundaryFetches may reach only the listed sites and the news API, and code refuses redirects to other domains. Only the post step holds a credential, and it can post only to the team's channel. Because the model has no tools, a page with hidden instructions can at worst distort a summary.
EvaluationReplay 20 past weeks that the team labeled, and count missed and invented changes.

Another design

  • An agent with web search can find new sources, but its coverage varies, so it fits a monthly run that suggests sources.

What could go wrong

  • A redesign makes a whole page look changed, so the model may invent a price change. Code sends very large changes to a person.

Coding agent: fix failing CI on pull requests

A monorepo gets 400 pull requests a day, and one in five fails CI, often for small reasons such as a lint error. The team wants an agent that pushes a fix or explains the failure.

  1. What starts a run, and what stops it from triggering itself forever?
  2. When may it push, and when should it only comment?
  3. How do you stop it from weakening a test to make it pass?
Reveal one reasonable architecture

An agent runs once per CI failure. Code decides whether to run, the model works out a fix, and code decides whether the fix may be pushed.

CI failed (webhook)
  |
  v
Opted in? Under 2 tries? (code)
  |
  v
Rerun the job once (code) --> passes: label "flaky", stop
  |
  v
Agent loop in a fresh sandbox (model): read log, edit, recheck
  |
  v
Diff check (code): passes, small, no tests weakened
  |-- ok -----> push one commit (code)
  |-- not ok -> comment with the diagnosis (code)

Tools

  • get_ci_log(job) Read-only It returns the failing step's log.
  • edit_file Reversible write Edits stay in the sandbox until code pushes them.
  • run_check(name) Reversible write It reruns only the command CI ran, inside the sandbox. The agent has no general shell.
Design decisions
StateCode records attempts and outcomes per pull request and commit, so a duplicate trigger does nothing.
MemoryA short file per repo, written by people, lists safe fixes.
SubagentsNo, because each failure is one small task.
TriggersA webhook, which is an HTTP call that CI makes when a job fails. The agent's own commit also triggers CI, so code stops after two attempts.
ApprovalsAuthors opt in with a label, which counts as consent. Without it, the agent only comments with a suggested patch.
Security boundaryThe sandbox has no secrets and reaches only the package registry, because pull request code may be hostile. Only the pushing code holds a token.
EvaluationReplay 200 past failures with known fixes. A trial passes only if CI passes and the diff leaves tests and CI config alone.

Another design

  • Run deterministic fixers, such as the linter's auto-fix, before the agent, because they need no model.

What could go wrong

  • The author pushes new commits during the run, so the fix is based on old code. Code checks the branch before pushing and drops the fix rather than overwrite the author's work.

Customer support agent

An online store gets 2,000 support messages a week, mostly about orders and refunds. Refunds up to $100 within 30 days are automatic, and larger ones need a person. The agent should resolve what it safely can.

  1. Which parts are a workflow, and which need an agent?
  2. What does the model see for one conversation?
  3. How might a customer trick it, and what stops that?
Reveal one reasonable architecture

Code routes each message, and a small agent handles anything beyond a status check. The refund limit lives in the refund tool, because only code can enforce a policy.

Message arrives (event)
  |
  v
Verify the customer, load their orders (code)
  |
  v
What is it about? (model, small and cheap)
  |-- order status --> answer from a template (code)
  |-- anything else --> support agent (model + narrow tools)
                           |
                           v  big refund, legal, 3rd contact
                        hand off to a person

Tools

  • get_order(id) Read-only It returns only this customer's orders, because code takes the customer id from the login.
  • update_address(order, address) Reversible write It works only before the order ships.
  • issue_refund(order, amount) External It applies the refund policy itself and pays only to the original card.
  • send_reply(text) External Every message to the customer goes through it.
Design decisions
StateThe helpdesk holds the customer, the orders, each action with its id, and the status. The agent keeps no copy.
MemoryNone of its own. It reads the customer's history each time, and never stores a customer's claim as a fact.
SubagentsNo, because conversations are short and independent.
ApprovalsA support lead approves refunds over the limit, after seeing the order, the rule, and the exact refund.
Security boundaryThe model sees this conversation, these orders, and the policy, but no card numbers or other customers. A fooled model can only request a refund, which the tool still checks.
EvaluationReplay 500 past conversations against fake systems. A trial fails on any policy break or any action that no tool confirmed.

Another design

  • Forms and templates cover the common requests more cheaply. Many teams start there and add an agent later.

What could go wrong

  • A refund call times out, and the retry pays twice. So code sends an idempotency key, an id the payment service uses to ignore repeats.

Personal research agent

You want an agent that answers questions such as "Compare three vector databases for 50 million documents" with a short report and citations. A run can take 20 minutes. Over time it should learn what you know and which sources you trust.

  1. Is this an agent or a workflow?
  2. Would subagents help?
  3. When should it stop?
Reveal one reasonable architecture

This needs an agent, because each search depends on what the last one found. A lead agent splits the question, and researcher subagents explore the parts in parallel. It stops when every part has notes with citations, or when the budget runs out.

Your question
  |
  v
Lead agent splits it into 3-5 parts (model)
  |
  +--> researcher 1 --+
  +--> researcher 2 --+--> notes with quotes and links (files)
  +--> researcher 3 --+
  |
  v
Lead agent merges notes, flags disagreements (model)
  |
  v
Check each quote against its source (code)
  |
  v
Report, plus suggested memory updates for you

Tools

  • web_search, fetch(url) Read-only Only researchers have them. Fetch saves each page to a file.
  • write_note(path, text) Reversible write It writes in this question's folder.
  • delegate(question, budget) Read-only Only the lead has it. It starts a researcher with read-only tools.
  • propose_memory(fact) Reversible write It adds a suggestion to your review list.
Design decisions
StateA folder per question holds the plan, notes, sources, and draft, so you can reopen it later.
MemoryA short profile you control holds your background, trusted sources, and preferred format.
SubagentsYes, because the parts are independent and pages fill a context fast. Each researcher returns at most 500 words with quotes and links.
Security boundaryPages can hide instructions, and a request can leak data in its URL. So researchers never see your profile, and fetch only opens links found in results.
EvaluationUse 30 questions with known good answers. Code checks every quote against its source, and you grade the coverage.

Another design

  • A single agent is cheaper and fine for narrow questions. Add researchers when page text starts to fill its context.

What could go wrong

  • A summary drops a caveat. The notes keep the full quotes, so the lead can check.
  • Trusted sources crowd out everything else. So keep some outside results, and show what was left out.

Sales agent

A software company gets 300 demo requests a week. It wants an agent that researches each lead, scores the fit, writes a first email, and books a call with the right rep. It must respect email consent and never offer discounts.

  1. Which steps are fixed, and which need judgment?
  2. How do you stop it from inventing facts about a prospect?
  3. Which numbers show it works, and which would fool you?
Reveal one reasonable architecture

This is mostly a workflow, because every lead goes through the same steps. The model drafts emails and explains unusual scores. Outbound email gets a gate, because a bad email cannot be unsent.

Demo request (event)
  |
  v
Remove duplicates, check consent, look up company (code)
  |
  v
Score fit: rules (code), unusual cases explained (model)
  |-- poor fit --> polite template (code)
  v
Draft an email from looked-up facts only (model)
  |
  v
Rep approves --> send and record in the CRM (code)
  |
  v
Replies --> reply agent: answers, books a call (model)

Tools

  • lookup_company(domain) Read-only It gets company facts from a data provider, each with its source.
  • save_draft(lead, text) Reversible write It saves a draft in the CRM, the sales team's customer database.
  • send_email(draft) External It runs after approval, and code blocks anyone who did not ask to be contacted.
  • book_meeting(lead, time) External It runs once the prospect picks a time.
Design decisions
StateThe CRM holds the lead, the facts, the score, the draft, the approval, and the replies.
MemoryThe agent loads a profile of a good customer and a writing guide, plus good past emails as examples.
SubagentsNo, because each lead is a short pipeline. Leads run in parallel from a queue.
ApprovalsReps approve every first email for a month. After that, code holds any email or reply that mentions a price or a discount until a rep approves it.
Security boundaryThere is no discount tool, so the agent cannot apply a discount. It could still promise one in plain text, which is why code holds pricing messages for a rep. The model sees one lead, not the whole CRM.
EvaluationScore 200 past leads and compare with the reps' judgment. Code checks that every personal claim in an email matches a looked-up fact.

Another design

  • As a safer first step, the agent writes a brief and a draft, and the rep sends the email.

What could go wrong

  • The reply rate rises because people reply angrily. So count meetings held and complaints, not replies.

Browser agent

Your operations team spends hours each week in a supplier's web portal that has no API. They download invoices, compare them with purchase orders, and file disputes for mismatches. They want a browser agent to do the downloads and draft the disputes.

  1. Which parts should the browser agent not do at all?
  2. Can the model see the password?
  3. What happens when the portal's layout changes?
Reveal one reasonable architecture

Split the job. The browser agent only finds and downloads invoices. Code compares them with purchase orders, because that is arithmetic, not judgment. The model drafts disputes, and a person submits them.

Weekly schedule (code)
  |
  v
Browser agent in a fresh, isolated browser (model)
  log in (code fills in the password)
  open invoices --> filter last week --> download each one
  |
  v
Downloads match the portal's invoice count? (code)
  |
  v
Compare with purchase orders (code)
  |-- all match --> mark ready to pay
  v   mismatch
Draft a dispute (model) --> a person submits it

Tools

  • read_page() Read-only It returns the accessibility tree: the page's buttons, links, and text, as a screen reader sees them. This is smaller and more exact than a screenshot.
  • click(element) Depends on target Code blocks buttons such as Submit, Pay, and Delete.
  • download(link) Reversible write It saves the file to the run's folder.
  • login() Read-only It takes no arguments, so the model never handles the password.
Design decisions
StateEach run records the invoices listed and the files downloaded, so a rerun after a crash skips finished files.
MemoryA short guide, written by people, describes the portal's quirks.
SubagentsNo, because one browser session is easier to follow and gentler on the portal's rate limits.
ApprovalsA person submits every dispute. Code blocks dangerous clicks instead of asking, because constant questions teach people to approve without reading.
Security boundaryA fresh browser in a container can reach only the portal and has no real cookies. The portal account can draft disputes but cannot pay.
EvaluationRecord the portal's pages and replay them offline. A trial passes when the agent finds every invoice and tries no blocked click.

Another design

  • Ask the supplier for an export or an API first. If the portal rarely changes, a plain script with no model also works.

What could go wrong

  • The layout changes and the agent cannot find the invoices. It stops after 15 steps and alerts a person instead of guessing.

Data-analysis agent

Product managers ask questions such as "Why did weekly signups drop 12% last week?" and wait two days for an analyst. The company wants an agent that answers in chat by querying the data warehouse and showing its work.

  1. What must the agent know before it writes a query?
  2. How does a manager know whether to trust an answer?
  3. When should it hand off to an analyst?
Reveal one reasonable architecture

This needs an agent, because each query depends on the last result. But every tool is read-only, each metric uses its approved definition, and every number in the answer comes from a query that ran.

Question in chat
  |
  v
Agent loop (model + read-only tools, at most 20 steps)
  look up metric --> write SQL --> run on a copy --> read result
  --> next query, until the answer holds up
  |
  v
Build the answer (code): copy numbers from results, attach SQL
  |
  v
Reply, labeled "approved metric" or "exploratory"

Tools

  • get_metric(name) Read-only It returns the approved definition, such as what counts as a signup.
  • search_catalog(term) Read-only It finds tables, columns, and known data problems.
  • run_sql(query) Read-only It runs on a read-only copy of the database, with limits on rows, time, and cost.
Design decisions
StateCode records each query with its SQL, row count, and data freshness. The model sees totals and sample rows, not full tables.
MemoryThe agent uses the approved metric definitions, known traps such as "signups before March 3 count some users twice", and past answers with their SQL.
SubagentsUsually no, because each query depends on the last. One exception helps: a checker recomputes the headline number without seeing the first answer.
ApprovalsIt needs none, because no tool can write. Costly queries and metrics without an approved definition go to an analyst.
Security boundaryQueries run with the asker's permissions, so the agent cannot show anyone data they could not query themselves.
EvaluationUse 100 questions with analyst-checked answers. Grade the number, the method, and whether the answer states its uncertainty.

Another design

  • One model call that writes SQL handles lookups such as "signups last week". But it cannot follow up, so it fails on "why" questions.

What could go wrong

  • A join counts rows twice and gives a believable wrong number. The recheck catches most of these.
  • The answer turns a correlation into a cause. So answers separate what the data shows from guesses about why.

Autonomous debugging agent

On-call engineers get paged 30 times a week, often for repeats such as a full disk or a bad deploy. The team wants an agent that starts investigating when an alert fires, posts findings in the incident channel, and may apply a few known fixes.

  1. What may it do alone, and what needs a person?
  2. How does it track hypotheses?
  3. What counts as "root cause found"?
Reveal one reasonable architecture

The agent only reads by default, and it keeps a list of hypotheses with evidence for and against. A cause counts as found only when it explains the symptom and when it started, and a fix confirms it.

Alert fires (event)
  |
  v
Gather recent deploys and past incidents (code)
  |
  v
Investigate (model + read-only tools, at most 15 minutes)
  H1 bad deploy 4512   3 for, 0 against
  H2 database load     0 for, 2 against
  |
  v
Post findings every 5 minutes (code)
  |
  v
Propose a fix: "roll back deploy 4512" (model)
  |
  v
On-call approves --> fix runs --> errors back to normal? (code)

Tools

  • query_logs, query_metrics, list_deploys Read-only Code caps their results and saves long outputs to files.
  • update_hypotheses(list) Reversible write It edits the list in the incident record.
  • propose_fix(action) External It offers fixes from a short approved list, such as a rollback. A separate service runs them after approval.
Design decisions
StateThe incident record holds the timeline, the hypotheses with links to their queries, and the fixes tried, so everyone shares one view.
MemoryCode finds past incidents by symptom. Runbooks, the written procedures for known problems, each include a check that proves the fix worked.
SubagentsNot at first, but if incidents span many services, helpers can gather evidence in parallel.
TriggersCode starts one investigation per incident, not per alert, because alerts arrive in bursts.
ApprovalsEvery fix needs approval. The request shows the evidence, the expected effect, and how to undo it.
Security boundaryThe agent's credentials are read-only. Only the fix service holds deploy credentials, and it runs only listed fixes. Log lines count as data, never as instructions.
EvaluationReplay 50 past incidents and check the top hypothesis against the real cause. Then use shadow mode: the agent sees real alerts but acts on none, and on-call grades it.

Another design

  • For alerts with one known fix, such as a full disk, a plain script is better than a model.

What could go wrong

  • A confident wrong answer misleads the people on call. So the agent shows its evidence and labels guesses as hypotheses.
  • A rollback undoes a database change. Each runbook lists its preconditions, and code checks them before offering the fix.

Long-running project agent

Your team wants an agent to build an admin dashboard from a spec with 40 features, in two-hour sessions over two weeks. The team reviews progress each morning, and the agent must pick up where it left off, even after a crash.

  1. What carries over between sessions, and where?
  2. What does "done" mean for one feature?
  3. How do you stop it from declaring victory early?
Reveal one reasonable architecture

Keep progress in files and git, not in a conversation. Each session starts fresh, reads the progress notes, builds one unfinished feature, and commits when its test passes. Only a passing test marks a feature done.

Session starts and takes the repo lock (code)
  |
  v
Read PROGRESS.md, features.json, git log; run smoke tests
  |
  v
Pick one unfinished feature (model)
  |
  v
Build it --> run its test --> fix --> repeat (model)
  |
  v
Test passes --> commit; code marks the feature passing
  |
  v
Budget nearly used --> write a handoff note, end (code)

Tools

  • edit_file Reversible write Each session starts from a clean checkout, so git can restore every file to the last commit.
  • run_command Depends on command It runs only in the sandbox, for tests and the dev server.
  • ask_team(question) External It posts a question, and the agent moves to another feature while it waits.
Design decisions
StateThe repo holds it: features.json lists each feature's test and status, PROGRESS.md holds notes and decisions, and git records every step.
MemoryA project instructions file lists the stack, the commands, and team decisions.
SubagentsA read-only reviewer checks each feature against the spec before the commit. Only one agent writes code, so edits never conflict.
TriggersSessions start on a schedule or on request. A lease, which is a lock that expires, stops two sessions from running at once.
ApprovalsPeople merge in the morning review, and only people change the spec, the feature list, or the tests.
Security boundaryA container holds the repo and fake data. The tests and features.json are read-only to the agent, so it cannot change what counts as done.
EvaluationFirst try a five-feature spec with a known result. Then track features passing and features that break again.

Another design

  • One long conversation with compaction is simpler, but each compaction can lose a decision.

What could go wrong

  • A new feature breaks an old one. So every passing feature's test runs before each commit.
  • A crash leaves half-done work. The next session discards uncommitted changes and restarts that feature.

Final scenario: migrate 300 repositories

No chapter covers this one directly. Your company is replacing its logging library across 300 repositories owned by 40 teams. The goal is a reviewed pull request in every repo within six weeks, with tests passing and no change to the log lines that alerts watch.

  1. Which parts are deterministic, and where does a model help?
  2. How do you avoid duplicate pull requests when a job retries?
  3. What counts as done, for one repo and for all 300?
  4. How do you avoid swamping 40 teams with reviews?
Reveal the rubric for this scenario

There is no single answer, so grade your design with this table, then with the general rubric below. Many strong designs use a queue with one job per repo. A codemod, which is a script that rewrites code mechanically, makes most of the changes, and an agent fixes the rest.

300 repos --> one job per repo in a queue (code)
  |
  v
In a fresh sandbox: the codemod rewrites the calls (code)
  |
  v
Build and test --> pass --> open or update the PR (code)
  |
  v  fail, or calls the codemod skipped
Agent fixes the rest (model, at most 40 steps)
  |-- pass --> open or update the PR (code)
  |-- fail --> report for a person
Rubric for the migration
AreaA strong designA weak design
Model vs codeA codemod does most of the work, and a pilot measures what is left.An agent rewrites every repo from scratch.
Unit of workOne job per repo, with its status and pull request stored in a database.One long conversation that "remembers" which repos are done.
DuplicatesA fixed branch name per repo, and a check for an existing pull request before opening one.A retry opens a second pull request.
Security boundaryA fresh container per repo, because build scripts are untrusted code. The token can open pull requests but not merge them.One machine with an admin token for every repo.
DoneTests pass, no old imports remain, and a golden test (a comparison with saved, known-good output) shows alert-related log lines are unchanged."The agent said done."
Human loadAbout 50 pull requests a week in total, one or two per team, paced by each team's review capacity. Each description says how the change was checked.300 pull requests on day one.
EvaluationA pilot on 10 varied repos before the full run.Run on all 300 and see.

Traps in this scenario

  • Structured log fields can record whole objects, including personal data that the old format left out.
  • Some repos call the logger through wrapper functions, so both the codemod and a text search miss them.

Grade your own design

Suppose a design says "a human reviews risky actions". That scores 1 on approvals, because it names neither the actions nor the reviewer. Score every row this way: 0 if it is missing, 1 if it is vague, and 2 if it is specific and says why.

Design rubric
CriterionScores 2Red flag
TradeoffsSays which steps the model decides and why, and names one rejected alternative."It's an agent", with no reason.
Security boundaryNames what the process can touch at all (files, network, credentials) and what enforces each limit."The prompt tells it not to."
ApprovalsNames each gated action, who approves, what they see, and what happens on a rejection."A human reviews it", or approvals for everything.
Done criteriaLists checks that code runs, separate from the model's claim."When the agent says it's done."
FailureSays what happens after a timeout, a retry, or a crash, for each external action.Retries that can repeat an action.
ContextSays what the model sees in each call and what stays out, such as secrets."Give it everything."
EvaluationUses cases with known answers, runs several trials each, and counts policy breaks as failures."We'll see if users like it."

Look at the zeros first. A zero on the boundary, the approvals, or the done criteria means the design is not ready to hand to an AI coding system, even if the total is high. Chapter 21 shows how to turn these rows into tests that the finished system must pass.

Try it on the reveals above. They are not perfect, so find a row where one scores 1.