For AI agents: a documentation index is available at https://www.mongodb.com/docs/llms.txt — markdown versions of all pages are available by appending .md to any URL path.
Docs Menu

Add Long-Term Memory to CrewAI Agents

In this tutorial, you use the crewai-mongodb-memory package to give a CrewAI agent durable long-term memory that persists in MongoDB Atlas. The agent stores user preferences as memory records and recalls them semantically with MongoDB Vector Search, so a brand-new crew in a later session can answer correctly by retrieving learnings from a previous session.

To learn more about the MongoDB CrewAI integration, see Integrate MongoDB with CrewAI.

To complete this tutorial, you must have the following:

  • Python 3.10 or later.

  • One of the following MongoDB cluster types:

    • An Atlas cluster running MongoDB version 6.0.11, 7.0.2, or later. Ensure that your IP address is included in your Atlas project's access list.

    • A local Atlas deployment created using Python and Docker. Install atlas-local-lib-py (pip install atlas-local-lib-py) to programmatically create and manage local deployments. To learn more, see the atlas-local-lib-py repository.

    • A MongoDB Community cluster with Search and Vector Search installed.

  • A Voyage AI API key. To create an API key, see Manage Voyage AI Model API Keys.

  • A large language model (LLM) API key, such as Anthropic or OpenAI, to power the agent's inference.

The crewai-mongodb-memory backend implements CrewAI's StorageBackend protocol. It stores each memory as a record in a MongoDB collection and uses MongoDB Vector Search to retrieve relevant records semantically. The backend uses the following components:

  • MongoDBStorageBackend: Connects to your Atlas cluster, stores memory records, and runs $vectorSearch queries to recall them.

  • MemoryRecord: Represents a single memory, including its text content, a hierarchical scope such as /users/alex/preferences, optional categories, and its vector embedding.

  • embed_text(): Generates 1024-dimensional embeddings with the Voyage AI voyage-4 model for both stored documents and queries.

Because the memory lives in Atlas instead of the crew's in-process context, any crew that connects to the same cluster and scope can recall the stored memories.

Complete the following steps to build and run an agent that stores and recalls user preferences:

1
  1. Run the following commands in your terminal to create a new directory named crewai-memory-project and install the required dependencies:

    mkdir crewai-memory-project
    cd crewai-memory-project
    pip install crewai-mongodb-memory crewai "crewai[google-genai]" voyageai python-dotenv
  2. In your project, create a .env file and add the following lines:

    ATLAS_URI="<connection-string>"
    VOYAGE_API_KEY="<voyage-api-key>"
    ANTHROPIC_API_KEY="<anthropic-api-key>"
    MODEL="<anthropic-model>"

    Note

    Replace <connection-string> with the connection string for your Atlas cluster or local Atlas deployment.

    Your connection string should use the following format:

    mongodb+srv://<db_username>:<db_password>@<clusterName>.<hostname>.mongodb.net

    IMPORTANT: Ensure that your connection string includes your database user's credentials. To learn more about finding your connection string, see Connect to a Cluster via Client Libraries.

2

Create a file named main.py in your project and paste the following code:

