Multi-Agent Orchestration with CrewAI
L1 named the multi-agent topologies; L5 gave you durable single graphs. Now you compose teams. CrewAI models a team as a crew of role-playing agents working tasks — the most approachable on-ramp to multi-agent, and a clean lens on when splitting the work actually helps.
Instead of one agent doing everything, you can have several specialized agents collaborate: a researcher, a writer, a reviewer. Orchestration is coordinating who does what and how they pass work along. This section covers the main frameworks (CrewAI, AutoGen) and patterns for reliable multi-agent systems.
The words you'll hear (in plain terms):
| Term | What it actually means |
|---|---|
| multi-agent | several agents, each with a role, working on parts of a task. |
| orchestration | coordinating agents — order, handoffs, and who decides what. |
| role | an agent's job + expertise (e.g. 'senior researcher'). |
| handoff | passing work (and context) from one agent to the next. |
| crew / team | a named group of agents with a shared goal. |
What you need before starting:
- Build a single agent (Ch 4) first — multi-agent is agents composed.
- Python basics; comfort with the agent loop concept.
pip install crewaiorautogen-agentchatfor 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.
- an Anthropic API key (
ANTHROPIC_API_KEY) +pip install anthropic
Learning objectives
- Explain CrewAI's model: Agents, Tasks, Crews, and Processes.
- Build a sequential crew where each agent's output feeds the next.
- Use a hierarchical (manager) process — the supervisor topology from L1.
- Give agents tools and know when to reach for CrewAI Flows instead.
- Decide honestly whether a task needs multiple agents at all.
30-second recap
- Agent & tool foundations: The model asks to call a tool, your code runs it and returns the result, and the loop repeats until the model answers — with step caps so it can't run forever.
- Agent architectures: Agents differ in who controls the flow: fixed workflows, a single tool-using loop, or graphs of steps with explicit state.
The CrewAI model: role-play as architecture essential
Picture how a good newsroom ships a story. A reporter chases down the facts, a writer shapes them into prose, an editor tightens and checks it before it runs. Nobody does all three jobs at once, and nobody starts from a blank page — each person receives the previous person's work and adds their specialty. That division of labour is exactly what CrewAI encodes: instead of one over-stuffed prompt trying to research and write and review, you spin up a small team where each member is narrow, sharp, and hands its output to the next.
Why bother splitting the work at all? Because a single prompt that tries to do everything tends to do each part a little worse — it forgets the tone halfway through, or drops sources when it starts writing. Giving each concern its own agent, its own instructions, and its own clear "done" target keeps every step focused, and it makes the pipeline legible: when the brief comes out weak you can see whether the research was thin or the writing was sloppy, and fix just that stage. This is the same instinct behind splitting a monolith into functions — smaller units, clearer contracts, easier debugging.
But here's the honest warning, and it's the theme of this whole chapter: a team is not free. Every extra agent is another LLM call, more tokens, more latency, and one more place for context to get lost in the hand-off. The classic beginner mistake is to reach for a crew because it looks sophisticated, when a single well-equipped agent would have been cheaper, faster, and just as good. Multi-agent earns its keep only when the work genuinely breaks into distinct specialties — hold that gate in your head as you read on (we make it explicit in the "do you need a crew?" section below).
CrewAI's core metaphor is a team of specialists. You define agents by role, give each a goal and a backstory, hand them tasks, and assemble them into a crew that runs under a process (sequential or hierarchical). The role/goal/backstory isn't fluff — it's how you steer each agent's behavior, exactly like a system prompt (Chapter 2).
| Concept | What it is |
|---|---|
| Agent | A role-playing worker: role, goal, backstory, an LLM, and optional tools. |
| Task | A unit of work with a description and an expected_output, assigned to an agent. |
| Crew | The team: a set of agents + tasks + a process that runs them. |
| Process | How tasks run: sequential (in order) or hierarchical (a manager delegates). |
This picture is the whole idea of a crew: a small team of AI agents, each with one job, passing work down a line like an assembly line. There are three specialists here and the work flows left to right.
- The three boxes are three agents, each a specialist: a Researcher who gathers facts, a Writer who drafts a report from those facts, and an Editor who polishes the draft. Each one is really just its own LLM with a role.
- The arrows are the hand-offs. The researcher's finished output becomes the writer's starting material; the writer's draft becomes the editor's. Nobody starts from scratch — each agent builds on the one before it.
- This left-to-right, fixed order is called a sequential process in CrewAI: tasks run one after another in the order you list them.
In short: A crew is a pipeline of experts. Read it as "first this specialist, then hand the result to the next" — exactly how a human team would pass a document around for research, writing, then editing.
Lab M1.1 · A sequential crew essential
shellpip install crewai
crew.pyfrom crewai import Agent, Task, Crew, Process
# CrewAI uses LiteLLM under the hood; point it at Claude via the model string.
LLM = "anthropic/claude-opus-4-8"
researcher = Agent(
role="Research Analyst",
goal="Find accurate, current facts on the topic",
backstory="You are meticulous and cite sources.",
llm=LLM,
)
writer = Agent(
role="Technical Writer",
goal="Turn research into a clear one-page brief",
backstory="You write plainly for busy engineers.",
llm=LLM,
)
research = Task(
description="Research the current state of {topic}.",
expected_output="5 bullet points with sources.",
agent=researcher,
)
write = Task(
description="Write a one-page brief from the research.",
expected_output="A markdown brief, < 300 words.",
agent=writer,
context=[research], # writer sees the researcher's output
)
crew = Crew(agents=[researcher, writer], tasks=[research, write],
process=Process.sequential)
print(crew.kickoff(inputs={"topic": "vector databases"}))
This is a complete, runnable CrewAI program. It builds two agents (a researcher and a writer), gives each a task, bundles them into a crew, and runs it. Read it top to bottom — that order (agents → tasks → crew → run) is the shape of almost every CrewAI script.
LLM = "anthropic/claude-opus-4-8"just picks which model every agent will think with. CrewAI talks to Claude through this model string — nothing else to configure.- Each Agent is defined by three plain-English fields:
role(its job title),goal(what it's trying to achieve), andbackstory(its personality and standards). These aren't decoration — they steer the agent's behaviour, just like a system prompt from Chapter 2. - Each Task says what work to do (
description) and what a finished result should look like (expected_output), and names whichagentowns it. The{topic}in the description is a blank filled in later when you run the crew. - The key hand-off line is
context=[research]on the writer's task: it tells CrewAI to feed the researcher's output into the writer as its starting material. This is the arrow in the diagram, written in code. Crew(...)assembles the team andprocess=Process.sequentialsays "run the tasks in the order I listed them."crew.kickoff(inputs={...})starts the run and fills{topic}with"vector databases".
What the output means: It prints the crew's final result — here the writer's one-page markdown brief on vector databases, built from the facts the researcher gathered first.
Try this: Change the topic to something you know well, run it, and check whether the brief matches your expectations. Then tighten the writer's expected_output (say, "exactly 3 bullet points") and watch the output obey it.
expected_output is structured output in disguiseEvery task declares what "done" looks like. That's the same discipline as Chapter 2's schemas and L1's plan-and-execute steps — a concrete target makes each agent's result checkable and makes the hand-off to the next agent reliable. Vague expected_output is the top cause of a crew drifting.Lab M1.2 · Hierarchical (manager) process essential
The sequential crew you just built made one big assumption: that you already know the right order. Research, then write, then edit — obvious, fixed, hard-coded by you. But plenty of real jobs aren't like that. "Handle this incoming support ticket" might need a database lookup, or a refund, or an escalation, or two of those, in an order that depends on what the ticket actually says. You can't wire that pipeline in advance because deciding the pipeline is itself the hard part. That's the moment you promote someone to boss.
Think of it as the difference between a factory line and a project manager. On a line, every widget takes the same stations in the same sequence. A project manager, by contrast, looks at each job, decides who should touch it and in what order, farms out the pieces, and stitches the results back together. CrewAI's hierarchical process gives you that manager: flip one flag and the framework inserts a coordinating agent that plans the work, delegates each subtask to the right specialist, and integrates their answers — no fixed order required.
The common mistake here is reaching for the manager too early. A manager is an extra agent, which means extra LLM calls on every run just to decide who does what — pure coordination overhead you pay whether or not the routing was actually ambiguous. For a fixed research→write→check flow that overhead buys you nothing; sequential is cheaper and equally good. And a second, subtler trap: people give the manager a weak, cheap model to save money, then wonder why the crew is confused. The manager does the hardest thinking in the whole system — decomposition, delegation, integration — so starving it produces a muddled team no matter how strong the workers are. Reach for hierarchical only when order is a genuine decision, and when you do, spend on the manager.
Switch the process to hierarchical and CrewAI adds a manager agent that decides which worker handles what, in what order — the supervisor topology from L1, built in.
This is the other way to run a crew. Instead of a fixed left-to-right line, one extra agent — the Manager — sits on top and decides who does what, and in what order.
- The top box (Manager) is a boss agent. It reads the overall goal, breaks it into pieces, and hands each piece to a worker. You don't pre-wire the order — the manager decides it at run time.
- The three arrows going down are the manager delegating to the workers (Researcher, Writer, Fact-checker). When they finish, the manager gathers and combines their results.
- This is called a hierarchical process. It's more flexible than sequential — good when the right order isn't obvious in advance — but the manager becomes the single most important (and most expensive) agent, so it needs a strong model.
In short: Sequential = a fixed assembly line you designed. Hierarchical = a boss who figures out the plan on the fly. Use the boss only when deciding the order is itself the hard part.
hierarchical.pycrew = Crew(
agents=[researcher, writer, fact_checker],
tasks=[research, write, check],
process=Process.hierarchical,
manager_llm="anthropic/claude-opus-4-8", # the manager needs its own capable model
)
result = crew.kickoff(inputs={"topic": "GraphRAG"})
Here's the surprise: turning the sequential crew above into the manager-led version from the diagram is basically one line. You reuse the same agents and tasks and just change how the crew runs them.
agents=[researcher, writer, fact_checker]andtasks=[research, write, check]are the same building blocks as before — three workers and the jobs for them.process=Process.hierarchicalis the switch. Instead of running the tasks in a fixed order, CrewAI now adds a manager agent that plans and delegates (the top box in the diagram).manager_llm="anthropic/claude-opus-4-8"gives that manager its own model. The manager does the hard thinking — splitting up the work and combining results — so it should be a capable model even if the workers are cheaper ones.crew.kickoff(inputs={"topic": "GraphRAG"})runs it exactly like before; only the coordination style changed.
What the output means: result holds the crew's combined answer on "GraphRAG" — but this time the manager decided which worker ran when, rather than you fixing the order.
Try this: Run the same task both ways — sequential vs hierarchical — and compare the token usage. The manager adds extra LLM calls, so hierarchical usually costs more; it's worth it only when the routing is a genuine decision.
Giving agents tools intermediate
An agent without tools is a very well-read colleague who never leaves the room. It can reason, summarize, and speculate from what it already knows — but it can't look anything up, can't check today's numbers, can't touch your systems. For a lot of real work that's a dealbreaker: your researcher agent is only as good as its ability to actually search your docs, not merely guess at what they might say. Tools are how you let an agent act on the world instead of only talking about it.
The mental model is refreshingly plain: a tool is just a normal Python function the agent is allowed to call. You write the function, label it with a short description of when to use it, and add it to the agent's tools list. From then on, mid-task, the agent can decide on its own to call it — search the knowledge base, hit an API, run a calculation — read the result, and keep going. The description you write is not decoration: it's the entire basis on which the agent picks the right tool, so a vague label ("does stuff") is how you get an agent that reaches for the wrong function at the wrong time.
The multi-agent-specific discipline is to scope tools tightly to each role. A tempting shortcut is to hand every agent every tool "just in case" — but that's the opposite of what a real team does. Your researcher needs search; it has no business holding a delete-database tool. Giving each specialist only the tools its job requires is least-privilege thinking (Exercise 4 makes it concrete), and it matters more in a crew than for a lone agent because there are now several actors, each an opportunity for an over-powered tool to be misused. Everything you learned about robust single-agent tooling in L3 — clear descriptions, returning errors instead of raising, gating anything destructive — applies unchanged to each agent inside the crew. A crew really is just several tool-using agents coordinating.
Agents are far more useful when they can act. CrewAI agents take a tools list — search, scraping, your own functions, or your L2/L3 retriever wrapped as a tool.
Requires: pip install crewai
Follow the blue arrow out from the agent on the left: mid-task it decides to call a tool, the tool runs, and the dashed arrow carries the result back.
- The green box on the left is the agent. Its
tools=[…]list is the exact set of functions it is permitted to call — here justsearch_docs. - The blue box is that tool: an ordinary Python function tagged with
@tool("Search internal docs"). The label and docstring are what the agent reads to decide whether this is the right tool for the moment; inside, it calls your RAGretriever(the amber box) and returns text. - The solid arrow out and the dashed arrow back are one call: the agent invokes the tool, reads the returned string, and continues reasoning.
- The red box is a tool this role is deliberately not given. Least-privilege scoping means the researcher can search but can never delete — the payoff of matching tools to roles.
In short: Tools turn a talking agent into an acting one — and giving each role only the tools it needs is the crew-scale version of least privilege.
tools.pyfrom crewai.tools import tool
@tool("Search internal docs")
def search_docs(query: str) -> str:
"""Search the internal knowledge base. Use for company-specific questions."""
return retriever.invoke(query) # your RAG retriever from L2 / Ch 3
researcher = Agent(role="Research Analyst", goal="...", backstory="...",
llm=LLM, tools=[search_docs])
Agents get much more useful when they can do things, not just talk. A tool is a normal Python function the agent is allowed to call — here, one that searches your internal docs. This snippet defines a tool and hands it to an agent.
- The
@tool("Search internal docs")line turns the function below it into a tool the agent can use. The text label and the docstring tell the agent when to reach for it — clear descriptions are what make the agent pick the right tool. - Inside, the function calls
retriever.invoke(query)— that's the RAG retriever from earlier chapters, wrapped so an agent can use it. The function takes aquerystring and returns text, which is all a tool needs to be. tools=[search_docs]on the Agent hands it that capability. Now, while working its task, the researcher can decide on its own to search the docs whenever it needs company-specific facts.
Try this: Think of a tool as a power you grant an agent. Add a second tool (say, a calculator function) to the same list and the agent can choose between them based on the descriptions you wrote — so write those descriptions carefully.
Crews vs Flows intermediate
There's a question every orchestration tool eventually forces on you: how much do you trust the agents to figure it out themselves? A crew is the trusting answer — you describe roles and a goal, and the agents work out the collaboration at run time. That autonomy is wonderful for open-ended work and terrible for anything you need to be exactly repeatable. If your boss asks "why did it approve that refund?" and the honest answer is "the agents decided to," you have a problem. Some workflows demand a paper trail and predictable branching, not improvisation.
That's the tension between a Crew and a Flow, and it's the same axis you've already met twice — agent vs workflow in L1, chain vs graph in L4. A crew optimizes for fast, readable, autonomous teamwork. A Flow (CrewAI's event-driven, code-first layer) optimizes for the opposite: you spell out the control flow explicitly — this branch, then that condition, this piece of state — so the path is deterministic, auditable, and testable. Think of a crew as handing a project to a capable team, and a Flow as writing the standard operating procedure they must follow to the letter.
The mistake is treating this as a "which is better" contest — it isn't. They sit at different points on a control dial, and reaching for full deterministic control when you don't need it just makes you write a lot of plumbing an autonomous crew would have handled for free; reaching for autonomy on a safety-critical path leaves you unable to explain or reproduce what happened. The mature move most teams land on is a blend: prototype quickly as a crew to find the shape of the problem, then rebuild the safety-critical or auditable paths as a Flow (or in LangGraph, from L4/L5). Pick by how much control the specific path actually needs — no more, no less.
A Crew is autonomous — agents figure out how to collaborate. When you need deterministic control over the sequence (branches, conditionals, precise state), CrewAI offers Flows, an event-driven, code-first orchestration layer. It's the same tension you met in L1 (agent vs workflow) and L4 (chain vs graph).
Read the axis left-to-right as one thing only: how much control you keep over the exact steps. Crew sits at the left end, Flow at the right.
- The line is a dial from less control (left) to more control (right) — the same axis as agent-vs-workflow (L1) and chain-vs-graph (L4).
- The green box (Crew) is the autonomous end: you give roles and a goal, and the agents work out the collaboration at run time. Fast and readable, but not exactly repeatable.
- The amber box (Flow / LangGraph) is the deterministic end: you write the branches, conditions, and state explicitly, so the path is auditable and testable.
- The middle note is the mature move: start as a crew to find the shape, then rebuild only the safety-critical or auditable paths as a Flow — pick the point on the dial the path actually needs.
In short: Neither end wins; you choose a spot on the dial per path — autonomy where it helps, determinism where it must be explainable.
| Use a Crew when… | Use a Flow (or LangGraph) when… |
|---|---|
| Roles collaborate on an open-ended goal | You need exact, auditable control flow |
| You want quick, autonomous teamwork | Branching/looping logic must be explicit |
| The exact steps don't need to be fixed | Determinism & testability matter most |
The honest question: do you need a crew? intermediate
This is the section that will save you the most money and grief, so slow down here. Multi-agent systems demo beautifully — three little specialists chatting and handing off work looks like the future — and that visual appeal is exactly the trap. A crew feels like progress even when it's making the system slower, pricier, and harder to debug than the one-agent version it replaced. The single most common architecture mistake in this whole area is splitting a task across agents that a single well-equipped agent would have handled fine.
Feel the actual cost before you commit to it. Every agent you add is another round of LLM calls, more tokens on the bill, more latency stacked end to end, and — the sneaky one — more context that has to survive each hand-off intact. A three-agent crew doesn't just triple the calls; it triples the places where a fact can get dropped between the researcher and the writer, or where the manager mis-routes a subtask. Coordination is not free overhead you can wave away — it's the dominant new cost multi-agent introduces, and it grows with every role you bolt on. A lone agent has none of it.
So the right question is never "would a crew be cool?" but "does this task genuinely decompose into distinct specialties, or is it one job I'm splitting out of enthusiasm?" If one agent with the right tools can do it, use one agent — it's cheaper, simpler, and easier to reason about. Save the crew for work that truly breaks into different kinds of expertise (or into parallelizable chunks). The decision gate below walks that reasoning top to bottom; when in doubt, start with a single agent and split only when it demonstrably fails, not preemptively. Adding agents later is easy; untangling an over-engineered crew is not.
Multi-agent looks impressive and is often the wrong tool. Before splitting work across agents, apply this gate:
Read it top-to-bottom as a single yes/no gate you run before reaching for a crew.
- The top box is any task in front of you. The one question that matters sits just below: can a single agent, given the right tools, do the whole thing?
- The left "yes" branch lands on the green box — use one agent. It is cheaper, simpler, and has fewer failure modes, so this is the default you should want to reach.
- The right "no" branch lands on the amber box — split into a crew, but only because the work genuinely breaks into distinct specialties or parallelizable chunks.
- The note under the crew box is the price you pay for taking the right branch: every extra agent adds LLM calls, tokens, latency, and one more hand-off where a fact can get lost. That cost is why the gate defaults left.
In short: Ask "can one agent do it?" first; only a genuine no — distinct roles or parallel work — earns the extra cost of a crew.
Common pitfalls advanced
Almost every crew that misbehaves fails in one of a small handful of ways, and the good news is they're all predictable once you've seen them. The theme running through this table is the same one from the whole chapter: a crew amplifies both the good and the bad. Sloppy instructions that a single agent might muddle through become a compounding drift as one vague hand-off feeds the next; a weak model in the wrong seat (usually the manager) poisons everything downstream; and coordination you forgot to wire — a missing context=[...] — quietly leaves an agent working blind. Read the fixes below as the flip side of the design advice you've already met: state concrete targets, spend on the coordinator, wire the hand-offs explicitly, and don't reach for a crew (or expect determinism from one) when the task didn't call for it.
Read the top row left-to-right and watch the boxes get redder: that widening is the point — a small early problem grows as it is handed down the line.
- The three boxes are the same pipeline as before (researcher → writer → editor), but now each red arrow carries a flaw forward. The Researcher starts with a small drift (amber); by the Writer and Editor it has grown (red) — that's amplification.
- The growing box size and reddening color encode the same idea: an error one agent could have shrugged off compounds once it feeds the next agent's input.
- The bottom band lists the fixes from the table — a concrete
expected_output, a strong manager model, and explicitcontext=[…]wiring. Each one closes the leak at a single stage, before the drift can multiply downstream.
In short: Every pitfall in the table is the same shape — a small crack that a pipeline widens — so the fixes all aim to seal each hand-off before it compounds.
| Pitfall | Fix |
|---|---|
Vague expected_output | State a concrete, checkable target per task |
Weak manager_llm in hierarchical mode | Give the manager a frontier model |
Forgetting context=[...] between tasks | Wire each task to the prior outputs it needs |
| Using a crew where one agent would do | Apply the "do you need a crew?" gate |
| Expecting determinism from an autonomous crew | Use Flows / LangGraph when control matters |
| Unbounded delegation loops | Cap iterations; monitor the manager's decisions |
Exercises advanced
Exercise M1.1 — Research → write → check
Context: A fact-checker that verifies the writer's claims against the research is the canonical three-role crew where context wiring matters.
Your task: Build a three-agent sequential crew — researcher (with a search tool), writer, and fact-checker — wiring context so each agent sees what it needs.
Requirements:
- Three agents: researcher (with a search tool), writer, fact-checker
- The fact-checker verifies the writer's claims against the research
- Wire each Task's context so it receives the right upstream output
- Run it end to end on a topic
💡 Hint: The fact-checker's context must include the research, not just the draft, so it can check claims against sources.
Exercise M1.2 — Flip to hierarchical
Context: Switching a fixed pipeline to a manager shows exactly when hierarchical is worth its extra token cost.
Your task: Take the three-agent crew and switch it to Process.hierarchical with a manager, then compare output and token cost to the sequential version.
Requirements:
- Reuse the same agents under a hierarchical process with a manager
- Run the same task
- Compare output quality and token cost to the sequential run
- State when the manager was worth it
💡 Hint: Hierarchical shines when task ordering isn't obvious up front; for a fixed research→write→check pipeline, sequential is usually cheaper and just as good.
Show what to look for
Hierarchical shines when task ordering isn't obvious up front; for a fixed research→write→check pipeline, sequential is cheaper and just as good. The manager earns its cost only when routing is a real decision.
Exercise M1.3 — One agent instead
Context: Sometimes one well-prompted agent beats a crew — and comparing them on quality, cost, and latency is the judgment the chapter is really after.
Your task: Rebuild the three-agent crew as a single agent (L3 create_react_agent) with the search tool and a "research, then write, then self-check" prompt, then compare to the crew.
Requirements:
- One agent with the search tool
- A prompt that folds research, writing, and self-checking into one flow
- Compare quality, cost, and latency to the crew
- Write two sentences on which you'd ship
💡 Hint: The single agent trades the crew's role separation for fewer calls and lower latency — whether that's worth it depends on how distinct the roles really are.
🪜 Practice ladder beginner → industry
Six graded exercises, easy to real-world. Try each before opening its solution.
Context: CrewAI's Agent is a role-playing actor defined by a role, a goal, and a backstory — the same idea as a system prompt, scoped per team member.
Your task: Model an Agent as a dataclass and instantiate a researcher and a writer.
Requirements:
- An Agent holds role, goal, and backstory fields
- Instantiate at least two distinct agents (e.g. researcher, writer)
- The three fields steer that agent's behavior
- Print each agent's role and goal
💡 Hint: A plain dataclass captures it — the point is that an agent is configuration, not magic.
Show solution
An agent is configuration, not magic. Runnable stdlib:
from dataclasses import dataclass
@dataclass
class Agent:
role: str
goal: str
backstory: str
researcher = Agent("Senior Researcher",
"find and summarize the key facts",
"You dig up primary sources and cite them.")
writer = Agent("Tech Writer",
"turn findings into a clear paragraph",
"You write plainly for a general audience.")
for a in (researcher, writer):
print(f"{a.role}: {a.goal}")
The role/goal/backstory steer the LLM's behavior for that agent — the same idea as a system prompt, scoped per team member.
Context: In a sequential process each Task's output becomes the next Task's context — the assembly-line metaphor CrewAI is built around.
Your task: Model a sequential crew that runs tasks in order, threading each result forward.
Requirements:
- Run a list of tasks in order over a shared context
- Each task receives the prior task's output as input
- The final context is the last task's output
- Show a research → analyze → write pipeline
- Print the intermediate output of each stage
💡 Hint: Fold the tasks over a running context variable; each agent's output is the next agent's input.
Show solution
Sequential = a pipeline of tasks over a shared context. Runnable:
def run_sequential(tasks, context=""):
for task in tasks:
context = task(context) # each task sees the prior output
print(f"[{task.__name__}] -> {context}")
return context
def research(ctx): return "facts: X grew 30% in 2025"
def analyze(ctx): return ctx + " | trend: strong growth"
def write(ctx): return "Report: " + ctx
final = run_sequential([research, analyze, write])
print("FINAL:", final)
Each agent's task output is the next agent's input — the assembly-line metaphor CrewAI is built around.
Context: The hierarchical process adds a manager that delegates subtasks to workers — L1's supervisor topology in code, rather than a fixed pipeline.
Your task: Model a manager that routes each subtask to the right specialist and collects the results.
Requirements:
- A registry maps specialist names to worker functions
- The manager routes each subtask to the matching worker
- Collect the results keyed by subtask
- Handle a subtask with no matching worker gracefully
- Show a plan of subtasks being delegated and collected
💡 Hint: The manager is a router over specialists; it decides who handles each subtask instead of running a fixed sequence.
Show solution
The manager is a router over specialists. Runnable:
WORKERS = {
"research": lambda t: f"researched: {t}",
"code": lambda t: f"coded: {t}",
"write": lambda t: f"wrote: {t}",
}
def manager(subtasks):
# decide which worker handles each subtask, then collect
results = {}
for name, payload in subtasks:
if name not in WORKERS:
results[name] = "no worker for this skill"
else:
results[name] = WORKERS[name](payload)
return results
plan = [("research", "market size"), ("write", "exec summary")]
for k, v in manager(plan).items():
print(k, "->", v)
The hierarchical process is L1's supervisor topology in code: one coordinator delegates to specialists rather than a fixed pipeline.
Context: Agents can call tools — but a specialist should stay in its lane, so scoping tools to roles is the least-privilege version of L3's tool binding.
Your task: Model a worker agent with a tool registry and a per-role guard that only lets it use tools its role is allowed, then run a task needing one.
Requirements:
- A tool registry maps tool names to callables
- A per-role allow-list defines which tools each role may use
- A tool call by a disallowed role is denied
- An allowed call runs the tool
- Show an allowed call, a denied call, and a privileged role's allowed call
💡 Hint: Check the role's allow-list before dispatching to the tool; a denied call returns a refusal, not a result.
Show solution
Scope tools to roles so an agent can't overreach. Runnable:
TOOLS = {"search": lambda q: f"results for {q}",
"delete": lambda x: f"deleted {x}"}
ROLE_ALLOWED = {"Researcher": {"search"}, "Admin": {"search", "delete"}}
def use_tool(role, tool, arg):
if tool not in ROLE_ALLOWED.get(role, set()):
return f"DENIED: {role} may not use '{tool}'"
return TOOLS[tool](arg)
print(use_tool("Researcher", "search", "vLLM")) # allowed
print(use_tool("Researcher", "delete", "prod-db")) # denied
print(use_tool("Admin", "delete", "temp")) # allowed
Tools plus a per-role allow-list keep a specialist agent inside its lane — the least-privilege version of L3's tool binding.
Context: The honest question CrewAI's chapter forces: many tasks don't need a crew, because multi-agent adds coordination cost that must be justified.
Your task: Write a decision helper that recommends single-agent vs multi-agent from the task's shape.
Requirements:
- If one prompt would do, a crew is overkill
- Distinct specialties or parallelizable work justify a crew
- Otherwise a single agent suffices
- Take the deciding signals as parameters
- Show a case for each of the three verdicts
💡 Hint: A crew earns its complexity only for genuinely distinct roles or parallel work — otherwise one agent (or one prompt) is cheaper and more reliable.
Show solution
Multi-agent adds coordination cost — justify it. Runnable:
def need_crew(distinct_specialties, steps_parallelizable, one_prompt_would_do):
if one_prompt_would_do:
return "single agent (or just a prompt) -- a crew is overkill"
if distinct_specialties >= 2 or steps_parallelizable:
return "crew -- distinct roles or parallel work justify the coordination"
return "single agent -- one role, sequential steps"
print(need_crew(1, False, True)) # overkill
print(need_crew(3, True, False)) # crew justified
print(need_crew(1, False, False)) # single agent
A crew earns its complexity only when there are genuinely distinct roles or parallelizable work — otherwise one agent (or one prompt) is cheaper and more reliable.
Context: The production version is real CrewAI: two Agents, two Tasks, and a sequential Crew — the same pipeline you modeled offline, now with the framework.
Your task: Write the real CrewAI code using documented crewai APIs. (Needs the framework installed.)
Requirements:
- Define two
Agents with role/goal/backstory - Define two
Tasks with descriptions and expected outputs, each assigned to an agent - Thread output forward with
context=[...]on the second task - Assemble a
CrewwithProcess.sequentialand runkickoff - Note that
Process.hierarchicalwould add a manager; label it as needingcrewai+ a model key
💡 Hint: context=[research_task] threads output forward, exactly like the offline sequential crew — class names shift across versions, so verify against what you install.
Show solution
Correct CrewAI. Needs pip install crewai + a model API key:
from crewai import Agent, Task, Crew, Process
researcher = Agent(
role="Senior Researcher",
goal="Find the key facts about {topic}",
backstory="You dig up primary sources and cite them.",
)
writer = Agent(
role="Tech Writer",
goal="Write a clear one-paragraph summary",
backstory="You write plainly for a general audience.",
)
research_task = Task(
description="Research {topic} and list the top 3 facts.",
expected_output="A bulleted list of 3 cited facts.",
agent=researcher,
)
write_task = Task(
description="Turn the research into one clear paragraph.",
expected_output="A single paragraph.",
agent=writer,
context=[research_task], # write_task consumes research_task's output
)
crew = Crew(
agents=[researcher, writer],
tasks=[research_task, write_task],
process=Process.sequential, # each task feeds the next
)
result = crew.kickoff(inputs={"topic": "local LLM serving"})
print(result)
Same pipeline you modeled offline: context=[research_task] threads output forward, and Process.hierarchical would swap in a manager. Class names shift across versions — verify against what you install.
✓ Checkpoint — you can move on when you can…
- Define Agent, Task, Crew, and Process and how they fit together.
- Build a sequential crew and wire task context.
- Run a hierarchical crew and explain the manager's role.
- Give a crew agent a tool and describe it well.
- Decide between a crew, a Flow/LangGraph, and a single agent.
Knowledge check check yourself
In CrewAI, what is the difference between a sequential and a hierarchical process, and what extra cost does hierarchical add?
Show answer
Why is a task's expected_output called "structured output in disguise," and what does a vague one cause?
Show answer
expected_output is the top cause of a crew drifting off course.