Skip to content
Docs for briefcase-ai v3.3.0see what’s new.

RAG Versioning

RAG Versioning

RAG versioning records exactly which documents were embedded, with which model, at which source commit — so a retrieval can be tied back to the exact corpus state, and a stale index is caught before it answers anything.

rag For: governance & audit

How it works

  1. Build a manifest. create_embedding_batch then create_manifest fingerprint the indexed documents into an EmbeddingManifest.

  2. Check invalidation. check_invalidation compares the current documents and model against the manifest to detect a stale index.

  3. Rebuild. When sources changed, rebuild_index produces a fresh manifest so retrievals reflect the current corpus.

flowchart LR
    A["Documents"] --> B["create_embedding_batch()"]
    B --> C["create_manifest()"]
    C --> D["EmbeddingManifest"]
    E["Current documents + model"] --> F["check_invalidation()"]
    D --> F
    F --> G{"is_valid?"}
    G -- "no" --> H["rebuild_index()"]
    G -- "yes" --> I["Reuse index"]

Install

Terminal window
pip install briefcase-ai[rag]
from briefcase.rag import VersionedEmbeddingPipeline, Document

Build an Index

A Document carries an id, content, and optional metadata. The pipeline’s embedding_model is any object with an embed(texts) -> list[list[float]] method; optional name and version attributes are recorded on the manifest.

from briefcase.rag import VersionedEmbeddingPipeline, Document
class HashingEmbedder:
"""Trivial deterministic embedder for the example."""
name = "demo-embedder"
version = "1.0"
def embed(self, texts):
return [[float(len(t) % 7), float(len(t) % 11)] for t in texts]
pipeline = VersionedEmbeddingPipeline(embedding_model=HashingEmbedder())
documents = [
Document(id="kb-1", content="How to reset your password.", path="handbook/auth.md"),
Document(id="kb-2", content="Refund policy for digital goods.", path="handbook/refunds.md"),
]
batch = pipeline.create_embedding_batch(documents)
manifest = pipeline.create_manifest("support_kb", [batch])
print(manifest.index_name) # "support_kb"
print(manifest.document_count) # 2
print(manifest.model) # "demo-embedder"
print(manifest.status) # "current"

rebuild_index() chains both steps when you just want a fresh manifest:

manifest = pipeline.rebuild_index("support_kb", documents)

Detect Staleness

check_invalidation() compares the latest manifest against the current document set and model, and returns an InvalidationReport describing what changed.

# A document changed and one was added since the manifest was built.
current = [
Document(id="kb-1", content="How to reset your password (updated)."),
Document(id="kb-2", content="Refund policy for digital goods."),
Document(id="kb-3", content="Two-factor authentication setup."),
]
report = pipeline.check_invalidation("support_kb", current)
print(report.is_valid) # False
print(report.status) # "stale_documents"
print(report.added_documents) # ["kb-3"]
print(report.changed_documents) # ["kb-1"]
print(report.removed_documents) # []
print(report.model_changed) # False

status is one of: current, stale_documents, stale_model, stale_both, rebuilding. When the index is stale, rebuild it:

if not report.is_valid:
manifest = pipeline.rebuild_index("support_kb", current)

EmbeddingManifest

The manifest is the versioning artifact. Persist it to compare future builds against it.

FieldDescriptionWhy it matters
manifest_idUnique id for this buildNames the corpus version a retrieval ran against.
index_nameName of the indexGroups builds of the same index over time.
model / model_versionModel that produced the embeddingsA model change invalidates the index too.
source_commitSource commit the documents came fromTies the corpus to a versioned source.
document_countNumber of documents embeddedQuick sanity check on coverage.
document_hashesdoc_id -> content_hash at embed timeWhat check_invalidation compares against.
statusCurrent ManifestStatus valuecurrent vs stale_*.
manifest_hashDeterministic SHA-256 over the manifest contentTamper-evident fingerprint of the whole build.
print(manifest.manifest_hash) # integrity hash
serialized = manifest.to_json()

Instrument Retrieval

InstrumentedRetriever captures version provenance on each retrieved document. It is a reference implementation: the base retrieve() returns placeholder results (and emits a RuntimeWarning) so the provenance shape is clear. Override retrieve() with a real vector-store query — returning your own RetrievalResult objects — to silence the warning and use it for real.

from briefcase.rag import InstrumentedRetriever
class KbRetriever(InstrumentedRetriever):
def retrieve(self, query, top_k=5, similarity_threshold=0.7):
# Query your real vector store here, then wrap hits in RetrievalResult.
return super().retrieve(query, top_k, similarity_threshold)
retriever = KbRetriever(
vector_store=None, # your vector store client
lakefs_client=None, # resolves document_version (commit SHA)
repository="support_kb",
)
results = retriever.retrieve("how do I reset my password?", top_k=3)
for r in results:
print(r.rank, r.document_id, r.score, r.document_version)

Each RetrievalResult carries document_id, content, score, rank, document_version (the commit SHA the document was read at), and metadata.

Where this fits