DS DevShelfHub Projects · AI tools
Cheatsheets / CrewAI
Cheatsheet · AI frameworks

CrewAI: Agents, Tasks, Flows and Crews Reference Guide

By DevShelfHub

Agents, Tasks, Crews, Processes, Flows — the everyday API surface plus the YAML config that drives a crew.

102 items 6 min Agents Tasks Flows

Start hereQuick start · 6 you’ll reach for daily

Define an agentAgent(role, goal, backstory)
Define a taskTask(description, expected_output, agent)
Wire a crewCrew(agents=[…], tasks=[…])
Run itcrew.kickoff(inputs={"topic":…})
Pick a processProcess.sequential | hierarchical
Scaffold projectcrewai create crew my_project

Target versions · paceVersions

Targets: crewai ≥ 0.95 crewai-tools ≥ 0.25 python ≥ 3.10

CrewAI dropped the LangChain dependency around 0.30 and is now a standalone framework. APIs are stable but Flows, Knowledge, and the @CrewBase decorator pattern continue to evolve. If a decorator import fails, check the official docs at docs.crewai.com. This sheet pins to names current as of May 2026.

Install · env · scaffoldSetup

bash
# pip — core + extras you'll likely need
pip install crewai
pip install 'crewai[tools]'                 # built-in tool suite
pip install crewai-tools                    # standalone tools package

# env — pick the providers you'll use
export OPENAI_API_KEY=sk-...
export ANTHROPIC_API_KEY=sk-...
export SERPER_API_KEY=...                   # optional, web search tool

# scaffold a new project (recommended layout)
crewai create crew my_project
cd my_project && crewai install
crewai run

Where things liveCommon imports

Core abstractions sit in crewai. The bundled tool catalogue lives in crewai_tools (separate package). Flows and decorators are under crewai.flow and crewai.project.

from crewai import Agent, Task, Crew, ProcessThe four core classes.
from crewai import LLMProvider-agnostic LLM wrapper (LiteLLM under the hood).
from crewai.project import CrewBase, agent, task, crew, before_kickoff, after_kickoffDecorators for YAML-driven projects.
from crewai.flow.flow import Flow, start, listen, router, and_, or_Event-driven Flows API.
from crewai.tools import BaseTool, toolBuild custom tools.
from crewai_tools import SerperDevTool, ScrapeWebsiteTool, FileReadToolPrebuilt tools (web, scraping, files).
from crewai_tools import RagTool, PDFSearchTool, DirectorySearchToolRAG-style search tools.
from crewai.knowledge.source.text_file_knowledge_source import TextFileKnowledgeSourceKnowledge sources for agents/crews.
from crewai.memory import LongTermMemory, ShortTermMemory, EntityMemoryBuilt-in memory stores.
from pydantic import BaseModelUsed everywhere for structured output + tool args.

role · goal · backstoryAgents

Agents are LLM personas with role, goal, backstory. Keep each agent narrow — one job, one persona. Broad agents pick the wrong tool and contradict their own goal.

Agent(role, goal, backstory)Minimum viable agent.
Agent(…, tools=[tool_a, tool_b])Bind tools the agent can call.
Agent(…, llm="openai/gpt-4o-mini")Pin model via LiteLLM-style id.
Agent(…, llm=LLM(model="…", temperature=0.2))Or pass a configured LLM instance.
Agent(…, allow_delegation=True)Lets this agent hand work to crew-mates.
Agent(…, max_iter=15)Cap reasoning loops. Default 25.
Agent(…, max_rpm=10)Per-agent request rate limit.
Agent(…, verbose=True)Stream the agent’s thinking to stdout.
Agent(…, memory=True)Opt in to short-term memory between tasks.
Agent(…, cache=True)Cache tool calls. Default true.
Agent(…, system_template="…")Override the auto-generated system prompt.
Agent(…, respect_context_window=True)Auto-summarise history when nearing token limit.
Treat the backstory as a behavior contract, not flavor text. “Cuts filler” materially changes output style.

work the crew doesTasks

Task(description, expected_output, agent)Minimum viable task.
Task(…, context=[other_task])Wire upstream task output into this task’s prompt.
Task(…, output_file="out/report.md")Persist result to disk.
Task(…, output_json=Schema)Force JSON conforming to a Pydantic model.
Task(…, output_pydantic=Schema)Return a typed Pydantic instance.
Task(…, tools=[tool_a])Override the agent’s tools for this task only.
Task(…, async_execution=True)Run in parallel with sibling async tasks.
Task(…, human_input=True)Pause for human approval before completing.
Task(…, guardrail=fn)Validator that can retry the task on failure.
Task(…, retry_count=2)How many times the guardrail can retry.
expected_output is the single biggest lever on quality. Be concrete — “Markdown list, 5 items, each ≤40 words” beats “a useful summary”.

