RAG · Updated from retriever-updated.pdf · Class 41 · 08 Aug 2026

Retriever Guide

The retriever finds evidence for the LLM — it does not write the answer. Covers transforms (HyDE, multi-query), retrieval patterns, fusion, MMR, reranking, and contextual compression.

Query Transform Retrieve Fuse Rerank Compress LLM

RAG pipeline & what a retriever is

In a RAG system the retriever sits after the vector store and before the LLM. It finds evidence; it does not write the final answer.

1
Documents → Parsing / Loading → Chunking
Raw knowledge becomes searchable pieces with metadata.
2
Embeddings → Vector store
Chunks become vectors; the store holds vectors + metadata for fast lookup.
3
Retriever
Takes the user query, searches the knowledge source, returns the most relevant documents/chunks.
4
LLM → Final answer
The model reads the retrieved context and generates the response.
“A retriever takes a user query and returns the most relevant documents or chunks from a knowledge source. It normally does not generate the final answer itself.” — Retriever Guide PDF

Simple example

Database has 1,000 chunks. User asks: What is semantic chunking? The retriever does not send all 1,000 chunks. It returns only relevant ones (definition, example, advantages) for the LLM.

Analogies

Library

Librarian + teacher

User = student · Documents = books · Retriever = librarian · LLM = teacher. The librarian finds pages; the teacher explains.

Restaurant

Sous-chef + head chef

User = customer · Documents = pantry · Retriever = chef’s assistant · LLM = head chef. The retriever gathers ingredients; it does not cook.

Library analogy: user as student, documents as books, retriever as librarian, LLM as teacher
Library analogy — retriever finds pages; LLM teaches
Restaurant analogy: user as customer, documents as pantry, retriever as chef’s assistant, LLM as head chef
Restaurant analogy — retriever gathers ingredients; LLM cooks
Check yourself: In one sentence, what does a retriever do — and what must it never claim to do?

LangChain as_retriever knobs

From a vector store you typically build a retriever with a search type and kwargs, then invoke a query.

retriever = vector_store.as_retriever(
    search_type="mmr",
    search_kwargs={
        "k": 4,          # final number of documents to return
        "fetch_k": 20    # candidates considered by MMR
    }
)
documents = retriever.invoke("What is a vector database?")
for document in documents:
    print(document.page_content)
    print(document.metadata)
KnobMeaning
kFinal number of results returned
filterMetadata restriction
score_thresholdMinimum acceptable relevance score
fetch_kCandidates fetched before MMR diversifies
lambda_multMMR balance: relevance vs diversity

search_type

Default

similarity

Return the most similar chunks (top-k).

Quality gate

score_threshold

Keep only chunks that pass score_threshold.

Diversity

mmr

Maximum Marginal Relevance — relevant and diverse; less redundant context.

Try it: how search type changes the top results

Query: “What is Llama 2?” · k=3 · threshold=0.75

Similarity metrics

Vector DBs commonly expose these three families:

MethodWhat it measuresBest result
Cosine similarityAngle / direction between vectors (common for text embeddings)Highest score
Euclidean distance (L2)Straight-line distanceLowest distance
Dot productSum of element-wise products; ≈ cosine when vectors are normalizedHighest score
Watch the polarity: cosine/dot are usually higher-is-better; L2 is lower-is-better. Know what your store’s score API returns.

Metadata filtering

Do not rely on semantic similarity alone. Metadata constrains search to department, year, document type, source, role, or tenant.

metadata = {
    "department": "HR",
    "year": 2026,
    "document_type": "policy",
    "access_role": "manager"
}
# User: "Show the HR leave policy for 2026."
filter = {"department": "HR", "year": 2026}

With that filter, the retriever ignores other departments/years even if content is semantically similar.

Common filter types

Exact {"department": "HR"}
Range {"year": {"$gte": 2024, "$lte": 2026}}
Boolean $and / $or / NOT combining conditions
Scope date · department · document-type · source · tenant · role (RBAC)
Syntax differs across Chroma, Pinecone, Qdrant, Weaviate, Elasticsearch — learn the idea once, then map to each store’s filter DSL.

Pre-filtering vs post-filtering

Preferred

Pre-filtering

Filter first → search later. Preferred for auth / tenant isolation: restricted docs never enter the candidate set.

Risky

Post-filtering

Search first → filter later. Risk: too few survivors; authorized docs may never appear in the initial top-k.

