Skip to content
xmemoryxmemory Website

xmemory vs LangMem and the LangGraph Store

If you build on LangGraph you already have long-term memory primitives: a Store that keeps JSON documents under namespaces and keys with semantic search, and LangMem, which adds tools and managers that extract memories from conversations and keep them consolidated. We like this stack and xmemory integrates with it. The question this page answers is a narrower one: when the memory has to behave as state, where should the logic that decides what to insert, update or delete actually live? In LangMem that logic is the model’s, prompted well, on every turn. In xmemory it is a schema the instance owns, executed by the engine.

Documentation reviewed 23 October 2026: LangGraph long-term memory, LangMem conceptual guide and quickstart; on our side How xmemory works, XMD, the API and the LangChain integration.

The Store is deliberately simple: put a JSON document under a namespace tuple such as ("acme", user_id, "preferences") and a key, get it back, or search a namespace with filters and a natural-language query ranked by embedding similarity. Namespaces carry tenancy; the backend is in-memory for development and database-backed in production.

LangMem builds the memory behaviour on top. You describe memories as Pydantic models, and a memory manager runs a model over conversation turns to extract instances, either in the hot path or in the background. For a profile it keeps one document per user and updates it in place. For a collection it keeps many narrow documents and must, in LangChain’s own words, “reconcile new information with previous beliefs”, inserting, updating or removing existing items. create_manage_memory_tool and create_search_memory_tool let the agent do the same by hand. Prompt optimisers turn feedback into revised instructions, which LangMem calls procedural memory.

The design is honest about its hard part. The LangGraph memory guide notes that profiles get error-prone to regenerate as they grow, and that with collections “models may over-insert or over-update”, and recommends evaluation and constrained decoding to manage it.

The merge logic is a contract, not a prompt. LangMem’s schemas are Pydantic classes: they give an extraction its shape and field types. Whether a new fact updates an existing document, creates a sibling or removes an old one is decided by the model each time it runs the manager, guided by prompts and the enable_inserts / enable_updates / enable_deletes switches. An XMD schema makes that decision structural: a primary key says which record a mention belongs to, enums say which values a field may take, relation keys say how many of a thing may exist, and the engine applies those rules on every write, from every writer, before anything is stored. A declared key removes one particular discretion the guide warns about: whether to create another record for a fact the extractor has already attributed to an existing key. It does not make extraction infallible; an omitted or wrongly attributed key still lands in the wrong place, which is why the XMD reference is strict about choosing keys the text reliably states.

Records, not documents. A Store value is a JSON document, retrieved by key or by similarity. A collection of them can be searched, and a profile can be read whole; neither can be joined, counted or checked for missing values without pulling documents into the model or into your code. xmemory holds records in typed tables with SQL underneath, so “how many open incidents does this customer have” and “which accounts have no owner” are queries over all matching stored records, with rows returned for code. That is the argument of Schema as the Core of Reliability, and the Why page shows it on worked examples.

Shared enforcement is part of the service, not something you assemble. LangMem’s functions are storage-independent and can be used from any application, so a team can put a memory manager behind a shared service and route every writer through it. The point is that the team has to: the manager’s prompts, schemas and switches are the application owner’s to build, deploy and keep consistent across writers. In xmemory that enforcement is the instance’s contract. The same resolution runs whether the write comes from a LangGraph node over MCP, a CLI, a batch job or another team’s service, because the rules live with the data rather than with any one caller.

The schema evolves without re-prompting. Changing what a LangMem profile holds means changing the Pydantic model and the extraction behaviour together, then dealing with the documents already written. xmemory treats the schema as a versioned artefact: validate a candidate, preview the migration, apply it, with the limits on backfill and key changes stated once. Schema Evolution is the long version.

On evidence. We have not run LangMem or a Store-based memory on our benchmarks and will not quote numbers we do not have. Our four-domain and Splitwise tasks test updates, deletions, relation changes, aggregation and arithmetic over stored facts (white paper; method and numbers in Chasing AI memory SOTA), which is the territory where a declared key and a SQL-backed store should matter most. If you evaluate the two side by side, run the same write sequences through both and count duplicate inserts, incorrect updates, missed updates and incomplete query answers; then, as a separate design check, confirm that xmemory’s keys match what your workflow means by “the same fact”.

