Short version: LangGraph's Store and Mem0 solve the same thing — long-term, cross-session agent memory, the tier where a fact from one conversation resurfaces in another. The difference the comparison tables miss is who decides what to remember. The Store is a primitive: it writes exactly what you tell it. Mem0 is a layer: it reads the conversation and extracts the facts for you. That one distinction drives cost, control, and lock-in — so decide on that, not on feature checklists.

Build: LangGraph StoreBuy: Mem0
Who extractsyouan LLM pass
Writestore.put(ns, key, val)memory.add(msgs, user_id)
Data lives inyour Postgresyour DB or Mem0 cloud
LicenseMITApache-2.0

Build: the Store writes exactly what you tell it#

LangGraph's long-term memory is a BaseStore — a namespaced key-value store with optional vector search built in (BaseStore source). You compile it alongside the checkpointer, then write and read inside your nodes:

from langgraph.store.memory import InMemoryStore

store = InMemoryStore(index={"embed": embed_fn, "dims": 1536})
graph = builder.compile(checkpointer=checkpointer, store=store)

# YOU decide what's worth keeping, and write it verbatim:
store.put(("users", user_id, "facts"), key="tz",
          value={"text": "Prefers UTC; based in the EU."})

# Read it back by meaning, in any future thread:
store.search(("users", user_id, "facts"),
             query="what timezone?", limit=3)

The defining property is right there in put(): nothing is inferred. The store holds the exact object you passed. That's the build cost — you write the "is this worth remembering?" logic yourself — and the build payoff: deterministic memory, a schema you control, and the data sitting in your own Postgres (swap InMemoryStore for the Postgres store in production). It's MIT-licensed and, if you're already on LangGraph, it's zero new infrastructure — the same box that runs your checkpointer runs this.

Buy: Mem0 decides what to remember for you#

Mem0 collapses the same tier into two calls, and the first one does the work you'd otherwise write yourself (Mem0 repo):

from mem0 import Memory

memory = Memory()
# add() runs an LLM pass that EXTRACTS the salient facts, then stores them:
memory.add(conversation_messages, user_id="alice")
memory.search(query="any dietary limits?",
              filters={"user_id": "alice"}, top_k=3)

You hand add() raw messages; it returns having decided what mattered and saved that. For a team that just wants "memory that works," this is the appeal — you skip the extraction layer entirely, and it's framework-agnostic, so the same memory follows an agent whether it's built on LangGraph, a bare loop, or something else. It's Apache-2.0 and runs three ways: a library (pip install mem0ai), a self-hosted server, or a managed cloud.

Two things to price in before you lean on it. First, the extraction isn't free — every add() spends an LLM call on top of the embedding, so a chatty agent that calls add() each turn pays a per-write token tax the Store doesn't have. Reads and writes to memory already cost differently; Mem0 tilts the write side further. Second, the 2026 managed platform defaults to single-pass, ADD-only extraction — memories accumulate and nothing is overwritten. That's simple and fast, but if your domain has facts that supersede each other ("moved from Berlin to Lisbon"), accumulation is how a store slowly fills with contradictions, one of the ways agent memory rots in production. Needing a timeline is the signal to look at a temporal-graph layer instead — the Mem0 vs. Zep vs. Letta split turns on exactly this.

The decision, in one line each#

If you're still deciding whether you need a long-term store at all — versus just retrieving from your documents at query time — that's the agent memory vs. RAG question, and it's worth answering before either of these. And if the answer is "one agent, one box, ship today," you may not need a service or a framework primitive: persistent memory in one SQLite file is the third door. Build, buy, or one file — decide on who extracts, not on the feature grid.