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
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
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
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
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
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
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
Common pitfalls advanced
| 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?