Skip to main content

ragfresh

CI

Detect stale, drifted, and ghost documents in RAG vector indexes.

Embeddings get created once. Source documents keep changing. Nothing tells you your index has drifted from reality until a user gets an answer grounded in a document that was edited — or deleted — weeks ago. ragfresh closes that gap with a lightweight sidecar ledger and a CI-gateable check.

Staleness classes

  • unchanged — source content hash matches what was indexed
  • changed, not reindexed — the source doc was edited since it was indexed
  • deleted, still indexed — the source doc is gone but the vector store still serves it (ghost citation risk — the worst class)
  • not yet indexed — present in the source, absent from the ledger/store

Quickstart

pip install ragfresh

ragfresh init ./docs --db ragfresh.db
# ... time passes, docs change ...
ragfresh check ./docs --db ragfresh.db

Exits non-zero when changed, not reindexed or deleted, still indexed is non-empty, so it's usable as a CI gate.

check only detects staleness. To resolve it -- re-embed changed/missing docs and remove ghost entries from both the store and the ledger -- run:

ragfresh reindex ./docs --db ragfresh.db --chroma-collection my-index

reindex uses a local embedding model (no API calls) and requires a writable store -- Chroma or pgvector; the default ledger-only store is read-only. Preview what it would do without writing anything:

ragfresh reindex ./docs --db ragfresh.db --chroma-collection my-index --dry-run

Sources & stores

  • Sources: filesystem, git (ragfresh.sources.GitSource)
  • Stores:
    • Chroma — pip install ragfresh[chroma], then --chroma-collection NAME [--chroma-path DIR]
    • pgvector (any Postgres table with a doc_id column and a vector-typed embedding column) — pip install ragfresh[pgvector], then --pg-dsn DSN [--pg-table documents] [--pg-id-column id] [--pg-embedding-column embedding] [--pg-document-column TEXT_COL] (the embedding/document columns are only needed for reindex, not check)
    • Neither flag given → the ledger itself stands in as the index (read-only)

Semantic drift scoring

Hash-based staleness treats any byte-level change as "changed" — a typo fix and a rewritten paragraph look identical. --score-drift classifies each changed_not_reindexed doc as cosmetic or meaningful using cosine distance between embeddings of the old and new text:

ragfresh init ./docs --db ragfresh.db          # snapshots content by default
ragfresh check ./docs --db ragfresh.db --score-drift
#   [CHANGED] report.md (meaningful, score=0.812)

Requires ragfresh[chroma] (reuses its bundled local ONNX MiniLM model — downloaded once, then fully offline, no API calls) and content snapshots from init (pass --no-store-content at init time to opt out and disable this later). Tune sensitivity with --drift-threshold (default 0.15).

By default drift scoring uses chromadb's bundled MiniLM model. If your production embeddings diverge enough from that model that drift scores wouldn't track them well, point --embedding-model (on check and reindex) at any sentence-transformers model name instead (requires the sentence-transformers package).

Chunked indexes

Most production RAG pipelines chunk a doc into several vectors (ids like report.md#0, report.md#1, ...), not one vector per doc. By default ragfresh assumes indexed ids equal doc_ids -- pass --chunk-separator on check to fold chunk ids back to their source doc:

ragfresh check ./docs --db ragfresh.db --chroma-collection my-index --chunk-separator "#"

A doc counts as indexed if any of its chunks are present. ragfresh doesn't chunk on your behalf -- it only needs to know how to reverse whatever chunking scheme you already use, via a separator (CLI) or a chunk_id_to_doc_id callable (library API, ragfresh.check.check). Detection-only: reindex doesn't support chunked stores yet -- it writes one vector per doc_id, so re-embedding into a chunked index would require ragfresh to own the chunking strategy, which it deliberately doesn't.

Development

pip install -e ".[dev]"
pytest
ruff check .
mypy

Status

v0.1.0. Filesystem + git sources; SQLite ledger; Chroma + pgvector stores (both readable and writable, for check and reindex); CLI with drift scoring (pluggable embedding model), chunk-aware detection, and reindex (flat indexes only, with --dry-run); staleness-injection benchmark (benchmarks/staleness_injection/); CI across 3 OSes × 3 Python versions plus a lint/type-check job (ruff + mypy). 44 tests passing.

Known gap: reindex doesn't support chunked indexes (writes one vector per doc_id; check handles chunking, reindex deliberately doesn't own a chunking strategy).

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ragfresh-0.1.0.tar.gz (21.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ragfresh-0.1.0-py3-none-any.whl (18.9 kB view details)

Uploaded Python 3

File details

Details for the file ragfresh-0.1.0.tar.gz.

File metadata

  • Download URL: ragfresh-0.1.0.tar.gz
  • Upload date:
  • Size: 21.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.5

File hashes

Hashes for ragfresh-0.1.0.tar.gz
Algorithm Hash digest
SHA256 373b38146915e8e683f3def09445fd3ccc7b52e95b2530022b52cb01375b85e9
MD5 689fe6ca234687c840f1a2975527a2ca
BLAKE2b-256 356614c4d591c3be461a7fbfb151c88481eeddcfcd9577f7d8529098f9a0bcd1

See more details on using hashes here.

File details

Details for the file ragfresh-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: ragfresh-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 18.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.5

File hashes

Hashes for ragfresh-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 26b1f0810d26f13734a434ce810f43e71f8389662ceb81dc1d99007d86446ca9
MD5 2332ce71115154013589617d0e7128ea
BLAKE2b-256 958c301eba27545cb5e769837bf953f435ea6fd96aadb05c09b58019ed6eba58

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page