n4nAI

How to manage memory across a CrewAI crew

A hands-on guide to CrewAI memory management across multi-agent crews: implement short-term, long-term, and entity memory with code and verification steps.

n4n Team4 min read783 words

Audio narration

Coming soon — every post will get a voice note here.

CrewAI memory management is the difference between a crew that repeats itself and one that accumulates context across tasks. In a multi-agent system, each agent needs scoped access to conversation history, persistent facts, and shared entity state without blowing up token costs or leaking irrelevant context.

Step 1: Scaffold a crew with shared memory enabled

Start by installing CrewAI and creating a minimal crew. The simplest way to give every agent in a crew a shared memory space is to set memory=True on the Crew object. This instantiates short-term and long-term memory backends that all agents read from and write to.

pip install crewai==0.30.0  # or latest
from crewai import Agent, Crew, Task

researcher = Agent(
    role="Researcher",
    goal="Find facts about the topic",
    backstory="You search diligently",
    allow_delegation=False,
)

writer = Agent(
    role="Writer",
    goal="Draft a report from findings",
    backstory="You write concisely",
    allow_delegation=False,
)

task1 = Task(description="Research X", agent=researcher)
task2 = Task(description="Write about X", agent=writer)

crew = Crew(
    agents=[researcher, writer],
    tasks=[task1, task2],
    memory=True,  # shared crew memory
    verbose=True,
)

When memory=True is set at the crew level, agents do not get isolated buffers. They share a short-term store (recent interactions) and a long-term store (persisted across runs if backed by durable storage). If you need per-agent isolation, set memory=True on individual Agent instances instead.

Step 2: Configure the embedder for memory retrieval

CrewAI memory uses embeddings to retrieve relevant past interactions. By default it calls OpenAI’s embedding API. If you self-host or use a gateway, pass an embedder config. This is also where you can route through an OpenAI-compatible inference gateway—n4n.ai exposes one endpoint for 240+ models and forwards provider cache-control hints, which complements CrewAI’s memory by avoiding re-embedding static context.

crew = Crew(
    agents=[researcher, writer],
    tasks=[task1, task2],
    memory=True,
    embedder={
        "provider": "openai",
        "config": {
            "model": "text-embedding-3-small",
            "api_base": "https://api.n4n.ai/v1",  # OpenAI-compatible
            "api_key": "YOUR_KEY",
        },
    },
)

If you omit embedder, CrewAI falls back to the OPENAI_API_KEY env var. For on-prem, swap the provider to ollama or huggingface and adjust the config.

Step 3: Control short-term memory scope

Short-term memory in CrewAI is a RAG buffer over the crew’s recent messages. Left untuned, it retrieves the top-k most similar exchanges to stuff into the prompt. That can confuse agents if unrelated tasks share keywords.

You can constrain scope by setting memory_config on the crew or agent. The two levers that matter: retrieval limit and score threshold.

crew = Crew(
    agents=[researcher, writer],
    tasks=[task1, task2],
    memory=True,
    memory_config={
        "short_term": {
            "retriever": "recent",   # or "semantic"
            "top_k": 5,
            "score_threshold": 0.7,
        }
    },
)

retriever="recent" disables semantic search and just feeds the last N turns—cheap and predictable. semantic uses the embedder. Set top_k low (3–5) for narrow tasks; raise it for research crews that need broad recall.

Step 4: Persist long-term memory across process restarts

Default crew memory uses an in-memory SQLite store that dies with the process. For durable cross-session memory, point the long-term backend at a file or Postgres.

from crewai.memory.storage.ltm_sqlite_storage import LTMSQLiteStorage

storage = LTMSQLiteStorage(db_path="./crew_ltm.db")

crew = Crew(
    agents=[researcher, writer],
    tasks=[task1, task2],
    memory=True,
    memory_config={
        "long_term": {
            "storage": storage,
        }
    },
)

Now facts the crew learns in run one are available in run two. Inspect the DB with sqlite3 crew_ltm.db ".tables" to confirm rows in long_term_memory.

If you run multiple crews on one machine, give each a distinct db_path unless you intentionally want them to share long-term memory.

Step 5: Enable entity memory for structured facts

Entity memory extracts named entities (people, organizations, dates) from conversations and stores them as key-value pairs. It is essential when later tasks need “what was the client’s name?” without re-reading transcripts.

Enable it per agent or crew-wide:

writer = Agent(
    role="Writer",
    goal="Draft a report from findings",
    backstory="You write concisely",
    memory=True,
    entity_memory=True,  # capture entities
)

When entity_memory=True, CrewAI runs a lightweight extraction step after each agent action. The extracted entities are queryable via the agent’s entity_memory attribute:

print(writer.entity_memory.get("client_name"))

Do not enable entity memory for agents that handle free-form creative text only—extraction adds latency and token spend for little gain.

Step 6: Verify memory is actually being used

A crew that claims to have memory but ignores it is worse than no memory. Verify with a two-task test where task two depends on a fact from task one, and the agents cannot see each other’s prompts directly.

task1 = Task(
    description="State the secret code: 42. Remember it.",
    agent=researcher,
)
task2 = Task(
    description="What was the secret code from the earlier task? Output only the number.",
    agent=writer,
)

crew = Crew(agents=[researcher, writer], tasks=[task1, task2], memory=True)
result = crew.kickoff()
assert "42" in result.raw, "Memory failed to propagate"

If the assertion fails, check: (1) memory=True is set at the right level, (2) embedder API is reachable, (3) score_threshold isn’t filtering out the only relevant memory. Lower threshold to 0.3 temporarily during debugging.

For long-term persistence, run the crew once, then re-instantiate with the same db_path and a task that queries an old fact. If it answers correctly, storage works.

Step 7: Avoid common memory leaks

Memory accumulates. In long-running crews, the SQLite file grows and semantic retrieval slows. Three practices keep it sane:

  • Rotate DB files per project phase: db_path=f"crew_{phase}.db".
  • Periodically delete low-value rows: DELETE FROM long_term_memory WHERE created_at < datetime('now','-30 days');
  • Disable entity_memory on agents that don’t need it.

Also watch token billing. Each retrieved memory block is injected into the prompt. With top_k=10 and verbose backstories, you can triple input tokens. Set top_k based on measured need, not guesswork.

Verify success

You now have a crew where agents share short-term context, persist facts across restarts, and extract entities. Success criteria:

  1. Agent B answers a question whose answer was produced only by Agent A in a prior task.
  2. After restarting the Python process and reusing the SQLite path, the crew recalls a fact from the previous run.
  3. writer.entity_memory.get("some_key") returns a value set by researcher.

If those hold, your CrewAI memory management is correct. If not, trace the verbose=True logs—CrewAI prints which memory chunks it retrieves before each LLM call.

That’s the full pipeline. Memory in multi-agent systems is not magic; it’s a storage and retrieval problem with tight token budgets. Treat it like any other stateful service.

Tagscrewaiai-agent-memoryai-agents

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All crewai multi-agent systems posts →