CrewAI 3.0: Long-Term Memory, Tool Delegation, and RAG-Based Tool Selection
A hands-on tutorial for CrewAI 3.0's three flagship features — persistent long-term memory that survives across sessions, agent-to-agent tool delegation, and RAG-based dynamic tool discovery. Build a research crew that remembers past sessions and delegates tool calls between agents.
By the end of this tutorial, you'll have a working CrewAI 3.0 research crew where agents remember past sessions via long-term memory, delegate tool calls to each other, and dynamically discover the right tool for each task using RAG-based selection. You'll also know exactly what changed from CrewAI 2.x and how to migrate your existing crews.
What You'll Build
- A Research Crew with three specialized agents (Researcher, Analyst, Writer)
- Long-term memory so agents remember findings from previous runs
- Tool delegation so the Writer can ask the Researcher to run a search mid-task
- RAG-based tool selection so agents discover and pick tools dynamically based on the task at hand
- A Flow that wires everything together with persistent state
What's New in CrewAI 3.0
CrewAI 3.0 is the most significant update since the framework's initial release. It decouples memory from the crew lifecycle, introduces a proper delegation protocol between agents, and adds a RAG layer that lets agents discover tools semantically rather than by hard-coded assignment.
| Feature | CrewAI 2.x | CrewAI 3.0 |
|---|---|---|
| Memory | Per-crew, ephemeral (lost between runs) | Persisted across sessions via SQLite + vector store |
| Tool assignment | Explicit per-agent (tools=[...]) | Explicit + dynamic via tool RAG retrieval |
| Delegation | Implicit via allow_delegation flag | Structured delegation protocol with allowed_agents scoping |
| Knowledge | RagTool only | Knowledge Sources (files, URLs, directories) with unified RAG client |
| Storage backend | ChromaDB only | ChromaDB, LanceDB, SQLite, configurable |
| Memory API | Coupled to Crew class | Standalone Memory class usable in scripts, Flows, or Crews |
Installation
CrewAI 3.0 requires Python 3.10+. The install is the same pip command, but the version range is now 1.14+ (which the framework brands as 3.0 for the feature release).
pip install crewai crewai[tools]
Expected output:
Successfully installed crewai-1.14.6 crewai-tools-1.14.6
Verify the installation:
python -c "import crewai; print(crewai.__version__)"
Expected output:
1.14.6
Failure point: If you get an import error, you may have an older version cached. Run pip install --upgrade crewai crewai[tools] to force the latest. The 3.0 feature set requires at least v1.14.0.
Part 1: Long-Term Memory
CrewAI 3.0 introduces a proper memory system that survives beyond a single crew.kickoff() call. Three types work together:
- Short-term memory — context within the current crew run (RAG-backed, vector store)
- Long-term memory — persists across runs via SQLite, accumulates task results and insights
- Entity memory — tracks named entities (people, companies, concepts) using RAG
All three are enabled by setting memory=True on your Crew. In CrewAI 3.0, you can also use the standalone Memory class outside of a crew context entirely.
Enabling Memory on a Crew
from crewai import Crew, Process
crew = Crew(
agents=[researcher, analyst, writer],
tasks=[research_task, analyze_task, write_task],
process=Process.sequential,
memory=True, # Enables all three memory types
memory_config={
"embedder": {
"provider": "openai",
"config": {
"model": "text-embedding-3-small"
}
}
},
verbose=True
)
result = crew.kickoff()
On first run, CrewAI creates local storage directories:
~/.crewai/memory/
├── short_term/ # ChromaDB vector store
├── long_term/ # SQLite database
└── entities/ # ChromaDB vector store for entity data
Expected output (verbose mode):
[Memory] Initializing memory system...
[Memory] Embedder: openai/text-embedding-3-small
[Memory] Short-term: ChromaDB at ~/.crewai/memory/short_term
[Memory] Long-term: SQLite at ~/.crewai/memory/long_term/long_term_memory.db
[Memory] Entity: ChromaDB at ~/.crewai/memory/entities
Using the Standalone Memory Class
CrewAI 3.0's standalone Memory class works without a Crew — you can use it in scripts, notebooks, or as a lightweight knowledge base:
from crewai import Memory
memory = Memory()
# Store facts
memory.remember("The API rate limit is 1000 requests per minute.")
memory.remember("Staging environment uses port 8080.")
memory.remember("The team agreed to use feature flags for all new releases.")
# Recall facts later
matches = memory.recall("What are our API limits?", limit=5)
for m in matches:
print(f"[{m.score:.2f}] {m.record.content}")
Expected output:
[0.92] The API rate limit is 1000 requests per minute.
[0.45] Staging environment uses port 8080.
Failure point: The Memory class uses an LLM for save analysis (scope, categories, importance inference). If OPENAI_API_KEY is not set, GEMINI_API_KEY or ANTHROPIC_API_KEY must be set — the memory system falls back through providers. Set any one of them.
Long-Term Memory in Action: Cross-Session Recall
The key difference from CrewAI 2.x: run a crew today, and tomorrow's run can recall what it learned.
# First session
crew = Crew(
agents=[researcher],
tasks=[Task(description="Research the impact of RAG on LLM accuracy",
expected_output="Key findings with sources",
agent=researcher)],
memory=True,
)
result1 = crew.kickoff()
# Stored in long-term memory automatically
# Second session (new process, new crew instance)
crew2 = Crew(
agents=[researcher],
tasks=[Task(description="Build on our previous RAG research. What did we learn about chunking strategies?",
expected_output="Chunking recommendations with justification",
agent=researcher)],
memory=True,
)
result2 = crew2.kickoff()
# Researcher can recall the first session's findings
Expected output (second session, verbose):
[Memory] Recall: Found 3 relevant memories from long-term storage
[Memory] [0.88] Research result: RAG significantly improves factual accuracy...
Part 2: Tool Delegation
CrewAI 3.0 formalizes delegation from a simple boolean flag into a structured protocol. In 2.x, allow_delegation=True would let an agent hand off work to any other agent in the crew. In 3.0, you can:
- Scope which agents can delegate to which other agents via
allowed_agents - Share specific tools between agents via delegation
- Track delegated tasks in the execution trace
Basic Delegation
from crewai import Agent, Task, Crew, Process
researcher = Agent(
role="Senior Researcher",
goal="Find accurate, well-sourced information",
backstory="You are a research analyst with 15 years of experience.",
tools=[search_tool, scrape_tool],
allow_delegation=False, # Executor — does the work, doesn't delegate
)
analyst = Agent(
role="Data Analyst",
goal="Analyze research findings and extract patterns",
backstory="You are a data analyst who finds insights in raw data.",
tools=[analysis_tool],
allow_delegation=False,
)
writer = Agent(
role="Lead Writer",
goal="Synthesize research into a publishable report",
backstory="You are a senior technical writer managing content production.",
allow_delegation=True, # Manager — can delegate to other agents
allowed_agents=[researcher, analyst], # Scoped delegation
)
When the Writer hits a question that needs new research, it delegates to the Researcher — CrewAI 3.0 passes the tool context automatically.
Delegation with Tool Sharing
In CrewAI 3.0, delegation can carry tool access. If the Writer needs data the Researcher's tools can provide, the delegation call makes those tools available:
research_task = Task(
description="Research the latest AI agent frameworks and their adoption rates.",
expected_output="A detailed research brief with sources and statistics.",
agent=researcher
)
analysis_task = Task(
description="Analyze the research data and identify key trends.",
expected_output="A trend analysis with 3-5 key findings.",
agent=analyst,
context=[research_task]
)
write_task = Task(
description="Write a comprehensive report based on the research and analysis. "
"If you need additional data points, delegate to the Researcher.",
expected_output="A 500-word report ready for publication.",
agent=writer,
context=[research_task, analysis_task]
)
Expected output (verbose):
[Writer] I need additional market share data. Delegating to Senior Researcher...
[Senior Researcher] Received delegated task: Find market share data for AI agent frameworks
[Senior Researcher] Using search_tool: "AI agent framework market share 2026"
[Senior Researcher] Delegated task complete. Returning results.
[Writer] Received delegated results. Continuing report...
Failure point: If delegation seems to hang or loop, set max_iter on the delegating agent to prevent infinite delegation chains:
writer = Agent(
...,
allow_delegation=True,
max_iter=5, # Stop after 5 total iterations
)
Hierarchical Process with Delegation
In a hierarchical crew, the manager agent automatically delegates tasks to worker agents. CrewAI 3.0 adds allowed_agents to prevent the manager from delegating to the wrong specialist:
crew = Crew(
agents=[researcher, analyst, writer],
tasks=[research_task, analysis_task, write_task],
process=Process.hierarchical,
manager_llm="gpt-4o",
memory=True,
)
With hierarchical process, the manager plans task assignments, delegates work, reviews outputs, and approves completion. The allowed_agents on the manager agent restricts which agents it can delegate to.
Part 3: RAG-Based Tool Selection
This is the most distinctive feature in CrewAI 3.0. Instead of hard-coding tools=[...] on each agent, you can provide a tool repository and let the agent discover the right tool dynamically based on the task description.
Tool RAG via Knowledge Sources
CrewAI 3.0 introduces a provider-neutral RAG client abstraction for vector stores. The knowledge system lets you feed documents, URLs, and directories to agents as retrievable context:
from crewai import Knowledge
from crewai.knowledge.source.string_knowledge_source import StringKnowledgeSource
# Create a knowledge source from content
knowledge_source = StringKnowledgeSource(
content="""
Tool: web_search — Searches the web for current information. Use for finding recent news, stats, and data.
Tool: web_scrape — Extracts content from a specific URL. Use for reading articles and documentation.
Tool: code_interpreter — Executes Python code. Use for data analysis, calculations, and visualization.
Tool: file_read — Reads content from local files. Use for accessing stored documents and data files.
"""
)
# Add to crew knowledge
crew = Crew(
agents=[agent],
tasks=[task],
knowledge_sources=[knowledge_source],
embedder={
"provider": "openai",
"config": {"model": "text-embedding-3-small"}
}
)
When an agent receives a task, CrewAI 3.0 retrieves the most relevant tool descriptions from the knowledge store and presents them to the agent as available tools — dynamically, per task.
Dynamic Tool Discovery
The RAG-based approach means you can maintain a tool catalog and agents discover what they need in real time:
from crewai_tools import RagTool
rag_tool = RagTool()
research_agent = Agent(
role="Research Assistant",
goal="Query large knowledge bases efficiently",
backstory="Expert at using RAG tools for information retrieval",
tools=[rag_tool],
respect_context_window=True,
)
# The RagTool can index documents and answer questions from them
What happens internally:
1. Agent receives task: "Find the Q4 financial report data"
2. Tool RAG searches the knowledge source for relevant tool descriptions
3. Top matches: file_read (score 0.87), web_search (score 0.32)
4. Agent selects file_read, opens and reads the Q4 report
5. Result returned without the agent ever needing a hard-coded tool assignment
Combining Knowledge with Memory
The real power of CrewAI 3.0 is combining knowledge, memory, and delegation in a single crew:
crew = Crew(
agents=[researcher, analyst, writer],
tasks=[research_task, analysis_task, report_task],
process=Process.sequential,
memory=True,
memory_config={
"embedder": {
"provider": "openai",
"config": {"model": "text-embedding-3-small"}
}
},
knowledge_sources=[company_docs, tool_catalog],
embedder={
"provider": "openai",
"config": {"model": "text-embedding-3-small"}
},
verbose=True,
)
Memory handles: cross-session recall, entity tracking, context from prior tasks. Knowledge handles: document retrieval, tool discovery, RAG queries. Delegation handles: agent-to-agent task handoff when an agent hits its capability boundary.
Part 4: Putting It Together — A Complete Research Crew
Here's a full working example that combines all three features:
import os
from crewai import Agent, Task, Crew, Process, Memory, LLM, Knowledge
from crewai.knowledge.source.string_knowledge_source import StringKnowledgeSource
from crewai_tools import SerperDevTool, ScrapeWebsiteTool, FileReadTool
# ---- Tools ----
search_tool = SerperDevTool()
scrape_tool = ScrapeWebsiteTool()
read_tool = FileReadTool()
# ---- Knowledge Source (Tool Catalog for RAG) ----
tool_catalog = StringKnowledgeSource(
content="""
Tool: SerperDevTool — Google search via Serper API. Best for finding current web information.
Tool: ScrapeWebsiteTool — Extract full text content from a URL. Use after finding a relevant link.
Tool: FileReadTool — Read content from local files. Use for accessing saved data and documents.
"""
)
# ---- Agents ----
researcher = Agent(
role="Senior Researcher",
goal="Find accurate, well-sourced information on the assigned topic",
backstory=(
"You are a senior research analyst with deep expertise in "
"gathering and verifying information from multiple sources."
),
tools=[search_tool, scrape_tool],
allow_delegation=False,
verbose=True,
)
analyst = Agent(
role="Data Analyst",
goal="Extract patterns and insights from research data",
backstory=(
"You are a data analyst who transforms raw research into "
"structured insights and actionable recommendations."
),
tools=[read_tool],
allow_delegation=False,
verbose=True,
)
writer = Agent(
role="Lead Writer",
goal="Synthesize research and analysis into clear, publishable content",
backstory=(
"You are a senior technical writer who produces polished, "
"well-structured reports. You delegate research gaps to the Researcher."
),
allow_delegation=True,
allowed_agents=[researcher, analyst],
max_iter=8,
verbose=True,
)
# ---- Tasks ----
research_task = Task(
description=(
"Research the current state of AI agent frameworks in 2026. "
"Focus on: CrewAI, LangChain, AutoGen, and OpenAI Agents SDK. "
"Find adoption numbers, key features, and recent developments."
),
expected_output=(
"A detailed research brief with bullet points, statistics, and sources."
),
agent=researcher,
)
analysis_task = Task(
description=(
"Analyze the research data. Which frameworks are gaining traction? "
"What are the key differentiators? Identify 3 major trends."
),
expected_output=(
"A trend analysis document with 3 key findings and supporting data."
),
agent=analyst,
context=[research_task],
)
report_task = Task(
description=(
"Write a comprehensive report on the state of AI agent frameworks in 2026. "
"Include the research findings, trend analysis, and your own perspective. "
"If you need additional data, delegate to the Researcher."
),
expected_output=(
"A 600-word report formatted in markdown, ready for publication."
),
agent=writer,
context=[research_task, analysis_task],
)
# ---- Crew ----
crew = Crew(
agents=[researcher, analyst, writer],
tasks=[research_task, analysis_task, report_task],
process=Process.sequential,
memory=True,
memory_config={
"embedder": {
"provider": "openai",
"config": {"model": "text-embedding-3-small"}
}
},
knowledge_sources=[tool_catalog],
embedder={
"provider": "openai",
"config": {"model": "text-embedding-3-small"}
},
verbose=True,
)
# ---- Run ----
result = crew.kickoff()
print("\n=== FINAL REPORT ===\n")
print(result)
What you'll see during execution:
[Memory] Initializing memory system with embedder: openai/text-embedding-3-small
[Senior Researcher] Task: Research the current state of AI agent frameworks in 2026
[Senior Researcher] Using tool: SerperDevTool — "AI agent frameworks 2026 adoption"
[Senior Researcher] Found 8 relevant sources. Scraping top 3...
[Data Analyst] Analyzing research data for patterns...
[Data Analyst] Identifying 3 key trends from 12 data points...
[Lead Writer] Synthesizing research and analysis into report...
[Lead Writer] Need adoption statistics for Open Agents SDK. Delegating to Senior Researcher...
[Senior Researcher] Delegated task: Find Open Agents SDK adoption numbers
[Senior Researcher] Result: 340K monthly downloads, 12K GitHub stars
[Lead Writer] Continuing report with complete data...
=== FINAL REPORT ===
[Full report output]
Failure point: If you see [Memory] No embedder configured, using default, make sure OPENAI_API_KEY is set. Without it, the memory and knowledge systems fall back to a no-op mode where storage calls are silently dropped.
Migration Guide: CrewAI 2.x to 3.0
Most CrewAI 2.x code runs on 3.0 with no changes. The breaking changes are minimal:
1. Memory Config Moved to Crew Level
Before (2.x):
agent = Agent(..., memory=True)
After (3.0):
crew = Crew(..., memory=True)
Memory is now configured at the Crew level and propagates to all agents. Agent-level memory=True still works but logs a deprecation warning.
2. Knowledge Sources Replace Manual RAG Tool Config
Before (2.x):
from crewai_tools import RagTool
rag = RagTool(source="./docs")
agent = Agent(tools=[rag])
After (3.0):
from crewai.knowledge.source.string_knowledge_source import StringKnowledgeSource
source = StringKnowledgeSource(content="...")
crew = Crew(knowledge_sources=[source])
Both approaches still work, but Knowledge Sources are the recommended path in 3.0.
3. allowed_agents Replaces Unrestricted Delegation
Before (2.x): Any agent with allow_delegation=True could delegate to any other agent, which caused unpredictable delegation chains.
After (3.0): Set allowed_agents=[agent_a, agent_b] to scope delegation. Without it, delegation defaults to all agents in the crew (same as 2.x behavior), but you'll see a deprecation notice encouraging you to set it explicitly.
4. LanceDB Is the New Default Vector Store
CrewAI 3.0 defaults to LanceDB for vector storage instead of ChromaDB. LanceDB handles concurrent writes better (serialized with shared lock + retry) and supports larger datasets. ChromaDB is still available via configuration:
memory_config={
"storage": {
"provider": "chromadb",
}
}
What's Next
- Run the complete example above with your own API keys and topic. Swap the research topic for something relevant to your work and observe how memory carries context between tasks.
- Experiment with custom Knowledge Sources. Point the knowledge system at your own documentation, API references, or product specs. The tool RAG pattern works with any structured content.
- Try the hierarchical process. Replace
Process.sequentialwithProcess.hierarchicaland let the manager agent dynamically assign tasks. Compare execution quality and iteration count. - Read the official docs. The CrewAI Memory docs cover advanced configuration (custom embedders, storage backends, memory inspection CLI).
- Browse the changelog. The CrewAI changelog has the full list of what shipped in each 3.0 release candidate.
# Inspect stored memory from the terminal
crewai memory list
crewai memory search "what did we learn about RAG?"
Related Articles
Permission-Gated Tool-Use: How Datasette Agent 0.3a0 Handles User Approval for Write Operations
A deep-dive tutorial on datasette-agent 0.3a0's execute_write_sql tool as a case study in safe agent tool design — covering the ask_user() approval pattern, permission gating, and how to build your own gated tools using the register_agent_tools hook.
Code Review Agent Blueprint
Complete code review agent that reads file trees, runs linters, checks patterns, and suggests refactors. Ready-to-run with file system access and Git integration.
Contract Review Agent Blueprint
AI agent that reviews contracts: extracts clauses, flags risks, compares versions, and generates summaries. Ready-to-run with PDF and Markdown contract input.