DataAIHub
DataAIHubNews · Research · Tools · Learning
Retrieval

RAG Guide

A comprehensive guide to RAG - the dominant pattern for building AI applications that answer questions using your own data. Covers architecture, chunking, retrieval, evaluation, and production deployment.

15 min readIntermediateUpdated Jul 5, 2026

Quick Summary

RAG searches first, then answers - grounding LLM responses in your own data.

One Analogy

RAG is an open-book exam: the model reads relevant pages before answering.

Engineering Rule

Retrieval quality matters more than prompt quality.

TL;DR

  • RAG retrieves relevant documents and feeds them to an LLM so it can answer questions grounded in real data instead of relying on training knowledge alone.

  • It solves hallucination and knowledge cutoff by injecting fresh, private, or domain-specific context at query time - no retraining required.

  • A production RAG pipeline has two phases: offline indexing (load → chunk → embed → store) and online querying (embed query → retrieve → rerank → generate).

  • Retrieval quality is the ceiling - most production failures trace back to bad chunking, poor recall, or missing metadata filters, not the LLM.

  • Start simple, measure everything - a naive pipeline with solid evaluation beats a complex one without metrics.

Why This Matters

Every AI application that needs to answer questions about specific data - company documents, product catalogs, support tickets, codebases, legal contracts - needs RAG or something like it.

Without RAG, you have two bad options: fine-tune a model on your data (expensive, slow, and the model still hallucinates) or stuff everything into the prompt (impossible once you exceed the context window, and costly at scale).

RAG gives you a third path: keep your data in a searchable index, retrieve only what's relevant, and let the LLM synthesize an answer. This is how most production AI applications work today - from enterprise search to customer support bots to coding assistants.

If you're building anything with LLMs beyond a simple chatbot, you'll encounter RAG. Understanding its architecture, tradeoffs, and failure modes is essential for any AI engineer.

The Problem RAG Solves

Large language models have two fundamental limitations that block production use for domain-specific applications:

Hallucination. LLMs generate fluent text that sounds correct but isn't grounded in fact. Ask GPT-4 about your company's refund policy and it will invent one unless you give it the actual policy text.

Knowledge cutoff and privacy. Models don't know about events after training, proprietary data, or internal systems. You cannot send customer PII to OpenAI for training, and you cannot retrain every time a document changes.

RAG addresses both by separating knowledge storage from reasoning. Your documents live in a searchable index you control. At query time, the system finds relevant passages and passes them to the LLM as context. The model synthesizes an answer from evidence, not from memory.

This pattern also solves operational problems: updating knowledge means re-indexing documents, not retraining models. Swapping GPT-4 for Claude requires changing one API call, not rebuilding a fine-tuned model. Auditors can trace every answer back to source documents.

What Is RAG?

Retrieval-Augmented Generation (RAG) is a pattern where you augment an LLM's input with relevant information retrieved from an external knowledge base. Instead of asking the model to answer from memory, you first search for relevant documents, then pass them alongside the question to the LLM.

The term was introduced in a 2020 paper by Lewis et al. from Meta AI, but the idea - search first, then generate - predates the paper. What made it practical was the combination of dense retrieval (embedding-based search) with large language models capable of synthesizing coherent answers from retrieved passages.

Here's the core idea in pseudocode:

def rag(question, knowledge_base):
    relevant_docs = knowledge_base.search(question, top_k=5)
    context = "\n\n".join(doc.text for doc in relevant_docs)
    prompt = f"""Answer based on the context below. If the context
does not contain the answer, say so.

Context:
{context}

Question: {question}
Answer:"""
    return llm.generate(prompt)

The sophistication is in how well you execute each step - not in the pattern itself.

How RAG Works

A RAG system operates in two distinct phases with different latency, cost, and failure profiles.

Indexing (Offline)

Documents are processed into a searchable representation:

  1. Load - Ingest from PDFs, databases, APIs, wikis, code repos.

  2. Chunk - Split into retrievable passages (see Chunking Strategies).

  3. Embed - Convert each chunk to a vector using an embedding model.

  4. Store - Persist vectors, text, and metadata in a vector database.

