Lab 01 — CrewAI Sequential Crew Engine
Phase 19 · Lab 01 · Phase README · Warmup
The problem
CrewAI's first-and-most-used process is sequential: you list Agents and Tasks, group
them into a Crew, and call crew.kickoff(inputs). The tasks run in the order you listed
them, each task's output feeds forward to the next, and runtime inputs get interpolated into
the prompts. It looks trivial — which is exactly why interviewers use it to find out whether
you actually know the mechanism or have only ever driven the framework from the outside. Three
questions separate the two:
- In what order do tasks run, and who decides? Listed order. No LLM, no planner. That
determinism is the point of
sequential(contrast the manager LLM in lab-02). - How does task 2 see task 1's work? Through context — and the rule is a three-way
switch:
context=Nonefeeds the previous task forward;context=[t1, t3]reads exactly those tasks;context=[]isolates the task. - How do runtime inputs reach the prompt? Through
{placeholder}interpolation at kickoff, so one crew definition serves a thousand inputs.
You build the whole engine, with the LLM injected as a pure policy(prompt, context) so the
crew is deterministic, offline, and fully testable.
What you build
| Piece | What it does | The lesson |
|---|---|---|
interpolate(template, inputs) | safe {key} substitution | one crew definition, many runs; why not str.format |
Agent (role/goal/backstory + policy) | renders persona+task into a prompt, calls the injected LLM | the persona is the system prompt |
Task (description, expected_output, agent, context) | a unit of work + its context rule | feed-forward vs fan-in vs isolated |
Crew.kickoff(inputs) | the sequential loop; returns a CrewOutput | order, context passing, per-task outputs |
CrewOutput / TaskOutput | final .raw + .tasks_output per task | how you read a crew's result |
File map
| File | Role |
|---|---|
lab.py | your implementation (fill the # TODOs) |
solution.py | reference + worked example (python solution.py) |
test_lab.py | 22 tests: order, context (3 modes), interpolation, validation, determinism |
requirements.txt | pytest only |
Run it
pytest test_lab.py -v # your lab.py (red until you implement)
LAB_MODULE=solution pytest test_lab.py -v # the reference (green)
python solution.py # worked example
Success criteria
-
You can state, without looking, the three
contextmodes and what each produces. -
You can explain why
sequentialneeds no LLM to decide control flow — and why that makes it cheaper and more debuggable thanhierarchical. -
Your
interpolateleaves a stray{brace}untouched (and you can say whystr.formatwould be wrong here). -
CrewOutput.rawis the last task's output and.tasks_outputholds every task in order. -
All 22 tests pass under both
labandsolution.
How this maps to the real stack
- This is CrewAI's
Process.sequential. RealAgent(role=, goal=, backstory=, llm=, tools=),Task(description=, expected_output=, agent=, context=[...]), andCrew(agents=, tasks=, process=Process.sequential).kickoff(inputs=)behave exactly as modeled: tasks run in listed order, outputs feed forward,contextoverrides the default feed-forward, and{placeholders}are interpolated frominputs. - The injected
policyis where the LLM lives in real CrewAI (llm=on the agent). Stubbing it is precisely how you unit-test a real crew — you assert control flow and context without paying for tokens or fighting non-determinism. CrewOutputmirrors the real return type:.raw,.tasks_output, and (in real CrewAI).pydantic/.json_dictfor structured outputs and.token_usagefor cost.
Limits of the miniature. Real agents also run a tool-use loop inside each task (the agent
may call tools several times before producing the task output — that is Phase 02/03 territory),
support output_pydantic / output_json structured results, guardrails, and async_execution.
We model the orchestration — order, context, interpolation, collection — which is the part the
process actually owns.
Extensions (your own machine)
- Add a tool-use loop inside
Agent.execute: let the policy return atool_call, run a looked-up tool, append the observation, and re-invoke — the ReAct loop from Phase 01, now inside a CrewAI task. - Add
output_pydantic-style structured output: a task declares a schema, andkickoffvalidates the raw output against it (reuse the validator from Phase 02). - Add
async_execution=Truefor tasks with no context dependency and run them concurrently, then fan them into a downstream task — the first step toward the parallelism Flows give you (lab-03).
Interview / resume signal
"Built a faithful miniature of CrewAI's sequential crew engine — agent/task/crew primitives, three-mode context passing (feed-forward / explicit fan-in / isolated), input interpolation, and per-task output collection — with the LLM injected as a pure policy so the whole crew is deterministic and unit-testable offline."