Starphix
Agentic Engineering / Sources
The Lab / Sources / Agentic Engineering / ContextEngineering/Advanced-RAG.md

Advanced RAG Patterns

Beyond basic retrieval: HyDE, Graph RAG, Agentic RAG, Multi-modal, and production patterns.


HyDE (Hypothetical Document Embeddings)

Concept

Generate a hypothetical answer → embed it → retrieve similar real documents.

from llama_index.core.retrievers import HyDERetriever

hyde_retriever = HyDERetriever(
    vector_retriever=vector_retriever,
    llm=llm,
    include_original=True,  # Also embed original query
    num_hypothetical=3  # Generate multiple
)

How It Works

Query: "What causes Alzheimer's?"
    ↓
LLM generates: "Alzheimer's is caused by amyloid plaques and tau tangles..."
    ↓
Embed hypothetical answer → Retrieve similar real docs
    ↓
Use real docs for grounded generation

When to Use

  • Queries where user lacks vocabulary
  • Abstract/conceptual questions
  • Domain-specific terminology gaps
  • Low recall with standard retrieval

Graph RAG

Knowledge Graph Construction

from llama_index.core import KnowledgeGraphIndex
from llama_index.graph_stores import Neo4jGraphStore
from llama_index.core import SimpleDirectoryReader

graph_store = Neo4jGraphStore(
    username="neo4j",
    password="...",
    url="bolt://localhost:7687"
)

documents = SimpleDirectoryReader("./data").load_data()

kg_index = KnowledgeGraphIndex.from_documents(
    documents,
    graph_store=graph_store,
    max_triplets_per_chunk=10,
    include_embeddings=True  # Hybrid: graph + vector
)

Querying

# Graph traversal query
query_engine = kg_index.as_query_engine(
    include_text=True,
    retriever_mode="keyword",  # or "embedding"
    response_mode="tree_summarize"
)

response = query_engine.query(
    "What proteins interact with BRCA1 in breast cancer?"
)

Graph RAG Patterns

Pattern Description Use Case
Entity-Centric Query by entity, traverse relationships Biomedical, legal
Path-Based Find paths between entities Root cause, dependency
Community Detection Summarize clusters Topic modeling
Global + Local Graph for structure + vector for details Best of both

Advanced: Graph RAG with LLM

from llama_index.core.query_engine import GraphRAGQueryEngine

query_engine = GraphRAGQueryEngine(
    graph_store=graph_store,
    llm=llm,
    similarity_top_k=10,
    # Uses LLM to generate Cypher queries
    # or natural language traversal
)

Agentic RAG

Agent as Retriever

from llama_index.agent import ReActAgent
from llama_index.core.tools import QueryEngineTool

rag_tool = QueryEngineTool.from_defaults(
    query_engine=query_engine,
    name="knowledge_base",
    description="Search internal knowledge base"
)

agent = ReActAgent.from_tools(
    tools=[rag_tool, web_search, calculator],
    llm=llm,
    verbose=True
)

# Agent decides when/how to retrieve
response = agent.query(
    "Find the latest research on transformer efficiency, "
    "compare with our internal benchmarks, and summarize"
)

Multi-Step Retrieval

from llama_index.agent import FunctionCallingAgent

agent = FunctionCallingAgent.from_tools(
    tools=[
        QueryEngineTool(query_engine, name="docs"),
        QueryEngineTool(web_engine, name="web"),
        FunctionTool.from_defaults(fn=search_code, name="code_search")
    ],
    llm=llm,
    max_iterations=10
)

# Agent plans multi-hop retrieval
response = agent.query(
    "Find our design doc for auth system, "
    "check if it mentions OAuth2, "
    "and compare with current implementation"
)

Retrieval Planning

from llama_index.core.workflow import Workflow, StartEvent, StopEvent, step

class RetrievalPlanWorkflow(Workflow):
    @step
    async def plan(self, ctx: Context, ev: StartEvent) -> RetrievalPlanEvent:
        plan = await llm.acomplete(f"""
        Create retrieval plan for: {ev.query}
        Available sources: internal_docs, web, code, api_docs
        Return JSON with queries per source.
        """)
        return RetrievalPlanEvent(plan=parse_json(plan.text))

    @step
    async def execute(self, ctx: Context, ev: RetrievalPlanEvent) -> RetrievalResultEvent:
        results = {}
        for source, queries in ev.plan.items():
            for q in queries:
                results[f"{source}:{q}"] = await self.retrieve(source, q)
        return RetrievalResultEvent(results=results)

    @step
    async def synthesize(self, ctx: Context, ev: RetrievalResultEvent) -> StopEvent:
        return StopEvent(result=await self.synthesize(ev.results))

