Graphiti - Temporal Knowledge Graphs for Agents Cheatsheet
Graphiti (by Zep) is an open-source framework for building and querying temporal knowledge graphs for AI agents. Unlike a static graph, Graphiti tracks how facts change over time: each edge carries validity intervals, so the graph knows not just what is true but when it was true, preserving provenance and history. It ingests data incrementally — episodes are added without recomputing the whole graph — making it well-suited to memory that must reason about evolving facts.
Requirements
- A graph database: Neo4j or FalkorDB
- An LLM provider (OpenAI, Anthropic, etc.) for entity/relationship extraction
- Python 3.10+
Installation
| Method | Command |
|---|
| pip | pip install graphiti-core |
| With FalkorDB | pip install "graphiti-core[falkordb]" |
| Set keys | export OPENAI_API_KEY=... and graph DB env vars |
| Verify | python -c "import graphiti_core; print('ok')" |
Core Concepts
| Term | Meaning |
|---|
| Episode | A unit of ingested data (message, text, JSON) |
| Node | An entity extracted from episodes |
| Edge (fact) | A relationship with validity time (valid_at/invalid_at) |
| Bi-temporal | Tracks both event time and ingestion time |
| Community | Clusters of related entities |
Initialize & Add Episodes
from graphiti_core import Graphiti
from graphiti_core.nodes import EpisodeType
import asyncio
async def main():
graphiti = Graphiti("bolt://localhost:7687", "neo4j", "password")
await graphiti.build_indices_and_constraints()
await graphiti.add_episode(
name="msg1",
episode_body="Nick joined Acme as CTO in 2024.",
source=EpisodeType.text,
reference_time=datetime.now(),
source_description="chat",
)
asyncio.run(main())
| Call | Description |
|---|
Graphiti(uri, user, pass) | Connect to the graph DB |
build_indices_and_constraints() | One-time schema setup |
add_episode(...) | Ingest data; entities/edges extracted automatically |
add_episode_bulk(...) | Batch ingestion |
Searching Memory
# Hybrid search: semantic + BM25 + graph, reranked
results = await graphiti.search("Where does Nick work?")
for r in results:
print(r.fact, r.valid_at)
| Search | Behavior |
|---|
search(query) | Hybrid (semantic + keyword + graph) edge search |
| Node search | Retrieve entities |
| Reranking | RRF / graph-distance / cross-encoder options |
| Center node | Bias results around a specific entity |
Temporal Reasoning
The differentiator: when a fact changes, Graphiti does not delete the old one — it marks the previous edge invalid and adds the new one, so history is preserved.
| Capability | Use |
|---|
| Fact invalidation | Old facts get invalid_at set when contradicted |
| Point-in-time queries | ”What was true as of date X?” |
| Provenance | Trace a fact back to its source episode |
| Custom entity types | Define Pydantic models for typed extraction |
Integration
| Target | How |
|---|
| Zep | Graphiti is the engine under Zep’s memory service |
| Agent frameworks | Use as a memory/retrieval tool |
| MCP | Graphiti MCP server exposes memory to agents |
| Custom | Query the graph directly for context |
Common Workflows
# Agent memory that survives changing facts
await graphiti.add_episode(name="e1", episode_body="Nick uses Postgres.", ...)
# later, the fact changes:
await graphiti.add_episode(name="e2", episode_body="Nick migrated to SQLite.", ...)
# Graphiti invalidates the Postgres fact and records the SQLite one with time
ctx = await graphiti.search("What database does Nick use now?")
Graphiti vs Other Memory Frameworks
| Aspect | Graphiti | Cognee | Mem0 |
|---|
| Core strength | Temporal/bi-temporal facts | Graph-native ECL | Personalization |
| History | Preserved with validity times | Graph snapshots | Recent-focused |
| Backend | Neo4j / FalkorDB | Multi-store | Vector/graph/KV |
| Best for | Facts that change over time | Local graph reasoning | Conversational memory |
Resources