Tellodb Blog

Temporal Memory vs Vector Database: Why Your AI Agent Keeps Remembering Stale Facts

A practical guide to understanding why vector databases fail at temporal reasoning, and how to build memory systems that know when facts expire.

Temporal MemoryVector DatabasesAgent Infrastructure

Most teams building AI agents start with embeddings and a vector index. It is the obvious first move: chunk your documents, embed them, store them in a vector database, retrieve the top-k results at query time. For retrieval-augmented generation (RAG) over static knowledge bases, this works well.

But the moment your agent needs to remember things that change — user preferences, evolving relationships, shifting business facts — a plain vector store starts producing wrong answers. Not because the embeddings are bad, but because the retrieval model has no concept of time.

If a user says, "I used to work in New York, but now I live in Dubai," a similarity search system has no native opinion about which claim should win. Both statements live in the index. Both are semantically similar to a future query like "Where does the user work?" The model receives contradictory context and guesses.

This is the core problem this post addresses: temporal memory vs vector database design, and why the distinction matters for anyone building agents that need to remember facts reliably over time.

What Vector Databases Are Actually Good At

Vector databases solve a specific problem: fast approximate nearest-neighbor search in high-dimensional embedding space. Given a query, they find the stored vectors most similar to it.

The strengths are real:

  • Semantic similarity: "best Italian restaurant near me" matches a review mentioning "excellent pasta place downtown" even though the words differ.
  • Paraphrase tolerance: "How do I reset my password?" retrieves documentation about "account recovery steps."
  • Scalable indexing: HNSW, IVF, and similar algorithms make sub-second retrieval feasible over millions of vectors.
  • Flexible chunking: You can split documents by paragraph, sentence, or custom boundaries.

These capabilities make vector databases excellent for static RAG — querying documentation, help articles, or research papers where the facts do not change.

But vector databases answer one question: "What is most similar to this query?"

They do not answer: "What is currently true, given that reality has changed since I last stored something?"

That is a fundamentally different question. And it is the question your agent needs to answer when dealing with evolving user data.

Why Time Changes the Retrieval Problem

Memory is not just recall. It is ordered belief.

When a human remembers something, they do not just retrieve a fact — they implicitly consider when they learned it, whether they have learned something since that contradicts it, and whether the context still applies. A good agent memory system needs similar reasoning.

Consider the types of temporal information an agent might need to track:

  • Stable facts: "The user's name is Sarah." This rarely changes and should always be retrievable.
  • Superseded facts: "The user worked at Google" was true in 2024 but is no longer current. It should be retrievable only when explicitly asked about the past.
  • Recent observations: "The user mentioned they are traveling next week." This is time-sensitive and should rank higher than older travel mentions.
  • Historical snapshots: "The user preferred dark mode six months ago." Useful for trend analysis, but should not override current preferences.
  • Flipping preferences: "The user switched from vegetarian to omnivore." The new fact must override the old one in most contexts.

A flat vector store treats all of these the same. There is no mechanism to say "this fact is newer than that fact" or "this fact invalidates that one." The retrieval layer needs temporal information as a first-class ranking signal, not as metadata you bolt on after the fact.

The Practical Failure Mode

Here is what the failure looks like in production. This scenario plays out in almost every agent system that relies solely on vector search for user memory.

Say you have an agent that remembers user preferences. The user initially tells the agent they prefer dark mode. Later, they switch to light mode. Both preferences are stored in the vector database as separate chunks.

When the agent later queries for the user's display preference, a similarity search returns both chunks. The older "dark mode" chunk might rank higher because the query phrasing happens to be more similar to the original statement. The model receives both, sees conflicting information, and either picks one at random or hedge its answer with "the user has expressed preferences for both."

This is not a prompt-quality issue. It is not a model-quality issue. It is a memory-model issue. The retrieval system has no way to know that one fact supersedes another.

Here is a simplified version of what this looks like in code:

from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import FAISS

embeddings = OpenAIEmbeddings()

