Temporal Knowledge Graph: How AI Agents Know What Is True Now

September 11, 2026 · 8 min read · knowledge-graph, temporal-knowledge-graph, ai-agents, graphiti

A temporal knowledge graph is a knowledge graph in which every fact carries time: when it was true in the world, and when the system learned it. When a new fact contradicts an old one, the old fact is closed rather than deleted. An AI agent can then ask what is true now, and an auditor can ask what the agent knew on any given day.

That one property decides whether an agent answers from the current state of your business or from whatever passage looks most like the question.

The problem: similar is not current

Take a customer whose contact person changed in June. The March email names Jana Keller, the June ticket names Tom Berger. A vector search for “who is the contact at Acme” returns both passages, because both are similar to the question. The model then picks one, usually the one that reads more confident, and nothing in the retrieved text says which is still true.

Documents do not expire. Facts do. Every system that feeds an agent from chat, tickets, wiki and CRM runs into this within weeks, because those sources are full of decisions that were later revised.

How it works: facts with two clocks

A temporal knowledge graph stores three kinds of things:

  • Entities are the nodes: a customer, a person, a project, a system.
  • Facts are the edges between entities, written in plain language, such as “Tom Berger is Acme’s contact for the rollout”.
  • Episodes are the raw inputs the facts were extracted from: a ticket comment, a meeting note, a CRM change. Every fact points back to its episodes.

Each fact carries two time axes. Valid time says when the fact held in the world. Transaction time says when the system recorded it, and when the system stopped treating it as current. A model with both axes is called bi-temporal. In Graphiti, every fact edge carries four timestamps for exactly this: valid_at and invalid_at for the world, created_at and expired_at for the system.

When the June ticket arrives, the graph does not overwrite anything. It adds the new fact and closes the old one:

Factvalid_atinvalid_atSource
Jana Keller is Acme’s contact for the rollout2026-03-042026-06-03Email, 4 March
Tom Berger is Acme’s contact for the rollout2026-06-03openJira comment, 3 June

A query for the current state returns the second row. A query as of 1 May returns the first. Both answers carry their source, so a person can open the email or the ticket and check.

MarAprMayJunJulAugSepJana Keller is Acme's contactclosed 3 Junfrom an email, 4 MarTom Berger is Acme's contactfrom a Jira comment, 3 Jun · still openas of 1 May → Janatoday → TomMarAprMayJunJulAugSepJana Keller, Acme's contactclosed 3 JunTom Berger, Acme's contactas of 1 May → Janatoday → Tom
Two facts about the same relation. The June episode closes the March fact instead of deleting it, so a query as of 1 May and a query for today get different answers, and both are correct.

Temporal knowledge graph, vector RAG and GraphRAG compared

Vector RAGGraphRAG-style knowledge graphTemporal knowledge graph
Unit of knowledgeText chunkEntities, relations, community summariesEntities, facts with validity windows, episodes
New dataEmbed and appendBatch indexing pipelineIncremental, one episode at a time
Contradicting sourcesBoth come backNo notion of which one is currentOld fact closed, still queryable
“What was true in May?”Not answerableNot modeledAnswerable
ProvenanceChunk to documentSummaries to source textFact to episode to source system
Main costEmbeddingsExtraction and summaries at index timeA model call per episode at ingest

Microsoft’s GraphRAG builds community summaries over a document collection and suits questions about the collection as a whole. A temporal knowledge graph suits questions about the current state of things that keep changing: customers, projects, systems, decisions.

How Graphiti builds it

Graphiti is an open-source framework for temporal knowledge graphs from Zep, under the Apache-2.0 license, at version 0.30.2 in September 2026 with about 30,800 GitHub stars. The parts that matter for an agent:

  • Episodes in, facts out. You add an episode (text, a message or JSON) with a reference time. A language model extracts entities and facts, resolves them against what the graph already knows, and closes facts the new episode contradicts. Entity and edge types can be prescribed with Pydantic models or left to emerge from the data.
  • Hybrid search without a model on the read path. Retrieval combines embeddings, BM25 keyword search and graph traversal, with optional reranking by graph distance, and returns facts rather than a generated summary.
  • Backends. Neo4j 5.26 or newer, FalkorDB 1.1.2 or newer, and Amazon Neptune. The Kuzu driver is deprecated because the upstream project is no longer maintained.
  • Models. OpenAI by default, with clients for Azure OpenAI, Anthropic, Google Gemini and Groq, and any OpenAI-compatible endpoint such as Ollama or vLLM. The README is explicit that Graphiti works best with models that support structured output, and that smaller models cause ingestion failures.
  • Two defaults to know. Anonymous telemetry is on by default and switches off with GRAPHITI_TELEMETRY_ENABLED=false. The official MCP server exposes write and delete tools, including one that clears a whole group.

A minimal round trip:

import asyncio
from datetime import datetime, timezone

from graphiti_core import Graphiti
from graphiti_core.nodes import EpisodeType