Querying (Online)

When a user asks a question:

  1. Embed the query - Same embedding model as indexing.

  2. Retrieve - Find top-k similar chunks via vector search, optionally with hybrid search and metadata filtering.

  3. Rerank - Rescore candidates with a cross-encoder reranker.

  4. Generate - Pass retrieved context + question to the LLM.

  5. Post-process - Add citations, validate faithfulness, log for evaluation.

RAG couples a dense vector index of external knowledge with a sequence-to-sequence generator. At query time, the retriever selects relevant passages and the generator conditions its answer on that evidence.

RAG model overview - retriever plus generator

Source: Meta AI

Architecture

A production RAG system has five layers:

Layer Responsibility Key Components
Ingestion Load, parse, normalize documents Loaders, OCR, web crawlers, CDC from databases
Indexing Chunk, embed, store with metadata Text splitters, embedding API, vector DB
Retrieval Find relevant chunks for a query ANN search, BM25, filters, reranker
Generation Synthesize answer from context LLM, prompt template, streaming
Evaluation Measure and regress quality Golden test set, RAGAS, logging

A production RAG stack separates offline indexing (load, chunk, embed, store) from online querying (embed query, retrieve, rerank, generate).

End-to-end RAG pipeline

Source: Survey on RAG

Keep ingestion and query paths separate. Re-indexing should not require redeploying your API. Store raw documents separately from vectors so you can re-embed without re-parsing.

Step-by-Step Flow

Building a Production Pipeline

Step 1: Define your corpus and access model. What documents? Who can see what? Metadata for filtering (tenant ID, department, doc type) must be decided before indexing.

Step 2: Ingest and parse. Extract clean text from PDFs (layout-aware parsers like Unstructured or Docling), HTML, markdown, and databases. Preserve structure - headings, page numbers, section IDs.

Step 3: Chunk with structure awareness. Prepend section titles to chunks. Use recursive splitting with overlap. See Chunking Strategies for domain-specific guidance.

Step 4: Embed and upsert. Batch embed chunks (OpenAI, Cohere, or self-hosted). Store vector + text + metadata. Version your embedding model in metadata.

Step 5: Build the retriever. Start with vector search, k=5. Add hybrid search if users search by SKU, error codes, or names. Add metadata filters for multi-tenant isolation.

Step 6: Add reranking. Retrieve top-20, rerank to top-5. This is the highest-ROI quality improvement for most pipelines.

Step 7: Design the generation prompt. Instruct the model to cite sources, refuse when context is insufficient, and answer only from provided context.

Step 8: Instrument and evaluate. Log query, retrieved chunks, scores, and response. Run retrieval evaluation weekly on a golden set.

Chunking Strategies

