Module 1: Architecture Choices
CrewAI Crews vs Flows
Learning objectives
- Explain the core mental model behind CrewAI Crews vs Flows
- Apply CrewAI Crews vs Flows within Architecture Choices
- Identify important boundaries, trade-offs, and failure modes
- Produce concrete evidence from the practice exercise
Related: CrewAI Crew Architecture | CrewAI Orchestration Patterns | CrewAI Planning and Reasoning | CrewAI Deployment and Production
CrewAI has two top-level constructs: Crews for collaborative agent teams, and Flows for structured event-driven workflows. Picking the right one is the most consequential architecture decision in a CrewAI project. This note covers when to use each, when to combine them, and what fails when you pick wrong.
The Difference in One Picture
CREW (collaborative) FLOW (controlled)
────────────────────── ──────────────────────────
┌──────────┐ event → state machine
│ agents │ │
└────┬─────┘ ▼
│ state A
process (seq/hier) │ on(condition)
│ ▼
┌────┴─────┐ state B ─── invoke crew
│ tasks │ │
└──────────┘ ▼
autonomous state C
execution human approvalCrew is a team of specialists doing language-heavy, judgment-laden work. Flow is a workflow / state machine with explicit control.
Engineering analogy: crew is a project team; flow is the project's process / FSM. Same project may need both — the team does the work, the process governs when and whether work happens.
CrewAI Documentation Distinction
CrewAI docs describe:
- Crews as teams of autonomous agents
- Flows as structured, event-driven workflows that manage state and control execution
| Feature | Crew | Flow |
|---|---|---|
| Primary idea | Collaborative agent team | Explicit workflow control |
| Best for | Open-ended, language-heavy work | State machines, branching, event-driven apps |
| Control style | Agents and process decide execution | Developer defines steps and transitions |
| State | Implicit via memory / context | Explicit state class |
| Branching | Limited (hierarchical manager) | First-class |
| Human approval | Add via guardrails | Native support |
| Resume / checkpoint | Via memory | Native support |
| Analogy | Team of engineers | Control FSM or workflow engine |
When to Use a Crew
Use a crew when:
- Different roles should collaborate
- Outputs need synthesis and judgement
- Delegation is useful (hierarchical)
- Tasks are language-heavy
- Review / refinement matters
Examples:
- Research → write → review a technical article
- Triage a regression run (collect logs → classify → root-cause → review)
- Generate a marketing brief (research market → outline → draft → critique)
- Summarize a long meeting (transcribe → cluster topics → action items)
crew = Crew(
agents=[researcher, writer, reviewer],
tasks=[research_task, draft_task, review_task],
process=Process.sequential,
)The crew itself doesn't know about external triggers, retries, or branching. It just does the team's work when called.
When to Use a Flow
Use a flow when:
- State transitions must be explicit
- Execution needs branching based on data
- Human feedback gates exist
- Retries and resume matter
- External triggers start work
- Output must be machine-routable to different paths
Examples:
- Email arrives → classify intent → if urgent, create ticket → ask human approval → send response
- Scheduled cron → fetch metrics → if anomaly, run analysis crew → post summary
- Webhook from CI → analyze build → file ticket OR notify on-call
from crewai.flow.flow import Flow, listen, start
class SupportFlow(Flow):
@start()
def receive_email(self):
return classify(self.state["email"])
@listen(receive_email)
def route(self, intent):
if intent == "refund":
return self.run_refund_crew()
if intent == "technical":
return self.run_triage_crew()
return self.escalate_to_human()This shape is impossible to build naturally in just a crew. The state transitions are the work.
Hybrid Pattern (the Common Case)
Many production systems use both:
Flow controls the workflow
├── Step A: deterministic (just code)
├── Step B: invoke a Crew for research
├── Step C: deterministic check / branching
├── Step D: invoke a Crew for writing
├── Step E: human approval gate
└── Step F: deterministic publishThe flow is the control plane. Crews are reasoning units inside that control plane.
class ContentFlow(Flow):
@start()
def gather_topic(self):
return self.state["topic"]
@listen(gather_topic)
def research(self, topic):
return ResearchCrew().crew().kickoff(inputs={"topic": topic})
@listen(research)
def gate_for_approval(self, research_output):
if not self.state["auto_approve"]:
return wait_for_human(research_output)
return research_output
@listen(gate_for_approval)
def write(self, research_output):
return WriterCrew().crew().kickoff(inputs={"research": research_output})The flow guarantees the human gate fires; the crew handles the fuzzy work between gates.
Engineering Analogy: Crew vs FSM
A crew is like a project team. It has specialists, goals, and judgement. The exact internal path may vary because agents decide how to solve tasks.
A flow is like an FSM or workflow controller. The state transitions are explicit. You know what happens after each event because the developer encoded the path.
Use that analogy when deciding:
- If you need judgement, use a crew
- If you need control, use a flow
- If you need both, wrap crews inside a flow
Example: Customer Support Automation
Pure crew (naive)
Support crew receives complaint → agents research → write replyRisk: the crew may decide a reply is ready even when the customer asked for cancellation, refund, or legal escalation. Crew doesn't know about your business policies.
Flow + crews (production)
Email received
├─▶ classify intent
├─▶ if refund request: route to refund policy flow
├─▶ if technical issue: run troubleshooting crew
├─▶ if legal issue: escalate to human (no agent)
├─▶ draft response (writer crew)
├─▶ require approval before send (gate)
└─▶ send + logHere, the flow protects the business process. The crew handles the fuzzy reasoning inside a controlled box.
Decision Table
| Question | If yes, lean toward |
|---|---|
| Does the workflow need strict branching? | Flow |
| Do several specialists need to synthesize language-heavy work? | Crew |
| Does a human approval gate exist? | Flow (possibly calling a crew) |
| Is the task mostly exploratory research? | Crew |
| Does execution need resume / checkpoint state? | Flow |
| Is the main challenge prompt / tool collaboration? | Crew |
| Is the trigger external (webhook, scheduler)? | Flow |
| Does output need machine-routable structure? | Flow |
If most answers say "Flow" but you have one fuzzy step, use a flow that calls a small crew for that step. Don't force everything into one shape.
Failure Modes
| Bad choice | Symptom | Fix |
|---|---|---|
| Crew used where flow is needed | Agent takes unsafe branch or skips required approval | Wrap in a flow with explicit gates |
| Flow used where crew is needed | Workflow becomes rigid and brittle | Replace fuzzy steps with crew calls |
| Hybrid without contracts | Flow can't parse crew output reliably | Make crew outputs structured (Pydantic / JSON) |
| Flow with no state | Can't recover from partial failure | Add explicit state |
| Crew called from many flow steps | Each call re-loads agents; slow | Cache crew instance |
For hybrids, make crew outputs structured. A flow should receive a machine-checkable result, not a paragraph it must interpret loosely.
class ResearchOutput(BaseModel):
findings: list[Finding]
gaps: list[str]
# Crew returns ResearchOutput parseable JSON
# Flow can then route on output.gaps reliablyAnti-Patterns
- Forcing branching into a hierarchical crew — the manager isn't a state machine
- Stuffing business policy into agent backstory — belongs in flow gates
- Flow with no crew at all — you're just writing Python; flow framework adds overhead
- Crew with 8 conditional tasks — should be a flow
- Flow that re-invents memory — let the crew handle it inside its step
- Mixing flow state with crew memory — different lifetimes, different stores
When You Don't Need Either
Don't reach for crew or flow for:
- Single LLM call (just call the API)
- Strict deterministic workflow with no LLM (use a workflow engine like Airflow / Temporal)
- One-off scripts
CrewAI shines for multi-step, language-heavy work. If your problem isn't that, simpler tools win.
Choosing in Practice
A pragmatic decision sequence:
- Can a single LLM call do this? → Use the API directly.
- Do I need 2-5 collaborating specialists? → Crew.
- Do I need branching / human gates / external triggers? → Flow.
- Do I need both? → Flow wrapping crews.
- Is the workflow strictly deterministic? → Workflow engine.
Most production CrewAI systems land at #4 within a few iterations.
Related
- CrewAI Crew Architecture — what's inside a crew
- CrewAI Orchestration Patterns — patterns within and across crews
- CrewAI Sequential Process / CrewAI Hierarchical Process — crew process choices
- CrewAI Deployment and Production — production workflow shapes
- CrewAI Safety and Guardrails — human review gates
- CrewAI Index — full topic map
Practice lab
Implement the smallest runnable agent workflow that demonstrates CrewAI Crews vs Flows. Trace inputs, state, model and tool calls, outputs, and cost; inject one failure and add a regression test that prevents it from returning. Add an operational constraint such as concurrency, recovery, security, latency, or cost, and defend the resulting design trade-off.
Review questions
- What problem does CrewAI Crews vs Flows solve, and what assumptions does it rely on?
- Which boundary or failure case is easiest to miss, and how would you expose it?
- What alternative design would you consider, and what trade-off would change the decision?
- What artifact, trace, test, or metric proves that your implementation is correct?
Completion evidence
- A working artifact, annotated trace, or reproducible experiment
- At least one normal case and one deliberately failing or boundary case
- A concise explanation of the design choice and its trade-offs
- Saved output showing how correctness was evaluated