agents + tasks + processCrews

Crew(agents=[…], tasks=[…])Minimum crew. Defaults to sequential.
Crew(…, process=Process.sequential)Tasks run in list order. Most predictable.
Crew(…, process=Process.hierarchical, manager_llm="…")A manager agent assigns tasks dynamically.
crew.kickoff(inputs={"topic": "…"})Run once. {topic} in templates is interpolated.
crew.kickoff_for_each(inputs=[{…}, {…}])Map the crew over a list of inputs.
crew.kickoff_async(inputs=…)Async variant. Returns a coroutine.
crew.replay(task_id="…")Re-run from a specific task. Great for debugging.
crew.train(n_iterations=5, filename="train.pkl")Iterative training with human feedback.
crew.test(n_iterations=3, eval_llm="gpt-4o")Run an LLM-judged evaluation pass.
result.rawFinal string output of the crew.
result.json_dict / result.pydanticTyped outputs when configured on the task.
result.tasks_outputPer-task outputs, in order.
result.token_usageTotal prompt / completion token counts.

orchestration modelProcesses

Process.sequentialTasks run in declared order. Each gets prior outputs via context.
Process.hierarchicalA manager agent (LLM) routes tasks to crew-mates. Requires manager_llm.
manager_agent=Agent(…)Use a custom manager instead of the default one.
async_execution=True on tasksWithin sequential, async tasks fan out in parallel.
Start sequential. Hierarchical adds an extra LLM hop per decision and tends to over-delegate on simple pipelines. Reach for it only when task routing is genuinely dynamic.

event-driven orchestrationFlows

Flows wrap crews in an event-driven state machine. Use when you need branching, loops, or to chain multiple crews. The state attribute is a typed Pydantic model shared across steps.

class MyFlow(Flow[State]): …Typed flow. State is a BaseModel.
@start()Marks the entry method.
@listen(prev_step)Runs after prev_step completes.
@listen(or_(a, b))Fires when either parent finishes.
@listen(and_(a, b))Fires only after both parents finish.
@router(step)Branch step. Return a string; methods @listen("that_string") fire.
flow.kickoff(inputs={…})Run the flow.
flow.plot()Render the flow graph as HTML.
self.state.field = valueMutate shared state inside a step.
python
from crewai.flow.flow import Flow, listen, start, router
from pydantic import BaseModel

class State(BaseModel):
    topic: str = ""
    draft: str = ""
    score: int = 0

class ContentFlow(Flow[State]):

    @start()
    def pick_topic(self):
        self.state.topic = "vector databases in 2026"

    @listen(pick_topic)
    def draft(self):
        self.state.draft = ContentCrew().crew().kickoff(
            inputs={"topic": self.state.topic}
        ).raw

    @router(draft)
    def review(self):
        self.state.score = score_draft(self.state.draft)
        return "ship" if self.state.score >= 8 else "revise"

    @listen("revise")
    def revise(self):
        # loops back into draft with feedback
        ...

    @listen("ship")
    def publish(self):
        publish_to_cms(self.state.draft)

ContentFlow().kickoff()

give agents capabilityTools

Built-in (crewai_tools)

SerperDevTool()Web search via Serper. Needs SERPER_API_KEY.
ScrapeWebsiteTool(website_url="…")Fetch + clean HTML to text.
FileReadTool(file_path="…")Read a single file.
DirectorySearchTool(directory="…")RAG over a folder of docs.
PDFSearchTool(pdf="…")RAG over a single PDF.
CodeInterpreterTool()Sandboxed Python execution.
BraveSearchTool() / TavilySearchTool()Alternative search backends.

Custom tools

@tool("name")
def fn(query: str) -> str: …
Quickest path. Docstring becomes the description.
class T(BaseTool): name, description, args_schemaPreferred for anything non-trivial. Typed args, validation.
def _run(self, **kwargs) -> str:Sync entry point.
async def _arun(self, **kwargs):Optional async entry point.
python
from crewai.tools import BaseTool
from pydantic import BaseModel, Field

class SearchArgs(BaseModel):
    query: str = Field(..., description="What to look up")
    top_k: int = Field(5, description="Max results")

class InternalDocs(BaseTool):
    name: str = "internal_docs_search"
    description: str = "Search the company wiki. Use for product or policy questions."
    args_schema: type[BaseModel] = SearchArgs

    def _run(self, query: str, top_k: int = 5) -> str:
        # Replace with your real retriever.
        hits = my_vector_store.search(query, k=top_k)
        return "\n\n".join(f"- {h.title}: {h.snippet}" for h in hits)