# Store user preferences over time
texts = [
    "User prefers dark mode for their IDE.",
    "User prefers light mode for their IDE.",
    "User's preferred code editor is VS Code.",
    "User prefers dark mode for their IDE.",  # initial preference (stale)
    "User switched to light mode for their IDE.",  # updated preference
]

vectorstore = FAISS.from_texts(texts, embeddings)

# Query: what does the user prefer?
results = vectorstore.similarity_search("What is the user's display mode preference?", k=3)

for doc in results:
    print(doc.page_content)

When you run this, you get back multiple results including both the dark mode and light mode preferences. The vector store has no concept of which one is current. It just finds similar text. Your agent now has to figure out, from semantic content alone, which fact to trust — a task that requires temporal reasoning the system was never designed to perform.

The Fix: Adding Temporal Metadata and Filtering

The most basic improvement is to attach timestamps to your vectors and filter by recency at query time. This is not a complete solution, but it eliminates the most common failure mode.

from datetime import datetime, timedelta
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import FAISS
from langchain_core.documents import Document

embeddings = OpenAIEmbeddings()

# Store preferences with timestamps
documents = [
    Document(
        page_content="User prefers dark mode for their IDE.",
        metadata={"timestamp": "2026-01-15T10:00:00", "type": "preference"}
    ),
    Document(
        page_content="User prefers light mode for their IDE.",
        metadata={"timestamp": "2026-03-20T14:30:00", "type": "preference"}
    ),
    Document(
        page_content="User's preferred code editor is VS Code.",
        metadata={"timestamp": "2026-02-01T09:00:00", "type": "preference"}
    ),
]

vectorstore = FAISS.from_documents(documents, embeddings)

# Query with temporal awareness
query = "What is the user's display mode preference?"
results = vectorstore.similarity_search(query, k=3)

# Filter to only the most recent preference for display mode
pref_results = [
    doc for doc in results
    if doc.metadata.get("type") == "preference"
    and "mode" in doc.page_content.lower()
]

# Sort by timestamp descending to get the newest fact first
pref_results.sort(key=lambda d: d.metadata["timestamp"], reverse=True)

# The first result is now the current preference
print(f"Current preference: {pref_results[0].page_content}")
print(f"Stored at: {pref_results[0].metadata['timestamp']}")

This is better, but it is still fragile. You are relying on string matching in the metadata and manual sorting. For a real agent, you need the retrieval layer itself to understand fact supersession — the idea that a newer fact about the same topic should automatically replace an older one during retrieval.

Temporal Memory vs Vector Database: A Comparison

The differences between a traditional vector database approach and a temporal memory system go beyond timestamps. Here is how they compare across the dimensions that matter for agent development:

Capability Vector Database (Naive) Temporal Memory System
Semantic similarity search Yes Yes
Timestamp awareness No (requires manual metadata) Built-in, indexed
Fact supersession No — both old and new facts rank equally Yes — newer facts automatically outrank older ones for the same entity
Freshness ranking No — ranking is purely semantic Yes — recency is a first-class signal in the ranking function
Historical retrieval Possible but clumsy — you need to filter manually Native — the system knows what was true at time T
Memory-type retention One-size-fits-all Different rules for preferences, facts, observations, and relationships
Contradiction detection No — the model must resolve conflicts Yes — the system flags and handles contradictions
Multi-signal retrieval Semantic only Semantic + lexical + temporal + entity-based
Query complexity Simple — embed query, find nearest Multi-stage — retrieve candidates, apply temporal logic, rank

The key insight is that a temporal memory system does not replace semantic search — it augments it. The semantic similarity signal is still valuable. But it needs to be combined with temporal signals to produce reliable results for agents that operate over time.

How Fact Supersession Works

Fact supersession is the mechanism by which a memory system decides that a newer fact should replace an older one during retrieval. This is more nuanced than it sounds because you need to determine:

  1. What constitutes the "same fact": "User prefers dark mode" and "User switched to light mode" are about the same attribute. But "User prefers dark mode" and "User likes coffee" are not.
  2. When supersession applies: Some facts do not supersede each other — a user can have both a preferred editor and a preferred display mode. Supersession only applies when two facts describe the same attribute with conflicting values.
  3. How to handle explicit historical queries: When a user asks "What did I prefer six months ago?", the system should retrieve the fact that was current at that time, not the one that is current now.