Chunking is where most RAG pipelines silently fail. The wrong chunk size means the retriever returns too little context (the LLM can't answer) or too much noise (the LLM gets confused).

Strategy How It Works Best For
Fixed-size Split every N tokens with overlap Quick prototypes, uniform text
Recursive Split by paragraph → sentence → word Articles, documentation
Semantic Group sentences by embedding similarity Documents with topic shifts
Document-aware Split by heading, section, or page Technical manuals, legal docs
Parent-child Small chunks for retrieval, large parent for context Long documents needing both precision and context

Tip

Start with recursive chunking at 512 tokens with 50-token overlap. Include the section heading in each chunk. Measure retrieval recall before optimizing chunk size.

Poor chunk boundaries split critical information across chunks - a table header in one chunk and its rows in another. Overlap (10–15% of chunk size) mitigates boundary loss.

Retrieval Techniques

Retrieval determines the ceiling on answer quality. No prompt engineering compensates for missing documents.

Technique Mechanism Strength Weakness
Dense (vector) Cosine similarity on embeddings Semantic matching, paraphrases Misses exact IDs, rare terms
Sparse (BM25) Term frequency scoring Exact keyword matches No semantic understanding
Hybrid Combine dense + sparse scores Best of both Tuning fusion weights
Metadata filter Pre-filter by attributes Tenant isolation, date ranges Requires clean metadata
Reranking Cross-encoder rescores pairs High precision Adds latency (~100–300ms)
Multi-query LLM generates query variants Better recall 2–3x retrieval cost

Production systems typically use: hybrid retrieve top-20 → metadata filter → rerank to top-5 → generate.

For relationship-heavy queries ("How does component A connect to service B across three systems?"), consider GraphRAG instead of pure vector retrieval.

Real Production Example

A support team at a SaaS company indexes 12,000 help articles, API docs, and release notes. Users ask questions like "Why am I getting a 429 error on the batch endpoint?"

from dataclasses import dataclass
from typing import Optional
import cohere
from openai import OpenAI

@dataclass
class Chunk:
    id: str
    text: str
    metadata: dict

class ProductionRAG:
    def __init__(self, vector_store, embed_model="text-embedding-3-small"):
        self.store = vector_store
        self.openai = OpenAI()
        self.cohere = cohere.Client()
        self.embed_model = embed_model

    def embed(self, text: str) -> list[float]:
        resp = self.openai.embeddings.create(
            model=self.embed_model, input=text
        )
        return resp.data[0].embedding

    def retrieve(
        self,
        query: str,
        tenant_id: str,
        top_k: int = 20,
    ) -> list[Chunk]:
        query_vector = self.embed(query)
        # Hybrid search with tenant isolation
        candidates = self.store.hybrid_search(
            vector=query_vector,
            query_text=query,
            filter={"tenant_id": tenant_id, "status": "published"},
            top_k=top_k,
            alpha=0.7,  # 70% vector, 30% BM25
        )
        return candidates

    def rerank(self, query: str, chunks: list[Chunk], top_n: int = 5) -> list[Chunk]:
        docs = [c.text for c in chunks]
        results = self.cohere.rerank(
            model="rerank-english-v3.0",
            query=query,
            documents=docs,
            top_n=top_n,
        )
        return [chunks[r.index] for r in results.results]

    def generate(self, query: str, chunks: list[Chunk]) -> dict:
        context_parts = []
        for i, chunk in enumerate(chunks, 1):
            source = chunk.metadata.get("url", chunk.metadata.get("title", "unknown"))
            context_parts.append(f"[{i}] ({source})\n{chunk.text}")
        context = "\n\n".join(context_parts)

response = self.openai.chat.completions.create(
            model="gpt-4o",
            temperature=0,
            messages=[
                {"role": "system", "content": (
                    "Answer using ONLY the provided context. "
                    "Cite sources as [1], [2], etc.

"
                    "If the context doesn't contain the answer, say so."
                )},
                {"role": "user", "content": f"Context:\n{context}\n\nQuestion: {query}"},
            ],
        )
        return {
            "answer": response.choices[0].message.content,
            "sources": [c.metadata for c in chunks],
        }

    def query(self, question: str, tenant_id: str) -> dict:
        candidates = self.retrieve(question, tenant_id)
        reranked = self.rerank(question, candidates)
        return self.generate(question, reranked)

# Usage
rag = ProductionRAG(vector_store=my_pinecone_index)
result = rag.query("429 error on batch endpoint", tenant_id="acme-corp")
print(result["answer"])

The tenant_id filter ensures multi-tenant isolation. Hybrid search catches "429" as an exact match while vector search handles "rate limit exceeded." Reranking pushes the API reference above generic troubleshooting articles.

Design Decisions

Decision Option A Option B When to choose
Retrieval Vector only Hybrid (vector + BM25) Hybrid when queries contain IDs, codes, or rare terms
Chunk size 256 tokens 512–1024 tokens 256 for factoid Q&A; 512+ for explanatory answers
Top-k before rerank 10 20–50 Higher k if recall is low; reranker handles precision
Embedding model API (OpenAI) Self-hosted (BGE) API for speed to market; self-hosted for data residency
Vector DB pgvector Dedicated (Pinecone, Qdrant) pgvector under 500K vectors; dedicated at scale
Generation model GPT-4o Smaller model (GPT-4o-mini) Smaller model when retrieval quality is high

⚠ Common Mistakes

  1. Optimizing prompts before fixing retrieval. If the correct document isn't in top-k, no prompt saves you. Measure recall@k first.

  2. Ignoring chunk boundaries. Splitting mid-sentence or separating tables from headers destroys retrieval. Always preserve structure.

  3. Skipping metadata. Without tenant filters, users retrieve other customers' documents. Without doc-type filters, release notes drown out API docs.

  4. Retrieving too many chunks. k=20 into the LLM adds noise and cost. Retrieve many, rerank to few.

  5. No source citations. Without citations, you cannot debug wrong answers or build user trust.

  6. Re-embedding without versioning. Changing embedding models without re-indexing creates a mixed vector space where search quality collapses.

  7. Treating RAG as set-and-forget. Documents change. Build incremental re-indexing and monitor eval metrics weekly.

Common Failure Modes

Understanding where RAG fails helps you diagnose production issues systematically.

Failure Symptom Diagnosis Fix
Missing document "I don't know" or hallucination Answer not in corpus Expand corpus or set expectations
Low recall Wrong answer despite data existing Correct chunk not in top-k Hybrid search, better chunking, query expansion
Low precision Correct but verbose/confused answer Irrelevant chunks retrieved Reranking, metadata filters, smaller k
Chunk fragmentation Partial answer Info split across chunks Overlap, parent-child, larger chunks
LLM ignores context Answer contradicts sources Generation failure Stronger prompt, lower temperature, faithfulness eval
Stale data Outdated answer Index not refreshed Incremental re-indexing, CDC pipeline
Access leak User sees unauthorized data Missing filter Enforce metadata filters at retrieval

Log every stage. When a user reports a bad answer, trace: was the right doc retrieved? Was it ranked high enough? Did the LLM use it?

Where It Breaks Down

RAG retrieves facts; it does not reason across complex multi-hop relationships. "Which suppliers of our Tier-1 vendor have had compliance violations in the last year?" requires traversing entity relationships - vector search alone will miss connections. Use GraphRAG for this class of query.

RAG also struggles when the answer requires synthesizing information scattered across dozens of documents with no single chunk containing enough context. Summarization-over-retrieval (map-reduce across chunks) adds latency and cost.

Embeddings are lossy. Negation ("not refundable"), numbers ("$4.2M vs $42M"), and domain jargon are common failure points. Hybrid search and reranking mitigate but don't eliminate these.

Finally, RAG cannot answer questions that require real-time data unless your indexing pipeline keeps pace. A 24-hour indexing lag means users get yesterday's truth.

Running RAG in Production

Best Practice

Best Practices - Instrument every stage, version embedding models, enforce access control at retrieval time, and evaluate on a fixed golden set before shipping changes.

Dimension Consideration
Scaling Vector DBs scale horizontally. Embedding cost grows linearly with corpus size. Plan full re-index when changing embedding models. Batch embed during off-peak.
Latency Typical: 50ms embed + 20–100ms search + 100–300ms rerank + 1–3s generation = 1.2–3.5s. Stream generation to mask LLM latency. Cache frequent queries.
Cost Embeddings: ~$0.02/1M tokens. Generation dominates: ~3K tokens/query at $2.50/1M (GPT-4o) ≈ $0.0075/query. Reranking adds ~$0.001/query via Cohere.
Monitoring Log query, filters applied, retrieved chunks with scores, reranker scores, LLM response, latency per stage. Alert on eval metric regressions.
Evaluation Weekly automated evals on golden set: recall@k, faithfulness, answer correctness. Block deploys that regress retrieval metrics.
Security Enforce document-level permissions via metadata filters. Never trust the LLM to self-filter - filter at the database query level. Audit retrieval logs.

Important

Fix retrieval before optimizing generation. The number one production mistake is prompt engineering a broken retriever.

Ecosystem

  • Orchestration: LangChain, LlamaIndex, Haystack - pipeline frameworks with loaders, splitters, and retrievers.

  • Vector Databases: Pinecone, Weaviate, Qdrant, Milvus, Chroma, pgvector.

  • Embedding Models: OpenAI text-embedding-3, Cohere embed-v3, BGE, E5, Nomic - see Embedding Models.

  • Rerankers: Cohere Rerank, bge-reranker, Jina Reranker - see Re-ranking.

  • Evaluation: RAGAS, DeepEval, LangSmith, Braintrust.

  • Managed: AWS Bedrock Knowledge Bases, Azure AI Search, Google Vertex AI Search.

Learning Path

Prerequisites: Embeddings · Large Language Models

Next topics: Chunking Strategies · Hybrid Search · Re-ranking · Retrieval Evaluation

Estimated time: 50 min · Difficulty: Intermediate

FAQs

When should I use RAG vs. fine-tuning?

Use RAG when you need access to specific, changing data. Use fine-tuning when you need to change behavior, tone, or output format. Many production systems use both.

How many documents can RAG handle?

No hard limit. Vector databases index millions of documents. The bottleneck is embedding cost and retrieval quality at scale, not storage capacity.

What chunk size should I use?

Start with 512 tokens and 50-token overlap. Shorter chunks (256) for factoid Q&A; longer (1024) when answers need surrounding context. Always measure recall@k.

How do I handle frequently changing documents?

Incremental indexing: re-chunk and re-embed only changed documents via upsert. Most vector DBs support this. For real-time data, consider shorter TTL indexes or hybrid with live keyword search.

Can RAG work with PDFs and images?

Yes. Use layout-aware PDF parsers (Unstructured, Docling). For images, use multimodal embeddings or OCR-extracted text. Store page numbers in metadata for citations.

What's the difference between RAG and long-context models?

Long-context models process more text per request but still can't access private data without it in the prompt. RAG retrieves relevant passages - more cost-efficient and works with unlimited corpus sizes. They complement each other.

How do I evaluate RAG quality?

Three layers: retrieval recall@k, faithfulness (answer matches context), and end-to-end correctness. See Retrieval Evaluation and RAGAS for automation.

What causes RAG to fail most often?

Wrong or missing retrieval (60%+ of failures), then chunk fragmentation, then LLM ignoring context. Log each stage to diagnose.

Do I need a vector database or is PostgreSQL enough?

pgvector works well under ~500K vectors and avoids new infrastructure. Dedicated vector DBs offer better ANN performance, hybrid search, and operational features at scale.

How do I add citations?

Pass source metadata with each chunk. Instruct the LLM to cite as [1], [2]. Map citation numbers back to URLs/titles in the response UI.

Should I use an agent instead of a fixed RAG pipeline?

Use a fixed pipeline when queries are straightforward Q&A. Use agentic RAG when the system needs query reformulation, multi-hop retrieval, or self-evaluation. Start fixed; add agent behavior when eval shows retrieval failures that reformulation would fix.

How often should I re-index?

Full re-index when changing embedding models. Incremental re-index on document change events (webhook, CDC, file watcher). Monitor index freshness as a metric.

References

Further Reading

Summary

  • RAG is the standard pattern for grounding LLM answers in your own data.
  • The architecture is simple - index, retrieve, generate - but quality depends on every stage.
  • Retrieval quality is the ceiling. Fix chunking and search before optimizing prompts.
  • Use hybrid search, metadata filters, and reranking in production.
  • Always return source citations and run automated evaluation weekly.
  • Start simple, measure everything, iterate based on failure analysis.

Next Topics

Learning Path

  1. RAGyou are here

Continue Learning

Related Tools

ToolCategoryPurposeWebsiteBest For
LangChainFrameworkFramework for building LLM-powered applications and workflows.langchain.comRAG systems
LlamaIndexRAGData framework for connecting LLMs to private and structured data.llamaindex.aiRAG over documents
PineconeVector DBManaged vector database for similarity search and RAG.pinecone.ioRAG systems
WeaviateVector DBOpen-source vector database with hybrid search and modules.weaviate.ioEnterprise search