Sequential Process
Put three agents in a line: a researcher picks facts from a real source, a writer turns them into a paragraph, an editor checks it. See exactly what each agent receives (all earlier outputs, not just the last), limit it with context=, and catch a CrewAI token count that was three times too high.
What you will be able to do
- Build a crew of three agents that run one after another
- Give the first agent real source material instead of relying on memory
- Explain what context each task receives, and change it with context=
- Use guardrails inside a crew to keep the final output clean
- Read result.tasks_output and result.token_usage - and check the numbers
The idea, in plain English
A sequential process is the simplest way to run a team: the tasks run in the order of the list, and each one builds on what came before - like an assembly line.
In Lesson 6.1 our researcher invented facts. This time it works from a real source text (the facts from Lesson 3.6). The writer turns them into a paragraph, and the editor checks the paragraph against the facts.
We recorded every request to see what each agent receives, and compared CrewAI’s token counts with Ollama’s own. CrewAI 1.15.27, llama3 on Ollama.
Worked example: Researcher -> writer -> editor about LangGraph checkpoints, working from the real facts of Lesson 3.6 - with every prompt recorded and Ollama’s own token counts checked.
1 - The researcher works from a source
The source text comes in through kickoff(inputs=...). The researcher picked 3 of the 4 facts - all correct this time.
What each agent received. Recorded with a spy between CrewAI and Ollama.
Words you will see in this lesson
A few words about running tasks in order.
Process.sequentialTasks run one after another, in list order. The default.ContextEarlier outputs that CrewAI pastes into a task’s prompt.context=[...]A task setting that chooses exactly which earlier tasks it sees.Source textReal material the agent must work from, given through inputs.tasks_outputA list with the output of every task, in order.token_usageCrewAI’s count of requests and tokens for the whole crew.An everyday example: a newspaper desk
- A reporter reads the official press release and picks the key facts. (Researcher, from a source.)
- A writer turns the facts into a short story. (Writer.)
- An editor reads the story with the facts next to it and fixes anything wrong. (Editor, with all earlier work as context.)
- Each person waits for the one before. Nobody works at the same time. (Sequential.)
Example 1 - researcher, writer, editor
Read the program in four parts:
- SOURCE: the real facts from Lesson 3.6. They reach the researcher through kickoff(inputs={"source": SOURCE}) and the {source} placeholder.
- Three agents, each with ITS OWN LLM object from new_llm() - the reason is in Question 3 below.
- clean_and_short: the guardrail from Lesson 6.4, plus one new rule - drop "(Note ...)" lines (see below).
- Three tasks in a list, process=Process.sequential: they run in this order.
import time
from crewai import LLM, Agent, Crew, Process, Task, TaskOutput
base = "http://localhost:11434"
def new_llm():
return LLM(model="ollama/llama3", base_url=base, temperature=0)
# Real facts from Lesson 3.6 - the researcher works FROM these, not from memory.
SOURCE = """A checkpointer saves the graph state after every step.
Each conversation or run has a thread_id; the saved state is stored under it.
With the same thread_id, a run can continue later, even after a restart.
InMemorySaver loses checkpoints when the process stops; SqliteSaver keeps them in a file."""
researcher = Agent(role="Researcher", goal="Pick the facts a beginner needs",
backstory="You only use the source text you are given. You write short bullet points.", llm=new_llm())
writer = Agent(role="Writer", goal="Turn facts into one clear paragraph for beginners",
backstory="You write short, simple paragraphs for new developers. You never add facts.", llm=new_llm())
editor = Agent(role="Editor", goal="Make a paragraph correct, short and friendly",
backstory="You check every sentence against the facts and remove anything not supported.", llm=new_llm())
def clean_and_short(output: TaskOutput):
lines = [line for line in output.raw.strip().splitlines()
if not line.strip().lower().startswith("(note")] # drop notes to us
if lines and lines[0].lower().startswith(("here is", "here's")): # drop the introduction line
lines = lines[1:]
text = "\n".join(lines).strip()
if len(text.split()) > 80:
return False, f"Your answer has {len(text.split())} words. Rewrite it in at most 60 words."
return True, text
research = Task(description="From this source, list the 3 facts a beginner needs about {topic}:\n{source}",
expected_output="Exactly 3 bullet points, each one taken from the source.", agent=researcher)
write = Task(description="Write one paragraph about {topic} for beginners, using only the research.",
expected_output="One paragraph, at most 60 words.", agent=writer, guardrail=clean_and_short)
edit = Task(description="Check the paragraph against the research facts. Fix anything wrong and return the final paragraph.",
expected_output="The final paragraph only, at most 60 words.", agent=editor, guardrail=clean_and_short)
crew = Crew(agents=[researcher, writer, editor], tasks=[research, write, edit], process=Process.sequential)
start = time.perf_counter()
result = crew.kickoff(inputs={"topic": "LangGraph checkpoints", "source": SOURCE})
for t in result.tasks_output:
print(f"--- {t.agent}:\n{t.raw}\n")
print(f"--- {len(result.raw.split())} words, {time.perf_counter() - start:.1f} s")
print("token_usage:", result.token_usage)--- Researcher:
Here are the 3 facts a beginner needs about LangGraph checkpoints:
• A checkpointer saves the graph state after every step.
• Each conversation or run has a thread_id; the saved state is stored under it.
• With the same thread_id, a run can continue later, even after a restart.
--- Writer:
A LangGraph checkpointer saves the graph state after every step, storing it under a unique thread_id for each conversation or run. This allows a run to continue later, even after a restart, as long as the same thread_id is used.
--- Editor:
A LangGraph checkpointer saves the graph state after every step, storing it under a unique thread_id for each conversation or run. This allows a run to continue later, even after a restart, as long as the same thread_id is used.
--- 40 words, 11.7 s
token_usage: total_tokens=777 prompt_tokens=581 cached_prompt_tokens=578 completion_tokens=196 reasoning_tokens=0 cache_creation_tokens=0 successful_requests=3Reading Example 1
- The facts are correct this time - compare Lesson 6.1, where the researcher invented a "neural network". A source beats a job title.
- The researcher left out the 4th fact (InMemorySaver vs SqliteSaver) - it chose what a beginner needs, as asked.
- The researcher has no guardrail, so its "Here are the 3 facts..." line stayed. It is only context for the next task, so that is fine.
- The editor changed nothing. The draft was already correct - so that was one model call for no change. An editor is worth it only if drafts are often wrong.
Question 1 - what does each agent receive?
We recorded the three requests CrewAI sent to llama3:
- Researcher: no context - it is the first task. The source came through {source}.
- Writer: the researcher’s output, under "This is the context you’re working with".
- Editor: the researcher’s output AND the writer’s draft, separated by a line of dashes. In a sequential crew, each task gets the outputs of ALL earlier tasks, not only the last one.
- That is why the editor could check the draft against the facts.
1. You are Researcher | context: (no context)
2. You are Writer | context: / Here are the 3 facts a beginner needs about LangGraph checkpoints: / / • A checkpointer saves the graph state after every step. / ...
3. You are Editor | context: / Here are the 3 facts a beginner needs about LangGraph checkpoints: / ... / • With the same thread_id, a run can continue later, even after a restart. / / ---------- / / A Lang...Question 2 - can I choose what a task sees?
Yes, with context=[...] on the task. We gave the editor only the draft:
- edit = Task(..., context=[write]) - the editor now sees only the writer’s output.
- Its prompt went from 228 to 165 tokens - cheaper and faster.
- But now the editor has no facts to check against! Its whole job became impossible.
- Use context= to cut what a task does not need - but think about what it DOES need.
edit = Task(description="Check the paragraph against the research facts. Fix anything wrong and return the final paragraph.",
expected_output="The final paragraph only, at most 60 words.", agent=editor, guardrail=clean_and_short,
context=[write]) # only the draft
# recorded: editor context = only the writer's paragraph; editor prompt tokens: 165 (was 228)Question 3 - are the numbers right?
Our first version shared ONE llm object between all three agents (llm=llm). We compared CrewAI’s token_usage with the numbers Ollama returned for each request:
- Ollama: 3 requests, 581 prompt tokens, 196 output tokens.
- CrewAI with a shared LLM: successful_requests=9, prompt_tokens=1743, completion_tokens=588 - exactly 3 times too much.
- With one LLM object per agent (new_llm()), CrewAI reported 3, 581 and 196 - exactly right.
- It looks like the shared object’s total is added once per agent. In Lesson 6.2 (one agent) the numbers were right.
- If you use token_usage for cost (Lesson 5.6), give each agent its own LLM - and check against your model server once.
shared LLM token_usage: total_tokens=2331 prompt_tokens=1743 cached_prompt_tokens=1734 completion_tokens=588 reasoning_tokens=0 cache_creation_tokens=0 successful_requests=9
Ollama itself requests 3 | prompt 581 | completion 196
one LLM each token_usage: total_tokens=777 prompt_tokens=581 cached_prompt_tokens=578 completion_tokens=196 reasoning_tokens=0 cache_creation_tokens=0 successful_requests=3Watch out: Never trust a cost number you have not checked once against the source. A dashboard built on the shared-LLM numbers would have shown three times the real cost.
Question 4 - why the "(Note" rule?
In the first run, the editor wrote the paragraph and then added a line for us:
- "(Note: The original paragraph meets the expected criteria, so no changes were necessary.)"
- It passed the 80-word check, so it would have reached the reader.
- The guardrail now drops lines starting with "(Note". Same idea as Lesson 6.4: remove small things in code.
- Read your final outputs - every model has its own little habits.
When sequential is the right choice
- The steps are always the same and in the same order: research, write, edit.
- Each step needs the work of the step before.
- You want the cost to be predictable: one model call per task (plus guardrail retries).
- If the order must change during the run - send it back to the writer, or skip the editor - use a manager (Lesson 6.6) or LangGraph (Lesson 3.8).
The recipe
- 1. Give the first agent real material through inputs - not just a job title.
- 2. One LLM object per agent.
- 3. List the tasks in order; Process.sequential runs them one by one.
- 4. Remember every task sees ALL earlier outputs; use context=[...] only when you know what it needs.
- 5. Guardrails on the tasks whose output users see.
- 6. Read result.tasks_output step by step, and check the numbers once.
Sequential crews at a glance
Run in orderThe default process.
Crew(agents=[...], tasks=[t1, t2, t3], process=Process.sequential)
Pass materialThrough placeholders.
crew.kickoff(inputs={"topic": "...", "source": SOURCE})Limit contextOnly these tasks.
Task(..., context=[write])
Every stepIn order.
for t in result.tasks_output: print(t.agent, t.raw)
Correct countsOne LLM per agent.
Agent(..., llm=LLM(model="ollama/llama3", ...))
Try it yourself
The code does not change. Swap the content string and the program does something else entirely.
“Put a wrong fact into SOURCE. Does the editor notice? Why or why not?”
“Run with only researcher and writer. Is the paragraph worse?”
“Give the crew the facts about interrupt() from Lesson 3.7 as SOURCE.”
“Share one llm between the agents again and compare token_usage with Ollama’s numbers.”
What usually goes wrong
In Lesson 6.1 it invented facts. Give real material through inputs.
token_usage was 3x too high with three agents.
✗ llm = LLM(...)
Agent(..., llm=llm) # for every agent✓ Agent(..., llm=LLM(...)) # one per agentcontext=[write] left the editor without the facts it had to check.
Every agent is a model call. Keep the editor only if drafts often need fixing.
Key points
- Process.sequential runs tasks in list order; each task builds on the earlier ones.
- Each task received the outputs of ALL earlier tasks; context=[...] chooses exactly which.
- A source text through inputs gave correct facts - a job title alone did not (Lesson 6.1).
- With one shared LLM object, token_usage was exactly 3x too high; one LLM per agent was correct.
- Guardrails cleaned the final output, including the editor’s "(Note: ...)" line.
Quick check before you move on
Quiz
- 1.
The editor returned the draft unchanged. What does that tell you about cost?
- 2.
context=[write] cut the editor’s prompt from 228 to 165 tokens. What was the problem?
- 3.
How did we know token_usage was wrong?
- 4.
When is a sequential process not enough?
Interview questions
How does context flow in a CrewAI sequential process?
Tasks run in order; by default each task receives the outputs of all preceding tasks as context in its prompt. The context parameter restricts it to specific tasks.
How would you control cost in a multi-agent pipeline?
Measure per-call tokens from the model server, verify framework-reported numbers, trim context to what each task needs, drop agents that rarely change outputs, and keep guardrail retries small.
Comments
Sign in to leave a comment. Your name and photo come from Google; nothing else is shared.
Loading comments...