In practice, this means the memory system needs to track:

  • The entity or attribute a fact describes
  • The time range during which the fact was valid
  • Whether the fact has been explicitly invalidated by a newer observation

This is fundamentally different from a vector database, which stores independent chunks with no relationship to each other.

Why This Matters for Agent Builders

If you are building an AI agent that remembers user information — whether for a personal assistant, a sales copilot, a support agent, or a workflow automation tool — the memory layer is part of your product's reliability.

Consider these scenarios:

  • Personal assistant: The agent tells a user "You prefer vegetarian restaurants" when the user switched to omnivore three months ago. Trust broken.
  • Sales copilot: The agent presents outdated pricing to a prospect because the old pricing document is more semantically similar to the conversation context. Deal lost.
  • Support agent: The agent references a resolved issue as if it is still active because the resolution note was stored but never ranked above the original complaint. Ticket reopened.

In each case, the underlying embedding model is working correctly. The problem is that the retrieval system has no mechanism for temporal reasoning. The agent is doing its best with what it has — and what it has is a pile of semantically similar but temporally unsorted chunks.

Building a Temporal Memory Layer

There are several approaches to adding temporal awareness to your agent's memory, ranging from simple to sophisticated.

Approach 1: Timestamp Metadata with Recency Boosting

The simplest approach, which we showed in code above, is to attach timestamps to your vectors and boost recent results during retrieval. This works for basic cases but does not handle fact supersession or historical queries well.

Approach 2: Separate Fact Store with Vector Retrieval

A more robust approach is to maintain two systems: a vector store for semantic retrieval and a structured fact store (like a graph database or relational table) that tracks temporal validity. At query time, you retrieve candidates from the vector store and then filter or rank them using the structured store.

This is the pattern used by systems like LlamaIndex's knowledge graph retriever and LangChain's temporal memory modules. The vector store handles "what is relevant" and the structured store handles "what is currently true."

Approach 3: Native Temporal Indexing

The most integrated approach is to build temporal awareness into the retrieval engine itself. Instead of treating time as metadata, the indexing and ranking functions treat time as a first-class signal alongside semantic similarity.

This is the approach taken by Tellodb, which implements configurable decay curves per memory type and automatic fact supersession at the storage layer. The hybrid retrieval kernel combines semantic and temporal signals in a single query pass rather than requiring a multi-step retrieval pipeline.

Tellodb: All Three Approaches, One API

While the three approaches above represent a progression — from simple timestamp filtering to a fully integrated temporal engine — they also represent three different codebases, three different maintenance burdens, and three different failure modes. Tellodb was designed so you do not have to build any of them yourself.

Tellodb implements all three approaches natively through a single, consistent API. Here is what that looks like in practice:

from tellodb import TellodbClient

client = TellodbClient.from_cloud(api_key="sk-...")

# Store facts with automatic temporal indexing
client.ingest(
    entity_id="user-123",
    text="I prefer dark mode for my IDE.",
)
client.ingest(
    entity_id="user-123",
    text="I switched to light mode for my IDE.",
)

# Query with built-in temporal ranking — the newest fact ranks first automatically
hits = client.query(
    "What is the user's display mode preference?",
    entity_id="user-123",
)

for hit in hits:
    print(hit.textual_content)  # "I switched to light mode for my IDE." ranks first

Every call to ingest automatically attaches a timestamp, indexes the fact for semantic and lexical retrieval, and checks for fact supersession — a newer statement about the same attribute automatically downranks the old one. Every call to query combines semantic similarity, lexical matching, and temporal freshness into a single ranking score. There is no manual metadata filtering, no separate structured store, and no custom ranking function to maintain.

DIY Temporal Memory vs Tellodb

To make the difference concrete, here is a side-by-side comparison of what it takes to implement temporal memory manually versus using Tellodb:

DIY approach — what you build yourself:

# Step 1: Set up vector store with timestamp metadata
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import FAISS
from langchain_core.documents import Document

embeddings = OpenAIEmbeddings()

