Multi-agent systems stop being abstract once you wire two roles together and watch them hand off work. This CrewAI tutorial for beginners builds a researcher–writer crew that answers a focused question and produces a short brief. You’ll install the library, define agents and tasks in code, kick off a run, and see exactly what the framework schedules behind the scenes.
Prerequisites
Before writing any agent code, confirm your environment:
- Python 3.10 or newer (
python --version) pipwith network access- An OpenAI API key, or an OpenAI-compatible endpoint credential
- Comfort with reading stack traces and JSON
Install the framework. CrewAI pulls in LangChain and OpenAI clients transitively, so the install is heavier than a micro-library:
python -m venv .venv
source .venv/bin/activate
pip install crewai
export OPENAI_API_KEY="sk-your-key-here"
If you prefer pinned versions for reproducible builds, check the version you get with pip show crewai and lock it in your requirements.txt. In this CrewAI tutorial for beginners we keep the surface area small, so the unpinned latest stable is fine.
Define your agents
An Agent in CrewAI is a role with a goal and a backstory. The backstory isn’t flavor text—it gets injected into the system prompt and shapes output tone. Set verbose=True so you can see the reasoning steps during the run.
from crewai import Agent
researcher = Agent(
role="Senior Research Analyst",
goal="Find concise, authoritative facts on a given topic",
backstory="You have 10 years of experience distilling complex subjects into bullet points.",
verbose=True,
allow_delegation=False,
)
writer = Agent(
role="Technical Writer",
goal="Turn research notes into a clean, skimmable markdown brief",
backstory="You write developer docs and hate fluff.",
verbose=True,
allow_delegation=False,
)
allow_delegation=False prevents the agent from spawning sub-tasks. For a first crew, disable it; delegation adds latency and makes debugging harder.
Define tasks
Tasks bind an agent to a concrete deliverable. The expected_output field is not enforced programmatically, but it strongly conditions the model. Be specific.
from crewai import Task
research_task = Task(
description="Research the current state of server-side WebAssembly. Focus on runtimes and adoption.",
agent=researcher,
expected_output="3-5 bullet points with concrete runtime names and one sentence each.",
)
write_task = Task(
description="Using the research bullet points, write a 150-word markdown brief for engineers.",
agent=writer,
expected_output="Markdown brief with a heading and no more than 150 words.",
)
Note the second task implicitly depends on the first. CrewAI does not auto-resolve cross-task data flow unless you use context or a sequential process. We’ll use the sequential process below, which passes the prior task’s output as context to the next.
Assemble and run the crew
A Crew ties agents and tasks together with a execution Process. Process.sequential runs tasks in list order, feeding each result forward.
from crewai import Crew, Process
crew = Crew(
agents=[researcher, writer],
tasks=[research_task, write_task],
process=Process.sequential,
verbose=True,
)
result = crew.kickoff()
print("FINAL RESULT:\n", result)
Run it:
python crew.py
Expected output at checkpoints
With verbose=True, you’ll see agent kickoffs. A truncated log looks like this:
[DEBUG] Working Agent: Senior Research Analyst
Thinking: I need to list server-side Wasm runtimes...
- Wasmtime: Bytecode Alliance's runtime, used in Fastly Compute.
- WasmEdge: Optimized for edge and cloud-native deployments.
- Wasmer: Supports multiple backends, embeds in many languages.
Task output: 3-5 bullet points with concrete runtime names...
[DEBUG] Working Agent: Technical Writer
Thinking: I'll convert those bullets into a brief...
# Server-Side WebAssembly
Server-side Wasm is moving from demo to production...
FINAL RESULT:
# Server-Side WebAssembly
Server-side Wasm is moving from demo to production...
The exact text varies by model. What matters: the researcher output is visible, and the writer received it. If you see the writer ignoring the research, check that process=Process.sequential is set—parallel execution will not share context.
Pointing CrewAI at an OpenAI-compatible gateway
If you don’t want to juggle multiple provider keys, point the underlying client at an OpenAI-compatible endpoint. For example, n4n.ai exposes one endpoint that fronts 240+ models with automatic fallback when a provider is rate-limited or degraded, and it honors client routing directives. CrewAI’s default LangChain OpenAI client reads OPENAI_API_BASE and OPENAI_API_KEY from the environment:
import os
os.environ["OPENAI_API_BASE"] = "https://api.n4n.ai/v1"
os.environ["OPENAI_API_KEY"] = "your-gateway-key"
# CrewAI now routes model calls through the gateway
This is useful when you want per-token metering across models without writing custom middleware. The gateway forwards provider cache-control hints, so prompt caching works as if you called the origin directly.
Adding a real tool (optional)
The crew above is self-contained, but most production agents call tools. CrewAI integrates LangChain tools via crewai_tools. Here’s a minimal custom tool that returns a static string—swap the body for a real API call later.
from langchain.tools import tool
@tool("get_runtime_list")
def get_runtime_list() -> str:
"""Return a hardcoded list of server-side Wasm runtimes."""
return "Wasmtime, WasmEdge, Wasmer"
researcher_with_tool = Agent(
role="Senior Research Analyst",
goal="Find concise, authoritative facts on a given topic",
backstory="You use tools to verify claims.",
verbose=True,
allow_delegation=False,
tools=[get_runtime_list],
)
Attach that agent to research_task and the model can invoke get_runtime_list during its turn. Tools add nondeterminism; log the tool calls when debugging.
Common pitfalls
No output from the writer. Usually caused by Process.sequential not being set, or tasks defined in wrong order. Print crew.tasks to verify ordering.
Rate limit errors. Default CrewAI uses gpt-4o or similar. If you hit 429s, switch to a smaller model or use a gateway with fallback (see above).
Token blowups. verbose=True and long backstories multiply across agents. Keep backstories under 40 words each for a first crew.
Delegation loops. With allow_delegation=True, an agent can ask another agent to do its task, which can cycle. Disable it until you have a working baseline.
Wrapping up
You now have a runnable two-agent crew: one researches, one writes, and the sequential process wires them together. The full code from this CrewAI tutorial for beginners is under 40 lines and runs against any OpenAI-compatible backend. From here, add tools, switch to Process.hierarchical for a manager agent, or persist task outputs to disk for longer pipelines. The framework stays out of your way once the agents and tasks are correctly specified.