Every agent framework now ships some flavor of "memory," and every vendor swears theirs is the one that will stop your agent from re-introducing itself to a user it's talked to fifty times. The trending conversation this week — Your AI Remembers Everything and Trusts All of It — points at the real failure mode: memory tools are good at storing, and bad at deciding what's still true. That's the axis I actually care about when picking a tool, not embedding dimensionality or vector store choice.
I ran four approaches against the same workload: a support agent that needs to remember user preferences, prior tickets, and corrections across sessions, with facts that occasionally contradict earlier facts ("actually I moved to Denver" after "I live in Austin"). Here's how Mem0, Zep, LangChain's memory classes, and a hand-rolled Redis setup actually behaved.
The contenders
Mem0 — a managed (or self-hosted) memory layer that sits between your LLM calls and storage. It extracts facts from conversation turns, scores them, and resolves conflicts against existing memory before writing.
Zep — session memory plus a temporal knowledge graph. It doesn't just store facts, it timestamps them and lets you query "what did we believe was true as of X."
LangChain memory (ConversationSummaryBufferMemory, VectorStoreRetrieverMemory) — not a product, a set of classes you wire to whatever vector store you already run. No extraction logic beyond what you write.
Redis + RedisVL, DIY — no framework at all. You store embeddings and metadata yourself, and write your own retrieval and conflict logic.
Where the differences actually show up
Fact extraction and conflict resolution
This is the part every vendor undersells and every homegrown system underestimates.
With Mem0, sending a new turn triggers an LLM call that extracts candidate facts, compares them against existing memory for the user, and either adds, updates, or discards:
from mem0 import Memory
m = Memory()
m.add("I moved to Denver last month, hate the traffic though", user_id="u123")
# later
results = m.search("where does the user live", user_id="u123")
# returns the Denver fact, with the Austin fact marked stale, not deleted
The "not deleted" part matters — Mem0 keeps history rather than overwriting, which is the only way to answer "did the user used to live somewhere else" later. That's real engineering, not just a wrapper around upsert.
Zep does something structurally different: it builds a temporal graph, so instead of "current fact wins," you get an edge with a validity window. That's more expressive if your agent needs to reason about when something changed, not just what's current — useful for, say, an agent that has to explain "your subscription was on the annual plan until March, then you switched." For a support bot that only needs "what's true now," that expressiveness is overhead you pay for in query complexity.
LangChain's memory classes do none of this. VectorStoreRetrieverMemory will happily return both the Austin and Denver facts with similar cosine scores, and it's on you to decide which one to trust. I've seen teams ship this to production assuming semantic similarity implies recency — it doesn't, and the failure is silent: the agent picks whichever fact happens to embed slightly closer to the query.
Redis DIY has the same problem, minus even the vector-store convenience. You get exactly the conflict resolution you write, which for most teams under deadline is "none," until a customer complaint reveals it.
Latency and cost
Mem0's extraction step is an extra LLM call per write — real latency (typically 300-800ms in my testing with a small extraction model) and real token cost. For write-heavy agents, where every turn creates memory candidates, that adds up. Mem0 mitigates this with async writes and batched extraction, but you're still paying for a second model call on top of your main completion.
Zep's graph writes are cheaper per-turn by default (no full LLM extraction unless you enable it), but querying the graph for anything beyond "latest fact" costs more at read time — graph traversal isn't as cheap as a vector similarity search.
LangChain memory and Redis DIY are the cheapest at write time because they do the least work — you're paying for embedding calls, not extraction. The cost shows up later as engineering time and, if you skip conflict resolution, as wrong answers in production.
Where each one actually wins
| Need | Pick |
|---|---|
| Managed fact extraction + conflict resolution, minimal glue code | Mem0 |
| Temporal reasoning ("what did we believe on date X") | Zep |
| Already deep in LangChain, prototyping, low memory volume | LangChain memory classes |
| Full control, existing Redis infra, team has bandwidth to own conflict logic | Redis + RedisVL |
The honest tradeoff nobody puts in the docs
Every memory tool that does extraction and conflict resolution for you is making a judgment call on your behalf about what to trust — which is exactly the failure mode this week's trending post is pointing at. Mem0's "keep history, mark stale" approach is more conservative than Zep's graph-edge model, which is more conservative than "vector similarity as truth," which is what LangChain memory and naive Redis setups default to whether you intended it or not.
If you're evaluating these for a production agent, don't benchmark retrieval latency first. Feed each one a deliberately contradictory conversation — a user correcting themselves twice — and check what the agent says a week later when asked "where do I live." That single test surfaces the actual difference between these tools faster than any latency chart, because it's the difference between a memory system and a fact dump with vectors on top.
For most teams building a support or personal-assistant agent that needs to stay right over months of conversation, that argues for Mem0 or Zep over rolling your own — the extraction and conflict-resolution logic is genuinely hard to get right, and it's exactly the kind of undifferentiated engineering worth buying instead of building.
Top comments (1)
The contradictory-conversation test is the right one, and I would add a second question to it: when the agent answers "Denver", can it also show that it once believed "Austin" and why it stopped? A system that returns the right current fact but has silently discarded the old one passes the test and still cannot be audited.
The other thing the write-time extraction approach hides is that the extractor is a model too. When it marks the wrong fact stale, nothing in the pipeline goes red. The JSON is valid, the store is consistent, the answer is just wrong. That is the same class of failure as the dict-key bug from your earlier post, one layer down. The read-time approach, returning both facts and letting the answering model see the conflict, is uglier, but the mistake is at least visible in the output instead of buried in the store.