Business

What to Store with Each Passage So Retrieval Can Be Audited

Antonio Evans11 min read

A passage card on a table with five labeled fields (source_id, doc_version, section, ACL, ingest_ts), each field connected by a dashed line to its origin in a stack of source documents.
Key points

Key points

  1. 1Store five fields on every passage: source_id, doc_version, section, ACL, and ingest_ts, because each one answers a different question when a query fails.
  2. 2Enforce ACL as a query-time filter rather than trusting the model to ignore restricted text after retrieval, since permissions often vanish when a document becomes chunks.
  3. 3Keep source_id and ingest_ts immutable per passage record, so edits create a new record and the audit trail stays readable.
  4. 4Separate retrieval scoring from generation scoring, because one end-to-end number cannot tell you whether a passage was missing, filtered, or simply stale.

Why a passage needs a paper trail

A retrieval-augmented generation pipeline is a chain, not a single model call. A query gets rewritten, embedded, searched, filtered, reranked, assembled into a prompt, and only then answered. Every stage can fail, and a poor answer at the end can originate anywhere along that chain. Evaluation frameworks split retrieval from generation for this reason: when the retriever never surfaced the right passage, no prompt tweak or model swap will fix the answer Braintrust.

The practical consequence is that you must be able to pick up one retrieved passage and answer three questions about it. Where did it come from? Did a version rule or an access rule explain its absence? Was it even in the index when the query ran? Without metadata attached to the passage, every one of those questions turns into guesswork, and "retrieval is bad" becomes the most precise diagnosis you can offer.

A four-layer evaluation framework makes the gap concrete. Data, retrieval, generation and system layers each answer a different question, and the data layer asks whether the right, current content sits in the index at all. Its freshness and cleanliness checks need no labels, while coverage measurement does Olostep. Metadata is what makes those data-layer checks possible after an incident. When a passage is missing from a result set, freshness and coverage tell you whether to tune the retriever or fix ingestion.

Metadata is also useful beyond auditing. A systematic study of metadata-aware retrieval on repetitive corpora such as regulatory filings found that including structural cues like section headings and document identifiers improves retrieval, because it raises cohesion inside a document and cuts confusion between near-identical documents arXiv. The same fields you store for audits help the retriever tell one company's boilerplate from another's.

The five-field schema

The minimal auditable passage record has five fields. Each is optional in some systems and mandatory here, because dropping any one of them removes a distinct diagnostic path.

FieldTypePopulated byChanges when
source_idstring, stableConnector at ingest, from the source system's own identifierOnly if the document is re-keyed at the source; never on edit
doc_versionstring or integerConnector at ingest, from a source revision or a content hashEvery time the source changes and is re-ingested
sectionstringParser, from heading hierarchy or page rangeWhen the document is re-chunked or the parser changes
ACLlist of group or tenant identifiersConnector, copied from source permissionsWhen source permissions change, and on ACL sync jobs
ingest_tstimestamp, UTCIndexing worker at write timeNever; immutable per passage record

A sixth field, a passage identifier, is assumed. It is the primary key that lets you look up a record at all.

The important design decision is that source_id and ingest_ts are immutable for a given passage record. If the source document changes, you write a new record with a new doc_version and a new ingest_ts, then delete or tombstone the old one. Mutation destroys the audit trail: a record whose doc_version and ingest_ts were overwritten can no longer tell you what was indexed when.

A minimal record looks like this in most vector stores, where the metadata payload carries everything except the embedding:

{
  "id": "psg_9f31c2",
  "source_id": "hr-policy-remote-work",
  "doc_version": "rev-42",
  "section": "Section 4.2 Eligibility",
  "acl": ["group:all-employees"],
  "ingest_ts": "2026-03-02T14:11:07Z"
}

Two conventions save trouble later. Store timestamps in UTC with an explicit zone, so comparing an ingest_ts against a query log is a subtraction rather than time-zone archaeology. Store ACL as a list of stable group identifiers, never display names, because names get edited and identifiers do not.

What each field is for and when it changes

source_id is the stable key for the source document. Take it from the source system where one exists: a document management ID, a path in a version-controlled repository, a ticket number. Where the source has no identifier, derive one from a canonical URL or path at ingest and keep it in a mapping table. The failure to avoid is deriving source_id from the document title, because titles get edited and the identifier silently changes underneath you.

doc_version answers "which revision of the source does this passage represent?" The simplest reliable form is a content hash of the parsed source, which changes exactly when content changes and asks nothing of the source system. Where the source carries its own revision numbers, store both. doc_version is what lets you state, with confidence, that the passage retrieved for a query came from the revision of the policy in force at the time, or that it did not.

section is the locator inside the document. For structured documents, use the heading hierarchy ("Section 4.2 Eligibility"). For paginated documents, use a page range, which is also what page-level chunking preserves: page-level chunking performed well in published benchmarks for paginated documents specifically Firecrawl. The section field changes whenever the document is re-chunked, so treat it as a property of the passage rather than of the document.

