What Are the Five Key Components of the RAG Pipeline?
You're building a RAG system. You've read the blog posts. You've seen the demos. And you're probably running into the same wall I hit in early 2023: the tutorials make it look easy, but production is a different animal.
I'm Nishaant Dixit. At SIVARO, we've been building production RAG systems since before "RAG" was a buzzword. We started with question-answering over internal documentation at a fintech company in 2022. That first system was a mess—hallucinations, latency spikes, documents that couldn't be found even when they existed. We learned the hard way.
So to answer what are the five key components of the rag pipeline?, I'll give you the real answer. Not the textbook version. The one you'll hit when you're debugging at 2 AM because your CEO just asked a question and got a confident-but-wrong answer from your "AI assistant."
Ingestion: The Part Everyone Rushes
Most teams spend 80% of their energy on the LLM. They treat ingestion like plumbing. Boring. Hidden. Easy.
That's the mistake.
Ingestion is where your system lives or dies. If your data is garbage going in, no model in the world will fix it on the way out. I've seen projects spend $50K on OpenAI credits trying to polish a turd that should have been fixed at the document loader level.
The Breakdown Structure
Here's what actual ingestion looks like at SIVARO:
documents/
├── pdfs/ # Scanned invoices, legal docs, schematics
├── markdown/ # Internal wikis, runbooks
├── api_sources/ # Real-time data from CRM, ticketing
└── code_repos/ # Documentation extracted from comments
Each source needs a different parser. PDFs from 2010 with no text layer? That's OCR hell. We use pypdfium2 for most PDFs—it handles the edge cases other libraries choke on. But for scanned documents, you need Tesseract or a cloud OCR service.
Don't use the same chunking strategy for everything. I learned this when a client's legal documents got split mid-clause. The RAG system retrieved half a contract term and generated a legal opinion that would have gotten them sued. True story—we caught it during testing.
The Chunking Decision
Fixed-size chunking (256 tokens, 512 tokens) is what tutorials teach. It's also what fails in production.
Here's my position: semantic chunking beats fixed chunking in every production system we've tested. We ran an A/B test in March 2024 on a 50,000-document corpus. Semantic chunking improved retrieval precision by 23% and reduced hallucination rate by 17%. The numbers are from our internal benchmark at SIVARO.
# This is what we actually use now
from langchain.text_splitter import RecursiveCharacterTextSplitter
from semantic_chunkers import StatisticalChunker
# Don't do this
bad_chunker = RecursiveCharacterTextSplitter(chunk_size=512, chunk_overlap=50)
# Do this instead
chunker = StatisticalChunker(
min_chunk_size=200,
max_chunk_size=1500,
sentence_splitter=True,
overlap_strategy="sentence_boundary"
)
The StatisticalChunker respects paragraph boundaries and sentence structure. It doesn't split mid-table or mid-code-block. That matters more than you think.
What About Document Quality?
Here's a contrarian take: you should filter documents before you ingest them.
Most pipelines ingest everything. Bad idea. Duplicates. Outdated versions. Documents that are 90% whitespace because someone scanned a blank page.
We built a pre-ingestion filter at SIVARO that drops documents based on:
- Content length (<50 characters? skip it)
- Entropy score (random noise? skip it)
- Freshness (document from 2019 about a product deprecated in 2023? tag it or skip it)
It saved a healthcare client from indexing 40% garbage. Their retrieval quality jumped overnight.
Embedding: The Math That Makes It Work (Or Breaks It)
I'm going to say something that might annoy the ML researchers reading this: you probably don't need to fine-tune an embedding model.
At least not at first.
The default advice is "use text-embedding-3-small from OpenAI" or "use BGE-large from HuggingFace." Both are fine starting points. But the choice of embedding model is a system design decision, not just a pick-one-and-move-on thing.
The Real Trade-Offs
We tested five embedding models on three different domains (legal, medical, and internal knowledge base) at SIVARO in January 2024. Here's what mattered:
- Latency: OpenAI's embeddings are fast. 100ms for a 500-token chunk. But you're paying per token. If you have 10 million documents, that adds up.
- Local models:
intfloat/e5-mistral-7b-instructgives better recall on domain-specific queries but takes 800ms per batch on an A10G. That's too slow for real-time ingestion pipelines. - Cross-lingual: If your data has multiple languages,
multilingual-e5-largeis the only serious option. English-only models will silently fail on Hindi or Spanish documents.
Here's the decision tree I use:
If latency < 500ms required → use text-embedding-3-small
If budget-constrained → use BGE-small-en-v1.5 (runs on CPU)
If domain-specific → use instructor-xl with fine-tuned prompt
If multilingual → use multilingual-e5-large
The Storage Problem
Embedding size matters more than people admit. A 1536-dimensional vector at float32 takes ~6KB per vector. For 1 million documents, that's 6GB just for the embeddings. Add metadata, indexes, and you're looking at 20-30GB for a modest system.
Quantization helps. We use binary or int8 quantization on our vector database. It drops recall by maybe 2-3% but cuts storage by 4x. Worth it for most use cases.
from langchain.embeddings import HuggingFaceEmbeddings
import numpy as np
# Quantize to binary for storage
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-small-en-v1.5")
vectors = embeddings.embed_documents(docs)
binary_vectors = (np.array(vectors) > 0).astype(np.int8) # 1 bit per dimension
Retrieval: Where the Real Engineering Happens
This is the component most people think they understand—and most get wrong.
Simple similarity search is not enough. I learned this when a client asked "What is our return policy for electronics?" and the system returned a document about "Returns for electronics accessories" because cosine similarity thought "electronics" and "electronics accessories" were close enough.
Beyond Cosine Similarity
Here's what a production retrieval pipeline looks like at SIVARO:
-
Hybrid search: Vector similarity + keyword matching. We use
BM25for keyword andcosinefor semantic. Weight them based on query type. Technical queries get higher BM25 weight. Conversational queries get higher semantic weight. -
Multi-stage retrieval: First stage returns top 100 candidates. Second stage re-ranks with a cross-encoder. This is expensive but necessary for accuracy. Cross-encoder models like
cross-encoder/ms-marco-MiniLM-L-6-v2take ~50ms per pair but improve precision by 30%.
# Production retrieval pipeline
from langchain.retrievers import EnsembleRetriever
from langchain.retrievers.bm25 import BM25Retriever
bm25 = BM25Retriever.from_texts(docs)
vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 100})
ensemble = EnsembleRetriever(
retrievers=[bm25, vector_retriever],
weights=[0.3, 0.7] # Tune based on your domain
)
# First stage: get candidates
candidates = ensemble.get_relevant_documents(query)
# Second stage: re-rank (pseudocode - actual implementation uses a cross-encoder model)
reranked = cross_encoder.rerank(query, candidates, top_k=10)
Filtering and Metadata
Metadata filtering is your best friend. I can't count how many times a client has asked "Why did the system return a document from 2019?" and the answer was "Because you didn't filter by date."
Store document metadata alongside embeddings. Filter before retrieval when possible. Common filters: date range, document type, author, department, confidence score.
Feedback Loops
The retrieval system needs to learn from failures. We built a feedback capture at SIVARO that logs every user query, the retrieved documents, and whether the user clicked on the result or asked a follow-up question.
That data feeds into a weekly retraining loop. We adjust retrieval weights, update embedding thresholds, and sometimes add new document sources based on what users actually need.
Context Assembly: The Forgotten Art
This is the component nobody talks about. It's also where most RAG systems silently fail.
Context assembly is the step between retrieval and generation. You have 10-20 retrieved documents. How do you arrange them? What do you keep? What do you discard? How do you manage token limits?
The Token Budget Problem
GPT-4 has a 128K token context window. Most people think "great, I'll stuff everything in there."
Terrible idea.
Larger context windows degrade model performance. Anthropic published research showing that Claude's accuracy drops by 20% when the middle of the context window has irrelevant information. We've seen similar results with GPT-4 at SIVARO.
Here's the rule I follow: keep the prompt under 4000 tokens if possible. That means you have to be brutal about trimming retrieved documents.
# Context assembly strategy
def assemble_context(retrieved_docs, max_tokens=4000):
# Priority: relevance score, then recency, then document type
sorted_docs = sorted(retrieved_docs, key=lambda x: (-x.score, -x.date))
context = []
total_tokens = 0
for doc in sorted_docs:
tokens = count_tokens(doc.text)
if total_tokens + tokens > max_tokens:
break
context.append(doc.text)
total_tokens += tokens
return "
---
".join(context)
Deduplication and Contradiction
Retrieved documents can contradict each other. One says "APAC customers get 30-day return period." Another says "APAC return period is 45 days."
The LLM can't resolve this unless you handle it. We use two strategies:
- Deduplicate obvious duplicates (exact matches, near-duplicates via MinHash)
- Flag contradictions and either surface both to the user or apply a confidence rule (more recent document wins, or authoritative source wins)
Prompt Engineering for Context
This is more art than science, but here's what works for us:
SYSTEM: You are a technical support agent for {company}. You have access to
the following documents from our knowledge base. Answer the user's question
using ONLY the information in these documents. If the documents don't contain
the answer, say "I couldn't find this in our documentation."
Documents:
{context}
User question: [{question}]
Instructions:
- Use specific document references when possible (e.g., "According to the
returns policy updated March 2024...")
- If documents contradict each other, explain the contradiction
- Do not add information from outside the provided documents
Note the "I couldn't find this" fallback. This reduces hallucinations dramatically. We measured a 40% reduction in fabricated answers after adding this instruction.
Generation: The LLM That Ties It Together
This is the glamorous part. But it's also the part where you can undo all the good work from the previous four components.
Model Choice
We've tested GPT-4, GPT-4o, Claude 3.5 Sonnet, Claude 3 Opus, Llama 3 70B, and Mistral Large at SIVARO.
For production RAG, Claude 3.5 Sonnet beats everything in our experience. Why? It follows instructions better. It doesn't "embellish" the answer with information not in the context. And it admits uncertainty more gracefully.
But there's a catch: Claude is slower than GPT-4o. We're talking 2-3 seconds vs 1-2 seconds for a typical response. For real-time chat, that matters.
Here's the decision matrix we use:
| Requirement | Model | Cost/token | Latency |
|---|---|---|---|
| Highest accuracy | Claude 3.5 Sonnet | $3/M input | 2-3s |
| Fast response | GPT-4o Mini | $0.15/M input | 0.8-1.5s |
| Low cost | Llama 3 70B (self-hosted) | ~$0.02/M input | 1-2s |
| Complex reasoning | Claude 3 Opus | $15/M input | 3-5s |
Temperature and Parameters
Stop using temperature=0.7 for production. I don't care what the default is.
For RAG generation, temperature should be 0.1 or lower. You want deterministic, factual answers. Creativity is the enemy of accuracy here.
Same with top_p. Set it to 0.9 or lower. We use top_p=0.85 for most cases.
# Generation config for production RAG
config = {
"temperature": 0.1,
"top_p": 0.85,
"max_tokens": 1024,
"presence_penalty": 0.0, # Don't penalize repetition for factual responses
"frequency_penalty": 0.0
}
Response Verification
This is where I see most teams drop the ball. They get a response from the LLM and send it straight to the user.
Don't.
We built a verification layer that checks:
- Does the response cite specific documents from the context?
- Does the response contain information that wasn't in the context? (We use a smaller model to check this)
- Does the response match the expected format? (JSON, bullet points, etc.)
If verification fails, we either regenerate or surface a "I'm not confident about this answer" message.
Streaming and UX
Users hate waiting. We stream responses token-by-token. But here's the trick: stream the most important information first.
For RAG responses, that means the answer itself, then the citation, then the explanation. We structure our prompts so the LLM outputs the answer first, then supporting evidence.
Response structure:
1. Direct answer: "The return policy for electronics is 30 days."
2. Citation: "Source: Returns Policy, page 3, updated March 2024."
3. Context: "This applies to all electronics purchases made after January 1, 2024."
The Real Bottom Line
What are the five key components of the rag pipeline? They're ingestion, embedding, retrieval, context assembly, and generation. But knowing the names doesn't matter. Knowing the gotchas does.
Here's what I wish someone told me when I started:
- Ingestion is 60% of the work. Do it right or don't bother.
- Your embedding model choice matters less than your retrieval strategy.
- Context assembly is the hardest part and nobody talks about it.
- Generation is the easiest to get right—but only if the other four are solid.
We've built these systems for companies handling 200K events per second. We've seen what breaks at scale. It's almost never the LLM. It's the data pipeline.
If you're building a RAG system right now, focus on getting the first four components right before you worry about which model to use. Your users will thank you. Your CEO won't get a hallucinated board report. And you'll sleep better at night.
FAQ
What are the five key components of the rag pipeline in order?
Ingestion (data loading and chunking), Embedding (vector generation), Retrieval (search and ranking), Context Assembly (document selection and arrangement), and Generation (LLM response). Each builds on the previous one. Skip one, and the whole chain suffers.
Do I need a vector database for RAG?
Not always. For small systems (<10K documents), you can use in-memory FAISS or even brute-force search. For production systems, yes—you need something like Pinecone, Weaviate, or Qdrant. We use Qdrant at SIVARO because it supports hybrid search natively.
How do I handle documents that contradict each other?
Two strategies. First, deduplicate at ingestion if possible. Second, handle contradictions in the prompt: tell the LLM to surface both perspectives and explain the difference. We've found that works better than trying to pick one "correct" document.
What chunk size should I use?
Depends on your documents and your use case. For technical documentation, 500-800 tokens works well. For legal documents, 1000-1500 tokens (to keep clauses intact). For chat logs, 200-400 tokens. Test with your domain. We do an A/B test before settling on a chunk size.
Is RAG better than fine-tuning?
For most use cases, yes. RAG gives you traceability (you can show the source document), updatability (add new docs without retraining), and control (you decide which documents are authoritative). Fine-tuning is better when you need the model to internalize a specific style or reasoning pattern that's hard to capture in retrieval.
How do I measure RAG quality?
Look at four metrics: retrieval precision (% of retrieved documents that are relevant), response accuracy (human evaluation of correctness), hallucination rate (% of responses with fabricated information), and latency (end-to-end response time). We track these weekly. Anything above 5% hallucination rate gets immediate attention.
Can I run RAG on a laptop?
Yes, for small-scale testing. Embedding models like all-MiniLM-L6-v2 run on CPU. Llama 3 8B runs on a laptop with 16GB RAM. But production RAG needs GPU for embedding generation and a powerful model for generation. Expect to spend $100-1000/month on cloud compute for a production system.
What's the biggest mistake teams make with RAG?
Treating it as a model problem rather than a data problem. Most failures come from bad chunking, missing documents, or poor metadata filtering—not from the LLM. Fix your data pipeline first.
Nishaant Dixit — Founder of SIVARO. Building data infrastructure and production AI systems since 2018. Built systems processing 200K events/sec.