# Persistent memory for CrewAI 1.x: keep crew memory across runs and redeploys

*Disclosure: I'm Mohammed, founder of MemorySync. I drafted this article with AI assistance and reviewed it for accuracy against the official MemorySync and CrewAI docs. The code is adapted from the docs and I haven't run it.*

CrewAI 1.x replaced its old short-term, long-term and entity memory classes with one `Memory` class. Turn it on with `memory=True` and the crew does the work for you: after each task it extracts discrete facts from the output and stores them, and before each task the agent recalls relevant context and injects it into the prompt ([CrewAI memory docs](https://docs.crewai.com/en/concepts/memory)).

The part that catches people is where that memory lives. By default it's LanceDB, stored under `./.crewai/memory` (or `$CREWAI_STORAGE_DIR/memory`). That's fine on a laptop. It's a problem when:

- your crew runs in a container whose filesystem is thrown away on every deploy,
- two services run the same crew and should learn from each other, or
- you want each of your customers to have their own memory, not one shared folder.

CrewAI's answer is a pluggable storage layer: implement the `StorageBackend` protocol and pass it to `Memory(storage=...)`. This tutorial uses MemorySync's backend for that, plus the two extras the package ships: agent tools and a context helper for task descriptions.

## What you'll set up

1. A crew whose automatic save and recall loop runs against a hosted store instead of local files.
2. A clear choice of who owns the memory: one crew, or one end user.
3. Optional: tools so an agent can decide to store or look something up itself.
4. Optional: a recall block you paste into a task description.

## Install

```bash
pip install crewai-memorysync
```

Per the [MemorySync CrewAI guide](https://docs.memorysync.io/guides/crewai?utm_source=hashnode&utm_medium=social&utm_campaign=pivot_oct19&utm_content=crewai-persistent-memory-memorysync), it needs CrewAI 1.0+ (the unified Memory system) and Python 3.10+. On an older CrewAI the import fails with a message naming the fix. Set `MEMORYSYNC_API_KEY` in your environment, or pass `api_key` explicitly, and keep it in server-side code.

One thing that doesn't change: CrewAI still runs its own LLM analysis and its own embedder. The backend receives a query embedding from CrewAI, so whatever embedder you've configured (OpenAI's `text-embedding-3-large` by default, per CrewAI's docs) still needs its credentials.

## Step 1: point the crew's memory at the backend

```python
from crewai import Agent, Crew, Task
from crewai.memory import Memory
from crewai_memorysync import MemorySyncStorage

crew = Crew(
    agents=[researcher, writer],
    tasks=[research_task, write_task],
    memory=Memory(
        storage=MemorySyncStorage(user_id="support-crew"),
    ),
)

crew.kickoff()
```

That's the whole change. You don't add search-and-paste steps to your tasks: CrewAI keeps driving saves and recalls, and the records now go to MemorySync. Per the guide, they survive redeploys and are shared by every service that uses the same `user_id`.

If you'd rather not pass the backend around, register it once at startup and refer to it by name:

```python
from crewai.memory import Memory
from crewai_memorysync import register_memorysync

register_memorysync(user_id="support-crew")   # once, at startup

memory = Memory(storage="memorysync")          # anywhere in the process
```

The guide notes that unknown storage names still go to CrewAI's built-ins, so `storage="lancedb"` elsewhere in the same process keeps working.

## Step 2: decide who owns the memory

`user_id` is the memory owner, and it's the most important choice in this setup:

- **One id per crew** (like `"support-crew"` above) gives the crew a shared knowledge base. Everything any run learns is available to the next run, on any machine.
- **One id per end user** gives each customer their own memory. A crew working for customer A doesn't see what it learned for customer B.

My rule of thumb: if the crew learns facts about *a person*, use that person's id, taken from your own authenticated user record. If it learns facts about *the work* (your stack, your product, your process), a crew-level id is the right shape. Don't use a session or run id here. A new id is a new, empty memory.

## Step 3: check the loop end to end

You normally never call recall yourself, because agents recall as they work. But after a first run it's worth checking that something was actually stored:

```python
matches = memory.recall("what stack does the team use?", depth="shallow")
for match in matches:
    print(match.score, match.record.content, match.record.scope)
```

According to the guide, scope prefixes, category and metadata filters and scores behave like the built-in LanceDB backend, and records keep their scopes (for example `/agent/researcher`), categories, importance and sources through a round trip. If you're debugging, `attach_memory_logging` logs CrewAI's memory save and query events as they happen.

Two behaviours worth knowing before production:

- **Replays don't double-store.** CrewAI runs saves on a background executor, so replays happen. The guide says saving an identical record again stores nothing twice, and an update replaces the previous version in place.
- **Failures are loud.** CrewAI's executor silently drops `RuntimeError`, so the package never raises one. Failures surface as typed errors instead of saves that quietly vanish.

## Step 4 (optional): let an agent manage memory itself

The automatic loop covers most cases. When you want an agent to explicitly decide "remember this" or "look that up", give it the five tools:

```python
from crewai import Agent
from crewai_memorysync import create_memorysync_tools

agent = Agent(
    role="Personal assistant",
    goal="Help using what you know about the user",
    backstory="You remember the user's preferences.",
    tools=create_memorysync_tools(user_id="customer-7"),
)

# For agents you don't fully trust: search and list only
read_only_tools = create_memorysync_tools(user_id="customer-7", read_only=True)
```

The tools are `add_memory`, `search_memory`, `list_memories`, `update_memory` and `delete_memory`. They never raise: errors come back as readable strings, so a tool failure can't abort a task. Memory text can't be edited, so `update_memory` changes tags and importance only. They're the same names and behaviour as the MemorySync LangChain tools, which helps if an agent moves between frameworks.

## Step 5 (optional): put known context in a task description

For a task that should start from what you already know about a user, there's a one-line helper:

```python
from crewai import Task
from crewai_memorysync import get_crew_context

context = get_crew_context("travel preferences", user_id="customer-7")

task = Task(
    description=f"Plan the trip.\nKnown preferences:\n{context or 'None yet.'}",
    expected_output="An itinerary honouring every stated preference.",
    agent=planner,
)
```

It returns an empty string for a new user. That's a normal state, not an error, so give it a fallback like the `or 'None yet.'` above (my addition to the guide's example).

## Resetting memory without wiping everything

`crew.reset_memories()` can end up calling `reset()` with no scope, which would erase everything that `user_id` owns. The backend refuses that by default:

```python
storage = MemorySyncStorage(user_id="support-crew")

storage.reset()                      # refused: raises ValueError mentioning allow_full_reset
storage.reset("/agent/researcher")   # scoped resets always work

# Opt in only when a full wipe is really intended:
MemorySyncStorage(user_id="support-crew", allow_full_reset=True).reset()
```

And if a delete removes nothing while matching records exist, it raises `MemorySyncDeleteRefusedError` instead of pretending it worked. For a crew that handles personal data, I'd want that to be loud too.

## Recap

1. `pip install crewai-memorysync` and set `MEMORYSYNC_API_KEY`.
2. `Memory(storage=MemorySyncStorage(user_id=...))` on your crew, or `register_memorysync()` once.
3. Pick the owner: one id per crew to share, one per end user to keep memory separate.
4. Check a first run with `memory.recall(...)`.
5. Add tools or `get_crew_context` only where an agent or task needs them.

## Sources

- MemorySync CrewAI Memory guide: docs.memorysync.io/guides/crewai
- CrewAI memory docs: docs.crewai.com/en/concepts/memory
- PyPI: crewai-memorysync 1.1.0

## Try it

If you want to see the save, reload and recall loop before wiring up a crew, the MemorySync playground runs it in the browser with each API call shown: [memorysync.io/try](https://memorysync.io/try?utm_source=hashnode&utm_medium=social&utm_campaign=pivot_oct19&utm_content=crewai-persistent-memory-memorysync).

## Related tutorials

- [Add persistent memory to a LlamaIndex agent](https://memorysync.hashnode.dev/llamaindex-agent-persistent-memory)
- [LangGraph memory explained: checkpointer vs store](https://memorysync.hashnode.dev/langgraph-checkpointer-vs-store-cross-thread-memory)
- [Persistent memory for a LangChain chatbot after LangChain 1.0](https://memorysync.hashnode.dev/langchain-persistent-memory-memorysync)

