Post

AI

Agentic RAG with LlamaIndex Workflows: Event-Driven Retrieval and Qdrant Indexing

A source audit of LlamaIndex Workflows, evaluating event-driven agentic orchestration, Qdrant vector indexing, and self-correcting retrieval pipelines.

Why Did Event-Driven Architecture Replace Static RAG Pipelines?

Standard Retrieval-Augmented Generation (RAG) implementations typically rely on linear execution chains. An incoming query is converted into a vector embedding, searched against a vector index, concatenated into a prompt template, and synthesized by a large language model. While this pipeline suffices for simple fact retrieval, it breaks down in complex technical workflows—such as Infrastructure as Code (IaC) generation, multi-stage compliance verification, and recursive query expansion.

When generating mission-critical artifacts like Terraform configurations, a single-pass generation risks catastrophic cloud misconfigurations, syntax invalidations, or security perimeter breaches. An autonomous system requires iterative cycles: generating a tentative configuration, dispatching it to a validation sandbox, parsing schema errors, querying vector stores for corrected API parameters, and compiling a verified deliverable.

flowchart TD
    subgraph LinearRAG["Static RAG Pipeline (Brittle)"]
        Q[User Query] --> E[Dense Embedding]
        E --> V[Vector Search]
        V --> L[LLM Synthesis]
        L --> O[Unverified Output]
    end

    subgraph EventDrivenWorkflow["LlamaIndex Event-Driven Workflow (Resilient)"]
        S[StartEvent] --> Step1[Step: Generate Artifact]
        Step1 --> E1[ArtifactEvent]
        E1 --> Step2[Step: Validate Syntax & Security]
        Step2 -->|Validation Failure| E2[ValidationErrorEvent]
        E2 --> Step3[Step: Query Qdrant for Fixes]
        Step3 --> Step1
        Step2 -->|Validation Success| E3[StorageEvent]
        E3 --> Step4[Step: Persist & Index Payload]
        Step4 --> Stop[StopEvent]
    end

In August 2024, LlamaIndex introduced Workflows, an event-driven framework designed to replace monolithic DAG runners and opaque agent executors. By modeling agentic logic as asynchronous steps consuming and emitting typed events, Workflows enabled robust error recovery, parallel fan-out/fan-in processing, and stateful human-in-the-loop validation.


Architectural Breakdown: LlamaIndex Workflows Primitives

LlamaIndex Workflows re-architects agent coordination around four fundamental constructs:

  1. Workflow Container: The orchestrator class that manages execution context, event dispatching, and step discovery.
  2. Event Classes: Strongly typed data contracts inheriting from llama_index.core.workflow.Event. Events encapsulate intermediate payloads, routing metadata, and execution telemetry.
  3. @step Decorator: Async methods within the workflow decorated with @step. A step declares the event types it accepts via type annotations and emits new events via return values or internal context queues.
  4. StartEvent & StopEvent: Standardized system boundaries marking the entry point of user input and the terminal delivery of final results.
sequenceDiagram
    autonumber
    participant Caller
    participant Workflow as Workflow Engine
    participant StepGen as Step: GenerateScript
    participant StepVal as Step: ValidateSyntax
    participant Qdrant as Qdrant Vector Store

    Caller->>Workflow: run(topic="Azure AKS Cluster") [StartEvent]
    Workflow->>StepGen: Dispatch StartEvent
    StepGen->>StepGen: LLM Generates main.tf
    StepGen-->>Workflow: Emit ScriptGeneratedEvent
    Workflow->>StepVal: Dispatch ScriptGeneratedEvent
    StepVal->>StepVal: Validate against Terraform CLI schema
    alt Syntax Valid
        StepVal->>Qdrant: Index validated artifact vector & payload
        StepVal-->>Workflow: Emit StopEvent(verified_script)
        Workflow-->>Caller: Return Result
    else Syntax Errors Found
        StepVal-->>Workflow: Emit ValidationErrorEvent(error_trace)
        Workflow->>StepGen: Re-invoke with error reflection
    end

Decoupled Execution and Native Fan-Out / Fan-In

Unlike rigid chain graphs where step $A$ must directly invoke step $B$, event-driven architectures decouple steps completely. A step simply declares: “When an event of type $T$ arrives, execute this logic.”

This enables effortless parallel fan-out and fan-in. If a document chunking step emits five ChunkExtractedEvent objects, LlamaIndex Workflows can automatically spin up five concurrent extraction steps. A downstream aggregation step can then use Context to await all five completion events before emitting a consolidated summary.


Implementing an Agentic Terraform Engine with Qdrant Indexing

Below is a production-grade implementation of an event-driven workflow that generates, validates, and indexes Terraform configurations into a Qdrant vector database:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import os
import asyncio
from typing import Optional
from llama_index.core.workflow import (
    Workflow,
    Event,
    StartEvent,
    StopEvent,
    step,
    Context
)
from llama_index.core import Document, VectorStoreIndex, StorageContext, Settings
from llama_index.llms.openai import OpenAI
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.vector_stores.qdrant import QdrantVectorStore
from qdrant_client import AsyncQdrantClient, models

# 1. Define Strongly Typed Workflow Events
class ScriptGeneratedEvent(Event):
    topic: str
    script: str
    iteration: int = 1

class ValidationFailedEvent(Event):
    topic: str
    script: str
    error_message: str
    iteration: int

class ScriptValidatedEvent(Event):
    topic: str
    script: str

# 2. Implement the Event-Driven Workflow
class TerraformIaCWorkflow(Workflow):
    def __init__(
        self,
        qdrant_client: AsyncQdrantClient,
        collection_name: str = "terraform_modules",
        max_retries: int = 3,
        **kwargs
    ):
        super().__init__(**kwargs)
        self.qdrant_client = qdrant_client
        self.collection_name = collection_name
        self.max_retries = max_retries
        self.llm = OpenAI(model="gpt-4o", temperature=0.1)

    @step
    async def generate_script(
        self, ctx: Context, ev: StartEvent | ValidationFailedEvent
    ) -> ScriptGeneratedEvent | StopEvent:
        if isinstance(ev, StartEvent):
            topic = ev.get("topic")
            iteration = 1
            prompt = (
                f"Generate clean, production-grade HCL Terraform configuration for: {topic}.\n"
                f"Output only valid HCL code without markdown backticks."
            )
        else:
            topic = ev.topic
            iteration = ev.iteration + 1
            if iteration > self.max_retries:
                return StopEvent(result=f"Failed to generate valid script after {self.max_retries} attempts.")
            prompt = (
                f"Fix the following Terraform script for {topic}.\n"
                f"Original Script:\n{ev.script}\n\n"
                f"Validation Error:\n{ev.error_message}\n"
                f"Output only corrected HCL code."
            )

        response = await self.llm.acomplete(prompt)
        return ScriptGeneratedEvent(topic=topic, script=str(response), iteration=iteration)

    @step
    async def validate_syntax(
        self, ctx: Context, ev: ScriptGeneratedEvent
    ) -> ScriptValidatedEvent | ValidationFailedEvent:
        # Heuristic / CLI verification pass
        script = ev.script
        if "resource " not in script or "provider " not in script:
            return ValidationFailedEvent(
                topic=ev.topic,
                script=script,
                error_message="Missing essential provider or resource declaration blocks.",
                iteration=ev.iteration
            )
        
        # Validated successfully
        return ScriptValidatedEvent(topic=ev.topic, script=script)

    @step
    async def index_and_persist(
        self, ctx: Context, ev: ScriptValidatedEvent
    ) -> StopEvent:
        # Construct document payload
        doc = Document(
            text=ev.script,
            metadata={
                "topic": ev.topic,
                "framework": "terraform",
                "validated": True
            }
        )
        
        # Ingest directly into Qdrant collection
        vector_store = QdrantVectorStore(
            aclient=self.qdrant_client,
            collection_name=self.collection_name
        )
        storage_context = StorageContext.from_defaults(vector_store=vector_store)
        
        # Build index and insert
        await VectorStoreIndex.async_from_documents(
            [doc],
            storage_context=storage_context
        )

        return StopEvent(result={
            "status": "success",
            "topic": ev.topic,
            "script": ev.script,
            "indexed_in": self.collection_name
        })

Architectural Comparison: Agentic Orchestration Frameworks

DimensionLangChain LCELLangGraphLlamaIndex Workflows
Primary AbstractionRunnable Composition (\|)Cyclical State GraphEvent-Driven Asynchronous Steps
State CoordinationEphemeral per-node kwargsCentralized TypedDict SchemaEmitted Event classes & Step Context
Concurrency ParadigmRunnableParallelSuperstep-based branch executionNative Event-Loop Fan-Out / Fan-In
Vector Store InteropRetriever adaptersState node integrationNative Indexing, Node Parsers, & Query Engines
Error HandlingChain-level fallbacksConditional edge reroutingEvent re-emission (e.g. ValidationFailedEvent)
Human-in-the-LoopExternal break coordinatorsNative interrupt() & checkpointerAsync event wait via step pause

Why Qdrant Provides the Optimal Vector Substrate for Agentic RAG

In an agentic retrieval loop, vector storage cannot function as a naive nearest-neighbor index. Agentic workflows require tight metadata coupling, as analyzed in our vector database architectural evaluation:

flowchart LR
    subgraph MultiStageSearch["Qdrant Single-Stage Filtered Search"]
        Q[Vector Query + Filter] --> HNSW[HNSW Index Traversal]
        HNSW --> PayloadCheck{Payload Matches Filter?}
        PayloadCheck -- Yes --> Candidate[Accept Top-K]
        PayloadCheck -- No --> Prune[Prune Search Branch]
    end
  1. Payload-Aware Single-Stage Filtering: Unlike vector databases that perform post-filtering (retrieving 100 nearest vectors and then discarding items that do not match metadata), Qdrant evaluates metadata payloads during HNSW graph traversal. In Terraform indexing, queries filtering by provider == "aws" and module_type == "vpc" evaluate filters directly on the index edges, preventing recall degradation.
  2. Dense + Sparse Hybrid Search with Reciprocal Rank Fusion: Production queries blend keyword identifiers (e.g., azurerm_kubernetes_cluster) with conceptual queries (e.g., “managed container orchestrator”). Qdrant natively combines dense embeddings with sparse lexical representations (such as BM25 or SPLADE), adhering to the best practices outlined in our systematic RAG workflow guide.
  3. Point Updates and Real-Time Vector Upserts: When an agent updates a modified script, Qdrant allows in-place point mutations without rebuilding the underlying HNSW index graphs.

 Agentic workflow with Llamaindex


How Agentic Retrieval and Workflows Evolved Across 2026

Between 2024 and 2026, agentic RAG transitioned from experimental code demos into high-reliability enterprise platforms:

  1. Self-Reflective and Speculative Retrieval: Rather than trusting the first retrieval pass, 2026 workflows implement Self-RAG reflection loops. Steps grade retrieved chunks for factual relevance and contextual completeness before invoking generation models. If passages lack critical syntax documentation, the workflow dispatches secondary queries to technical documentation endpoints or ColPali visual document stores, following principles detailed in our ColPali document retrieval audit.
  2. Distributed Checkpointing and Step Replay: Modern workflow engines serialize event histories into persistent key-value databases. If an external API or sandbox fails at step 4, the workflow can resume directly from the last valid Event without repeating expensive embedding and model generation calls.
  3. Unified Protocol Standards (MCP Integration): Standalone tool implementations gave way to Model Context Protocol (MCP) servers. Workflow steps invoke remote MCP tools for sandboxed code execution (Docker, WebAssembly) and schema validation, isolating untrusted LLM-generated code from host environments.
  4. Convergence of State Graphs and Event Engines: The conceptual boundary between LangGraph cyclical state machines and LlamaIndex Workflows narrowed. Modern architectures often deploy event-driven routers at the edge to ingest incoming tasks, while utilizing specialized sub-agent state graphs for deep domain execution.

Key Takeaways

  • Linear Chains Fail Under Iterative Tasks: Technical generation requires recursive feedback, automated syntax validation, and self-correcting error loops that static DAGs cannot support.
  • Event-Driven Steps Decouple Complexity: LlamaIndex Workflows replaces brittle routing logic with typed event broadcasting, enabling native fan-out/fan-in concurrency and modular step isolation.
  • Qdrant Delivers Single-Stage Filtering Precision: In-graph payload filtering prevents recall drop-off in high-cardinality metadata environments like Infrastructure as Code indexing.
  • 2026 Standards Prioritize Observability and Sandboxing: Modern enterprise workflows combine event checkpointing, self-reflective retrieval grading, and MCP-isolated execution environments.

New Questions

  • How do event-driven workflows prevent infinite self-correction loops when the underlying model repeatedly encounters contradictory syntax errors?
  • What are the memory overheads of serializing full document payloads inside intermediate event buses during high-throughput enterprise batch indexing?
  • At what document scale does cross-encoder reranking become a prohibitive latency bottleneck within real-time interactive workflow steps?

References

Editorial method: claims in this article point to primary documentation, pinned source code, runnable checks, or dated measurements, with AI-assisted research under the site's evidence-gated policy. How this site verifies claims and discloses AI assistance · Report a correction.

Working on something like this?

I take a small number of paid, scoped reviews: AI agent/RAG architecture diagnosis, Unity CI & build-automation audits, and multimodal QA design review. Each one ends in a written findings document.

Work with me
This post is licensed under CC BY 4.0 by the author.

© Jang Young Jeong. Some rights reserved.

Search article titles, categories, and tags. Full text is searched when needed.

Type to search articles.