Deciding whether an AI system's retrieved context is still true is not a new problem. Distributed systems solved it decades ago, and most retrieval pipelines are quietly failing because they never borrowed the answer.

The pattern you need is a versioned cache key: one that encodes what the answer was built from, not just when it was built. Everything below is that idea applied to a retrieval-augmented generation (RAG) pipeline, the standard setup where you fetch documents from a store and paste them into a model's prompt.

The industry keeps calling this a new discipline

"Context architecture" now has named pillars, vendor products, and a build-versus-buy debate (Stack Overflow ran one in August). Read enough of that material and you'll notice it already speaks fluent cache: eviction, warming, partitioning, hit rate. The vocabulary is borrowed. The engineering underneath it mostly isn't.

That matters because vendors selling a platform have an incentive to describe stale context as a missing product category rather than a missing key design. It isn't. The genuinely new part is the failure mode, not the problem. A distributed cache serving a stale entry eventually shows up as a wrong number in a report. A language model serving stale context produces a fluent, confident, well-formatted answer that is wrong, and nothing in your stack throws.

Recency is not the same problem as provenance

Nearly every pipeline patches staleness with a time to live (TTL), a clock that says refetch if this entry is older than N hours. TTL answers one question: how long has this sat here. It cannot answer the question you actually care about: is the thing this was derived from still the thing it was derived from.

That turns cache invalidation from a simple expiry problem into a provenance problem.

Provenance means the chain of where a piece of context came from and what version of the source it reflects. A key built from sha256(query) is stable across source edits by design. Update the underlying document and you still get a hit. Same question, different truth, no error.

Build the key out of what actually changed

Hash the source, not just the question. If your store gives you an ETag or a content hash, use it. If not, hash the bytes. Fold in the last-modified timestamp and the query, and a cache hit becomes a strong signal: this answer was built from exactly this version of exactly these documents.

Versioned context cache key
import hashlib
import json
from dataclasses import dataclass
from datetime import datetime


@dataclass(frozen=True)
class SourceDoc:
    doc_id: str
    version: str        # ETag, content hash, or git SHA
    modified_at: datetime


def context_cache_key(query: str, docs: list[SourceDoc], *, tier: str) -> str:
    """Key a cached context on its provenance, not on a clock."""
    provenance = sorted(
        [d.doc_id, d.version, d.modified_at.isoformat()]
        for d in docs
    )
    payload = json.dumps(
        {"tier": tier, "query": query.strip().lower(), "sources": provenance},
        separators=(",", ":"),
    )
    digest = hashlib.sha256(payload.encode()).hexdigest()[:32]
    return f"ctx:{tier}:{digest}"

Two details do the work. sorted() makes the key independent of retrieval order, so the same document set always hashes the same way. And each entry carries a version plus a last-modified timestamp, so editing a source changes the key and the old entry becomes unreachable instead of quietly correct-looking. The 128-bit truncated digest is far more than enough headroom for a cache namespace; keep the full 64 characters if you'd rather not think about it at all.

Requires Python 3.9 or newer for the built-in list[SourceDoc] annotation. On 3.7 or 3.8, add from __future__ import annotations.