Agentic AI Foundations & Agent Architectures
You built one agent loop by hand (Chapter 4). Before you pick up a framework, learn the named architectures that loop can take — ReAct, plan-and-execute, reflection, and multi-agent topologies. Knowing which shape fits a problem is what stops a framework from being a black box.
Once agents get complex (many steps, tools, memory, branching), hand-writing the loop gets messy. LangChain is a toolkit of building blocks (models, prompts, tools, memory) and LangGraph adds a way to describe an agent as a graph of steps with loops and branches. This section shows when a framework helps and how to use these two well.
The words you'll hear (in plain terms):
| Term | What it actually means |
|---|---|
| framework | pre-built structure so you don't hand-code everything (vs. the from-scratch agent in Ch 4). |
| chain | a sequence of steps: prompt → model → parse, wired together. |
| graph (LangGraph) | an agent drawn as nodes + edges, allowing loops, branches, and state. |
| state | the data carried between steps (messages, scratchpad, results). |
| tool | a function the agent can call — same idea as Ch 4's tools. |
What you need before starting:
- You should have built the from-scratch agent (Ch 4) first — frameworks make more sense once you've felt the manual version.
- Python basics + comfort with functions and classes.
pip install langchain langgraph langchain-anthropicfor the labs.
New to the topic? Read this box, then take the chapters in order — each section is tagged essential → expert so you always know the depth you're at.
Learning objectives
- Distinguish a workflow from an agent and know which to reach for.
- Name and diagram the core single-agent architectures: ReAct, plan-and-execute, reflection.
- Describe the common multi-agent topologies: supervisor, network, hierarchical.
- Explain why frameworks (LangChain, LangGraph) exist and what they add over raw API calls.
- Choose an architecture for a given problem and justify it.
Workflow vs agent — the first fork essential
Here's a mistake that costs teams months. Someone reads a blog post about autonomous agents, gets excited, and rebuilds a boring three-step pipeline — "summarize this ticket, look up the customer, draft a reply" — as a fully autonomous agent that "figures it out." Now, instead of a script that runs the same way every time, they have a system that takes a different route on every call, sometimes skips the lookup, occasionally loops three times, and can't be unit-tested. They spent the LLM's freedom on a problem that had no ambiguity to resolve. The steps were already known — they just handed the model a decision it never needed to make.
So before you draw a single box, you make one fork, and it's the most consequential decision in this whole chapter: is this a workflow or an agent? The plain-language test: do you know the steps in advance? If yes — if you could write them down as a numbered list before the code ever runs — that's a workflow: you write the control flow, and the LLM just fills in the wordy bits (the summary, the classification, the draft). If no — if the right sequence of steps depends on what the model discovers along the way — that's an agent: the LLM decides the path at runtime.
The useful analogy is a recipe versus a detective. A recipe is a workflow: the steps are fixed, you follow them in order, and the same inputs give the same dish — the only "intelligence" is in executing each step well. A detective is an agent: they don't know in advance whether the next move is to interview a witness, check an alibi, or re-examine the scene; each clue reshapes the plan. You'd never write a murder investigation as a fixed recipe, and you'd never turn "boil pasta for 9 minutes" into an open-ended investigation. The common mistake runs in exactly that second direction — reaching for the detective when a recipe would do — because agents feel more impressive. But every ounce of runtime freedom you grant is an ounce of predictability, testability, and cost-control you give up. Freedom you don't need is pure downside. The table below sharpens the four axes that separate the two, and the callout after it states the rule you should default to.
Not everything that uses an LLM is an agent. The distinction drives every later choice.
| Workflow | Agent | |
|---|---|---|
| Control flow | You code the path; the LLM fills in steps | The LLM decides the path at runtime |
| Predictability | High — same route every time | Lower — route varies with the input |
| Best for | Well-understood, repeatable tasks | Open-ended tasks hard to fully script |
| Example | "Summarize → translate → email" pipeline | "Investigate this alert and fix it" |
Architecture 1 · ReAct (reason + act) essential
You already built this — you just didn't know it had a name. The hand-written agent loop from Chapter 4, the one that called a tool, read the result, thought about it, and called another tool, is a real architecture with a paper behind it and a name everyone in the field uses: ReAct, short for reason + act. Naming it matters more than it sounds. Once you can say "this is a ReAct agent," you can reason about its known strengths and its known failure modes instead of rediscovering them by accident at 2 a.m. in production.
The idea in one breath: the model reasons about what it needs ("I need today's weather for Boston"), acts by calling a tool to get it, observes the result, and then reasons again — around and around until it has enough to answer. Think of how you fix an unfamiliar bug. You don't write a plan first; you form a hunch, run one command to test it, read the output, form a new hunch based on what you saw, and repeat. Each observation reshapes your very next move. That tight, adaptive think–act–observe loop is ReAct, and it's why it's the default meaning of "an agent": it's the simplest shape that can handle surprises, because it never commits to more than one step at a time.
That same one-step-at-a-time nature is also its weakness, and it's worth naming the common mistake now: without any plan to anchor it, a ReAct agent can wander. It re-checks things it already knows, chases a tangent because one observation looked interesting, or — the classic failure — loops forever calling the same tool with slightly different arguments, never deciding it's done. The mental model to keep: ReAct is brilliant at reacting (the name is honest) and poor at staying on a long track. The fixes you'll meet in the next sections — a plan up front, a step budget, a reflection pass — all exist to give this loop the discipline it lacks on its own. For now, read the diagram as the skeleton under every agent runtime you'll ever touch.
The default agent loop you already wrote is ReAct: the model alternates reasoning ("I need the weather") and acting (calls a tool), observes the result, and repeats until done. It's the workhorse — simple, general, and what most "an agent" means by default.
This is the basic agent loop — the same one you hand-wrote earlier. "ReAct" just means the agent Reasons and then acts, over and over, until it's done.
- Read the boxes left to right. Think is the model deciding what to do next ("I need today's weather"). Act (tool) is it calling a tool to actually do that thing (look up the weather). Observe is it reading the tool's result.
- Each solid arrow is one step handing off to the next: Think → Act → Observe.
- The dashed arrow curving back from Observe to Think is the loop: after seeing a result, the agent thinks again. It keeps circling until it has enough to answer.
- When the model decides it's finished (it stops asking for tools), control leaves the loop along the arrow to Answer on the right — the final reply to the user.
In short: Think → Act → Observe, repeat. The agent isn't following a fixed script; it decides its next move each time it goes around. Flexible, but it can wander without a plan to anchor it.
Architecture 2 · Plan-and-execute essential
Picture handing a junior colleague a big task with no plan. "Migrate the whole billing service to the new API." They start enthusiastically, fix the first file, get curious about a second, notice something in a third, and two hours later they've touched twelve files, half-finished, with no idea whether they're a quarter done or nearly there — and neither do you. Now picture the same task where they first spend ten minutes writing a numbered checklist, get it approved, and then work down it. The second version is calmer, cheaper, and — crucially — inspectable: you can read the plan before any work happens and catch a wrong turn before it costs anything. That gap is exactly the gap between ReAct and plan-and-execute.
The shape is right there in the name. First a planner looks at the whole goal and writes an ordered list of steps, once. Then an executor works down that list in order. ReAct asks "what's my next single move?" at every turn; plan-and-execute asks "what's the whole route?" up front and then just drives it. The everyday analogy is planning a road trip before leaving versus deciding each turn at the moment you reach an intersection. Deciding-at-every-intersection (ReAct) adapts beautifully to a road closure but is exhausting and easy to get lost on; mapping the route first (plan) is coherent and lets someone else check it, at the cost of some agility when reality diverges from the map — which is what the optional re-plan loop in the diagram is for.
Two payoffs make this worth the rigidity. First, coherence on long tasks: a single up-front plan keeps a ten-step job pointed at the goal, where a step-at-a-time ReAct agent tends to drift. Second, and this is the one people miss, cost: planning is the hard part that wants your best (most expensive) model, but once the plan exists, each individual step is often simple enough for a cheaper, faster model to execute. You pay for the smart model once instead of on every loop iteration. The common mistake is forcing this pattern onto genuinely unpredictable work — if you can't know the steps until you've seen intermediate results (a live investigation, an open-ended debug), a rigid up-front plan is fiction, and you're back to needing ReAct's adaptivity. Plan-and-execute shines precisely when the task is long and its shape is knowable in advance: research reports, data migrations, multi-stage document processing.
ReAct decides one step at a time. Plan-and-execute first writes a whole plan, then executes the steps (optionally re-planning if reality diverges). Trading some flexibility for structure makes long tasks more coherent and cheaper — the expensive planner runs once, and cheaper models can execute steps.
Here the agent writes the whole plan first, then works through it — instead of deciding one step at a time like ReAct.
- Planner (left) is the model breaking the task into an ordered to-do list. The solid arrow sends that list to the three stacked boxes in the middle — step 1, step 2, step 3 — which are the plan it produced.
- Executor (right) is what actually carries out each step in order. The solid arrow from the steps into the Executor means "now go do these."
- The dashed arrow looping back from Executor to Planner is re-planning: if doing a step reveals something unexpected, the agent can return to the Planner and adjust the list. The caption marks this as optional — simple runs never take it.
- So control flows plan once → execute the steps → (only if needed) re-plan, rather than rethinking from scratch on every single step.
In short: Decide the route up front, then follow it. Good for long tasks with a knowable shape (research, a migration): the expensive planning happens once, and cheaper steps do the legwork.
Architecture 3 · Reflection intermediate
You already trust this pattern — with your own writing. Think about the last important email you sent. You didn't fire off the first sentence that came to mind. You wrote a draft, then reread it as if you were the recipient, winced at a clumsy line, and fixed it before hitting send. The reread is the whole trick: the version of you that drafts is optimistic and fast; the version that rereads is skeptical and catches what the first version glossed over. Reflection is that exact move, wired into an agent.
Mechanically: a generator produces a draft, a reflector critiques it against some standard, the generator revises in response, and the loop repeats until the critique comes back clean (or you hit a round cap so it can't spin forever). The detail that makes it actually work — and that people skip — is that the reflector usually gets a fresh context, not the drafting conversation. Why does that matter so much? Because a model that just spent effort writing something is invested in it; asked "is this good?" in the same breath, it tends to defend its work. Give the critique a clean slate and a rubric ("check for factual errors, unstated assumptions, missing edge cases") and it judges honestly instead of rationalizing. It's the difference between grading your own exam and handing it to a stranger with the answer key.
The trade-off is blunt and you should say it out loud: reflection costs more tokens per result — you're paying for at least a draft, a critique, and a revision instead of one shot. In return you buy markedly higher quality, which is why it's the backbone of serious code, writing, and research agents where being right beats being cheap. The common mistake is bolting it onto everything, including tasks where the first draft is already fine — you triple the cost and latency to fix problems that weren't there. The other mistake is forgetting the round cap: a critic that never declares the draft good enough will loop until your budget is gone. Reach for reflection when the cost of a wrong answer is high and a second, colder look would plausibly catch it — and bound the rounds. You'll build this pattern explicitly in the Deep Agents chapter of the next module.
An agent that critiques its own output and revises. A generator produces a draft; a reflector (often the same model, fresh context) finds flaws; the generator fixes them; repeat until good enough or a limit. This is how "self-correcting" agents get their reliability — the pattern behind strong code, writing, and research agents.
This is a self-correcting agent: it makes something, criticizes its own work, and fixes it — the way you'd draft an email, reread it, and edit before sending.
- Generate (left) produces a first draft — the label on the arrow tells you that's what's being passed to the right.
- Reflect / critique (right) is a checking pass that looks for flaws in the draft. It's often the same model given a clean, fresh view so it judges the work honestly instead of defending it.
- The dashed arrow labelled "revise" loops the criticism back to Generate, which rewrites to fix the problems. This generate → critique → revise circle repeats until the result is good enough (or a retry limit is hit).
- When the critique finds nothing worth fixing, control exits along the arrow to ship on the right — the finished output.
In short: A second "is this actually good?" pass catches mistakes the first pass missed. It costs extra tokens per answer but buys noticeably higher quality — common in strong code and writing agents.
Multi-agent topologies intermediate
There's a moment where one agent stops coping, and it's worth recognizing before you hit it. You keep adding tools — now it has forty. You keep stuffing the system prompt with instructions for billing and refunds and shipping and legal. The context window fills with irrelevant tool descriptions on every call, the model starts picking the wrong tool because it's drowning in choices, and quality quietly slides. This is the same thing that happens to a person who's been handed five unrelated jobs at once: not incapable, just overloaded. The instinct at that point — "let me split this across several specialized agents" — is right. But how you wire those agents together is a real design decision, and picking wrong is where multi-agent systems earn their bad reputation.
The organizational analogy is almost exact, which is why it's the one to hold in your head. A team of specialists needs a structure, and there are three that cover most cases. A supervisor is a manager with a team: one router reads the task, delegates to the right specialist (billing, tech, sales), and merges the results. A network is a group of peers with no boss: any agent can hand off to any other, whoever seems most relevant next. A hierarchy is a company org chart: supervisors of supervisors, teams made of teams, for problems too big for one manager. You already have intuitions about which org structures work — and those intuitions transfer.
The single most common mistake here is reaching for multi-agent too early, and it's worth being blunt: one good agent almost always beats three confused ones. Every agent you add multiplies the ways coordination can go wrong — messages lost in handoff, two peers ping-ponging a task neither owns, latency and token cost stacking up with each hop. Split the work only when a single agent genuinely can't hold the context or the tool set, not because "a team sounds more powerful." And when you do split, start with a supervisor: it keeps control in one place, so you can see who was asked to do what and debug it. Networks are flexible but hard to reason about (they can loop or stall with no one in charge); hierarchies scale to large problems but multiply latency and cost with every level. The diagram and table below lay out all three side by side — read them as "what does the coordination cost me?" not "which is most sophisticated?"
When one agent's context or tool set gets unwieldy, split the work across specialized agents. Three topologies cover most designs (you'll build these in CrewAI and AutoGen next module).
When one agent gets overloaded, you split the work across several specialist agents. This diagram shows three common ways to wire them together — read it as three separate mini-pictures, left to right.
- Supervisor (left): one boss box (
sup) at the top with arrows fanning down to three worker boxes. The supervisor routes each task to the right specialist and combines their results. Most common and easiest to control. - Network (middle): peer agents drawn as circles with lines running between them in many directions — no boss. Any agent can hand off to any other. Flexible, but harder to follow and it can loop or stall.
- Hierarchical (right): a tree — a top box points to mid-level boxes, which each point to their own workers. It's supervisors of supervisors, for big tasks that split into sub-teams.
- Arrows here mean "delegates to" / "talks to." The one-line caption at the bottom sums it up: supervisor routes to workers, network peers talk freely, hierarchy nests supervisors.
In short: Pick the simplest shape that fits. Start with supervisor (clear roles, controllable); reach for network or hierarchy only when the work genuinely needs it — one good agent usually beats three confused ones.
| Topology | Use when | Watch out for |
|---|---|---|
| Supervisor | Clear specialist roles; you want control & observability | Supervisor is a bottleneck / single point of failure |
| Network | Peers genuinely need to collaborate dynamically | Hard to debug; can loop or stall |
| Hierarchical | Very large tasks that decompose into sub-teams | Latency & cost multiply with depth |
Why frameworks exist intermediate
Notice the small annoyances that have piled up. By now you've written the tool-call loop by hand. And the retry-on-failure logic. And the JSON parsing for tool arguments. And the "remember the last N messages" bookkeeping. Each one was easy the first time. But you wrote the same loop again in the next project, and the retry logic slightly differently, and when your teammate wanted to swap the vector database you realized the choice was hard-coded in six places. None of it was hard — it was just repetitive, and every hand-rolled copy is a subtly different bug waiting to happen. That accumulated friction is the entire reason frameworks like LangChain and LangGraph exist.
The honest analogy is a web framework. You can handle raw HTTP sockets, parse request headers, and route URLs yourself — people did, for years — but Django and Flask exist because almost everyone was writing the same plumbing over and over, and shared, tested plumbing beats a hundred private reimplementations. A framework's real offering is threefold: reusable components (a memory object, a retriever, a retry wrapper you configure instead of code), standard interfaces (swap Claude for another model, or one vector DB for another, by changing a line rather than rewriting), and orchestration (LangGraph gives you explicit state, branches, and cycles as first-class things instead of tangled if statements). It's abstraction plus glue — the same two things every framework in software history has sold.
But here's the part that most tutorials won't tell you, and it's why this course made you build the raw loop first: a framework is a trade, not a free upgrade. In exchange for those components you take on abstraction (another layer between you and what's actually sent to the model), dependencies (that break, deprecate, and pin you to versions), and a learning curve for the framework's own concepts. Worst of all, a framework can hide what's happening — when your agent misbehaves, you now debug through someone else's abstractions instead of your own code. The common mistake is adopting a framework reflexively, because it's what the blog posts use, on a problem small and fixed enough that raw API calls would have been clearer and lighter. The rule is unglamorous: use a framework when its components save you real, repeated work and its structure genuinely fits your problem — and because you learned the loop by hand, you'll always be able to tell what it's doing for you versus to you.
You've done all of this with raw messages.create calls. So why LangChain and LangGraph?
| Raw API (what you've done) | What a framework adds |
|---|---|
| You write the loop, retries, parsing each time | Reusable components: chains, memory, retrievers, agents |
| Swapping model/vector-DB means rewrites | Standard interfaces — swap a provider by changing one line |
| State & control flow are ad-hoc | LangGraph: explicit state, branches, cycles, persistence |
| No built-in tracing | LangSmith tracing across every step (later module) |
Choosing an architecture advanced
The trap at this point is picking the architecture you find most interesting. You've just met ReAct, plan-and-execute, reflection, and three multi-agent shapes, and the natural pull is toward the impressive ones — a hierarchical multi-agent system sounds like a much better answer at a dinner party than "a fixed workflow." Resist it. The architecture is not the achievement; solving the problem cheaply and reliably is. The right question is never "which pattern is coolest?" but "what is the least machinery that actually fits this task?"
The good news is that the choice isn't a matter of taste — it falls out of a short series of yes/no questions you can ask about the problem itself, in order. Are the steps known and fixed? Then you don't even have an agent — you have a workflow, and you're done. Not fixed, but one agent with tools can handle it? ReAct. Long task whose shape you can know up front? Plan-and-execute. Does quality genuinely depend on catching your own mistakes? Add a reflection loop. Are there truly distinct specialist roles that overflow one agent? A supervisor. The decision sketch below is exactly that ladder, written as code so the ordering is unmissable — and the ordering is the point.
The mistake to name here is starting at the bottom of the ladder instead of the top. Every rung you climb — from workflow to agent, from single to multi — adds unpredictability, cost, and debugging surface. So you climb only when the rung below has demonstrably failed you, never preemptively "to be safe." The default in the last line of the sketch is the whole philosophy in one clause: start simplest; escalate only when it fails. An over-engineered agent that works in the demo and falls over in production is worse than a boring workflow that just runs — read the sketch as permission to choose boring on purpose.
Common pitfalls advanced
Every mistake in the table below, someone has shipped to production — probably last week. These aren't hypothetical gotchas; they're the specific ways smart people misuse the exact patterns you just learned, and they cluster around one root cause: reaching for more machinery than the problem needs. If you notice a theme running through this whole chapter, it's that — and this section is where it comes home to roost.
The through-line worth internalizing: complexity is a cost you pay whether or not it buys you anything. An agent where a workflow would do, a multi-agent swarm where one agent would do, a framework adopted before you understood the loop — each one adds unpredictability, tokens, latency, and debugging pain, and each was almost always chosen because the fancier option felt more capable. The habit that prevents all of these is the same: describe your problem plainly, pick the simplest shape that fits, and add structure only when the simpler thing has actually failed in front of you. Read the table as a checklist of "did I over-build this?" — and note the last row especially, because the architecture names are a vocabulary for describing designs, not rigid boxes you must force a system into.
| Pitfall | Fix |
|---|---|
| Building an agent when a workflow would do | Script fixed steps; reserve agents for open-ended tasks |
| ReAct agent wandering without a plan | Add a plan-and-execute layer or a step budget |
| Reaching for multi-agent too early | One good agent beats three confused ones; split only when context/tools overflow |
| Adopting a framework before understanding the loop | You already learned the raw loop — keep that lens |
| Treating architecture names as rigid boxes | They compose; describe your design, don't force it |
Exercises advanced
Exercise L1.1 — Classify the design
Context: Naming the architecture behind a plain description is the core design literacy this chapter builds.
Your task: For four systems, name the architecture and justify each: (a) "summarize each PDF then email a digest", (b) "research a company and write a report", (c) "draft a PR then review and fix it", (d) "triage tickets to billing, tech, or sales specialists".
Requirements:
- Assign an architecture to each of the four
- Fixed steps → workflow; knowable multi-step → plan-and-execute
- Self-correction → reflection; specialist roles → supervisor multi-agent
- Justify each choice in a sentence
💡 Hint: Ask for each: are the steps fixed, does it self-correct, and are there distinct specialist roles?
Show answers
(a) workflow — fixed steps. (b) plan-and-execute — knowable shape, multi-step. (c) reflection — self-correction. (d) supervisor multi-agent — specialist roles.
Exercise L1.2 — Reflection by hand
Context: Building reflection by hand — before a framework hands it to you — is what makes the pattern stick.
Your task: Using only the raw Chapter-2 model API, implement a reflection loop: call 1 drafts, call 2 (fresh context) critiques against a rubric, call 3 revises; then compare the revision to the draft.
Requirements:
- Three distinct model calls: draft, critique, revise
- The critique call gets a fresh context, not the drafting conversation
- Critique against an explicit rubric
- Compare the revised answer to the original draft
💡 Hint: Giving the critic a fresh context stops it from rationalizing the draft it just wrote.
Exercise L1.3 — Justify a framework (or not)
Context: Whether to reach for a framework at all is a judgment call — and the reasoning, not a universal answer, is the skill.
Your task: Pick a project idea and write two short paragraphs: one arguing to build it with the raw API, one arguing for LangChain/LangGraph — then decide which wins and why.
Requirements:
- One paragraph for the raw-API case, one for the framework case
- Ground both in the specific project, not generalities
- Reach a decision and state the deciding factor
- Accept that there's no universally right answer
💡 Hint: The framework usually wins as the number of tools, custom routing, or state/HITL needs grows; the raw API wins for something small and fixed.
🪜 Practice ladder beginner → industry
Six graded exercises, easy to real-world. Try each before opening its solution.
Context: Not everything that calls an LLM is an agent. A workflow follows a fixed path; an agent lets the model choose the path — and knowing the fork is the first design decision.
Your task: Write a classifier that labels a plain-English description as 'workflow' or 'agent'.
Requirements:
- Decide based on whether the control flow is fixed or model-decided
- Detect model-decided language (chooses/decides which/loops until/picks the tool)
- Return 'agent' when the model picks the path, else 'workflow'
- Show it on one fixed-path example and one model-decides example
💡 Hint: Scan the description for phrases that mean the model is choosing at runtime; the absence of those is a workflow.
Show solution
The fork: is the control flow fixed, or model-decided? Runnable:
def classify(description):
d = description.lower()
model_decides = any(k in d for k in
["decides which", "chooses", "loops until", "picks the tool", "figures out"])
return "agent" if model_decides else "workflow"
print(classify("prompt -> model -> parse, always the same steps")) # workflow
print(classify("the model decides which tool to call and loops until done")) # agent
Reach for a workflow when the steps are known; reach for an agent only when the path must be decided at runtime.
Context: ReAct — Reason → Act → Observe, repeated — is the skeleton under every agent runtime. It's a loop, not a chain.
Your task: Model the ReAct loop offline: a 'brain' proposes a tool and arguments, you run the tool, feed the observation back, and repeat until it emits a final answer.
Requirements:
- A brain function returns either an act step (tool + args) or a final answer
- Execute the requested tool and append the observation to a scratchpad
- Loop reason→act→observe until a final answer
- Bound the loop with a max-steps cap so it can't run forever
- Demonstrate it solving a tiny task
💡 Hint: Keep a scratchpad list the brain reads to decide its next step; the bounded loop is what a framework's agent runtime provides.
Show solution
ReAct is a loop, not a chain. Model it in stdlib:
TOOLS = {"add": lambda a, b: a + b}
def brain(goal, scratch):
# toy reasoner: if we haven't added yet, act; else answer
if not scratch:
return {"type": "act", "tool": "add", "args": (2, 3)}
return {"type": "final", "answer": scratch[-1]["observation"]}
def react(goal, max_steps=5):
scratch = []
for _ in range(max_steps):
step = brain(goal, scratch)
if step["type"] == "final":
return step["answer"]
obs = TOOLS[step["tool"]](*step["args"]) # ACT
scratch.append({"thought": step, "observation": obs}) # OBSERVE
return "gave up (step limit)"
print(react("what is 2+3?")) # 5
Reason->Act->Observe with a bounded loop is the skeleton under every framework's agent runtime.
Context: Plan-and-execute makes the whole plan up front then runs it; ReAct decides each step as it goes. Plan-first trades adaptivity for a predictable, inspectable plan.
Your task: Model a planner that emits an ordered plan once, then an executor that runs the steps in sequence, threading state forward.
Requirements:
- The planner returns an ordered list of step names from the goal
- The executor runs the steps in order, passing state between them
- Each step transforms shared state (e.g. gather → analyze → write)
- Show the executor producing a final state for a multi-step goal
💡 Hint: Separate 'decide the plan' from 'run the plan' into two functions; the plan is fixed before execution begins.
Show solution
Plan first, then execute the fixed list. Runnable:
def plan(goal):
# produce an ordered plan once, up front
if "report" in goal:
return ["gather_data", "analyze", "write_report"]
return ["answer"]
STEPS = {
"gather_data": lambda s: {**s, "data": [1, 2, 3]},
"analyze": lambda s: {**s, "mean": sum(s["data"]) / len(s["data"])},
"write_report":lambda s: {**s, "report": f"mean={s['mean']}"},
}
def execute(goal):
state = {}
for step in plan(goal):
state = STEPS.get(step, lambda s: s)(state)
return state
print(execute("write a report")) # {... 'report': 'mean=2.0'}
Plan-and-execute trades ReAct's adaptivity for a predictable, inspectable plan — better when the path is knowable up front.
Context: Reflection is the agent critiquing its own draft and revising — it buys quality at the cost of extra model calls, so the loop must be bounded.
Your task: Model a generate → critique → revise loop that stops when the critique finds no issues (or a round cap is hit).
Requirements:
- A critique function returns a list of issues; empty means good enough
- Revise the draft in response to the critique each round
- Exit as soon as the critique is clean
- Cap the number of rounds so it can't spin forever
- Return the final draft and how many rounds it took
💡 Hint: The exit condition is "no issues found"; the round cap is the backstop for a critique that never clears.
Show solution
Reflection is a self-critique loop with an exit. Runnable:
def generate(draft):
return draft + " (v+1)"
def critique(text):
# returns a list of issues; empty means good enough
issues = []
if "TODO" in text:
issues.append("contains TODO")
if len(text) < 10:
issues.append("too short")
return issues
def reflect(draft, max_rounds=3):
for r in range(max_rounds):
issues = critique(draft)
if not issues:
return draft, r
draft = generate(draft) # revise in response to critique
return draft, max_rounds
print(reflect("TODO short")) # revised until critique passes or rounds run out
Reflection buys quality at the cost of extra model calls — bound the rounds so it can't spin forever.
Context: Multi-agent systems come in named topologies — supervisor (a router delegates), network (peers hand off freely), hierarchical (teams of teams) — and the shape should follow the problem.
Your task: Write a selector that picks a topology from the problem's shape.
Requirements:
- Choose from supervisor, network, and hierarchical (plus single-agent when none is needed)
- Central control → supervisor; equal peers → network; deep sub-teams → hierarchical
- Take the deciding signals as parameters, not free text
- Show a case selecting each topology
💡 Hint: Supervisor is the safe default (clear control); reach for network or hierarchical only when the coordination need genuinely demands it.
Show solution
Match topology to coordination need. Runnable:
def pick_topology(central_control, peers_equal, deep_subteams):
if deep_subteams:
return "hierarchical -- teams of teams, managers over managers"
if central_control:
return "supervisor -- one router delegates to specialists"
if peers_equal:
return "network -- peers hand off to each other freely"
return "single agent -- no topology needed yet"
print(pick_topology(True, False, False)) # supervisor
print(pick_topology(False, True, False)) # network
print(pick_topology(False, False, True)) # hierarchical
Supervisor is the safe default (clear control); network is flexible but harder to bound; hierarchical scales large orgs of agents.
Context: The whole chapter folds into one decision: given a real problem's constraints, recommend an architecture and say why — the thing that stops a framework from being a black box.
Your task: Given constraints (steps known? needs revision? multiple specialties? bounded cost?), output an architecture recommendation with reasons.
Requirements:
- Known steps and no revision → a fixed workflow/plan-and-execute; else a ReAct agent
- Add a reflection step when quality/revision is required
- Add a supervisor when there are multiple specialties
- Bound steps/recursion when the budget is strict
- Return the architecture plus a list of justifying reasons
💡 Hint: Compose the earlier forks into one function that accumulates both a base architecture and the reasons behind each addition.
Show solution
Fold the chapter's forks into one decision function. Runnable:
def recommend(known_steps, needs_revision, many_specialties, strict_budget):
parts = []
if known_steps and not needs_revision:
base = "workflow / plan-and-execute"
parts.append("steps are known -> a fixed path is cheaper and safer")
else:
base = "ReAct agent"
parts.append("path is decided at runtime -> agent loop")
if needs_revision:
parts.append("add a reflection step for quality")
if many_specialties:
base += " under a supervisor"
parts.append("multiple specialties -> supervisor delegates")
if strict_budget:
parts.append("bound steps/recursion to cap cost")
return base, parts
arch, why = recommend(known_steps=False, needs_revision=True,
many_specialties=True, strict_budget=True)
print(arch)
for r in why:
print(" -", r)
Knowing which shape fits — and why — is what stops a framework from being a black box. The real build wires these shapes with LangGraph nodes/edges (needs langchain installed).
✓ Checkpoint — you can move on when you can…
- State the workflow-vs-agent distinction and pick correctly.
- Diagram ReAct, plan-and-execute, and reflection from memory.
- Describe supervisor, network, and hierarchical multi-agent topologies.
- Explain what a framework adds over raw API calls — and its costs.
- Choose an architecture for a given problem and defend it.
Knowledge check check yourself
What is the core distinction between a workflow and an agent, and why prefer a workflow when you can specify the steps?
Show answer
How does plan-and-execute differ from ReAct, and what cost advantage does it give on long tasks?