from __future__ import annotations
import os
import sys
import time
import warnings
from dotenv import load_dotenv
load_dotenv()
warnings.filterwarnings("ignore")
from crewai import Agent, Crew, Task
from crewai.tools import tool
from crewai_mongodb_memory import MemoryRecord, MongoDBStorageBackend, embed_text
from crewai.events import CrewKickoffStartedEvent, CrewKickoffCompletedEvent, AgentExecutionCompletedEvent
from crewai.events import BaseEventListener
DEMO_DB = "crewai_mem_agent_demo"
SCOPE = "/users/alex/preferences"
MODEL = os.environ.get("ANTHROPIC_MODEL", "anthropic/claude-3-5-sonnet-latest")
EMBEDDING_MODEL = os.environ.get("VOYAGE_MODEL", "voyage-4")
# Module-level backend handle so the function tools can reach it.
_BACKEND: MongoDBStorageBackend | None = None
_SESSION_LABEL = ""
# Custom event listener for logging Crew and Agent events.
class EventListener(BaseEventListener):
def __init__(self):
super().__init__()
def setup_listeners(self, crewai_event_bus):
@crewai_event_bus.on(CrewKickoffStartedEvent)
def on_crew_started(source, event):
print(f"Crew '{event.crew_name}' has started execution!")
@crewai_event_bus.on(CrewKickoffCompletedEvent)
def on_crew_completed(source, event):
print(f"Crew '{event.crew_name}' has completed execution!")
print(f"Output: {event.output}")
@crewai_event_bus.on(AgentExecutionCompletedEvent)
def on_agent_execution_completed(source, event):
print(f"Agent '{event.agent.role}' completed task")
print(f"Output: {event.output}")
def banner(title: str) -> None:
print(f"\n{'=' * 70}\n{title}\n{'=' * 70}")
def embed(text: str, input_type: str) -> list[float]:
"""Generate embeddings with an explicit Voyage model."""
try:
return embed_text(text, input_type=input_type, model=EMBEDDING_MODEL)
except TypeError:
# Backward compatibility for older crewai-mongodb-memory versions.
return embed_text(text, input_type=input_type)
@tool("remember_preference")
def remember_preference(fact: str) -> str:
"""Store a durable user preference in MongoDB Atlas long-term memory."""
assert _BACKEND is not None
rec = MemoryRecord(
content=fact,
scope=SCOPE,
categories=["preference"],
embedding=embed(fact, input_type="document"),
)
_BACKEND.save([rec])
return f"Stored preference: {fact}"
@tool("recall_preferences")
def recall_preferences(query: str) -> str:
"""Retrieve relevant user preferences from Atlas via $vectorSearch."""
assert _BACKEND is not None
qv = embed(query, input_type="query")
hits = _BACKEND.search(qv, scope_prefix=SCOPE, limit=3)
if not hits:
return "No relevant preferences found."
for rec, score in hits:
print(f" - [{score:.3f}] {rec.content}")
return "\n".join(f"- {rec.content} (score={score:.3f})" for rec, score in hits)
def build_agent() -> Agent:
return Agent(
role="Personal Concierge",
goal="Help the user with durable long-term memory stored in MongoDB Atlas.",
backstory=(
"""You remember user preferences across sessions. When the user shares
a durable preference, call remember_preference. When a request may
depend on what you know about them, call recall_preferences first
and use the results."""
),
tools=[remember_preference, recall_preferences],
llm=MODEL,
verbose=True,
)
def run_task(description: str, expected_output: str) -> str:
"""Run a single-task crew (a fresh crew each call is a fresh session)."""
agent = build_agent()
task = Task(description=description, expected_output=expected_output, agent=agent)
crew = Crew(agent, [task])
return str(crew.kickoff())
def main() -> None:
global _BACKEND, _SESSION_LABEL
uri = os.environ.get("ATLAS_URI")
# Fast fail if the required environment variables are not set.
if not all([uri, os.environ.get("ANTHROPIC_API_KEY"), os.environ.get("VOYAGE_API_KEY")]):
print("This script needs ATLAS_URI, ANTHROPIC_API_KEY, and VOYAGE_API_KEY.")
sys.exit(1)
print(f"=== CrewAI and MongoDB Atlas long-term memory (model={MODEL}) ===")
_BACKEND = MongoDBStorageBackend(uri, database_name=DEMO_DB)
_BACKEND.delete(scope_prefix=SCOPE) # clean slate for a repeatable run
print("Ensuring Atlas Vector Search index (first build can take ~1 min)...")
if not _BACKEND.ensure_vector_index(wait=True):
print("Vector index did not become queryable in time.")
sys.exit(1)
print("Index queryable.")
# Initialize the event listener for the agent.
listener = EventListener()
# Session 1: the agent learns and stores preferences.
banner("SESSION 1 - agent stores durable preferences in Atlas")
session_1_input = (
"""The user says: 'I'm vegetarian, I avoid dairy, and I always prefer
window seats on flights.' Store each durable preference."""
)
_SESSION_LABEL = "SESSION 1"
print(f" {session_1_input}")
out1 = run_task(
description=session_1_input,
expected_output="A short confirmation of what was stored.",
)
print(f"\nAgent (session 1): {out1}")
# Atlas indexing is asynchronous, so wait until the facts are searchable.
print("\nWaiting for Atlas to index the new memories...")
for _ in range(20):
if _BACKEND.search(embed("food", input_type="query"), scope_prefix=SCOPE, limit=1):
break
time.sleep(2)
print("Memories searchable.")
# Session 2: a brand-new crew with no shared state recalls from Atlas.
banner("SESSION 2 - fresh crew answers via recall_preferences")
session_2_input = (
"""The user is booking a long flight and pre-ordering an in-flight meal.
Recommend a seat and a meal that fit their preferences."""
)
_SESSION_LABEL = "SESSION 2"
print(f" {session_2_input}")
out2 = run_task(
description=session_2_input,
expected_output="A seat and meal recommendation grounded in recalled preferences.",
)
print(f"\nAgent (session 2, brand-new crew): {out2}")
banner("Proof: direct $vectorSearch recall over stored preferences")
hits = _BACKEND.search(
embed("dietary and seating preferences", input_type="query"),
scope_prefix=SCOPE,
limit=5,
)
for rec, score in hits:
print(f" - [{score:.3f}] {rec.content}")
_BACKEND.delete(scope_prefix=SCOPE)
_BACKEND.close()
print("\nDone - CrewAI stored and recalled preferences via MongoDB Vector Search.")
if __name__ == "__main__":
main()