ACL is the access scope required to see the passage. It is populated by copying permissions from the source system at ingest and kept current by an ACL sync job. This is the field most often lost in flight: source permissions can be dropped when a document is split into chunks and embeddings, and authorization must stop unauthorized text from entering the model context rather than relying on the model to ignore it OneSourceCloud. A passage without a populated ACL is either invisible to everyone or, worse, visible to everyone. Neither is defensible, so make ingest fail closed: a missing ACL blocks the write.

ingest_ts records when the passage entered the index and never changes. It is the field that separates a stale index from a weak retriever, and the one teams most often skip, because it feels like infrastructure trivia until the first answer that cites a superseded policy.

Using the fields to audit a failed retrieval

Suppose a user asks a question and the system answers from an old policy, or returns nothing useful. The audit is a short sequence of checks, and each check leans on one field.

Step 1: confirm the passage existed. Look up the cited passage by identifier and read its record. If the user insists the right answer exists but the system cited nothing, search the index directly by source_id and section. A miss here is a coverage failure in the data layer, not a retrieval failure, and no retriever tuning will surface text that was never indexed Olostep.

Step 2: trace the passage to its source and version. Fetch the source document at doc_version and confirm the stored passage text matches. A mismatch means the parser or chunker produced text that differs from the source, a data-quality problem that will recur until the parser is fixed. A match means the index faithfully represents a revision of the document, and the question becomes whether it was the right revision.

Step 3: check the ACL. Take the ACL from the passage record and evaluate it against the groups the querying user belonged to at query time. If the user's groups matched no ACL entry, the passage was excluded by design and the answer was right to omit it. This check stops you from launching an expensive retrieval investigation when the real cause is that a permissions sync removed a user from a group, or that a document was classified more tightly than its readers expected. Security audit practice tests these cases explicitly:

Authorization testExpected resultEvidence to keep
User removed from a source groupNew queries cannot retrieve restricted passages within the stated propagation timeIdentity change, index update, query trace, denial
Two tenants use similar termsEach tenant receives only its own documentsFiltered candidate set, cross-tenant negative test
Document access expiresPassages and caches become unavailable on scheduleExpiry event, retrieval test
Privileged support account usedAccess is approved, attributable, limited, loggedSession record, reviewed audit event

That table comes from a security audit framework that treats retrieval quality as a control in its own right, on the grounds that poor retrieval becomes a security problem when it cites obsolete policy or omits a critical warning OneSourceCloud.

Step 4: compare ingest_ts with the query time. If the query was written before the passage was ingested, the index was not stale; the question predates the content and the answer is correct. If the query came after ingest_ts and a newer doc_version of the same source is already in the index, the system may have retrieved an older passage because both revisions ranked close together. This is where version-aware filtering pays off: when sources revise often, filter to the current doc_version per source_id at query time, or boost current versions in ranking.

A worked example makes the arithmetic plain. The user asked on 3 June. The retrieved passage has ingest_ts of 14 May, doc_version rev-41, and ACL of group:all-employees. The source's current revision, rev-42, was ingested on 28 May. The user belongs to all-employees. The passage was present, authorized, and indexed before the query, but it was not the current revision. The diagnosis is a versioning problem, not a permissions or freshness problem, and the fix is to prefer current revisions in ranking rather than to re-ingest anything.

Measuring whether the audit trail is paying off

Metadata lets you audit individual failures. It also lets you measure the pipeline, provided retrieval and generation scores stay separate. Retrieval metrics ask whether the right passages ranked near the top; generation metrics ask whether the answer was supported and correct; measuring them apart turns a vague "quality is low" into a diagnosis that names a component Braintrust.

The two classic retrieval measures are precision and recall. Precision is the fraction of retrieved instances that are relevant; recall is the fraction of relevant instances that were retrieved Wikipedia. In ranking systems both are computed at a cutoff K: precision at K divides the relevant items in the top K by K, and recall at K divides them by the total number of relevant items Evidently AI. A worked calculation shows the mechanics: with 80 relevant records in a database, a search returning 60 records of which 45 are relevant achieves recall of 45/80, or 56 percent, and precision of 45/60, or 75 percent Creighton.

The catch is that recall requires knowing how many relevant records exist, which requires labels, and the labels must point at a specific document revision. A golden question that says "the remote work policy" is not a label; one that says "the remote work policy, rev-42, section 4.2" is. When reference answers expire because source pages change, questions need re-checking on a schedule and whenever a monitored source changes, and storing the source identifier with every golden question is what maps a changed page back to the questions needing review Olostep.

This is where the schema earns its keep twice. In production, source_id and doc_version let you trace why a specific query failed. In evaluation, the same fields let you catch drift: a rising share of passages whose doc_version is not current is a versioning problem you can see before anyone complains. Track three numbers on a dashboard: the share of indexed passages older than your freshness window, the share of sources with more than one live doc_version, and the share of golden questions whose reference source has changed since labeling.