async def main():
    graphiti = Graphiti("bolt://localhost:7687", "neo4j", "password")
    await graphiti.build_indices_and_constraints()

    await graphiti.add_episode(
        name="jira-comment-4711",
        episode_body="From today Tom Berger is Acme's contact for the rollout. "
                     "Jana Keller moved to procurement.",
        source=EpisodeType.text,
        source_description="Jira comment",
        reference_time=datetime(2026, 6, 3, tzinfo=timezone.utc),
    )

    for edge in await graphiti.search("Who is Acme's contact for the rollout?"):
        print(edge.fact, edge.valid_at, edge.invalid_at)

    await graphiti.close()


asyncio.run(main())

How Graphiti compares with Mem0 is in Graphiti vs Mem0, and what running it takes is in Graphiti in production.

What it costs to run

The read side is cheap. The write side is where the cost sits: every episode means model calls for extraction and for resolving entities and facts against the existing graph, and that context grows with the graph. Graphiti’s own issue tracker has threads on high ingestion cost and on resolution prompts that grow with the number of nodes. Plan for it:

  • Batch what can wait. A nightly ingest of a wiki space costs the same as a live one and is easier to watch.
  • Keep concurrency low at first. Graphiti defaults to low concurrency to stay under provider rate limits; raise it once you know your quota.
  • Pick the extraction model for structured output, not for price alone. A cheap model that returns malformed JSON costs more in retries and dropped facts than a mid-range model that follows the schema.
  • Set the embedding width explicitly. A local embedder such as nomic-embed-text returns 768 dimensions, while graphiti-core defaults to 1,024 unless EMBEDDING_DIM says otherwise, and a mismatch corrupts every vector it writes without an error.

When a temporal knowledge graph is the wrong tool

  • Static reference text such as manuals and policies: plain retrieval is simpler and cheaper.
  • Memory inside one conversation: the context window already holds it.
  • Numbers and aggregates: a warehouse answers “revenue per quarter” better than any graph of facts.

Use one when facts change, when two sources can disagree, and when someone will ask “since when” or “who said so”.

Three rules from running them

I run temporal knowledge graphs in two settings: a company’s operational context layer on its own cloud tenant, and my own working graphs on FalkorDB, with a single-file LadybugDB graph on my laptop. Three rules came out of both:

  1. The agent proposes, a person approves. A record from a system of record, with its own source id, can land automatically. An interpretation, such as “this customer is at risk”, waits in a queue for a person. Writes go through a separate command, never through the tools the agent reads with.
  2. Nothing generates on the read path. The agent gets facts with their validity window and their source, and the model only phrases the answer. That keeps answers checkable and reads fast.
  3. Pin what you read with to what you wrote with. Keep reader and writer on the same graphiti-core version, and take query embeddings from the same embedding model, served the same way, that built the index. In my graphs, a reader one patch version behind the writer returned no results at all, without an error, and the same embedding model served by two different Ollama builds produced vectors with a cosine similarity of only 0.80 to each other, enough to lose matches.

Those rules are built into Graphiti Local, the open-source interface I maintain: six read-only MCP tools, a kg command line, and a write path that waits for a person. In client work they become the context layer for AI agents: the same kind of graph, fed from the systems a company already runs, on its own tenant.

Graphiti Local is an independent community project built on Graphiti. It is not affiliated with or endorsed by Zep. Versions and counts as of 11 September 2026.

Frequently asked questions

What is a temporal knowledge graph?

A knowledge graph in which every fact carries time: when it was true in the world and when the system recorded it. A contradicting fact closes the old one instead of deleting it, so you can ask what is true now and what was true on any earlier date.

How is a temporal knowledge graph different from a static knowledge graph?

A static knowledge graph holds one current state and overwrites it on change, or is rebuilt in a batch. A temporal knowledge graph keeps each fact with a validity window and ingests new data incrementally, so history stays queryable and a change lands as soon as its source arrives.

Is a knowledge graph better than RAG for AI agents?

For facts that change, yes. Vector retrieval returns every passage that looks like the question, including the outdated one, while a temporal knowledge graph returns the fact that holds today, with its source. For static reference text plain retrieval stays simpler, and many systems use both.

Is Graphiti open source?

Yes. Graphiti is Zep's framework for temporal knowledge graphs, released under the Apache-2.0 license and at version 0.30 in September 2026. It runs on Neo4j, FalkorDB or Amazon Neptune. Zep Cloud is the vendor's managed service built on it.

Does a temporal knowledge graph need a language model?

For ingestion, yes: a model extracts entities and facts from each episode and decides which existing facts a new one contradicts. For reading, no: hybrid search over embeddings, keywords and graph distance returns facts without a model generating anything.

Can a temporal knowledge graph run inside the EU or fully on premises?

Yes. The graph database runs in your own tenant or on your own hardware. On Azure, extraction can use OpenAI's GPT-5.6 models deployed as Data Zone Standard in the EU. With a local model through Ollama and an embedded backend, nothing leaves the machine.

Book a 30-minute architecture call
The context layer for your AI agents

Your agents answer from whatever the retriever finds, and too often that is last quarter's truth. I build the context layer they answer and act from: a temporal knowledge graph that keeps every fact with its source and the time it held, reads with each person's own permissions, and writes nothing without a person's approval. On your own tenant, billed by the hour, step by step.

Get your AI pilot checked