Pre-filter flow

1
All stored documents
2
Apply metadata filter → allowed set only
Example: 10,000 docs → ~150 HR 2026 docs.
3
Similarity / keyword search on the allowed set
Return most relevant leave-policy chunks.
retriever = vector_store.as_retriever(
    search_type="similarity",
    search_kwargs={
        "k": 4,
        "filter": {"department": "HR", "year": 2026}
    }
)
documents = retriever.invoke("Show the HR leave policy for 2026.")

smaller search space fewer irrelevant hits better tenant isolation can improve speed

Post-filter flow

Top-5 semantic hits might include Finance / Legal / wrong year; after filtering for HR + 2026 only one remains — even if better HR docs never made the initial five.

documents = vector_store.similarity_search(
    "Show the HR leave policy for 2026.", k=5
)
filtered = [
    d for d in documents
    if d.metadata.get("department") == "HR"
    and d.metadata.get("year") == 2026
]
Post-filtering is weaker for strict access control: unauthorized documents can still sit in the intermediate candidate set, and you may need a larger initial k.

Sparse, dense, hybrid

TypeHow it worksExample
SparseExact keywords / BM25 / TF-IDFQuery employee leave policy hits docs with those terms
DenseEmbeddings + semantic similarityHow many days off… retrieves “20 days of annual leave”
HybridSparse + dense, then combineHR leave policy 2026 gets exact terms and semantic paraphrases
Hybrid often wins when queries mix precise identifiers (years, codes, names) with natural language. How you merge lists matters — see Result fusion and RRF vs Reranker.

Query transformation

Improve the user’s query before retrieval so the system can find more relevant information. Rewrite, expand, decompose — or use HyDE / multi-query (next sections).

1
Query rewriting
Conversational / vague → clear standalone search query.
“What did he say about it?”“What did the CEO say about the 2026 acquisition?”
Critical for chat RAG that depends on history.
2
Query expansion
Add synonyms / acronyms / variants: employee leave policyvacation policy, annual leave guidelines. Also: car → automobile, vehicle.
3
Query decomposition
Complex question → atomic sub-queries → retrieve per sub-query → merge evidence. Example: compare Company A vs B revenue + growth factors → four focused sub-queries.
Production stack (PDF summary): Query understanding (intent, metadata, security) → transform (rewrite / expand / multi-query / HyDE / decompose) → optional routing (vector / SQL / graph / web / APIs) → retrieve → fuse → rerank → contextual compression → LLM.

Hypothetical Document Embeddings (HyDE)

HyDE = generate a hypothetical answer first, then use its embedding to retrieve real documents.

1
User query
e.g. “How does Llama 2 improve safety?”
2
LLM creates a hypothetical answer/document
“Llama 2 improves safety using supervised safety fine-tuning, RLHF, red teaming, and safety evaluation.”
3
Embed that hypothetical text
4
Vector search for real documents similar to it
A full hypothetical answer is often closer to document wording than a short question.

Multi-Query Retriever

Creates multiple versions of the same user query, runs retrieval for each, then merges and deduplicates.

1
User query → generate variations
2
Search for each variation
3
Merge + remove duplicates → final docs

Example variations for “How does Llama 2 improve safety?”:

  1. What safety techniques are used in Llama 2?
  2. How was Llama 2 safety fine-tuned?
  3. How does Llama 2 reduce unsafe responses?

Multi-query

Several questions

Generates multiple query versions and searches with each. Main goal: improve recall.

HyDE

One hypothetical answer

Does not create multiple questions. Embeds a hypothetical answer and searches with that. Main goal: improve semantic matching.

Parent Document vs Sentence Window

Parent Document

Search small, return big

Embed child chunks (~300 tokens) for precise matching, but return the larger parent section (~1,500–2,000 tokens) that contains the hit.

Sentence Window

Search sentence, return neighbors

Embed single sentences for precision; when one matches, return that sentence plus nearby sentences as context.

Parent Document flow

Large doc → child chunks → embed children → query hits child → return its parent section. Small chunks retrieve accurately; parents give the LLM enough context.

Sentence Window example

Query: How is Llama 2 aligned using human feedback? may match Sentence 10 (“RLHF is used…”). Return Sentences 8–12 so the LLM sees SFT → preference data → RLHF → reward model → PPO.

