Run icon
Get icon
Script icon
IngestDocument icon
OpenAI icon
KestraKVStore icon
AIAgent icon
EmbeddingStoreRetriever icon
Log icon
DiscordIncomingWebhook icon
Schedule icon

Turn an Apify web crawl into a fresh RAG vector index

Build a self-refreshing RAG pipeline with Kestra and Apify. Crawl the web, embed clean content into a vector store, and answer grounded questions on a schedule.

Categories
AI

Keep a retrieval-augmented generation (RAG) knowledge base in sync with the live web. This Apify web crawling and RAG ingestion pipeline scrapes a website with Apify's Website Content Crawler, embeds the cleaned, boilerplate-free text into a vector store, and answers natural-language questions grounded in the freshly crawled content. It solves the stale-index problem that breaks RAG chatbots and AI agents: documentation, pricing, and product pages change constantly, but a one-time embedding job never catches up. Here the crawl, embed, and query steps run on a schedule so the index always reflects what is actually published.

How it works

  1. The nightly_refresh trigger (io.kestra.plugin.core.trigger.Schedule, cron 0 3 * * *) starts the flow every night at 03:00.
  2. run_actor (io.kestra.plugin.apify.actor.Run) runs the apify/website-content-crawler actor against start_url, capped by maxItems and maxTotalChargeUsd to control Apify cost.
  3. get_dataset (io.kestra.plugin.apify.dataset.Get) reads the actor's defaultDatasetId and returns only the url and text fields.
  4. build_corpus (io.kestra.plugin.scripts.python.Script) concatenates the crawled pages into a single corpus.md file, skipping empty pages.
  5. ingest (io.kestra.plugin.ai.rag.IngestDocument) chunks the corpus with a RECURSIVE splitter and embeds it via OpenAI text-embedding-3-small into a KestraKVStore. drop: true clears the previous index first so the store mirrors the latest crawl.
  6. ask (io.kestra.plugin.ai.agent.AIAgent) answers question using an EmbeddingStoreRetriever over the same store, with gpt-4o constrained to the retrieved context.
  7. log_answer (io.kestra.plugin.core.log.Log) prints the answer and notify (io.kestra.plugin.discord.DiscordIncomingWebhook) posts a refresh confirmation with the chunk count.

What you get

  • A self-refreshing RAG index built from any public website or list of URLs.
  • Clean, deduplicated page text instead of raw HTML, courtesy of the Website Content Crawler.
  • Cost guardrails via maxItems and maxTotalChargeUsd.
  • A grounded question-answering step that cites only retrieved content.
  • A Discord notification confirming each successful refresh.

Who it's for

  • AI and platform engineers building RAG chatbots or agents over external content.
  • Data engineers who need scheduled, observable web-to-vector pipelines.
  • Developer relations and docs teams keeping an assistant current with live documentation.

Why orchestrate this with Kestra

Apify's scheduler can rerun a crawl, but it stops at the dataset. It cannot then chunk the output, embed it into a vector store, drop the stale index, run a grounded LLM query, and fan out a Discord alert as one observable run. Kestra wires the whole chain in declarative YAML with event and schedule triggers, per-task retries, and full execution lineage across Apify, Python, the AI plugin, and Discord. You get one auditable pipeline instead of glue scripts stitched across separate tools.

Prerequisites

  • A Kestra instance (local or cloud).
  • An Apify account with the apify/website-content-crawler actor.
  • An OpenAI API key with access to text-embedding-3-small and gpt-4o.
  • A Discord channel with an incoming webhook.

Secrets

  • APIFY_API_TOKEN: authenticates the Apify actor run and dataset fetch.
  • OPENAI_API_KEY: powers embeddings and the answering agent.
  • DISCORD_WEBHOOK_URL: the Discord incoming webhook for refresh notifications.

Quick start

  1. Add APIFY_API_TOKEN, OPENAI_API_KEY, and DISCORD_WEBHOOK_URL as secrets.
  2. Import this flow into your Kestra instance.
  3. Run it manually, accepting the default start_url, max_items, and question, or set your own.
  4. Check log_answer for the grounded response and your Discord channel for the confirmation.
  5. Leave the nightly schedule on so the index stays current.

How to extend

  • Point start_url at your own docs, pricing, or knowledge-base site.
  • Swap the KestraKVStore embeddings for Pinecone, Qdrant, PGVector, or Weaviate for production scale.
  • Tune maxSegmentSizeInChars, maxOverlapSizeInChars, maxResults, and minScore for retrieval quality.
  • Replace the Discord notification with Slack, email, or a downstream subflow.
  • Add an event trigger so a webhook can refresh the index on demand.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.