Multi-Modal RAG

Vision + Text

from llama_index.core import MultiModalVectorStoreIndex
from llama_index.core import SimpleDirectoryReader
from llama_index.multi_modal_llms import OpenAIMultiModal

# Load images + text
documents = SimpleDirectoryReader(
    "./data",
    required_exts=[".pdf", ".png", ".jpg"]
).load_data()

# Multi-modal index
index = MultiModalVectorStoreIndex.from_documents(
    documents,
    embed_model=OpenAIMultiModal(model="gpt-4o")
)

query_engine = index.as_query_engine(
    llm=OpenAIMultiModal(model="gpt-4o"),
    similarity_top_k=5
)

response = query_engine.query(
    "What does the architecture diagram show?",
    image_paths=["diagram.png"]
)

Table/Chart Understanding

from llama_index.core import TableIndex

table_index = TableIndex.from_documents(documents)

query_engine = table_index.as_query_engine(
    llm=llm,
    response_mode="tree_summarize"
)

response = query_engine.query(
    "What's the revenue trend in Q3 vs Q4?"
)

Long-Context RAG

Context Window Optimization

from llama_index.core.postprocessor import LongContextReorder

query_engine = RetrieverQueryEngine.from_args(
    retriever=retriever,
    node_postprocessors=[
        LongContextReorder()  # Put most relevant in middle
    ]
)

Recursive Summarization

from llama_index.core.query_engine import SummaryQueryEngine

summary_engine = SummaryQueryEngine(
    retriever=retriever,
    summary_query="Summarize key findings",
    detail_query="Provide details on {topic}"
)

Hierarchical Retrieval

# Document → Section → Chunk
from llama_index.core.retrievers import RecursiveRetriever

retriever = RecursiveRetriever(
    "document",
    retriever_dict={
        "document": document_retriever,    # Top-level
        "section": section_retriever,      # Mid-level
        "chunk": chunk_retriever           # Leaf
    }
)

Production Patterns

Query Understanding

class QueryProcessor:
    async def process(self, query: str) -> ProcessedQuery:
        # 1. Decompose
        subqueries = await self.decompose(query)

        # 2. Rewrite for retrieval
        rewritten = await self.rewrite_for_retrieval(subqueries)

        # 3. Identify required sources
        sources = self.identify_sources(rewritten)

        return ProcessedQuery(
            original=query,
            subqueries=rewritten,
            sources=sources
        )

Adaptive Retrieval

class AdaptiveRetriever:
    async def retrieve(self, query: ProcessedQuery) -> list[Node]:
        strategy = self.select_strategy(query)

        if strategy == "hybrid":
            return await self.hybrid_retrieve(query)
        elif strategy == "hyde":
            return await self.hyde_retrieve(query)
        elif strategy == "graph":
            return await self.graph_retrieve(query)
        else:
            return await self.default_retrieve(query)

Feedback Loop

class RAGWithFeedback:
    def __init__(self):
        self.feedback_store = FeedbackStore()

    async def query(self, query: str) -> Response:
        response = await self.rag.query(query)

        # Log for feedback
        await self.log_query(query, response)

        return response

    async def collect_feedback(self, query_id: str, rating: int, correction: str):
        await self.feedback_store.save(query_id, rating, correction)

        # Retrain retriever/reranker periodically
        if len(self.feedback_store) % 100 == 0:
            await self.retrain()

Evaluation & Monitoring

RAG-Specific Metrics

Metric Target
Retrieval Recall@5 > 0.8
Rerank Precision@3 > 0.7
Faithfulness > 0.85
Answer Relevancy > 0.8
Latency (p95) < 2s
Cost per Query Track & optimize

A/B Testing

class RAGABTest:
    async def query(self, query: str, user_id: str) -> Response:
        variant = self.assign_variant(user_id)  # "baseline" | "hyde" | "graph"

        if variant == "baseline":
            return await self.baseline_rag.query(query)
        elif variant == "hyde":
            return await self.hyde_rag.query(query)
        elif variant == "graph":
            return await self.graph_rag.query(query)


Sources: "HyDE" (Gao et al., 2022), "Graph RAG" (Edge et al., 2024), "Agentic RAG" (LlamaIndex), "LongLoRA" (Chen et al.), "Recursive Retrieval" (LlamaIndex), production RAG post-mortems (Notion, GitHub, Stripe), RAG evaluation papers.

From the Agentic Engineering research vault · CC BY 4.0 · all notes · raw markdown