AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
Graphiti is Zep's open-source framework for temporal knowledge graphs — Context Graphs — for AI agents, with incremental updates and hybrid retrieval.
Developer toolsPython · DockerfileGitHub @getzepOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Build Real-Time Knowledge Graphs for AI Agents
Taken from the repository README
Loading the full README.
Read the full README ↗[!NOTE] We're Hiring! Build context graphs that power reliable, personalized, fast production AI agents. Come build with us — we're hiring Engineers and Developer Relations folks. View open roles.
⭐ Help us reach more developers and grow the Graphiti community. Star this repo!
Graphiti is a framework for building and querying temporal context graphs for AI agents. Unlike static knowledge graphs, Graphiti's context graphs track how facts change over time, maintain provenance to source data, and support both prescribed and learned ontology — making them purpose-built for agents operating on evolving, real-world data.
Unlike traditional retrieval-augmented generation (RAG) methods, Graphiti continuously integrates user interactions, structured and unstructured enterprise data, and external information into a coherent, queryable graph. The framework supports incremental data updates, efficient retrieval, and precise historical queries without requiring complete graph recomputation, making it suitable for developing interactive, context-aware AI applications.
Use Graphiti to:
A context graph is a temporal graph of entities, relationships, and facts — like "Kendra loves Adidas shoes (as of March 2026)." Unlike traditional knowledge graphs, each fact in a context graph has a validity window: when it became true, and when (if ever) it was superseded. Entities evolve over time with updated summaries. Everything traces back to episodes — the raw data that produced it.
What makes Graphiti unique is its ability to autonomously build context graphs from unstructured and structured data, handling changing relationships while preserving full temporal history.
A context graph contains:
| Component | What it stores |
|---|---|
| Entities (nodes) | People, products, policies, concepts — with summaries that evolve over time |
| Facts / Relationships (edges) | Triplets (Entity → Relationship → Entity) with temporal validity windows |
| Episodes (provenance) | Raw data as ingested — the ground truth stream. Every derived fact traces back here |
| Custom Types (ontology) | Developer-defined entity and edge types via Pydantic models |
Graphiti is an open-source Python framework for building and querying temporal context graphs. You supply the graph database, LLM, and embedder, and you build the service around it: ingestion, tenancy, access control, and operations.
Zep is the unified context layer for enterprises. Zep brings data from across a business into governed context graphs and serves that context to agents in production. Graphiti is the graph construction framework inside Zep. Konig, Zep's graph database service, stores and serves millions of context graphs with low-latency retrieval.
Teams use both to give agents memory. Neither is an agent memory product: Graphiti is a framework for building graphs, and Zep unifies, governs, and serves an enterprise's context.
Read our paper: Zep: A Temporal Knowledge Graph Architecture.
| Aspect | Zep | Graphiti |
|---|---|---|
| What it is | The unified context layer for enterprises | An open-source framework for building temporal context graphs |
| Scope | Unifies data from across an enterprise into governed context and serves it to agents | Builds and queries graphs inside your own application |
| Context graphs | Manages millions of context graphs with governance | Builds and queries the graphs you define |
| Graph database | Konig, Zep's graph database service, built for millions of context graphs with low-latency retrieval; no third-party graph database required | Bring your own: Neo4j, FalkorDB, or Amazon Neptune |
| Users, threads, and ingestion | Built-in users, threads, and ingestion for messages and business data | Build your own |
| Retrieval & performance | Pre-configured, production-ready retrieval with sub-200ms performance at scale | Custom implementation required; performance depends on your setup |
| Developer tools | Dashboard with graph visualization, debug logs, API logs; CLI; SDKs for Python, TypeScript, and Go | Build your own tools |
| Enterprise features | SLAs, support, security guarantees | Self-managed |
| Deployment | Fully managed or in your cloud (BYOC) | Self-hosted only |
Choose Zep if you want an enterprise's context unified, governed, and served to agents in production, without building and operating that system yourself.
Choose Graphiti if you are building temporal context graphs into your own system and will operate the graph database, retrieval, and surrounding service yourself.
Traditional RAG approaches often rely on batch processing and static data summarization, making them inefficient for frequently changing data. Graphiti addresses these challenges by providing:
| Aspect | GraphRAG | Graphiti |
|---|---|---|
| Primary Use | Static document summarization | Dynamic, evolving temporal knowledge graphs |
| Data Handling | Batch-oriented processing | Continuous, incremental updates |
| Knowledge Structure | Entity clusters & community summaries | Temporal context graph — entities, facts with validity windows, episodes, communities |
| Retrieval Method | Sequential LLM summarization | Hybrid semantic, keyword, and graph-based search |
| Adaptability | Low | High |
| Temporal Handling | Basic timestamp tracking | Explicit bi-temporal tracking with automatic fact invalidation |
| Contradiction Handling | LLM-driven summarization judgments | Automatic fact invalidation with temporal history preserved |
| Query Latency | Seconds to tens of seconds | Typically sub-second latency |
| Custom Entity Types | No | Yes, customizable via Pydantic models |
| Scalability | Moderate | High, optimized for large datasets |
Graphiti is specifically designed to address the challenges of dynamic and frequently updated datasets, making it particularly suitable for applications requiring real-time interaction and precise historical queries.
Requirements:
[!IMPORTANT] Graphiti works best with LLM services that support Structured Output (such as OpenAI, Anthropic, and Gemini). Using other services may result in incorrect output schemas and ingestion failures. This is particularly problematic when using smaller models.
Optional:
[!TIP] The simplest way to install Neo4j is via Neo4j Desktop. It provides a user-friendly interface to manage Neo4j instances and databases. Alternatively, you can use FalkorDB on-premises via Docker and instantly start with the quickstart example:
docker run -p 6379:6379 -p 3000:3000 -it --rm falkordb/falkordb:latest
pip install graphiti-core
or
uv add graphiti-core
If you plan to use FalkorDB as your graph database backend, install with the FalkorDB extra:
pip install graphiti-core[falkordb]
# or with uv
uv add graphiti-core[falkordb]
# or embedded version (requires Python 3.12+)
pip install graphiti-core[falkordblite]
# or with uv
uv add graphiti-core[falkordblite]
[!WARNING] Kuzu is deprecated and will be removed in a future release — the upstream Kuzu project is no longer maintained. New projects should use Neo4j or FalkorDB. The driver still ships for now but emits a
DeprecationWarning.
If you plan to use Kuzu as your graph database backend, install with the Kuzu extra:
pip install graphiti-core[kuzu]
# or with uv
uv add graphiti-core[kuzu]
If you plan to use Amazon Neptune as your graph database backend, install with the Amazon Neptune extra:
pip install graphiti-core[neptune]
# or with uv
uv add graphiti-core[neptune]
# Install with Anthropic support
pip install graphiti-core[anthropic]
# Install with Groq support
pip install graphiti-core[groq]
# Install with Google Gemini support
pip install graphiti-core[google-genai]
# Install with multiple providers
pip install graphiti-core[anthropic,groq,google-genai]
# Install with FalkorDB and LLM providers
pip install graphiti-core[falkordb,anthropic,google-genai]
# Install with Amazon Neptune
pip install graphiti-core[neptune]
Graphiti's ingestion pipelines are designed for high concurrency. By default, concurrency is set low to avoid LLM Provider 429 Rate Limit Errors. If you find Graphiti slow, please increase concurrency as described below.
Concurrency controlled by the SEMAPHORE_LIMIT environment variable. By default, SEMAPHORE_LIMIT is set to 10
concurrent operations to help prevent 429 rate limit errors from your LLM provider. If you encounter such errors, try
lowering this value.
If your LLM provider allows higher throughput, you can increase SEMAPHORE_LIMIT to boost episode ingestion
performance.
[!IMPORTANT] Graphiti defaults to using OpenAI for LLM inference and embedding. Ensure that an
OPENAI_API_KEYis set in your environment. Support for Anthropic, Gemini, and Groq is available, too. Other LLM providers — both hosted OpenAI-compatible APIs (DeepSeek, Together, OpenRouter, …) and local servers (Ollama, vLLM, llama.cpp, LM Studio) — may be used via their OpenAI-compatible endpoints; see Using Graphiti with OpenAI-compatible providers and local LLMs.
For a complete working example, see the Quickstart Example in the examples directory. The quickstart demonstrates:
The example is fully documented with clear explanations of each functionality and includes a comprehensive README with setup instructions and next steps.
You can use Docker Compose to quickly start the required services:
Neo4j Docker:
docker compose up
This will start the Neo4j Docker service and related components.
FalkorDB Docker:
docker compose --profile falkordb up
This will start the FalkorDB Docker service and related components.
The mcp_server and server directories contain reference implementations of an MCP server and a REST server. These
servers show how to put Graphiti behind a protocol. These servers are not production deployments.
[!WARNING] The reference servers have no authentication, no authorization, and no prompt-injection protection. These omissions are intentional. Do not expose the reference servers to an untrusted network.
The reference servers have these limits:
group_id.Security reports about these limits are out of scope. Pull requests that add authentication, authorization, or prompt-injection filters to the reference servers are also out of scope. For more information, see the security policy scope.
For a production deployment, use one of these options:
graphiti-core library in your own service. Your service must supply authentication, authorization, tenant
isolation, and input controls.The mcp_server directory contains a reference Model Context Protocol (MCP) server for Graphiti. This server
allows AI assistants to interact with Graphiti's context graph capabilities through the MCP protocol.
Key features of the MCP server include:
The MCP server can be deployed using Docker with Neo4j, making it easy to integrate Graphiti into your AI assistant workflows.
For detailed setup instructions and usage examples, see the MCP server README.
The server directory contains a reference REST service for the Graphiti API. The service uses FastAPI.
Please see the server README for more information.
In addition to the Neo4j and OpenAi-compatible credentials, Graphiti also has a few optional environment variables. If you are using one of our supported models, such as Anthropic or Voyage models, the necessary environment variables must be set.
Database names are configured directly in the driver constructors:
neo4j (hardcoded in Neo4jDriver)default_db (hardcoded in FalkorDriver)As of v0.17.0, if you need to customize your database configuration, you can instantiate a database driver and pass it
to the Graphiti constructor using the graph_driver parameter.
from graphiti_core import Graphiti
from graphiti_core.driver.neo4j_driver import Neo4jDriver
# Create a Neo4j driver with custom database name
driver = Neo4jDriver(
uri="bolt://localhost:7687",
user="neo4j",
password="password",
database="my_custom_database" # Custom database name
)
# Pass the driver to Graphiti
graphiti = Graphiti(graph_driver=driver)
from graphiti_core import Graphiti
from graphiti_core.driver.falkordb_driver import FalkorDriver
# Create a FalkorDB driver with custom database name
driver = FalkorDriver(
host="localhost",
port=6379,
username="falkor_user", # Optional
password="falkor_password", # Optional
database="my_custom_graph" # Custom database name
)
# Or use embedded FalkorDB Lite (requires Python 3.12+)
# from redislite.async_falkordb_client import AsyncFalkorDB
# falkordb_client = AsyncFalkorDB(dbfilename='/path/to/database.db')
# driver = FalkorDriver(falkor_db=falkordb_client)
# Pass the driver to Graphiti
graphiti = Graphiti(graph_driver=driver)
[!WARNING] Kuzu is deprecated (upstream project unmaintained) and will be removed in a future release. Prefer Neo4j or FalkorDB.
from graphiti_core import Graphiti
from graphiti_core.driver.kuzu_driver import KuzuDriver
# Create a Kuzu driver
driver = KuzuDriver(db="/tmp/graphiti.kuzu")
# Pass the driver to Graphiti
graphiti = Graphiti(graph_driver=driver)
from graphiti_core import Graphiti
from graphiti_core.driver.neptune_driver import NeptuneDriver
# Create a Neptune driver
driver = NeptuneDriver(
host='<NEPTUNE_ENDPOINT>',
aoss_host='<AMAZON_OPENSEARCH_SERVERLESS_HOST>',
port=8182, # Optional, defaults to 8182
aoss_port=443, # Optional, defaults to 443
)
# Pass the driver to Graphiti
graphiti = Graphiti(graph_driver=driver)
Contributing a new graph backend? See Adding a graph driver.
Graphiti supports Azure OpenAI for both LLM inference and embeddings using Azure's OpenAI v1 API compatibility layer.
from openai import AsyncOpenAI
from graphiti_core import Graphiti
from graphiti_core.llm_client.azure_openai_client import AzureOpenAILLMClient
from graphiti_core.llm_client.config import LLMConfig
from graphiti_core.embedder.azure_openai import AzureOpenAIEmbedderClient
# Initialize Azure OpenAI client using the standard OpenAI client
# with Azure's v1 API endpoint
azure_client = AsyncOpenAI(
base_url="https://your-resource-name.openai.azure.com/openai/v1/",
api_key="your-api-key",
)
# Create LLM and Embedder clients
llm_client = AzureOpenAILLMClient(
azure_client=azure_client,
config=LLMConfig(model="gpt-5-mini", small_model="gpt-5-mini") # Your Azure deployment name
)
embedder_client = AzureOpenAIEmbedderClient(
azure_client=azure_client,
model="text-embedding-3-small" # Your Azure embedding deployment name
)
# Initialize Graphiti with Azure OpenAI clients
graphiti = Graphiti(
"bolt://localhost:7687",
"neo4j",
"password",
llm_client=llm_client,
embedder=embedder_client,
)
# Now you can use Graphiti with Azure OpenAI
Key Points:
AsyncOpenAI client with Azure's v1 API endpoint format:
https://your-resource-name.openai.azure.com/openai/v1/gpt-5-mini, text-embedding-3-small) should match your Azure OpenAI deployment namesexamples/azure-openai/ for a complete working exampleMake sure to replace the placeholder values with your actual Azure OpenAI credentials and deployment names.
Graphiti supports Google's Gemini models for LLM inference, embeddings, and cross-encoding/reranking. To use Gemini, you'll need to configure the LLM client, embedder, and the cross-encoder with your Google API key.
Install Graphiti:
uv add "graphiti-core[google-genai]"
# or
pip install "graphiti-core[google-genai]"
from graphiti_core import Graphiti
from graphiti_core.llm_client.gemini_client import GeminiClient, LLMConfig
from graphiti_core.embedder.gemini import GeminiEmbedder, GeminiEmbedderConfig
from graphiti_core.cross_encoder.gemini_reranker_client import GeminiRerankerClient
# Google API key configuration
api_key = "<your-google-api-key>"
# Initialize Graphiti with Gemini clients
graphiti = Graphiti(
"bolt://localhost:7687",
"neo4j",
"password",
llm_client=GeminiClient(
config=LLMConfig(
api_key=api_key,
model="gemini-2.0-flash"
)
),
embedder=GeminiEmbedder(
config=GeminiEmbedderConfig(
api_key=api_key,
embedding_model="embedding-001"
)
),
cross_encoder=GeminiRerankerClient(
config=LLMConfig(
api_key=api_key,
model="gemini-2.5-flash-lite"
)
)
)
# Now you can use Graphiti with Google Gemini for all components
The Gemini reranker uses the gemini-2.5-flash-lite model by default, which is optimized for
cost-effective and low-latency classification tasks. It uses the same boolean classification approach as the OpenAI
reranker, leveraging Gemini's log probabilities feature to rank passage relevance.
Graphiti can use any OpenAI-compatible /v1 endpoint for LLM inference via OpenAIGenericClient — both hosted
providers (DeepSeek, Together, OpenRouter, Fireworks, etc.) and local servers (Ollama, vLLM, llama.cpp, LM
Studio). Local servers are ideal for privacy-focused applications or avoiding API costs. The example below uses Ollama;
for any other provider, point base_url at its endpoint and set the appropriate api_key and model.
Note: Use OpenAIGenericClient (not OpenAIClient) for these endpoints. It is optimized for local models with a
higher default max token limit (16K vs 8K) and handles structured outputs across compatible providers.
Install the models:
ollama pull deepseek-r1:7b # LLM
ollama pull nomic-embed-text # embeddings
from graphiti_core import Graphiti
from graphiti_core.llm_client.config import LLMConfig
from graphiti_core.llm_client.openai_generic_client import OpenAIGenericClient
from graphiti_core.embedder.openai import OpenAIEmbedder, OpenAIEmbedderConfig
from graphiti_core.cross_encoder.openai_reranker_client import OpenAIRerankerClient
# Configure Ollama LLM client
llm_config = LLMConfig(
api_key="ollama", # Ollama doesn't require a real API key, but some placeholder is needed
model="deepseek-r1:7b",
small_model="deepseek-r1:7b",
base_url="http://localhost:11434/v1", # Ollama's OpenAI-compatible endpoint
)
llm_client = OpenAIGenericClient(config=llm_config)
# Initialize Graphiti with Ollama clients
graphiti = Graphiti(
"bolt://localhost:7687",
"neo4j",
"password",
llm_client=llm_client,
embedder=OpenAIEmbedder(
config=OpenAIEmbedderConfig(
api_key="ollama", # Placeholder API key
embedding_model="nomic-embed-text",
embedding_dim=768,
base_url="http://localhost:11434/v1",
)
),
cross_encoder=OpenAIRerankerClient(client=llm_client, config=llm_config),
)
# Now you can use Graphiti with local Ollama models
Ensure Ollama is running (ollama serve) and that you have pulled the models you want to use.
Graphiti depends on structured (JSON) output for entity/edge extraction and deduplication, and works best with models
and providers that reliably honor it (OpenAI, Anthropic, Gemini). Reliability varies across OpenAI-compatible providers and
especially on smaller or local models, so OpenAIGenericClient exposes a structured_output_mode:
"json_schema" (default): requests native structured output via response_format. Best on capable models and
providers that enforce the schema via constrained decoding."json_object": requests plain-JSON mode and injects the schema into the prompt instead. Use this for
providers/models that don't reliably honor json_schema — including some local servers that accept the json_schema
request but don't actually constrain output to it, where json_object can be more reliable.When using smaller or local models:
```json code fences are stripped automatically.SEMAPHORE_LIMIT low (see above) — local
servers and some providers have limited concurrency.Graphiti lets you override LLM prompt builders per client instance. Override callables must return
ChatPrompt (system + user). Response schemas are fixed and not overridable:
from graphiti_core import Graphiti
from graphiti_core.prompts import ChatPrompt, SystemMessage, UserMessage, create_prompt_library
def custom_extract_message(context: dict) -> ChatPrompt:
return ChatPrompt(
system=SystemMessage(content='Extract entities for my domain.'),
user=UserMessage(content=str(context.get('episode_content', ''))),
)
prompt_library = create_prompt_library({
'extract_nodes': {
'extract_message': custom_extract_message,
},
})
graphiti = Graphiti(..., prompt_library=prompt_library)
Unspecified prompts keep the built-in defaults. Returning list[Message] from an override raises
TypeError — migrate with return ChatPrompt(system=..., user=...). Unicode handling is applied
via ChatPrompt.as_messages().
The package-level prompt_library keeps the existing list[Message] API.
The opt-in prompt customization API uses ChatPromptLibrary and
DefaultChatPromptLibrary.
For per-prompt routing across providers, pass an opt-in llm_runtime. Create
one LLMTransport for each provider client. Each LLMModel binds a provider
model ID to one transport. An unrouted prompt uses the default model.
llm_runtime cannot be combined with llm_client or prompt_library.
Response schemas stay fixed. You can configure prompt text and model routes:
from typing import Literal
from graphiti_core import Graphiti
from graphiti_core.llm_client.anthropic_client import AnthropicClient
from graphiti_core.llm_client import (
LLMRuntime,
LLMTransport,
OpenAIClient,
PromptRoutes,
)
OpenAIModels = Literal['gpt-5.1', 'gpt-5-nano']
openai = LLMTransport[OpenAIModels](
OpenAIClient(),
models=['gpt-5.1', 'gpt-5-nano'],
)
AnthropicModels = Literal['claude-sonnet-5', 'claude-haiku-5-5']
anthropic = LLMTransport[AnthropicModels](
AnthropicClient(),
models=['claude-sonnet-5', 'claude-haiku-5-5'],
)
main = openai.model('gpt-5.1')
nano = openai.model('gpt-5-nano')
haiku = anthropic.model('claude-haiku-5-5')
runtime = LLMRuntime(
model=main,
routes=PromptRoutes(
extract_nodes=PromptRoutes.ExtractNodes(extract_attributes=nano),
dedupe_edges=PromptRoutes.DedupeEdges(
resolve_edge=haiku,
),
),
)
graphiti = Graphiti(..., llm_runtime=runtime)
LLMModel does not have a small_id field. The runtime passes
model_size for legacy compatibility, but this value does not select a
smaller model. The selected model ID controls provider calls. A routed prompt
uses that model ID. An unrouted prompt uses the default model ID. Route prompts
that need a smaller model to that model explicitly. Legacy calls without
llm_runtime retain LLMConfig.small_model behavior.
GLiNER2 binds model objects at initialization. It does not support per-prompt
model routing. See spec/llm-runtime.md for the runtime API.
JevClient routes node dedupe and edge resolution to TypeSafe Jev. Set
JEV_API_KEY before you create the client. The duplicate threshold defaults
to 0.7. The contradiction threshold defaults to 0.8.
Jev is a decision model. It answers typed questions with probabilities. It
does not generate text. Use a general LLM as the default model. Route only
dedupe_nodes.nodes and dedupe_edges.resolve_edge to Jev. Do not use Jev
for extraction, summaries, or other generation tasks.
TypeSafe hosts the Jev API. Graphiti sends dedupe prompt content to TypeSafe. See the FalkorDB Jev dedupe example for a runnable example.
from graphiti_core import Graphiti
from graphiti_core.llm_client import (
JevClient,
LLMRuntime,
LLMTransport,
OpenAIClient,
PromptRoutes,
jev_prompt_overrides,
)
openai = LLMTransport(OpenAIClient(), models=['gpt-6-luna'])
jev = LLMTransport(JevClient(), models=['jev-latest'])
jev_dedupe = jev.model('jev-latest', prompt_overrides=jev_prompt_overrides())
runtime = LLMRuntime(
model=openai.model('gpt-6-luna'),
routes=PromptRoutes(
dedupe_nodes=PromptRoutes.DedupeNodes(nodes=jev_dedupe),
dedupe_edges=PromptRoutes.DedupeEdges(resolve_edge=jev_dedupe),
),
)
graphiti = Graphiti(..., llm_runtime=runtime)
OpenAIDecisionsClient routes node dedupe and edge resolution to the OpenAI
Decisions API. Use a general LLM as the default model. A decision model
answers typed questions with probabilities and does not generate text. Do not
use it for extraction, summaries, or other generation tasks.
See the OpenAI Decisions FalkorDB example for a runnable example.
Graphiti collects anonymous usage statistics to help us understand how the framework is being used and improve it for everyone. We believe transparency is important, so here's exactly what we collect and why.
When you initialize a Graphiti instance, we collect:
~/.cache/graphiti/telemetry_anon_idWe are committed to protecting your privacy. We never collect:
This information helps us:
By sharing this anonymous information, you help us make Graphiti better for everyone in the community.
The Telemetry code may be found here.
Telemetry is opt-out and can be disabled at any time. To disable telemetry collection:
Option 1: Environment Variable
export GRAPHITI_TELEMETRY_ENABLED=false
Option 2: Set in your shell profile
# For bash users (~/.bashrc or ~/.bash_profile)
echo 'export GRAPHITI_TELEMETRY_ENABLED=false' >> ~/.bashrc
# For zsh users (~/.zshrc)
echo 'export GRAPHITI_TELEMETRY_ENABLED=false' >> ~/.zshrc
Option 3: Set for a specific Python session
import os
os.environ['GRAPHITI_TELEMETRY_ENABLED'] = 'false'
# Then initialize Graphiti as usual
from graphiti_core import Graphiti
graphiti = Graphiti(...)
Telemetry is automatically disabled during test runs (when pytest is detected).
We encourage and appreciate all forms of contributions, whether it's code, documentation, addressing GitHub Issues, or helping others on GitHub. For detailed guidelines on code contributions, please refer to CONTRIBUTING.
Open a GitHub issue to ask questions, report bugs, or discuss Graphiti.