# Attach to an agent
support_agent = Agent(
    role="Support Specialist",
    goal="Answer customer questions using internal docs",
    backstory="Polite, exact, never invents URLs.",
    tools=[InternalDocs()],
)

persist across tasks & runsMemory

Crew(…, memory=True)Enable all three default memories at crew level.
ShortTermMemory()Per-run context. Cleared after kickoff.
LongTermMemory()Survives runs. Stored in SQLite by default.
EntityMemory()Tracks named entities (people, projects) across runs.
Crew(…, embedder={"provider":"openai", "config":{…}})Override embedding backend for memory.
memory_config={"provider":"mem0"}Plug in external memory provider (e.g. mem0).
Memory writes happen at task completion. If a task crashes, that turn isn’t remembered — design tasks to be idempotent before relying on long-term memory.

give agents documentsKnowledge

Knowledge sources are documents indexed once and queried by agents during a run. Lower friction than wiring RAG tools manually when the corpus is fixed.

TextFileKnowledgeSource(file_paths=["…"])Plain text files.
PDFKnowledgeSource(file_paths=["…"])PDF files.
CSVKnowledgeSource(file_paths=["…"])Structured CSV.
StringKnowledgeSource(content="…")Inline string content.
Crew(…, knowledge_sources=[src])Available to every agent in the crew.
Agent(…, knowledge_sources=[src])Scoped to a single agent.

pick & configure modelsLLM integration

Model strings follow the LiteLLM convention: provider/model. Set the matching *_API_KEY env var and CrewAI handles the rest.

llm="openai/gpt-4o-mini"OpenAI — needs OPENAI_API_KEY.
llm="anthropic/claude-sonnet-4-6"Anthropic — needs ANTHROPIC_API_KEY.
llm="gemini/gemini-2.5-flash"Google — needs GEMINI_API_KEY.
llm="ollama/llama3.1"Local via Ollama. Set OLLAMA_BASE_URL if non-default.
LLM(model="…", temperature=0.2, max_tokens=1024)Configured instance, reusable across agents.
LLM(…, response_format=Schema)Force structured output at the LLM level.
manager_llm="…"Model for hierarchical crew’s default manager.

declarative crewsYAML project layout

crewai create crew scaffolds a project with agents.yaml, tasks.yaml, and a thin Python file that uses @CrewBase, @agent, @task, @crew to wire them. Configs are interpolated against kickoff(inputs=…).

agents.yaml

yaml
# config/agents.yaml — declarative agent definitions
researcher:
  role: >
    Senior {topic} Researcher
  goal: >
    Surface the 5 most consequential developments in {topic} this quarter
  backstory: >
    A meticulous analyst who values primary sources over hot takes.
  llm: openai/gpt-4o-mini
  verbose: true

writer:
  role: >
    Tech Editor
  goal: >
    Turn the research brief into a 600-word post for a developer audience
  backstory: >
    Former staff writer at a respected eng newsletter. Cuts filler.
  llm: anthropic/claude-sonnet-4-6

tasks.yaml

yaml
# config/tasks.yaml — declarative task definitions
research_task:
  description: >
    Investigate recent advances in {topic}. Pull 5 primary sources from the
    last 90 days and summarise each in 2-3 sentences.
  expected_output: >
    A markdown list of 5 items, each with title, URL, and a 2-sentence summary.
  agent: researcher

write_task:
  description: >
    Using the research brief, write a 600-word post for working developers.
    Lead with the most actionable finding.
  expected_output: >
    A markdown post with H2 sections and code blocks where relevant.
  agent: writer
  context: [research_task]
  output_file: out/post.md

crew.py

python
from crewai import Agent, Crew, Task, Process
from crewai.project import CrewBase, agent, crew, task

@CrewBase
class ContentCrew:
    """Research + write crew driven by YAML configs."""
    agents_config = "config/agents.yaml"
    tasks_config = "config/tasks.yaml"

    @agent
    def researcher(self) -> Agent:
        return Agent(config=self.agents_config["researcher"], verbose=True)

    @agent
    def writer(self) -> Agent:
        return Agent(config=self.agents_config["writer"], verbose=True)

    @task
    def research_task(self) -> Task:
        return Task(config=self.tasks_config["research_task"])

    @task
    def write_task(self) -> Task:
        return Task(config=self.tasks_config["write_task"])

    @crew
    def crew(self) -> Crew:
        return Crew(
            agents=self.agents,
            tasks=self.tasks,
            process=Process.sequential,
            verbose=True,
        )

