Lesson 09 CrewAI Intro
Theme:

Lab 09 — First crew with CrewAI

Lesson 09 · Building Your Own Agents · ~30 min hands-on

Goal: at the end of this lab you will have (a) verified crewai is installed at the pinned version and pointed at Anthropic Claude, (b) defined two agents with distinct roles, goals, and backstories, (c) wired two Tasks into a sequential Crew, (d) run crew.kickoff() end-to-end and read the verbose log, and (e) felt how a single sentence in a backstory changes behaviour. This is the short CrewAI exploration — just enough to put the framework in your hands. L10 builds the canonical 3-role planner + worker + reviewer crew on top of these same primitives.

Prereqs

Step 1 — Confirm crewai is installed

python -c "import crewai; print(crewai.__version__)"

Expected: a version string in the 0.80.x–0.89.x range (e.g., 0.86.0 — the version snapshotted in agents/SDK-VERSIONS.md). If you get ModuleNotFoundError, re-run pip install -r agents/requirements.txt and confirm against agents/SDK-VERSIONS.md.

✓ Self-check: a version like 0.86.0 prints. No traceback.

Step 2 — The 40-line hello world (two-agent crew)

Create hello_crew.py anywhere convenient (the repo root works fine):

from crewai import Agent, Task, Crew, Process

MODEL = "anthropic/claude-3-5-sonnet-latest"

researcher = Agent(
    role="Senior Researcher",
    goal="Find 3 short factual statements about the topic",
    backstory=(
        "You are a meticulous researcher. You prefer specs, papers, "
        "and primary sources over blog posts. You always cite."
    ),
    llm=MODEL,
    verbose=True,
)

writer = Agent(
    role="Technical Writer",
    goal="Write a 2-3 sentence brief based on the research",
    backstory=(
        "You are a concise technical writer. You explain ideas "
        "without jargon and never invent facts."
    ),
    llm=MODEL,
    verbose=True,
)

research_task = Task(
    description="Research the topic: {topic}. Return 3 short factual statements with sources.",
    expected_output="3 bullet points, each with a brief source attribution.",
    agent=researcher,
)

write_task = Task(
    description="Write a 2-3 sentence brief on {topic} using the research above.",
    expected_output="A 2-3 sentence paragraph, no jargon.",
    agent=writer,
    context=[research_task],
)

crew = Crew(
    agents=[researcher, writer],
    tasks=[research_task, write_task],
    process=Process.sequential,
    verbose=True,
)

result = crew.kickoff(inputs={"topic": "Linux kernel 6.14 networking"})
print("\n=== final brief ===")
print(result)

Run it:

python hello_crew.py

Expected: a stream of [DEBUG] / agent-coloured log lines as each agent works its task, then a === final brief === section with a short paragraph. The researcher runs first (because it appears first in tasks=), then the writer runs with the researcher’s output wired in via context=[research_task].

✓ Self-check: the script exits cleanly; the final paragraph reads like a 2–3 sentence brief on the topic; you saw two distinct agent turns in the log.

Step 3 — Read the verbose log

Scroll back through the output. You should see, in order:

This is what verbose=True gives you out of the box. In production you’d disable it and pipe structured events to your logger instead — but for a lab, verbose is the fastest way to see what each agent actually got asked and what it answered.

Step 4 — Tighten a backstory; observe behaviour change

Edit the researcher’s backstory to add a recency constraint:

backstory=(
    "You are a meticulous researcher. You prefer specs, papers, "
    "and primary sources over blog posts. You always cite. "
    "You only trust sources from the last 12 months and refuse "
    "to cite anything older."
),

Re-run python hello_crew.py. Expected: the researcher’s bullets now either include explicit recency in source attributions (“per kernel 6.14 release notes, 2025”) or the agent calls out that older sources were rejected. The behaviour change is in the prompt; CrewAI just made it trivial to attribute the change to a specific agent’s persona — you didn’t touch tasks, the crew, or the writer.

✓ Self-check: the researcher’s output for this run is visibly different from Step 2’s output, in a way that traces back to your one-sentence backstory edit.

Step 5 — Drop the context=; see what happens

Edit write_task and remove context=[research_task]:

write_task = Task(
    description="Write a 2-3 sentence brief on {topic} using the research above.",
    expected_output="A 2-3 sentence paragraph, no jargon.",
    agent=writer,
)

Re-run. Expected: the writer produces a brief based on the {topic} string alone — the researcher’s bullets are nowhere in the writer’s system prompt. (Look at the verbose log to confirm.) The brief will often still read fine, because the model has plenty of background knowledge about most topics — but it’s not grounded in the researcher’s actual findings.

This shows that artefact flow between agents is not automatic — you wire it explicitly with context=[...]. If you forget, your agents become disconnected solo runs that share a topic but not their work. Restore the context=[research_task] line before continuing.

✓ Self-check: with context= removed, the writer’s system prompt in the log no longer contains the researcher’s 3 bullets. With it restored, it does.

Step 6 — Add a third agent (reviewer)

Add a skeptical reviewer that checks the writer’s brief against the researcher’s evidence. Append these definitions:

reviewer = Agent(
    role="Skeptical Reviewer",
    goal="Identify any factual errors or weak claims in the brief",
    backstory=(
        "You are a friendly but rigorous reviewer. "
        "You highlight unsupported claims."
    ),
    llm=MODEL,
    verbose=True,
)

review_task = Task(
    description="Review the brief on {topic}. List any unsupported claims or factual issues.",
    expected_output="A short review with 0-3 issues (or 'no issues found').",
    agent=reviewer,
    context=[research_task, write_task],
)

Update the Crew:

crew = Crew(
    agents=[researcher, writer, reviewer],
    tasks=[research_task, write_task, review_task],
    process=Process.sequential,
    verbose=True,
)

Re-run. Expected: three agent turns in the verbose log, in the order researcher → writer → reviewer. The final printed result is now the reviewer’s output — because Process.sequential returns the last task’s output as the crew result.

✓ Self-check: the final printed text is a review (issues found or “no issues found”), not the writer’s brief. The writer’s brief is still accessible via crew.tasks[1].output if you want to see both.

What you should leave with

Troubleshooting

Common stumbles