LangChain has ParentDocumentRetriever built in. Sentence-window style retrieval needs custom metadata + replacement logic (LlamaIndex has a more direct built-in).

Multi-Hop Retrieval

Retrieve → use the result to retrieve again → combine evidence. Used when one retrieval pass cannot answer the question.

1
User query
“Who founded the company that developed Llama 2?”
2
Hop 1
Which company developed Llama 2? → Meta
3
Hop 2
Who founded Meta? → Mark Zuckerberg and co-founders
4
Combine evidence → final answer
No universal MultiHopRetriever in LangChain — implement with LangGraph (retriever + LLM + state), or use GraphVectorStoreRetriever with search_type="traversal" for graph corpora.

Hybrid merge strategies & Result Fusion

Hybrid retrieval = use multiple retrievers. It does not by itself define how to merge outputs. Common merge options:

ApproachHow it mergesNotes
Simple mergeConcatenate listsNo proper ranking; BM25 may dominate — weak for production
Weighted fusionCombine normalized scores with weightsScore-based; normalize first if scales differ
RRFCombine rank positionsRank-based; scales need not match
Rerank after mergeUnion candidates → cross-encoderCommon: top 50 → rerank → top 5

Weighted Fusion

Score-based

Final = (0.4 × BM25) + (0.6 × vector). Example: 0.4×0.8 + 0.6×0.9 = 0.86. Normalize scores when scales differ.

RRF

Rank-based

Points from rank positions across lists. Docs near the top in multiple lists win. Original RRF needs no weights.

Memory trick: Weighted Fusion = score-based fusion · RRF = rank-based fusion. Interactive walkthrough: RRF vs Reranker Pipeline.
LangChain EnsembleRetriever(weights=[...]) does weighted RRF, not raw score-weighted addition. For pure score fusion, normalize and combine yourself (or use DB-native hybrid).

Maximal Marginal Relevance (MMR)

MMR first takes the most relevant document, then picks remaining docs by balancing query relevance against diversity — avoiding near-duplicates of what is already selected.

Goal: relevant results without redundant / highly repetitive chunks for the LLM.

Worked example (from PDF)

Query: How does Llama 2 improve safety? · fetch_k=5 · k=2 · λ = 0.5

DocQuery similaritySim with Doc AMMR score (λ=0.5)
Doc A0.95Selected first (highest relevance)
Doc B0.900.95−0.025 (too similar to A)
Doc C0.880.400.24
Doc D0.840.200.32
Doc E0.800.100.35 ← chosen next

Similarity only

Doc A, Doc B

Both may say almost the same thing.

MMR

Doc A, Doc E

E is slightly less relevant but adds new information.

YouTube trick: without MMR you get “Python Tutorial Part 1–4”; with MMR you get Tutorial + Project + Interview + Best Practices.

retriever = vector_store.as_retriever(
    search_type="mmr",
    search_kwargs={"k": 4, "fetch_k": 20, "lambda_mult": 0.5}
)

Reranking

Reranking takes the initial candidate set, scores each document more carefully against the query, and reorders so the best evidence reaches the LLM.

1
User query
2
Initial retriever (BM25 / vector / hybrid)
Fast + broad; good candidates, imperfect order.
3
Reranker
Slower, more accurate scoring on a small window only.
4
Top documents → LLM

better order drop weak candidates less irrelevant context / tokens works on sparse, dense, or hybrid pools

Cost: extra latency, compute, and inference. Apply only to a limited candidate set — never the whole corpus.

Bi-encoder vs cross-encoder

Bi-encoder (retrieval)Cross-encoder (rerank)
EncodingQuery and documents encoded separately; doc vectors stored ahead of timeQuery + candidate processed together each time
StrengthFast search over large collectionsFine-grained query–document interactions
CostCheap at query time for millions of docsExpensive — score each pair separately (20 candidates ⇒ 20 forwards)

Production cascade

  1. User submits query → apply metadata + security filters
  2. Retrieve a broad candidate set (BM25 / vector / hybrid)
  3. Rerank → optional minimum-score threshold
  4. Send best documents to the LLM

Example scale: 1,000,000 chunks → retrieve 50 → rerank → pass top 5. Elasticsearch exposes the window as rank_window_size.

Ranking families

Pointwise Score each candidate alone (A→0.91, B→0.67), then sort. (monoBERT-style)
Pairwise Compare two docs: for this query, is A or B better? (duoBERT-style)
Listwise Consider several candidates together and emit an ordered list.
Reranking is older than RAG. Cascade ranking (2011) and BERT passage re-ranking (2019) popularized multi-stage IR; RAG reused: search → rerank → generate.