the crewai commandCLI

crewai create crew my_projectScaffold a YAML-style project.
crewai create flow my_flowScaffold a Flow-style project.
crewai installInstall deps with uv.
crewai runRun the project’s default entry.
crewai train -n 5 -f train.pklTrain with human feedback for N iterations.
crewai test -n 3 -m gpt-4oLLM-judged eval run.
crewai replay -t TASK_IDReplay from a specific task.
crewai reset-memories -aWipe long-term / entity / kickoff memories.

research → write · ~30 linesEnd-to-end · Minimal crew

Two agents, two tasks, sequential process. Replace the topic and you have a working content pipeline.

python
from crewai import Agent, Task, Crew, Process
from crewai_tools import SerperDevTool

search = SerperDevTool()

researcher = Agent(
    role="Senior Researcher",
    goal="Surface the most consequential developments in {topic}",
    backstory="A meticulous analyst who prefers primary sources.",
    tools=[search],
    llm="openai/gpt-4o-mini",
    verbose=True,
)

writer = Agent(
    role="Tech Editor",
    goal="Turn the brief into a tight 600-word post",
    backstory="Former newsletter staff writer. Cuts filler.",
    llm="anthropic/claude-sonnet-4-6",
)

brief = Task(
    description="Investigate {topic}. Five primary sources, 2-3 sentences each.",
    expected_output="Markdown list with title, URL, summary.",
    agent=researcher,
)

post = Task(
    description="Write a 600-word post from the brief. Lead with the action.",
    expected_output="Markdown with H2 sections.",
    agent=writer,
    context=[brief],
    output_file="out/post.md",
)

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

result = crew.kickoff(inputs={"topic": "vector databases in 2026"})
print(result.raw)

Best practiceGood to know

One job per agent. A narrow role plus an outcome-shaped expected_output outperforms a generic “assistant” with a long prompt. Split before tuning.
Wire data through context, not memory. For sequential crews, pass upstream task outputs via context=[t1]. Memory is for soft recall across runs, not deterministic data hand-off.
Reach for Flows the moment you need branching. A Process.hierarchical crew burns tokens making routing decisions an @router step would settle in one line of Python.

Common trapsWatch out for

YAML interpolation only fills {var} from kickoff(inputs=…). Stray curly braces in your prompt — e.g. JSON examples — will raise KeyError at runtime. Double them: {{ / }}.
allow_delegation=True can stall a crew. Agents will ping-pong work back and forth if their goals overlap. Leave it off unless the roles are genuinely complementary, and set a low max_iter as a backstop.
Don’t mutate self.state from inside async tasks without care. Flows’ state is shared. Two concurrent steps writing to the same field will race — merge in a downstream @listen(and_(…)) step instead.

Go deeperSee also

CrewAI FAQ

What is CrewAI used for?

CrewAI is a Python framework for building multi-agent AI systems. You define agents with roles and backstories, assign tasks with expected outputs, and combine them into a crew that runs sequentially or hierarchically. It handles tool calling, memory, and LLM interactions so you focus on the workflow.

What is the difference between an Agent and a Task in CrewAI?

An Agent represents a specialized worker with a role, goal, backstory, and optional tools. A Task is a unit of work assigned to an agent, with a description of what to do and what a good output looks like. Agents are re-usable; tasks are the concrete jobs they execute.

Is CrewAI free to use?

CrewAI the framework is open source and free to use under the MIT license. You provide your own LLM API keys (OpenAI, Anthropic, Gemini, or local via Ollama). The crewai.com cloud platform offers a paid managed service, but the core library has no cost.

What LLMs does CrewAI support?

CrewAI uses LiteLLM under the hood, so it supports any provider LiteLLM covers — OpenAI, Anthropic Claude, Google Gemini, Mistral, Ollama (local), Azure OpenAI, Cohere, and more. Set the model with the LLM class or the OPENAI_MODEL_NAME environment variable.

How do CrewAI Flows work?

Flows are an event-driven orchestration layer on top of crews. You subclass Flow, decorate a method with @start to mark the entry point, and use @listen(event) to chain subsequent steps. Flows support conditional branching with @router, parallel fan-out with and_/or_ combinators, and persistent state via BaseModel.

What is the difference between CrewAI and LangChain agents?

CrewAI is opinionated about multi-agent collaboration — it gives each agent a role, goal, and backstory, then coordinates them through crews and tasks with explicit handoffs. LangChain agents are more composable primitives you wire together yourself. Use CrewAI when you need structured role-based teamwork; use LangChain when you need fine-grained control over a single agent's tool loop.