Choose the LangGraph Store and LangMem when you want memory behaviour assembled inside your own stack, the agent itself should decide what to remember, and you are comfortable owning the extraction prompts, the reconciliation behaviour and the evaluation that keeps them honest. Choose xmemory when the rules for what a memory is should be declared once and enforced for every writer, when code reads the memory as state, or when the memory outlives the graph that first wrote it. The integration guide shows a LangGraph agent doing exactly that.

A LangMem profile and collection as an XMD schema

Section titled “A LangMem profile and collection as an XMD schema”

LangMem’s guide uses a user profile (one document, updated in place) and an episodic collection (many documents, each a situation, the reasoning, and the outcome). Both map directly; the profile becomes a keyed object and the episodes become a keyless one.

xmd_version: v1
title: Assistant memory
description: What the assistant knows about each user, and the episodes it has learned from.
objects:
UserProfile:
description: >-
The current profile of one user, identified by the user id supplied with the
input. New information updates this record; there is never a second profile
for the same user.
fields:
user_id: { type: str, required: true, description: "Supplied with the input; do not invent one." }
preferred_name: { type: str, required: false }
response_style:
type: str
required: false
enum: [concise, detailed, step_by_step]
description: >-
How the user wants answers shaped. "Just give me the short version" is
concise; "walk me through it" is step_by_step.
language: { type: str, required: false, description: "BCP 47 tag such as en-GB, when the user states or clearly uses a language." }
primary_key: [user_id]
Episode:
description: >-
One past interaction worth learning from: the situation, what the assistant
did and why, and how it went. Each episode is its own record; nothing is
updated here, only added.
fields:
user_id: { type: str, required: true }
situation: { type: str, required: true, description: "The user's request and relevant context, in two or three sentences." }
approach: { type: str, required: true, description: "What the assistant did and the reasoning behind it." }
outcome:
type: str
required: true
enum: [succeeded, partially_succeeded, failed]
description: Judged from the user's reaction or an explicit statement, not assumed.
lesson: { type: str, required: false, description: "What to repeat or avoid next time, if the text supports one." }
primary_key: []
relations:
episode_of:
description: Connect an Episode to the UserProfile it belongs to.
objects:
episode: { type: Episode, on_delete: cascade }
profile: { type: UserProfile, on_delete: cascade }
keys:
one_profile_per_episode: [episode]

The profile pattern is implemented by the key: each supplied user id gets one profile, and subsequent mentions update it. The collection pattern is primary_key: []: every episode is its own record, which also means replaying the same interaction adds another episode, since nothing here deduplicates. response_style and outcome are enums; LangMem’s Pydantic schemas can constrain values too, so that is parity rather than a difference. What is different is that the identity rule, the relation to the profile and the persistence are one declaration the engine enforces, instead of a schema, a manager configuration and a store assembled per application. A LangGraph node writes to this over MCP with the sentence it would have given the memory manager, and reads it back with a question.

Why we think this matters, and for whom

xmemory works well for short-running agents, and it is at its best for repeated tasks, where each run adds to a body of knowledge the next run can use. Long-horizon agents deserve a special mention, because there is very little objective evidence yet about what memory design works for them. Our working belief, and the reason xmemory exists: an agent running a long, multi-step task does better on structured state than on accumulated context. A schema gives it an explicit feedback loop at write time (what was accepted, what was rejected, what changed), so memory is updated rather than bloated and the trajectory stays on course: fewer re-reads, faster steps, more reliable outcomes.

If that is your workload, try xmemory on it. We are building objective measurements for this claim. The Toloka evaluation is our first independent measurement of the benefits of structured memory; evaluating changing working state over long-horizon tasks is next, and we would welcome collaborators on it. Write to info@xmemory.ai.