This script does the following:

  • Connects to your Atlas cluster with MongoDBStorageBackend and ensures a MongoDB Vector Search index exists on the memory collection.

  • Defines two tools, remember_preference and recall_preferences, that the agent calls to store and retrieve memories.

  • Defines a CrewAI agent that uses the memory tools, then runs one crew that stores the user's preferences.

  • Runs a second, brand-new crew that recalls the stored preferences from Atlas and grounds its recommendation in them.

3

Run the following command to execute the script:

python main.py
======================================================================
SESSION 1 - agent stores durable preferences in Atlas
======================================================================
The user says: 'I'm vegetarian, I avoid dairy, and I always prefer
window seats on flights.' Store each durable preference.
Crew 'crew' has started execution!
╭────────────────────────────── 🤖 Agent Started ──────────────────────────────╮
│ │
│ Agent: Personal Concierge │
│ │
│ Task: The user says: 'I'm vegetarian, I avoid dairy, and I always prefer │
│ window seats on flights.' Store each durable preference. │
│ │
╰──────────────────────────────────────────────────────────────────────────────╯
Tool remember_preference executed with result: Stored preference: The user is vegetarian....
Tool remember_preference executed with result: Stored preference: The user avoids dairy....
Tool remember_preference executed with result: Stored preference: The user always prefers window seats on flights....
[Finalize] todos_count=0, todos_with_results=0
╭─────────────────────────── ✅ Agent Final Answer ────────────────────────────╮
│ │
│ Agent: Personal Concierge │
│ │
│ Final Answer: │
│ Stored: │
│ - The user is vegetarian. │
│ - The user avoids dairy. │
│ - The user always prefers window seats on flights. │
│ │
╰──────────────────────────────────────────────────────────────────────────────╯
Agent 'Personal Concierge' completed task
Output: Stored:
- The user is vegetarian.
- The user avoids dairy.
- The user always prefers window seats on flights.
Crew 'crew' has completed execution!
Output: Stored:
- The user is vegetarian.
- The user avoids dairy.
- The user always prefers window seats on flights.
Agent (session 1): Stored:
- The user is vegetarian.
- The user avoids dairy.
- The user always prefers window seats on flights.
Waiting for Atlas to index the new memories...
Memories searchable.
======================================================================
SESSION 2 - fresh crew answers via recall_preferences
======================================================================
The user is booking a long flight and pre-ordering an in-flight meal.
Recommend a seat and a meal that fit their preferences.
Crew 'crew' has started execution!
╭────────────────────────────── 🤖 Agent Started ──────────────────────────────╮
│ │
│ Agent: Personal Concierge │
│ │
│ Task: The user is booking a long flight and pre-ordering an in-flight │
│ meal. │
│ Recommend a seat and a meal that fit their preferences. │
│ │
╰──────────────────────────────────────────────────────────────────────────────╯
- [0.794] The user always prefers window seats on flights.
- [0.748] The user is vegetarian.
- [0.739] The user avoids dairy.
Tool recall_preferences executed with result: - The user always prefers window seats on flights. (score=0.794)
- The user is vegetarian. (score=0.748)
- The user avoids dairy. (score=0.739)...
[Finalize] todos_count=0, todos_with_results=0
╭─────────────────────────── ✅ Agent Final Answer ────────────────────────────╮
│ │
│ Agent: Personal Concierge │
│ │
│ Final Answer: │
│ I recommend booking a **window seat** for the long flight, since you │
│ prefer window seats. │
│ │
│ For your in-flight meal, choose a **vegetarian dairy-free meal**. If the │
│ airline offers specific meal codes, look for something like **VGML / vegan │
│ meal**, since that will typically meet both preferences: vegetarian and no │
│ dairy. │
│ │
╰──────────────────────────────────────────────────────────────────────────────╯
Agent 'Personal Concierge' completed task
Output: I recommend booking a **window seat** for the long flight, since you prefer window seats.
For your in-flight meal, choose a **vegetarian dairy-free meal**. If the airline offers specific meal codes, look for something like **VGML / vegan meal**, since that will typically meet both preferences: vegetarian and no dairy.
Crew 'crew' has completed execution!
Output: I recommend booking a **window seat** for the long flight, since you prefer window seats.
For your in-flight meal, choose a **vegetarian dairy-free meal**. If the airline offers specific meal codes, look for something like **VGML / vegan meal**, since that will typically meet both preferences: vegetarian and no dairy.
Agent (session 2, brand-new crew): I recommend booking a **window seat** for the long flight, since you prefer window seats.
For your in-flight meal, choose a **vegetarian dairy-free meal**. If the airline offers specific meal codes, look for something like **VGML / vegan meal**, since that will typically meet both preferences: vegetarian and no dairy.
======================================================================
Proof: direct $vectorSearch recall over stored preferences
======================================================================
- [0.770] The user always prefers window seats on flights.
- [0.768] The user is vegetarian.
- [0.754] The user avoids dairy.
Done - CrewAI stored and recalled preferences via MongoDB Vector Search.

Note

Your output might differ, as LLM responses and vector search scores can vary between runs.