One decision rule, because the precision-recall tradeoff is real and teams often tune it blindly. Precision and recall pull in opposite directions, and which matters depends on the application: a system that must find everything on a topic needs strategies that favor recall and tolerate noise, while a system that must return a few good results favors precision Creighton. For a compliance assistant, a missed current policy passage is a recall failure with legal consequences, so tune toward recall. For a customer-facing answer box, noise costs trust, so tune toward precision. The schema does not change the tradeoff, but it tells you which side you are failing on.

Vocabulary matters too. Stories about search quality are often stories about synonyms, and precision problems commonly come from the query and the index using different words for the same thing. Expanding queries with synonyms and related terms raises recall but lowers precision, because synonyms are rarely exact Creighton. When you audit a "nothing found" complaint, check whether query and passage share vocabulary before rebuilding anything, since the fix is sometimes query expansion rather than a change to the corpus.

Where the schema interacts with chunking

The section field is the one tied to chunking, and chunking is not a neutral preprocessing step. A systematic study of fixed-size chunking across datasets found that chunk size materially affects retrieval: smaller chunks of 64 to 128 tokens worked best for datasets with concise, fact-based answers, while larger chunks of 512 to 1,024 tokens improved retrieval where broader context was needed, and different embedding models showed different sensitivities to chunk size arXiv.

The implication for the schema is that section should identify the unit a reader would use to find the passage, not merely a token offset. A 400-token passage that spans the end of one section and the start of another should record both, or be split. If your chunking strategy changes, every section value in the index changes with it, which is why section churn is tracked separately from doc_version: re-chunking is not a new document revision, and conflating the two makes audit logs unreadable.

Retrieval quality also shifts with how the pipeline handles chunks. Chunk boundaries, embedding choices, lexical search, metadata filters, rerankers and corpus freshness can all change the result for the same model and prompt Online Inference. The five-field schema pins down three of those variables (freshness, revision and access scope), so when you compare two configurations the difference is the thing you changed rather than the corpus drifting underneath you.

For teams on managed knowledge bases the same principle applies one level down. Amazon Bedrock Knowledge Bases, for example, manages ingestion, indexing and retrieval over your data sources, with options to use your own models for embedding and ranking AWS. Even with managed retrieval, store the five fields in the chunk payload wherever custom metadata is allowed, because the audit questions are yours to answer, not the platform's.

When the retrieval layer is stable and the remaining sprawl is model calls across embedding, reranking and generation, Quanta Inference puts text, image, video, audio and embedding models behind one OpenAI-compatible API with prepaid pricing in US dollars: https://quantaintelligence.ai/inference.

FAQ

FAQ

Do I need doc_version if I already store ingest_ts?

Yes, because they answer different questions. ingest_ts tells you when a passage entered the index; doc_version tells you which revision of the source it represents. A passage can carry an old ingest_ts while still being the current revision if the source never changed, and two passages can share an ingest_ts while representing different revisions if a batch re-index pulled mixed revisions.

Should ACL be stored on the passage or resolved at query time?

Store it on the passage and enforce it as a query-time filter. Resolving permissions live against the source system on every query is slower and fails when that system is unavailable, while storing nothing means the model decides what to reveal, which is no control at all. Combine the two: store the ACL, filter on it at query time, and run a sync job that updates stored ACLs when source permissions change.

How do I handle a document that is deleted at the source?

Delete or tombstone its passages at the same time, and keep an audit record. If a passage vanishes entirely you lose the ability to explain past answers that cited it. A tombstone holding source_id, doc_version, section, the final ACL, ingest_ts and a deletion timestamp preserves the trail while keeping content unretrievable, and security audit practice expects deletion to reach chunks, embeddings, caches and backups, not just the source OneSourceCloud.

What is the smallest useful version of this schema?

If you can add only three fields this week, add source_id, doc_version and ingest_ts. They answer the two most common failure questions, which document is this and was it current, and they cost almost nothing at ingest time. Add ACL next, since its absence is a security incident rather than an inconvenience, and section last, because it mainly speeds up debugging and helps retrieval on repetitive corpora.

How do I know the metadata itself is correct?

Sample and test it the way you would test any pipeline output: pick a random set of passages weekly, fetch the source at the recorded doc_version, and confirm the text, section and ACL all match. Test ACL cases explicitly, including a user removed from a group, two tenants with overlapping vocabulary, and an expired access grant, keeping evidence for each OneSourceCloud. If you grade these checks with an LLM judge, Evidently publishes guidance on building judges and test sets Evidently AI.

Sources

Sources

  1. What is RAG evaluation? Measuring retrieval quality and ...
  2. How to Evaluate RAG Systems: A Four-Layer Framework
  3. Utilizing Metadata for Better Retrieval-Augmented Generation
  4. Best Chunking Strategies for RAG (and LLMs) in 2026
  5. Audit RAG Security Across Data, Retrieval, and Output- ...
  6. Precision and recall - Wikipedia
  7. Precision and recall at K in ranking and recommendations - Evidently AI
  8. PDF Measuring Search Effectiveness
  9. Rethinking Chunk Size for Long-Document Retrieval
  10. RAG evaluation metrics
  11. Retrieve data and generate AI responses with ...
  12. A complete guide to RAG evaluation: metrics, testing and ...
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles