New to Kestra?
Use blueprints to kickstart your first workflows.
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.
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.
nightly_refresh trigger (io.kestra.plugin.core.trigger.Schedule, cron 0 3 * * *) starts the flow every night at 03:00.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.get_dataset (io.kestra.plugin.apify.dataset.Get) reads the actor's defaultDatasetId and returns only the url and text fields.build_corpus (io.kestra.plugin.scripts.python.Script) concatenates the crawled pages into a single corpus.md file, skipping empty pages.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.ask (io.kestra.plugin.ai.agent.AIAgent) answers question using an EmbeddingStoreRetriever over the same store, with gpt-4o constrained to the retrieved context.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.maxItems and maxTotalChargeUsd.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.
apify/website-content-crawler actor.text-embedding-3-small and gpt-4o.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.APIFY_API_TOKEN, OPENAI_API_KEY, and DISCORD_WEBHOOK_URL as secrets.start_url, max_items, and question, or set your own.log_answer for the grounded response and your Discord channel for the confirmation.start_url at your own docs, pricing, or knowledge-base site.KestraKVStore embeddings for Pinecone, Qdrant, PGVector, or Weaviate for production scale.maxSegmentSizeInChars, maxOverlapSizeInChars, maxResults, and minScore for retrieval quality.