Here's an uncomfortable truth: your RAG pipeline is probably retrieving the wrong information a third of the time.
Not wrong enough to crash. Not wrong enough to obviously fail. Just wrong enough to produce confident-sounding responses that are subtly, dangerously incorrect.
I've audited dozens of RAG implementations. The pattern is consistent: teams build pipelines, test them on a few happy-path examples, declare success, and ship. Six months later, users are complaining that the system "doesn't really work" and nobody knows why.
The problem isn't the LLM. It's retrieval. And almost nobody is measuring it.
The Hidden Failure Mode
Let me show you what I mean.
Scenario: Customer Support RAG
Knowledge base: 10,000 documents. User asks: How do I reset my password?
What happens:
- 1. Query gets embedded
- 2. Vector search finds "similar" chunks
- 3. Top 5 chunks go to the LLM
- 4. LLM generates an answer
What you see: A reasonable-looking response about password reset.
What you don't see:
The retrieved chunks were actually about:
- ❌Chunk 1: Password policy requirements (not reset instructions)
- ⚠️Chunk 2: Account recovery (close, but different process)
- ✓Chunk 3: Password reset (correct!)
- ❌Chunk 4: Two-factor authentication setup (wrong)
- ❌Chunk 5: Password expiration policy (wrong)
The LLM saw one relevant chunk out of five. It improvised with its parametric knowledge to fill the gaps. The answer looks right but contains details that aren't in your knowledge base.
This happens 30-40% of the time in typical RAG deployments.
Why Nobody Catches This
Three reasons this failure mode persists:
Reason 1
You're Only Testing End-to-End
Most teams test by asking questions and checking if the answer seems reasonable. This tests the entire pipeline—retrieval, augmentation, and generation—as a black box. When the answer is wrong, you don't know why. Was it bad retrieval? Bad prompting? LLM hallucination?
You need to test retrieval separately.
Reason 2
LLMs Are Good at Sounding Confident
When the LLM gets mostly-wrong context, it doesn't say "I'm not sure." It generates a plausible answer using whatever scraps of relevance it found plus its training knowledge.
The response sounds authoritative. Users trust it. The failure is invisible.
Reason 3
Retrieval Metrics Aren't in Your Dashboard
You're tracking:
- • Response latency
- • Token usage
- • User satisfaction (maybe)
- • Error rates
You're NOT tracking:
- • Retrieval precision
- • Retrieval recall
- • Chunk relevance scores
- • Context utilization
If you don't measure retrieval quality, you don't know when it's failing.
How to Catch It
Here's the evaluation framework I use:
Step 1: Build a Retrieval Test Set
Create a dataset of queries paired with the correct source documents.
retrieval_test_set = [
{
"query": "How do I reset my password?",
"relevant_doc_ids": ["doc_123", "doc_456"],
"relevant_chunks": ["chunk_123_2", "chunk_456_1"]
},
{
"query": "What's the refund policy for annual subscriptions?",
"relevant_doc_ids": ["doc_789"],
"relevant_chunks": ["chunk_789_3", "chunk_789_4"]
},
# 50-100 examples covering your key use cases
]How to Create This
- • Sample real user queries from logs
- • Have a human identify the correct source documents
- • Map to specific chunks
Yes, this is manual work. It's worth it. Start with 50 examples covering your most important queries.
Step 2: Measure Retrieval Quality
For each test query, run your retrieval and measure:
def evaluate_retrieval(test_set, retriever, k=5):
results = []
for test in test_set:
# Run retrieval
retrieved = retriever.retrieve(test["query"], top_k=k)
retrieved_ids = [chunk.id for chunk in retrieved]
# Calculate metrics
relevant_set = set(test["relevant_chunks"])
retrieved_set = set(retrieved_ids)
# Precision: What fraction of retrieved chunks are relevant?
precision = len(relevant_set & retrieved_set) / len(retrieved_set)
# Recall: What fraction of relevant chunks did we retrieve?
recall = len(relevant_set & retrieved_set) / len(relevant_set)
# Hit rate: Did we get at least one relevant chunk?
hit = len(relevant_set & retrieved_set) > 0
# MRR: Where does the first relevant chunk appear?
mrr = 0
for i, chunk_id in enumerate(retrieved_ids):
if chunk_id in relevant_set:
mrr = 1 / (i + 1)
break
results.append({
"query": test["query"],
"precision": precision,
"recall": recall,
"hit": hit,
"mrr": mrr
})
return resultsKey Metrics & Targets
| Metric | What It Measures | Target |
|---|---|---|
| Hit Rate | % of queries with at least 1 relevant chunk | >95% |
| Precision@k | % of retrieved chunks that are relevant | >60% |
| Recall@k | % of relevant chunks that are retrieved | >80% |
| MRR | Average rank of first relevant chunk | >0.7 |
If your hit rate is below 90%, you have a serious problem. If precision is below 50%, you're flooding the LLM with noise.
Step 3: Analyze Failure Patterns
Don't just measure—understand why retrieval fails.
Pattern 1
Semantic Confusion
Query about "password reset" retrieves chunks about "password policy"—semantically similar, functionally different.
Pattern 2
Missing Context
The relevant information spans multiple chunks, but retrieval only gets part of it.
Pattern 3
Keyword Mismatch
User says "cancel subscription" but docs say "terminate service"—semantic gap.
Pattern 4
Freshness Issues
Retrieved chunks are from outdated documents.
Pattern 5
Popularity Bias
Frequently accessed content gets retrieved even when not relevant.
The Most Common Problems (and Fixes)
Problem 1: Chunks Are Too Small
Symptom: High recall, low precision. You're retrieving relevant content, but each chunk lacks context.
Why it happens: Aggressive chunking (200-300 tokens) splits logical units of information.
Fix: Increase chunk size to 500-800 tokens. Use semantic chunking that respects document structure.
Problem 2: Chunks Are Too Big
Symptom: Low precision, high noise. Retrieved chunks contain relevant info buried in irrelevant content.
Why it happens: Large chunks (1000+ tokens) mix multiple topics.
Fix: Decrease chunk size. Use hierarchical retrieval—retrieve big chunks, then re-rank smaller segments.
Problem 3: No Chunk Overlap
Symptom: Important information at chunk boundaries gets missed.
Why it happens: Adjacent chunks don't share context.
Fix: Add 10-20% overlap between chunks.
Problem 4: Embedding Model Mismatch
Symptom: Semantically relevant chunks don't surface.
Why it happens: Your embedding model wasn't trained on your domain. "Cancel" and "terminate" might not be close in embedding space.
- • Try different embedding models (e.g., text-embedding-3-large vs voyage-2)
- • Fine-tune embeddings on your domain
- • Add keyword search as a fallback (hybrid search)
Problem 5: No Query Preprocessing
Symptom: Conversational queries don't match document language.
Why it happens: Users ask "How do I change my password?" but docs say "Password Reset Procedure."
Fix: Rewrite queries before embedding. Use LLM to convert to document-style language.
Problem 6: No Metadata Filtering
Symptom: Retrieving from wrong document categories.
Why it happens: A question about "Enterprise pricing" retrieves chunks from "Starter pricing" docs—semantically similar, wrong context.
Fix: Add metadata filters before vector search (category, version, date, etc.).
A Retrieval Debugging Checklist
When retrieval quality drops, run through this:
- □Check embedding model performance on your domain
Run same queries through different models. Look for systematic gaps.
- □Analyze chunk boundaries
Are logical units split? Is overlap sufficient?
- □Review failure patterns
Semantic confusion? → Better embeddings or query rewriting
Missing context? → Larger chunks or parent retrieval
Wrong category? → Metadata filtering - □Test hybrid retrieval
Add BM25/keyword as fallback. Compare hit rates.
- □Check for stale content
Are outdated docs still indexed? Is freshness weighted in ranking?
- □Examine edge cases
Queries with typos. Multi-part questions. Negations ("NOT including...").
Quick Wins to Implement Today
If you do nothing else:
1
Create 50 test queries
with labeled relevant documents
2
Measure hit rate and precision
on your current retrieval
3
Add hybrid search
vector + keyword as a safety net
4
Log every retrieval
for later analysis
5
Review 10 random retrievals per week
manually
That's a few hours of work. It will save you months of debugging mysterious answer quality issues.
RAG isn't just "add search to your LLM." It's a retrieval problem that happens to use an LLM for generation.
The retrieval community has decades of research on this. Precision, recall, ranking, evaluation—these are solved problems. The RAG ecosystem has mostly ignored them.
Don't make that mistake.
Build evaluation into your pipeline from day one.
Start measuring. Start improving. Stop lying.
Building RAG systems and want help getting retrieval right?
Let's talkThis is one of the most common problems we fix.
