Skip to content
xmemoryxmemory Website

xmemory vs Cognee

Cognee is the competitor whose design we respect most on the schema question. Its custom graph models are Pydantic classes handed to the LLM as the output shape, with identity fields that give nodes deterministic ids and let a re-added object upsert onto the one already stored. That is a real step toward typed memory. The difference is where the schema lives and who runs the update. In Cognee the model configures extraction, and keeping records current is a workflow your application drives: version, re-add, review conflicts. In xmemory the schema is owned by the instance, and resolving every natural-language write against stored state is the engine’s job.

Documentation reviewed 23 October 2026: Cognee overview, graph generation, custom graph model, DataPoints, update, improve and the Cognee paper; on our side How xmemory works, XMD and the API.

Cognee is an open-source knowledge engine. remember ingests text or files, chunks them, asks an LLM for entities and relationships, summarises each chunk, and writes the result into three stores: relational for documents and provenance, vector for embeddings, graph for nodes and edges, with a long list of swappable backends. recall searches the graph and vector stores and, in its graph-completion mode, turns retrieved triplets into context for an answer. improve enriches the graph and can review conflicting facts across dated sources; forget deletes by document, dataset or user. Version 1.0 added session memory that can be promoted into the permanent graph.

By default the schema emerges from the data: nodes carry name, type and description, edges a free-text relationship name, and identity is the normalised name. With a custom graph model you replace that with your own Pydantic DataPoint classes, edge names constrained by a Literal, and identity_fields for deterministic node ids. Cognee’s own paper is about tuning this pipeline for multi-hop QA on HotPotQA, TwoWikiMultiHop and MuSiQue, which tells you what it is built for: connecting a corpus and answering questions across it.

The schema is owned by the instance, and the engine runs the mutation. Cognee’s custom model is the structured-output schema you pass to a remember call. Identity fields give nodes deterministic ids, and re-adding an object with the same identity upserts onto the existing node; but getting there is application work: mutate the fields, call update_version(), re-add. An XMD schema belongs to the instance, not the call, and the resolution is the write path itself. Every natural-language write, from any source, is extracted against the stored records, matched by primary key, applied, and kept as lineage. The schema is the same whether you write over MCP, CLI, REST or an SDK; Cognee’s typed Edge generics and FromIdentity markers work only through the Python SDK and are lost over HTTP, by their own documentation.

Corrections are the write path, not a review pass. In Cognee v1.0, finding facts that disagree is the opt-in review_conflicts stage of improve, after ingestion. Replacing a source is explicit too: cognee.update() swaps a document under its existing id, while simply remembering a newer, conflicting text adds a second source beside the first. Conflicting sources can coexist until an enabled conflict-review stage reconciles them. That review is separate from extraction, even when configured to run automatically after ingestion. For a corpus that is the right design. For agent state it means “the customer moved to Pro” can sit next to “the customer is on Basic” until that stage runs and decides. In xmemory the key does the reconciling inside the write, and “which plan is the customer on” has one answer. This is the argument of Schema as the Core of Reliability and the worked examples on the Why page.

Reads are queries, not graph completion. Cognee’s answer path retrieves triplets and chunks and has a model compose them. xmemory’s answer path translates the question into SQL over typed records and, if you want, hands you the rows instead of prose. Counts, sums, joins and “which required facts are missing” are therefore first-class, and the answer is over every matching record rather than the retrieved ones. When the memory feeds code rather than a chat reply, that is the capability that matters.

Schema evolution is one workflow, not several. Cognee handles an added field per store: the graph store needs no migration because it has no fixed schema, the default vector store migrates a collection on the next write (and aborts on a new required field without a default), stored nodes are not backfilled, and changing identity fields creates duplicates; applying a changed extraction model to existing source content requires reprocessing that content. xmemory treats the schema as one versioned artefact: validate a candidate, preview the migration, apply it, with the documented limits on backfill and key changes stated once, up front. The Schema Evolution post is the long version.

The measurements. On our four-domain end-to-end benchmark (updates, deletions, relation changes, aggregation, negative queries) Cognee scored 86.18 F1, the best of the graph-based systems we ran and 1.06 points behind Mem0 without graph, against xmemory’s 97.10. On the Splitwise task, where every read needs arithmetic, Cognee reached 68.0% to xmemory’s 95.2% (white paper; numbers also in Chasing AI memory SOTA). The paper’s setup: Cognee self-hosted, SDK 1.0.3, Anthropic Sonnet 4.6 as the LLM, text-embedding-3-large for embeddings, temporal and graph-completion search enabled. These results measure the tested configuration. They show a substantial gap on these workloads, but do not isolate its cause or establish the best result achievable with a custom graph model.

Choose Cognee when the job is a corpus: ingest documents, build a graph across them, answer multi-hop questions, and you want the pipeline in your own Python with your own backends. Choose xmemory when the job is state: the memory is written continuously by agents, corrected often, read by code as much as by models, and has to stay consistent without your application driving the updates. Cognee’s custom graph model is a good first draft of the XMD; the difference is that in xmemory the instance owns it and the engine applies it on every write.

Cognee’s custom graph model as an XMD schema

Section titled “Cognee’s custom graph model as an XMD schema”

Cognee’s own example is people, groups and roles, with Role as a node referenced through FromIdentity(). The XMD below adapts it rather than translating it: the role moves onto a keyed membership record, which is the habit these pages keep returning to, because a relationship that carries data should be something you can update.

xmd_version: v1
title: People and groups
description: People, the groups they belong to, and the role they hold in each, as current state.
objects:
Person:
description: An individual named in the text.
fields:
name: { type: str, required: true }
primary_key: [name]
Group:
description: A team, committee or organisation that people belong to.
fields:
name: { type: str, required: true }
primary_key: [name]
Membership:
description: >-
One person's current standing in one group. A stated change of role updates
the existing record and leaves status unchanged; leaving the group sets status
to left; only an explicit statement that they rejoined sets it back to active.
fields:
person_name: { type: str, required: true }
group_name: { type: str, required: true }
role: { type: str, required: false, enum: [member, lead, advisor] }
status: { type: str, required: true, enum: [active, left], default: active }
primary_key: [person_name, group_name]
relations:
member:
description: Connect a Membership to its Person and Group.
objects:
membership: { type: Membership, on_delete: cascade }
person: { type: Person, on_delete: cascade }
group: { type: Group, on_delete: cascade }
keys:
one_pair_per_membership: [membership]

Cognee’s members: list[Person] field becomes the member relation, and the Role node becomes a role enum on the membership. “Maya now leads the platform group” matches (Maya, platform) and rewrites role; “Maya left the platform group” sets status to left. The update is extracted from the new input and applied to the existing record; the original corpus does not need to be reprocessed. One sequence worth testing before relying on it: a later role change alone should not flip a left membership back to active, so the description makes reactivation an explicit statement rather than a side effect of default: active.

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.