Data Intelligence · RAG · PostgreSQL · Provenance

The citation is a row, not a sentence: logging retrieval so a pipeline can be audited

Published · Updated

The RAG paper left provenance as an open problem. If nobody reads the output before it publishes, the only audit trail you get is the one you write at query time.

The paper that introduced RAG was explicit about what it did not solve. Alongside strong results on three open-domain QA tasks, Lewis and co-authors noted that providing provenance for their decisions and updating their world knowledge remain open research problems. Those two sentences describe the daily reality of any pipeline that retrieves, writes and publishes without a human reading the output first. Provenance is not a model feature you enable. It is a row you insert.

Only the retrieved set has addresses

RAG pairs a parametric memory, a pre-trained seq2seq model, with a non-parametric memory, a dense vector index accessed by a neural retriever. The parametric half has no addresses: there is nothing to point at when you ask where a sentence came from. The index half is made of rows with identifiers. So the auditable part of any generated paragraph is the retrieved set, and it only stays auditable if you record it as the query runs.

What a citation row has to contain

Start with the obvious fields: the query text, the identifiers of the chunks returned, and their rank order. That alone separates what the model saw from what it produced. But rank order is not meaningful without the configuration that produced it, and in pgvector that configuration is a choice you made rather than a constant. Record it beside the result.

By default pgvector performs exact nearest neighbour search, which provides perfect recall. Adding an index switches to approximate search and trades some recall for speed. HNSW has better query performance than IVFFlat on the speed-recall trade-off, at the cost of slower build times and more memory. The dynamic candidate list for search defaults to 40. A citation that omits which of these was in force cannot be reproduced later.

Checking the quote is an exact-match job

Logging which chunk was retrieved does not prove the published sentence reflects it. Verification means asking whether a specific string occurs in a specific source document, and embedding models are the wrong tool: they excel at capturing semantic relationships but can miss crucial exact matches. Postgres full-text search exists for precisely this, with tsvector for preprocessed documents and tsquery for processed queries, where plain pattern operators lack essential properties.

Because both live in the same database, the check is a join rather than a second system. Take the citation row, reach the source document it names, and test the span against a full-text index. Full-text indexing allows documents to be preprocessed and an index saved for rapid searching, so the verification pass stays cheap enough to run on every publication instead of on a sample.

A failed retrieval reads like a confident paragraph

Retrieval failure is routine, not exceptional. Contextual retrieval reduces the number of failed retrievals by 49 per cent, and by 67 per cent when combined with reranking, which tells you how much failure was there to remove. Anthropic measures this as 1 minus recall@20: the percentage of relevant documents that fail to be retrieved within the top 20 chunks. Without a log, a missing chunk and an ignored chunk look identical in the output.

With the retrieved set stored, those two cases separate cleanly, and they take different fixes. If the relevant chunk never appeared in the top 20, the work is on the retrieval side: contextual retrieval, reranking, a wider candidate list than the default 40, or exact search as a reference point. If it did appear and the text went elsewhere, no index change will help.

Nobody is reading the eighth article

Scale is what turns this from hygiene into necessity. On the Tatano Energy platform we run eight SEO articles published every day, seven languages served, four country domains indexed separately, and manual intervention required: 0. At that cadence there is no reviewer who could plausibly trace a claim by hand. The only reviewer available is a query over the retrieval log, which means the log has to exist before you need it.

Treat provenance as a schema decision made at build time. One table keyed to the generation, holding the query, the chunk identifiers, the ranks and the search settings, plus a full-text check on any quoted span. None of it improves retrieval quality on its own. What it buys you is the ability to answer where a published sentence came from, and to tell a retrieval problem apart from a generation problem before you start tuning the wrong one.

Sources

pgvector (GitHub) — Open-source vector similarity search for Postgres — https://github.com/pgvector/pgvector

arXiv — Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks (Lewis et al., arXiv:2005.11401) — https://arxiv.org/abs/2005.11401

Anthropic — Introducing Contextual Retrieval — https://www.anthropic.com/engineering/contextual-retrieval

PostgreSQL 18 Documentation — Full Text Search: 12.1. Introduction — https://www.postgresql.org/docs/current/textsearch-intro.html

Neurolinks case study — Four markets, one codebase — https://neurolinks.be/work/tatano-energy

Working on a project where these methods apply?