CrewAI Flows – Orchestrating End‑to‑End Workflows
CrewAI Flows are the orchestration layer that controls when and how your code, LLM calls, agents, and crews run together in a structured, event‑driven workflow. Where Crews are “teams of agents,” Flows are the workflow engine that chains steps, manages state, and routes execution.
1. What Flows Are (and Why They Exist)
- A Flow is a Python class that defines an AI workflow as connected methods, decorated with
@start,@listen, and optional routing / control decorators. - Flows let you mix: raw code, direct LLM calls, Agents, and full Crews, all under one event‑driven control structure.
- They add “software‑engineering discipline” on top of multi‑agent systems: clear steps, explicit data passing, and testable logic.
Key Flow capabilities:
- Simplified workflow creation (chain many steps and crews)
- State management across steps
- Event‑driven architecture (steps react to prior outputs)
- Flexible control flow (branching, fan‑out, fan‑in, human gates)
2. Core Building Blocks: Flow, @start, @listen
2.1 Flow class
You define a Flow by subclassing Flow:
What’s happening:
@start()marks entry methods that fire when the Flow begins.@listen(generate_city)runs whengenerate_cityfinishes and receives its output as an argument.self.statecarries shared data (city , fun_fact) across steps.
2.2 @start()
-
Defines entry points into your Flow.
-
You can have:
- Simple start:
@start() - Start gated on an event/label:
@start("method_or_label") - Start with a callable condition to control when it fires.
- Simple start:
Multiple @start() methods can run in parallel when the Flow begins or resumes.
2.3 @listen()
-
@listen()makes a method react to the output of another step. -
You can reference the source step by:
- Name:
@listen("generate_city") - Function:
@listen(generate_city)
- Name:
The listener’s parameters correspond to the upstream output, so the data flow is explicit and typed.
3. Flow Output and Running Flows
3.1 Final output
- The final output of a Flow is the return value of the last method that completes.
flow.kickoff()returns that final value directly.
Example:
3.2 Running and streaming
You can run flows in several ways:
- Programmatically:
- With streaming for real‑time updates:
- From the CLI (recommended):
4. State Management in Flows
State is the memory of your Flow: it keeps context between steps and across restarts.
4.1 State lifecycle
The state follows a predictable lifecycle:
- Initialization – created as an empty dict or Pydantic model.
- Modification – methods read and update it.
- Transmission – passed automatically across methods.
- (Optional) Persistence – can be saved to storage and reloaded.
- Completion – final state reflects all updates.
4.2 Unstructured state (dict)
- self.state is a dictionary; you add keys dynamically.
- Flow automatically adds a unique id (UUID) to track executions.
Example:
Use when you want maximum flexibility and are still exploring your schema.
4.3 Structured state (Pydantic model)
- You define a
BaseModelthat represents state. Flow[YourStateModel]enforces types and gives IDE auto‑completion.- An
idfield is automatically added and managed.
Example:
Use structured state when you want reliability, validation, and clear contracts.
5. Persisting Flow State (@persist)
Persistence lets flows survive restarts and long‑running processes.
@persistat class level: persist all flow method states (default backend: SQLite).@persistat method level: persist only that method’s state (fine‑grained control).
Example – class‑level:
Key points:
- Each state has a unique UUID that is preserved across saves/loads.
- SQLite is default; you can plug in custom persistence backends.
- Strong error handling and validation around save/load.
Use persistence for long‑running, resumable, or production workflows.
6. Flow Control: or_, and_, router, and Human Feedback
Flows support rich control‑flow primitives beyond simple chains.
6.1 or_ – listen to any of multiple sources
Triggers when any of the specified methods emit output.
6.2 and_ – wait for all sources
Triggers only when all specified methods have emitted outputs.
6.3 @router – conditional routing
Routes based on method output (like a switch/case).
6.4 @human_feedback – human‑in‑the‑loop gates
Requires CrewAI ≥ 1.8.0.
- Pauses flow execution to collect human feedback.
- Optionally maps free‑form feedback into discrete outcomes like
"approved" / "rejected"using an LLM, then routes to matching listeners.
You can also use it without routing just to capture comments and access them via self.last_human_feedback or self.human_feedback_history.
7. Integrating Agents and Crews into Flows
Flows can orchestrate Agents directly or full Crews, turning them into steps in a larger application.
7.1 Using Agents inside a Flow
Example: market research with structured output.
Flows are the macro‑orchestrator,
Agents are micro‑problem solvers.
7.2 Using Crews inside a Flow
Flows can also call entire crews, using Flow state as inputs and capturing outputs.
Folder scaffold (from crewai create flow name_of_flow):
Path
name_of_flow/ Root flow project.
crews/poem_crew/ A sample crew with its own config.
crews/poem_crew/config/ agents.yaml, tasks.yaml.
crews/poem_crew/poem_crew.py Crew definition (agents, tasks, crew).
tools/ Custom tools.
main.py Main flow definition and entry point.
Example: connecting a PoemCrew:
This is the signature pattern: Flows orchestrate multiple domain crews into one cohesive business process.
8. Building Your First Flow Project (CLI)
The First Flow guide shows a typical project setup.
- Create a Flow project:
- Add a specialized crew (e.g., content writer):
This generates a new crew folder with agents.yaml, tasks.yaml, and a crew Python file wired for use inside your flow.
- Edit main.py to define your Flow and connect it to that crew (similar to the Poem example).
This scaffold naturally separates concerns:
- Flow logic in main.py
- Specialized crews in crews/
- Tools in tools/
Here is a mental model: Flows = “orchestrator app”; Crews = “specialist teams”.
9. Visualizing Flows with plot()
Plots help you and your students see the workflow.
-
Call flow.plot("my_flow_plot") on a Flow instance to generate my_flow_plot.html with an interactive graph.
-
Or use:
in a structured project.
Plots show:
- Nodes = methods (@start, @listen, routers, human_feedback stops)
- Edges = data / control‑flow connections
10. When to Reach for Flows
Use Flows when:
- They need multi‑step orchestration (not just a single crew run).
- They want explicit state management across steps (and maybe persistence).
- They must integrate multiple crews, agents, tools, and raw code into one system.
- They need branching, fan‑out, human approval, or complex routing.
In Summary:
-> Crews turn LLMs into specialist teams. -> Flows turn those teams into a product‑grade application with memory, control‑flow, and observability.