Picking an agent memory tool is mostly a question of what shape your memories have: loose notes, extracted facts about a user, or relationships that change over time. Cognee, Mem0, Letta, Zep (via Graphiti), LangMem and the Anthropic memory tool each fit one of those shapes. This post compares them on what they store, how they retrieve, and what it costs you to run them. For the underlying concepts (working, episodic, semantic memory) read AI agent memory architectures first; I won't repeat them here. For what to write down and when to delete it, see AI agent memory best practices.
Everything below was checked against vendor pages on 2026-10-30. Where a page didn't say, I say so. There are no benchmark numbers here on purpose: I didn't run any, and vendor numbers are marketing until you reproduce them on your data.
What does each AI agent memory tool actually do?
Cognee turns documents and text into a knowledge graph plus vector index. Its README describes four operations: remember, recall, improve and forget. It's Apache-2.0, Python 3.10 to 3.14, and can run locally, self-hosted or on Cognee Cloud. Source: github.com/topoteretes/cognee.
Mem0 is a memory layer that extracts facts from conversations and retrieves them for later prompts. Its README describes semantic, BM25 keyword and entity-based retrieval. It's Apache-2.0, installs with pip install mem0ai, and comes as a library, a self-hosted server (Docker Compose) or a hosted platform. Source: github.com/mem0ai/mem0.
Letta (formerly MemGPT) builds stateful agents whose memory the agent edits itself through tools. Per its docs, memory blocks are strings pinned into the system prompt, and agents change them with memory tools; there's also an archival store the agent searches on demand. Apache-2.0. Source: docs.letta.com. Caveat: the letta-ai/letta README I fetched now centres on a coding agent called letta-code (npm install -g @letta-ai/letta-code, with a letta server command), and it did not describe the memory model. I did not verify how to self-host the older Python server, so check the current docs before you plan around it.
Zep and Graphiti. Graphiti is an open-source Python framework that builds a temporal graph: facts carry validity windows, so a changed fact is invalidated rather than deleted. Zep is the managed product built on it. The Zep repo says Community Edition is no longer supported and its code moved to a legacy/ folder. Sources: Graphiti, Zep repo.
LangMem is a library from LangChain that extracts and consolidates memories, with a native fit for LangGraph's store. It works with any storage, per its docs. Source: langchain-ai.github.io/langmem. If you're not on LangGraph yet, the LangGraph stateful agents post covers the store side.
Anthropic memory tool is not a library. It's a tool type, memory_20250818, where Claude issues file commands against a /memories directory and your application executes them. Source: Anthropic docs.
OpenAI has, as far as I found, no standalone memory API. The Agents SDK has sessions for conversation history. More on that below.
How do they store and retrieve memory?
| Tool | Storage model | Retrieval | Who decides what to remember |
|---|---|---|---|
| Cognee | Graph plus vector plus relational metadata | Graph, chunk and hybrid search via recall | Your code calls remember |
| Mem0 | Vector store (local Qdrant by default in library mode) plus a SQLite history file | Semantic search, with keyword and entity signals per the README | An LLM extraction pass on add |
| Letta | Memory blocks in context plus a vector archival store, all persisted in a database | Blocks are always in context; archival is semantic search by tool call | The agent, via memory tools |
| Zep / Graphiti | Temporal graph in Neo4j, FalkorDB or Neptune | Hybrid: embeddings, BM25 and graph traversal | Ingestion extracts entities and facts |
| LangMem | Whatever you plug in; LangGraph store natively | Store search, called by the agent (hot path) or a background manager | Either the agent or a background extractor |
| Anthropic memory tool | Files under /memories in storage you choose | Claude lists and reads files itself | Claude, guided by your prompt |
| OpenAI Agents SDK sessions | Message history rows (SQLite, Redis, SQLAlchemy and others) | Replay of history for a session ID | Nothing is extracted |
Notes behind the table:
- Mem0 defaults, per its open-source overview: OpenAI
gpt-5-minifor the LLM,text-embedding-3-smallfor embeddings, Qdrant at/tmp/qdrant, history in~/.mem0/history.db. The self-hosted server defaults to Postgres with pgvector instead. Note that/tmpis a poor home for anything you want to keep. - Graphiti supports Neo4j 5.26, FalkorDB 1.1.2 and Amazon Neptune; Kuzu is marked deprecated in its README. It defaults to OpenAI for inference and embeddings, and warns that small models can fail ingestion because they don't produce valid structured output.
- LangMem's docs example uses
InMemoryStore, which loses data on restart. The page recommends a database-backed store such asAsyncPostgresStorefor production. - Zep's own concepts page describes facts as graph edges and offers a high-level
thread.get_contextcall plus lower-level graph search methods. I couldn't confirm hosting options beyond the Cloud product from that page.
Install, license and hosting facts
| Tool | Install | License | Needs an LLM key by default? | Hosting options |
|---|---|---|---|---|
| Cognee | pip install cognee (1.6.6 on PyPI, 2026-10-30) | Apache-2.0 | No for local text ingestion and retrieval (I ran it) | Local, self-hosted, Cognee Cloud |
| Mem0 | pip install mem0ai | Apache-2.0 | Yes, OpenAI by default | Library, Docker server, hosted platform |
| Letta | Check current docs (see caveat above) | Apache-2.0 | Not stated on pages I read | Self-hosted, Letta Cloud |
| Graphiti | pip install graphiti-core | Apache-2.0 | Yes, OpenAI by default; Anthropic, Gemini, Groq and OpenAI-compatible servers supported | You run it plus a graph database |
| Zep | Managed service | Not verified | n/a | Zep Cloud (managed) |
| LangMem | pip install -U langmem | Not shown on the docs page; check the repo | Yes, an LLM provider key | Runs in your app; LangGraph Platform includes the store |
| Anthropic memory tool | Part of the Messages API; SDK helpers in Python, TypeScript, C#, Java | n/a (API feature) | Yes, an Anthropic key | Storage is yours, client-side |
One detail from the Anthropic page worth knowing: the Python and TypeScript SDKs ship BetaLocalFilesystemMemoryTool, a ready-made local-filesystem handler, and the docs say your handler must reject any path outside /memories because a path like /memories/../../secrets.env is a real traversal risk. That's your code's job, not the model's.
Which one should you pick? A decision table
| Your situation | Start with | Why |
|---|---|---|
| A coding or ops agent that resumes work across sessions | Anthropic memory tool with a progress-log file | Plain files are inspectable and diffable; no extra infrastructure |
| A support or sales assistant that must remember user preferences | Mem0 | Extraction and dedup of per-user facts is its core use |
| Facts that change ("moved cities", "changed plan") and you need to know what was true when | Graphiti, or Zep Cloud if you'd rather not run a graph database | Temporal validity windows are the design goal |
| An agent that should curate its own long-running state | Letta | The agent edits its own memory blocks |
| Already on LangGraph, want to stay in one framework | LangMem on a Postgres-backed store | Native store integration |
| Documents and notes you want queried as a graph, and you want to try it offline | Cognee | Ran locally with no key in my test below |
| You use OpenAI models and only need chat history across runs | Agents SDK sessions | Built in; it is history, not memory |
My opinion, flagged as such: don't adopt a graph tool because graphs sound smart. If your memories are "user prefers INR" and "user is on Pro", a fact store or even a table does the job. The GraphRAG post explains when relationships actually earn their cost.
Can you try one locally with no API key?
Cognee, yes. Here's what I ran on 2026-10-30 on macOS, Python 3.14.3, in a fresh virtualenv.
python3 -m venv venv
venv/bin/pip install cognee
import asyncio
import cognee
async def main():
await cognee.remember(
"Priya prefers invoices in INR and is on the Pro plan.",
dataset_name="local_quickstart",
)
results = await cognee.recall(
"What currency does Priya prefer?",
datasets=["local_quickstart"],
)
for result in results:
print(result)
asyncio.run(main())
The code follows the quickstart in Cognee's README, with my own sentence. No LLM_API_KEY was set. Installed version: cognee 1.6.6 (Apache-2.0 per package metadata). The first run took about five minutes of wall clock time from process start to output, which I assume includes first-time local model downloads; I didn't isolate that, so treat it as "slow the first time".
The recall result, trimmed:
kind='graph_completion' search_type='HYBRID_COMPLETION' ...
Context:
## Relevant passages
Priya prefers invoices in INR and is on the Pro plan.
## Relevant entities
### priya (person)
### inr (money)
## Related facts
- money: INR; person: Priya made from Priya prefers invoices in INR and is on the Pro plan..
Three observations from the actual run:
- No LLM wrote an answer. With no key,
recallreturned the assembled context (passages, entities, facts) inside the result's text, not a sentence saying "INR". You'd pass that context to your own model. The log showed graph extraction done by a function namedextract_graph_and_summarize_with_gliner, a local extractor. - The extractor is shallow. It found a person (Priya) and a money entity (INR) but did not make "Pro plan" an entity. The fact survived only because the raw passage was returned alongside. If you rely on graph facts, check what got extracted.
- It persisted across processes. A second script that only called
recallfor "What plan is Priya on?" returned the same passage. By default the databases landed inside the virtualenv'ssite-packages/cognee/.cognee_system/databases(a SQLite file, a LanceDB folder and a graph database file), so set a proper data path before you put anything real in it.
I did not run Mem0, Letta, Graphiti, Zep or LangMem. Mem0 and Graphiti want an OpenAI key by default; running them against a local model is possible per their docs but I haven't tried it, so I'm not giving you code for them that I haven't run.
What about the Anthropic memory tool and OpenAI?
The Anthropic memory tool is the lowest-magic option, which is its appeal. You declare {"type": "memory_20250818", "name": "memory"} in tools, and Claude then issues view, create, str_replace, insert, delete and rename commands. The docs say the API automatically adds a system-prompt instruction telling Claude to view its memory directory first and to assume its context might reset at any moment. The page lists it as generally available and eligible for zero data retention, with the caveat that storage is client-side so retention is your responsibility. I didn't run it because it needs an Anthropic key; the code on the docs page is the reference.
Pair it with caps: track file sizes, expire stale files, and strip sensitive data before writing, all of which the docs list as your responsibility. For the context-budget side of the same problem, see Claude context compaction.
For OpenAI, the Agents SDK sessions page lists SQLiteSession, RedisSession, SQLAlchemySession, MongoDBSession, DaprSession, OpenAIConversationsSession and a compaction wrapper, plus EncryptedSession. All of these store conversation history for a session ID. I found no mention of extracting durable facts. If you need that with OpenAI models, you pair the SDK with one of the libraries above. ChatGPT's own consumer memory is a separate product; see how memory works in ChatGPT and Claude.
A five-minute evaluation checklist
Before committing, run this against your own data rather than trusting anyone's leaderboard.
- Write 20 real facts your agent should remember and 20 questions that need them.
- Add 5 contradictions (a moved address, a changed plan). Does the tool return the newest fact, or both?
- Add 5 questions whose answer is not in memory. Does it say so, or return something plausible?
- Delete one fact. Confirm it's gone from every store (vector, graph, history file).
- Record latency and cost per write and per read, including extraction LLM calls.
- Check where the data lives on disk and who can read it.
Items 2 and 4 are where most of these tools differ, and they're the ones demos skip.



