---
name: graphiti-temporal-memory
description: "Build bi-temporal knowledge-graph agent memory with Graphiti and Zep — facts with validity windows, not flat vector recall. Production Graphiti/Zep memory: add_episode ingestion with entity and edge extraction, bi-temporal tracking (valid_at/invalid_at vs created_at) so facts get validity windows and contradictions invalidate prior edges, hybrid retrieval combining semantic, BM25, and graph-distance search with RRF reranking, custom entity types via Pydantic, FalkorDB/Neo4j backends, group_id namespacing, and Zep's user/session memory API for CRM, compliance, and support agents."
license: MIT
---

# Graphiti Temporal Memory

Most agent memory collapses to a vector store: you embed past turns, retrieve the nearest neighbors, and hope the model sorts out what's still true. Graphiti Temporal Memory takes a different approach, building a **bi-temporal knowledge graph** where facts are edges with explicit validity windows. When new information contradicts old, the prior edge is invalidated rather than overwritten — so your agent can answer "what did the user prefer *last March*" as easily as "what do they prefer now." It's aimed at developers building long-running assistants, support agents, or anything where state changes over time and provenance matters.

## When to use it

- A user's facts mutate over a session or across sessions (job title, address, current project, preferences) and stale recall causes wrong answers.
- You need to reconstruct *what was known at a point in time* — audits, debugging agent decisions, "as of" queries.
- Flat RAG keeps surfacing outdated or conflicting chunks with no way to reconcile them.
- You want graph-aware retrieval (entities and their relationships), not just semantic similarity.

## How it works

The skill wraps the [Graphiti](https://github.com/getzep/graphiti) library and the Zep service. Core flow:

- **Ingestion** — `graphiti.add_episode(...)` takes raw text or structured messages. An LLM extracts entities (nodes) and relationships (edges), deduplicates against existing nodes, and writes them to a graph backend (Neo4j or FalkorDB).
- **Bi-temporal tracking** — every edge carries two timelines: *event time* (`valid_at` / `invalid_at`, when the fact was true in the world) and *system time* (`created_at`, when the graph learned it). Contradicting an existing edge sets its `invalid_at` instead of deleting it.
- **Hybrid retrieval** — `graphiti.search(...)` combines semantic (embedding) search, BM25 full-text, and graph-distance traversal, fused with reciprocal rank fusion (RRF) and optional reranking.

## Usage

```python
from graphiti_core import Graphiti

g = Graphiti("bolt://localhost:7687", "neo4j", "password")
await g.build_indices_and_constraints()

await g.add_episode(
    name="onboarding",
    episode_body="Priya moved from the Data team to the Platform team.",
    reference_time=datetime.now(timezone.utc),
)

results = await g.search("which team is Priya on?")
for edge in results:
    print(edge.fact, edge.valid_at, edge.invalid_at)
```

## Tips & gotchas

- `reference_time` is what populates `valid_at` — pass the real event timestamp when backfilling history, not `now()`, or your timelines will be wrong.
- Call `build_indices_and_constraints()` once per database; skipping it makes search fall back to slow scans.
- Ingestion is LLM-bound and not cheap. Batch episodes and choose a smaller extraction model where you can.
- Deduplication is name- and embedding-based — inconsistent entity naming spawns duplicate nodes.

## FAQ

**Graphiti library or Zep service?** Graphiti is the open-source engine you self-host against Neo4j/FalkorDB. Zep is the managed product built on it. This skill covers both.

**Does old data disappear on contradiction?** No. The superseded edge stays with `invalid_at` set, so point-in-time queries still work.

**Can I run without an LLM?** No — entity/edge extraction requires one. The backend graph DB is separate.
