← Back to Agentic AI map
Lesson 6.5 · CrewAI

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.

crewai

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.

workflowThree agents in a linestep 1 / 3

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.

context
none (first task)
facts
taken from the source

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.

Small dictionary
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.
team.py
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)
Output - a real run
--- 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=3

Reading 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.
Recorded - the context part of each request (shortened)
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.
Only the draft for the editor
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.
Output - the same crew, counted three ways
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=3

Watch 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 order

The default process.

Crew(agents=[...], tasks=[t1, t2, t3], process=Process.sequential)
Pass material

Through placeholders.

crew.kickoff(inputs={"topic": "...", "source": SOURCE})
Limit context

Only these tasks.

Task(..., context=[write])
Every step

In order.

for t in result.tasks_output: print(t.agent, t.raw)
Correct counts

One 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.

Break the source

“Put a wrong fact into SOURCE. Does the editor notice? Why or why not?”

Remove the editor

“Run with only researcher and writer. Is the paragraph worse?”

Another topic

“Give the crew the facts about interrupt() from Lesson 3.7 as SOURCE.”

Count it yourself

“Share one llm between the agents again and compare token_usage with Ollama’s numbers.”

What usually goes wrong

No source for the researcher

In Lesson 6.1 it invented facts. Give real material through inputs.

One shared LLM object

token_usage was 3x too high with three agents.

✗ llm = LLM(...)
Agent(..., llm=llm)  # for every agent
✓ Agent(..., llm=LLM(...))  # one per agent
Cutting context too far

context=[write] left the editor without the facts it had to check.

An editor that changes nothing

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

What context did the editor receive by default?
The outputs of both earlier tasks: the research and the draft.
How do you give a task only some earlier outputs?
Task(..., context=[the tasks it should see]).
What was wrong with token_usage in the first version?
With one shared LLM object it reported 3x the real requests and tokens.
Why were the facts correct this time?
The researcher worked from a real source text passed through inputs.

Quiz

  1. 1.

    The editor returned the draft unchanged. What does that tell you about cost?

  2. 2.

    context=[write] cut the editor’s prompt from 228 to 165 tokens. What was the problem?

  3. 3.

    How did we know token_usage was wrong?

  4. 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...