Mastering RAG: Advanced Retrieval Architectures for Production
Most developers start building Retrieval-Augmented Generation (RAG) applications using a simple setup: load document $\rightarrow$ split into 500-character chunks $\rightarrow$ embed using OpenAI's text-embedding-3-small $\rightarrow$ perform vector search.
While this approach works for basic demos, it quickly falls apart in production. Users ask complex questions, queries contain domain-specific acronyms, and vector retrieval returns irrelevant context, leading to LLM hallucinations.
To achieve production-grade retrieval accuracy, we must implement an advanced RAG architecture.
1. Hybrid Search: Vector + Keyword
Dense vector search is great at capturing semantic meaning, but poor at matching exact keywords, product IDs, or spelling variations. Keyword search (like BM25) excels at exact matches but misses synonyms.
Production systems combine both using Reciprocal Rank Fusion (RRF):
def reciprocal_rank_fusion(vector_results, keyword_results, k=60):
rrf_score = {}
# Process vector matches
for rank, item in enumerate(vector_results):
rrf_score[item.id] = rrf_score.get(item.id, 0) + 1.0 / (k + rank)
# Process keyword matches
for rank, item in enumerate(keyword_results):
rrf_score[item.id] = rrf_score.get(item.id, 0) + 1.0 / (k + rank)
# Sort documents by score
sorted_docs = sorted(rrf_score.items(), key=lambda x: x[1], reverse=True)
return sorted_docs2. Parent-Document Retrieval (Semantic Chunking)
When we chunk a document, we often lose the surrounding context. If we retrieve a small chunk containing a single metric, the LLM might miss the section title explaining *which* year that metric refers to.
Parent-Document Retrieval solves this:
This delivers the precision of small-chunk search combined with the rich context of large-chunk reading.
3. Reranking (The Silver Bullet)
Retrieving the top 20 documents via vector search is fast and cheap, but many retrieved items might be noise. Feeding 20 documents to an LLM increases latency, costs, and risks the LLM missing the answer due to the "lost in the middle" phenomenon.
Reranking is the solution:
import cohere
co = cohere.Client("API_KEY")
results = co.rerank(
model="rerank-english-v3.0",
query="How does the vector DB cache queries?",
documents=retrieved_docs,
top_n=3
)Advanced RAG Pipeline Architecture
Here is how the complete request pipeline looks in a production system:
[User Query] ──> [Query Rewriter] ──┬──> [Vector Search (Pinecone)] ──┐
└──> [Sparse BM25 Search] ───────┼──> [RRF Fusion] ──> [Cohere Rerank] ──> [LLM Prompt]Implementing hybrid search, parent retrieval, and cross-encoder reranking shifts RAG applications from simple QA systems into robust, search-grade business assets.