RealtimeTrigger icon
If icon
DeleteVectors icon
Log icon
Script icon
Process icon
Upsert icon
DescribeIndexStats icon
SlackIncomingWebhook icon

Streaming RAG Incremental Ingest with Tombstone Deletes

Incrementally ingest a Kafka document stream into Pinecone with chunking, tombstone deletes, and a vector-count freshness watermark in Kestra.

Categories
AIData

How it works

  • The on_doc_change trigger (io.kestra.plugin.kafka.RealtimeTrigger) subscribes to the doc-updates topic with a JSON deserializer and starts one execution per document event using the kestra-rag-ingest consumer group.
  • route_event (core.flow.If) branches on the event's op field: delete events go to delete_vectors (pinecone.DeleteVectors) with ids delivered as a CSV string ({"op": "delete", "ids": "doc_1,doc_2"}), everything else is an upsert.
  • embed_chunks (scripts.python.Script, Process runner) slides a max_segment_chars window with overlap_chars of context across the text, calls the OpenAI embeddings API once per event, and writes a Pinecone-ready vectors.ion file where each line is {"id": "doc_id#chunk", "values": [...], "metadata": {...}}. Malformed events, missing keys, or a provider failure fail the execution on purpose so the errors handler pages the channel.
  • upsert_vectors (pinecone.Upsert) pushes the file - stable doc#chunk ids make re-ingesting the same document idempotent.
  • check_stats (pinecone.DescribeIndexStats) reads totalVectorCount afterwards, and evaluate_threshold (core.flow.If) pages Slack when the index falls below the min_vector_count watermark - the tripwire for missed upserts and over-eager deletes.
  • log_summary writes one ingest line per change, and the errors handler covers anything that fails along the way.

What you get

  • A Pinecone index that tracks the document stream incrementally, with idempotent upserts and CSV-driven tombstone deletes.
  • A min_vector_count freshness watermark that turns a silent consumer failure into a Slack alert with the measured total.
  • Truncation visibility (truncated) when a document exceeds max_chunks_per_event, plus per-execution outputs for chunks, vectors_upserted, vectors_deleted, index_vectors, and below_watermark.
  • An execution history that reads as a per-document ingest log.

Who it's for

  • Teams running retrieval-augmented generation on a corpus that changes constantly - release notes, tickets, docs - and who need the vector index to follow the source of truth instead of waiting for a nightly batch.

Why orchestrate this with Kestra

  • The consumer, the embedding call, the delete path, and the watermark are four failure modes that usually hide inside one bespoke worker. As a Kestra flow every document gets a retryable execution, a visible branch for deletes vs upserts, and an alert the moment the index stops matching the stream.

Prerequisites

  • A Kafka broker and a doc-updates topic carrying JSON events: {"op": "upsert", "doc_id": "...", "text": "...", "source": "..."} or {"op": "delete", "ids": "a,b"}.
  • A Pinecone index whose dimension matches the embedding model (for example 1536 for text-embedding-3-small).
  • An OpenAI account for embeddings.

Secrets

  • KAFKA_BOOTSTRAP_SERVERS - Kafka broker list for the trigger.
  • PINECONE_API_KEY - Pinecone API key used by upsert, delete, and stats tasks.
  • OPENAI_API_KEY - OpenAI key used by the embeddings call.
  • SLACK_WEBHOOK_URL - Incoming webhook used for watermark breaches and flow errors.

Quick start

  • Create the index, set the four secrets, and set min_vector_count to your steady-state vector total.
  • Enable the trigger (it ships disabled: true) and publish one upsert event - the execution shows four chunk-level vectors in vectors.ion and the new totalVectorCount in the log line.
  • Publish a tombstone for those ids and confirm vectors_deleted counts them; drop min_vector_count above the total and confirm the watermark alert fires.

How to extend

  • Fan the same stream into a second sink (Postgres full-text or an ai.rag.IngestDocument KV store) by adding a task inside the upsert branch.
  • Combine with rag-knowledge-base-vector-reconciliation on a schedule to clean up orphaned doc#chunk ids after large document shrinks.
  • Swap the OpenAI embeddings call for a self-hosted endpoint by changing one URL in the script.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.