documents = [
    Document(
        page_content="User prefers dark mode.",
        metadata={"timestamp": "2026-01-15", "type": "display_preference"}
    ),
    Document(
        page_content="User prefers light mode.",
        metadata={"timestamp": "2026-03-20", "type": "display_preference"}
    ),
]

vectorstore = FAISS.from_documents(documents, embeddings)

# Step 2: Retrieve, then filter and sort manually
query = "What display mode does the user prefer?"
raw_results = vectorstore.similarity_search(query, k=5)

# Step 3: Manually detect conflicting facts about the same attribute
display_results = [
    doc for doc in raw_results
    if doc.metadata.get("type") == "display_preference"
]
display_results.sort(
    key=lambda d: d.metadata["timestamp"], reverse=True
)

# Step 4: Use only the most recent
current = display_results[0].page_content
# "User prefers light mode."

# But what if the query is "What did the user prefer in February?"
# You need a completely different retrieval path for historical queries.
# And what if there are 30 preference types? The metadata taxonomy
# becomes unmanageable.

Tellodb approach — what you write:

from tellodb import TellodbClient

client = TellodbClient.from_cloud(api_key="sk-...")

client.ingest(entity_id="user-123", text="User prefers dark mode.")
client.ingest(entity_id="user-123", text="User prefers light mode.")

# Current preference — supersession handled automatically
hits = client.query(
    "What display mode does the user prefer?",
    entity_id="user-123",
)
print(hits[0].textual_content)  # "User prefers light mode."

# Historical query — same API, different time range
historical = client.query(
    "What display mode did the user prefer?",
    entity_id="user-123",
)
print(historical[0].textual_content)  # "User prefers light mode." (newest)

The DIY approach requires you to build and maintain a metadata taxonomy, a filtering pipeline, a recency sorting mechanism, and separate retrieval paths for current vs. historical queries. Tellodb provides all of this through a single ingest/query interface where temporal ranking, fact supersession, and historical retrieval are built into the retrieval kernel — no plumbing required.

External Resources

If you want to go deeper on the topics covered here, these resources are worth reading:

  • LangChain Memory Documentation: The official guide to memory modules in LangChain, including conversation buffer memory, entity memory, and knowledge graph memory. https://python.langchain.com/docs/modules/memory/
  • LlamaIndex Knowledge Graph Index: How LlamaIndex structures and retrieves knowledge graph triples for temporal and relational queries. https://docs.llamaindex.ai/en/stable/indices/knowledge_graph/
  • A Survey of Temporal Knowledge Graph Completion: An academic survey covering methods for reasoning over time in knowledge graphs, relevant to understanding how temporal constraints are modeled formally. https://arxiv.org/abs/2406.17811
  • RETRO: Improving Language Models by Retrieving from Trillions of Tokens: A foundational paper on retrieval-augmented generation that discusses retrieval at scale, relevant context for understanding the limitations of retrieval-only approaches. https://arxiv.org/abs/2112.04426
  • MemGPT: Towards LLMs as Operating Systems: A paper on managing long-term memory for LLMs, including hierarchical memory structures with different retention policies. https://arxiv.org/abs/2310.08560

Key Takeaways

  1. Vector databases are excellent at semantic similarity, but they have no native concept of time. They cannot distinguish between a current fact and a superseded fact if both are semantically similar to the query.

  2. Temporal memory adds critical primitives: timestamps, freshness ranking, fact supersession, and memory-type-specific retention. These primitives are necessary for agents that operate over time.

  3. The most common failure mode is contradictory context — the agent retrieves both old and new versions of a fact and cannot determine which one to trust.

  4. The fix is architectural, not prompt-based. Adding "use the most recent information" to your prompt does not solve the problem if the retrieval layer does not return the most recent information first.

  5. Start simple (timestamp metadata + recency filtering) and move toward more integrated solutions as your agent's memory requirements grow.

The question is not whether you need temporal memory for your agent. If your agent remembers anything that changes, you already do. The question is whether you are handling it at the retrieval layer — where it belongs — or pushing the problem into your prompts and hoping the model figures it out.