# xmemory vs Zep

> Zep builds a temporal knowledge graph of extracted facts and hands the agent a context block. xmemory executes a schema and stores records you can query as state. Both let you declare types; they constrain different things, and that decides what each memory can answer.

Canonical page: https://xmemory.ai/compare/zep/

Of the memory products we get asked about, Zep is the closest to us in spirit. It has an ontology.
Facts carry time. Contradictions are handled rather than ignored. The short version of this page:
Zep's types decide how extracted facts are *classified and matched*; XMD types decide how
extracted values are *shaped and resolved*. Those are different constraints, and the difference
shows up the moment code, rather than a model, has to consume the memory.

Documentation reviewed 23 October 2026: Zep v4
[concepts](https://help.getzep.com/v4/concepts), [customizing graph structure](https://help.getzep.com/v4/customizing-graph-structure),
[facts](https://help.getzep.com/v4/facts), [querying a graph](https://help.getzep.com/v4/querying-a-graph),
[Zep vs Graphiti](https://help.getzep.com/v4/zep-vs-graphiti) and the
[Zep paper](https://arxiv.org/abs/2501.13956); on our side [How xmemory works](https://xmemory.ai/integration-overview/),
[XMD](https://xmemory.ai/xmd/) and the [API](https://xmemory.ai/api/).

## What Zep does

Zep ingests episodes (messages, documents, JSON) into a per-user or shared *context graph*: nodes
are entities, edges are facts written as sentences with a relationship name, and every fact
carries bi-temporal timestamps (`valid_at`, `invalid_at`, plus when Zep learned each). When a new
episode contradicts an old fact, the old edge is invalidated rather than deleted, so the graph can
answer "what was true in March". Retrieval is hybrid (semantic, BM25, graph traversal, rerankers);
the usual output is a *context block*, prompt-ready text listing relevant facts with their date
ranges, and nodes and edges can also be listed and fetched as records.

You can shape the graph with custom entity and edge types: up to ten typed attributes per type,
each with a description, `identity_properties` that decide which node a mention matches,
`protected` attributes that keep their first value, and a `strict_ontology` switch that drops
entities and edges outside the types you configured. The engine underneath, Graphiti, is open
source on Neo4j, FalkorDB or Neptune; Zep is the managed service on top.

That is a serious design, and for conversational agents that need to know how a relationship
changed over time it works well.

## Where xmemory is stronger

**What the types constrain.** Zep's constraints are about membership and identity: which type a
fact belongs to, which node it attaches to, whether an unmatched fact is kept or dropped. Inside a
type, attributes are text or boolean with a description; there are no enumerated values, no
required-field semantics, and the docs say classification favours recall. XMD's constraints are
about values: enums bound what a field may hold and extraction picks from them, keys decide which
record a mention updates, relation keys fix cardinality, and a required field the text cannot
ground stays empty rather than being invented (the [XMD reference](https://xmemory.ai/xmd/#required-fields-and-defaults)
is precise about what `required` does and does not mean). When downstream code reads a populated
`status`, it reads one of the values you allowed.

**Where state lives, and what you can do with it.** Zep can hold an account's status as an
attribute on an entity; by default it holds it as a fact, "account Emily0e62 has a suspended
status due to payment failure (2024-11-14 - present)". Either way the ways to get it back are
search, traversal, list and fetch, and an experimental natural-language `graph.query` that runs a
retrieval loop. None of those is an aggregate. In xmemory `status`, `reason` and `suspended_on`
are columns in a store with SQL underneath, so "how many accounts were suspended this week, and
which ones" is a query over all matching stored records, rather than a ranked selection of facts. That is the argument of
[Schema as the Core of Reliability](https://xmemory.ai/schema-as-the-core-of-reliability/), and it is the
difference the benchmark below is built to test.

**How a correction lands.** Zep's invalidation is a judgement made at ingestion: the engine
searches for related edges and decides whether the new fact supersedes one, then stamps
`invalid_at`. xmemory looks up the record whose primary key matches, writes the new value, and
keeps the previous value as lineage. No similarity search is involved, and you express identity
and update rules in the schema. The [Why page](https://xmemory.ai/why/) walks through the
"Amy is no longer Kevin's manager" case for both approaches.

**Postgres, not a graph database.** This one is a deployment preference rather than a capability.
Graphiti needs Neo4j, FalkorDB or Neptune; Zep Cloud removes that by hosting it. xmemory keeps
relations as schema objects over Postgres, so self-hosting means a database your team already
runs, and the same API serves relations, records and queries.

**The measurements.** Zep's paper reports 94.8% on Deep Memory Retrieval (MemGPT: 93.4%) and up
to 18.5% better accuracy on LongMemEval, both recall-style benchmarks. On our four-domain
end-to-end benchmark, which tests updates, deletions, relation changes, aggregation and negative
queries, Zep scored 80.16 F1 against xmemory's 97.10; on the Splitwise task, where every read
needs arithmetic over stored facts, 25.7% against 95.2% ([white paper](https://arxiv.org/abs/2604.27906),
summarised in [Chasing AI memory SOTA](https://xmemory.ai/chasing-sota-in-ai-memory/)). We ran Zep Cloud through
the `zep-cloud` Python SDK with edge-scoped search, the cross-encoder reranker and ten facts per
query, then the same synthesis prompt as every other baseline
([setup in section 8.3](https://arxiv.org/html/2604.27906v1#S8.SS3)); the numbers measure that
configuration, not everything v4 can be tuned to do. They do not prove *why* the gap exists. They do show that on the operations this benchmark isolates, the gap
is large.

## Choosing

Choose Zep when the product is a timeline of what people said and how relationships shifted, the
main consumer is a model reading a context block, and search plus fetch covers what code needs.
Choose xmemory when code has to count, join, filter or act on memory, and you want the values it
acts on bounded by a contract. The ontology you would write for Zep is a good first draft of the
schema; the difference is what each system does with it.

## Zep's ontology as an XMD schema

What you would declare as entity and edge types in Zep maps onto XMD objects and relations, with
one habit to change: anything you want to query lives in a typed field, and a relationship that
carries state becomes an object with a key that expresses the rule you want.

```yaml
xmd_version: v1
title: Org memory
description: People, where they work, and who they report to, kept as current state.
objects:
  Person:
    description: An individual named in the text. Not a team or a company.
    fields:
      name: { type: str, required: true }
      title: { type: str, required: false }
    primary_key: [name]
  Organisation:
    description: A company, team or department.
    fields:
      name: { type: str, required: true }
    primary_key: [name]
  ReportingLine:
    description: >-
      Who a person currently reports to. One record per report; a statement that
      someone has a new manager updates that person's record.
    fields:
      report_name: { type: str, required: true }
      manager_name: { type: str, required: true }
    primary_key: [report_name]
relations:
  works_at:
    description: Link a Person to the Organisation that employs them.
    objects:
      person: { type: Person, on_delete: cascade }
      organisation: { type: Organisation, on_delete: nullify }
    keys:
      one_employer: [person]
  reports_to:
    description: Connect a ReportingLine to the report and the manager it names.
    objects:
      line: { type: ReportingLine, on_delete: cascade }
      report: { type: Person, on_delete: cascade }
      manager: { type: Person, on_delete: cascade }
    keys:
      one_pair_per_line: [line]
```

Zep's `WORKS_AT` edge is the `works_at` relation. Its "Amy manages Kevin (March - present)" fact
is a `ReportingLine` record keyed on the report. "Kevin now reports to Priya" matches that key and
rewrites `manager_name`; the engine keeps Amy as the previous value in lineage, and nothing else has
to notice. The rule is in the key: one current manager per person. The record is deliberately
narrow. A field such as `since` would need its own rule for what happens when the manager changes
and no new date is given, and if the product needs each period as its own record (Kevin reports to
Amy, then Priya, then Amy again), periods should get an identity and be keyed on that instead. That is the trade: Zep gives you a time range on every edge
automatically, with the supersession decided at ingestion; xmemory gives you the exact rule you
wrote, with history kept by the engine.

## 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](https://xmemory.ai/toloka-agent-memory-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](mailto:info@xmemory.ai).