Contextual Compression

First retrieve relevant documents, then remove parts that are not useful for the current query.

1
Retriever gets relevant documents
2
Compressor checks them against the query
3
Irrelevant text removed → only useful context to the LLM

Example: a 1,000-token HR chunk covers leave, dress code, WFH, travel, holidays. Query “How many annual leave days?” keeps only the leave sentences.

Retriever

Finds docs

Brings candidates into the window.

Reranker

Orders docs

Same content, better ranking. 1000 words stay 1000 words.

Compression

Trims content

1000 words → ~120 useful words. Content changes.

Reducing k is not the same: you may still send a 1000-token chunk where only 20 tokens matter. Compression shortens each document.

Compressor types

1
LLMChainExtractor
LLM reads query + document and extracts only relevant sentences. Highest quality; costs an LLM call.
2
EmbeddingsFilter
Keep / discard docs (or sub-chunks) by embedding similarity threshold. Faster and cheaper — no LLM call.
3
CrossEncoderReranker
Can act as a compressor: drop low-relevance documents from the candidate set (reorder + filter).
from langchain_classic.retrievers.contextual_compression import (
    ContextualCompressionRetriever,
)
from langchain_classic.retrievers.document_compressors import LLMChainExtractor

compressor = LLMChainExtractor.from_llm(llm)
compression_retriever = ContextualCompressionRetriever(
    base_retriever=dense_retriever,
    base_compressor=compressor,
)

Multimodal retriever

Retrieves across modalities (text ↔ image) via multimodal embeddings in a shared vector space — text-to-image, image-to-text, and image-to-image.

Cross-modal

Text → Image

Text query → text encoder → shared space → search image vectors → relevant images.

Same-modal

Image → Image

Image query → image encoder → search image vectors → similar images.

Concept → LangChain class

From the updated PDF (langchain_classic / community imports — check your package versions).

ConceptClass / API
MMRvector_store.as_retriever(search_type="mmr")
Multi-QueryMultiQueryRetriever
HyDEHypotheticalDocumentEmbedder (or LCEL: prompt → LLM → embed → search)
Parent DocumentParentDocumentRetriever
Hybrid / RRFEnsembleRetriever + BM25Retriever + vector retriever
Contextual CompressionContextualCompressionRetriever
LLM extractLLMChainExtractor
Embedding filterEmbeddingsFilter
Cross-encoder rerankCrossEncoderReranker + HuggingFaceCrossEncoder
Listwise LLM rerankLLMListwiseRerank
Sentence windowCustom (metadata window + replace)
Multi-hopLangGraph / iterative calls; graph: GraphVectorStoreRetriever
# Hybrid via EnsembleRetriever (weighted RRF internally)
from langchain_community.retrievers import BM25Retriever
from langchain_classic.retrievers import EnsembleRetriever

bm25 = BM25Retriever.from_documents(chunks); bm25.k = 5
dense = vector_store.as_retriever(search_kwargs={"k": 5})
hybrid = EnsembleRetriever(retrievers=[bm25, dense], weights=[0.5, 0.5])
docs = hybrid.invoke("Llama 2 grouped query attention")
# Multi-query
from langchain_classic.retrievers.multi_query import MultiQueryRetriever
mq = MultiQueryRetriever.from_llm(
    retriever=vector_store.as_retriever(), llm=llm, include_original=True
)

Practice checklist

  1. Define a retriever in one sentence; mark where it sits in the RAG pipeline.
  2. Compare similarity vs mmr vs score-threshold (use the demo above). Walk the MMR Doc A / Doc E example.
  3. Explain k, fetch_k, lambda_mult, and when pre-filter beats post-filter.
  4. Contrast sparse / dense / hybrid; sketch simple merge vs weighted fusion vs RRF vs rerank-after-merge.
  5. Contrast HyDE vs Multi-Query; give one rewrite / expansion / decomposition example.
  6. Explain Parent Document vs Sentence Window; when you’d use multi-hop.
  7. State why a cross-encoder is second-stage only; how compression differs from reranking.
  8. Map three concepts to LangChain classes from the table above.

Source: retriever-updated.pdf · 41-Day-08Aug26 · Full-Stack GenAI Bootcamp