Schedule icon
Webhook icon
Get icon
WorkingDirectory icon
Clone icon
Script icon
Docker icon
CreateCollection icon
If icon
Upsert icon
Delete icon
Query icon
Log icon
Fail icon
Set icon
SlackIncomingWebhook icon

Incremental Docs-to-Chroma RAG Sync with Deletions and a Retrieval Smoke Test

Sync Git docs into Chroma incrementally, embed only changed chunks with local FastEmbed, delete stale vectors, and gate each run on a retrieval test.

Categories
AIData

Most RAG ingestion jobs re-embed the whole corpus on every run and never delete anything, so the vector store keeps answering from sections that were rewritten or removed months ago. This blueprint syncs a Markdown documentation repository into a Chroma collection incrementally: every chunk gets a content-addressed id, so only new or edited sections are embedded, chunks whose text disappeared are deleted, and a retrieval smoke test proves the collection still answers a known question before the run is recorded as done. Embeddings are computed locally with FastEmbed, so there is no embedding API key or per-token cost.

How it works

  1. nightly (Schedule) or docs_changed (Webhook, called from CI after a docs merge) starts a run. Flow-level concurrency: limit 1 prevents two syncs from racing on the same collection.
  2. manifest (core.kv.Get) loads the chunk ids, source files and embedding model recorded by the last successful sync.
  3. workspace (core.flow.WorkingDirectory) runs git.Clone at the requested branch or tag, then plan (scripts.python.Script on the Docker runner):
    • splits each Markdown file on headings, keeping the heading trail as section metadata and cutting long sections on paragraph boundaries without breaking code blocks;
    • hashes file path, section and text into a chunk id, so an unchanged chunk keeps its id wherever it moves;
    • diffs the ids against the manifest, embeds only the new ones with BAAI/bge-small-en-v1.5, and writes them to an ION file with source, section, url and ref metadata for citations;
    • re-embeds everything when the embedding model changes or full_resync is set, and embeds the smoke test question.
  4. collection (chroma.CreateCollection, getOrCreate) makes sure the cosine collection exists. upsert_changes runs chroma.Upsert from the ION file with retries, and delete_stale runs chroma.Delete on the ids that no longer exist. Both are skipped when there is nothing to do.
  5. smoke_query (chroma.Query) retrieves the 5 nearest chunks for the smoke test question, and smoke_check (core.flow.If) fails the run with core.execution.Fail unless one of them comes from the expected file.
  6. save_manifest (core.kv.Set) runs last, so a failed run leaves the manifest untouched and the next run retries the same diff. report posts the counts, the changed files and the smoke test result to Slack, and errors alerts on any failure.

What you get

  • Incremental embedding: a release that touches 10 of 23 files re-embeds only the sections that changed, not the corpus.
  • Deletions handled: rewritten or removed sections leave the collection on the same run, so answers never cite outdated text.
  • A retrieval regression gate on every run instead of discovering a broken index from user complaints.
  • Citations out of the box: every chunk carries its file, heading trail, git ref and a link to the source on GitHub.
  • No embedding API key: FastEmbed runs the ONNX model on the worker's CPU.
  • Outputs (embedded, removed, changed_sources, smoke_top_hit) for dashboards and downstream flows.

Who it's for

  • Teams running a docs assistant, support bot or internal search on top of Chroma.
  • Platform and AI engineers who want a RAG ingestion pipeline that is observable, retryable and cheap to run on every merge.
  • Anyone who has re-embedded an entire knowledge base to fix a single typo.

Why orchestrate this with Kestra

Ingestion is where RAG systems quietly rot. Kestra keeps the manifest between runs in the KV store, retries flaky network steps, refuses to record a sync whose smoke test failed, runs on a schedule and on a CI webhook from the same definition, and logs exactly which files and chunks changed in every execution. Chroma's own tasks do the writes and the retrieval check, so the flow stays declarative and readable instead of a script that only its author can operate.

Prerequisites

  • A Chroma server (1.x, API v2) reachable from the Kestra worker, or Chroma Cloud.
  • A Kestra worker able to run Docker task runner containers, with access to PyPI and Hugging Face to install FastEmbed and download the model.
  • A Git repository with Markdown documentation, public or with credentials added to git.Clone.
  • A Slack incoming webhook.

Secrets

  • CHROMA_URL: Base URL of the Chroma server, for example http://chroma:8000. Add authToken: "{{ secret('CHROMA_TOKEN') }}" to the Chroma tasks for an authenticated or Chroma Cloud deployment.
  • SLACK_WEBHOOK_URL: Slack incoming webhook for the sync report and failure alerts.

Quick start

  1. Create the secrets above.
  2. Run once with the defaults: it indexes the httpx documentation into httpx_docs and checks that "How do I configure timeouts?" retrieves docs/advanced/timeouts.md.
  3. Run again with git_ref set to another tag and watch only the changed sections being embedded and the stale ones deleted.
  4. Point repository_url, git_ref, docs_glob and collection at your own documentation, and pick a smoke test question and expected file that your users actually ask about.
  5. Change the Webhook key and call it from CI after merges to the docs folder.

How to extend

  • Bake FastEmbed and the model into a custom image (or mount a model cache) so runs skip the install and the download.
  • Swap the plan embedding code for an OpenAI-compatible endpoint or Ollama when you need a larger model; the change detection stays the same.
  • Add several smoke test questions and fail only when more than one regresses.
  • Index several repositories into one collection by looping over a list of sources and adding a repo metadata field.
  • Trigger a re-ranking or answer-quality evaluation flow with a Subflow after each successful sync.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.