Retrieval-augmented generation · PostgreSQL · Data modeling

Temporal RAG: Answer Questions Using the Correct Historical Version

Build version-aware retrieval with valid-time ranges, immutable sources, time-filtered ranking, and citations that preserve what was known when.

Published
Updated
Code examples
Illustrative
Reading time
4 minutes

Temporal RAG retrieves evidence that was valid at the time named by the question. It requires immutable document versions, explicit validity intervals, time filtering before ranking, and citations bound to the historical version. A “last updated” timestamp on the current document is not enough to answer what a policy said six months ago.

This guide uses PostgreSQL range types to model valid time. It also distinguishes valid time—when a fact applies in the modeled world—from system time—when your system learned or stored it.

Decide which time the question means

“What was the refund policy on March 1?” asks for valid time. “What did our support agent know on March 1?” asks for system time. A correction entered today may apply retroactively, so these axes can differ.

Store both when auditability matters:

AxisQuestion answeredExample fields
Valid timeWhen did this source or fact apply?valid_during
System timeWhen did the system store this version?recorded_during

Do not infer validity solely from crawl time. Prefer effective dates supplied by an authoritative source; otherwise record the value as unknown or observed-at and label the limitation.

Store immutable versions with non-overlapping validity

PostgreSQL provides timestamp range types and exclusion constraints that can reject overlapping ranges. Its range-type documentation shows tstzrange for time zones and GiST exclusion constraints for non-overlap. See PostgreSQL 18 Range Types.

CREATE EXTENSION IF NOT EXISTS btree_gist;
CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE document_versions (
  id uuid PRIMARY KEY,
  tenant_id uuid NOT NULL,
  document_id uuid NOT NULL,
  content_sha256 text NOT NULL,
  valid_during tstzrange NOT NULL,
  recorded_at timestamptz NOT NULL DEFAULT now(),
  canonical_url text NOT NULL,
  EXCLUDE USING gist (
    tenant_id WITH =,
    document_id WITH =,
    valid_during WITH &&
  )
);

Use half-open intervals such as [2026-01-01, 2026-04-01) so adjacent versions meet without overlapping. Document the boundary convention in the product; time-zone ambiguity at midnight can otherwise select the wrong version.

Corrections complicate non-overlap. If newly discovered evidence changes an old valid interval, insert a corrected system-time record rather than silently rewriting audit history. A full bitemporal schema stores a system-time range too; the simpler table above records only the insertion instant.

Attach chunks to versions, never only documents

CREATE TABLE temporal_passages (
  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  version_id uuid NOT NULL REFERENCES document_versions(id),
  ordinal integer NOT NULL,
  body text NOT NULL,
  embedding vector(1536) NOT NULL,
  UNIQUE (version_id, ordinal)
);

A citation must include version_id. Linking only to document_id allows a later edit to make the displayed source disagree with the generated answer.

Preserve a reader-visible historical snapshot only when you have the necessary rights and the deployment's documented retention rules allow it. If only the live canonical URL can be shown, state that the linked page may have changed and display the cited version's effective date and a limited excerpt you are authorized to reproduce.

Resolve time before semantic ranking

Parse an explicit as_of value from the request or require the caller to supply it. Do not let the model silently guess dates like “last quarter.” Resolve relative time against a known time zone and show the interpretation to the user.

SELECT p.id, p.body, v.id AS version_id, v.valid_during
FROM temporal_passages p
JOIN document_versions v ON v.id = p.version_id
WHERE v.tenant_id = $1
  AND v.valid_during @> $2::timestamptz
ORDER BY p.embedding <=> $3::vector, p.id
LIMIT $4;

The temporal predicate is part of candidate eligibility, just like authorization. Retrieving current neighbors and removing them after ranking can omit the best historical evidence, particularly with approximate indexes.

If the user did not specify time, define product behavior explicitly: current effective knowledge, latest recorded knowledge, or a clarifying question. Do not mix several versions without labels.

Handle sources with uncertain or conflicting dates

Use a three-state model rather than manufacturing precision:

  • asserted validity from an authoritative effective date;
  • observed validity from first and last successful observations; or
  • unknown validity.

Conflicting sources should remain separate evidence, not be overwritten into one synthesized “fact.” Return the conflict with publisher, version, and applicable dates. Source authority is domain-specific and belongs in retrieval or review policy, not in the embedding distance.

Evaluate temporal retrieval directly

Create cases where the same question has different correct answers at different dates. Include:

  • adjacent boundary instants;
  • gaps where no version applies;
  • retroactive corrections;
  • sources observed late;
  • conflicting authorities;
  • daylight-saving and time-zone boundaries; and
  • a current question after historical versions exist.

Measure version-selection accuracy before answer quality. Then score whether every time-sensitive claim cites a version applicable to the requested instant. A current answer produced from a historical version is as incorrect as the reverse.

Know the limits

Temporal RAG cannot reconstruct history you never stored. Starting version capture today does not prove what an unversioned page contained yesterday. Web archives may help, but their completeness and legal status must be evaluated separately.

Validity intervals can also differ within one document. If individual policies have separate effective dates, model assertions or sections at that granularity instead of assigning one interval to the entire file.

Production checklist

  • Valid time and system time have documented meanings.
  • Immutable versions retain hashes and provenance.
  • Temporal intervals use one boundary and time-zone convention.
  • Constraints reject accidental overlap where overlap is invalid.
  • Chunks reference versions, not only logical documents.
  • Authorization and time predicates are applied before ranking.
  • Citations expose the selected version and effective interval.
  • Unknown dates remain unknown instead of becoming crawl dates.
  • Tests cover boundaries, gaps, corrections, and conflicts.