Fun with Learning Technology
LearnCoursesQuestionsTracksToolsNewsExplorePractice
Fun with Learning Technology

A new problem, explained clearly, every day.

Subscribe
Learn
  • Lessons
  • Topics
  • News
  • Tools
  • Courses
  • Career tracks
  • Everything
Site
  • About
  • Contact
  • Support
  • Privacy
  • Terms
Get the daily one

One email per new problem. No spam.

Request a tutorial

Requests shape what gets made next.

© 2026 Fun with Learning TechnologyRSS
Home›Courses›LangChain›Contextual Compression

Retrieval

Contextual Compression

Contextual Compression is a post-retrieval technique that filters and shortens retrieved documents to retain only the most relevant information for a query. By reducing the noise passed to the model, it significantly improves the quality of generated answers while curbing token usage and preventing context window overflow. Developers should implement this when retrieving large chunks of data where specific details are buried within otherwise irrelevant surrounding text.

The Problem of Retrieval Overlap

Standard retrieval methods often pull in entire document chunks that contain a mixture of relevant and extraneous information. Because embedding models map text to high-dimensional vectors, they are excellent at finding semantic neighbors but poor at identifying the exact sentence that answers a specific user intent. When you retrieve multiple documents, you often pass hundreds of tokens of background context to the language model, which can lead to 'lost in the middle' phenomena where the model struggles to focus on the signal amidst the noise. Contextual compression solves this by adding an intermediary layer between the retriever and the final generation step. By acknowledging that retrieval is inherently imprecise, we shift the burden of precision from the vector search itself to an intelligent, query-aware filter that trims the fat from every retrieved block before it is ever sent to the synthesis phase.

# Initial setup: Define a retriever and wrap it for compression
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import LLMChainExtractor
from langchain_openai import OpenAI

# We start with a base retriever (e.g., VectorStoreRetriever)
base_retriever = vectorstore.as_retriever()

# We initialize the compressor with an LLM to evaluate relevance
llm = OpenAI(temperature=0)
compressor = LLMChainExtractor.from_llm(llm)

# Wrap the base retriever with the compressor
compression_retriever = ContextualCompressionRetriever(
    base_retriever=base_retriever, base_compressor=compressor
)

# The final call fetches, then filters, then returns
docs = compression_retriever.invoke("What is the company policy on remote work?")

Document Transformers for Structural Filtering

Beyond using language models to rewrite or extract content, you can utilize structural document transformers. These tools leverage metadata or document structure to prune results without requiring the computational overhead of an LLM call for every segment. This approach works on the reasoning that if you have tagged your documents with specific categories or metadata filters, you can effectively discard entire branches of irrelevant data before performing a deep search. By defining a compressor that looks at the metadata, you ensure that even if a query is semantically ambiguous, you enforce a strict logical boundary that keeps the retrieved set within the desired domain. This strategy is highly effective for reducing latency in production systems where cost and speed are critical, yet accuracy remains paramount for legal or technical documentation where context is everything.

# Using an EmbeddingsFilter to prune documents based on semantic distance
from langchain.retrievers.document_compressors import EmbeddingsFilter
from langchain_openai import OpenAIEmbeddings

# Filter out chunks that aren't closely related to the query vector
embeddings = OpenAIEmbeddings()
filter = EmbeddingsFilter(embeddings=embeddings, similarity_threshold=0.75)

# Integrate into the compression retriever
compression_retriever = ContextualCompressionRetriever(
    base_retriever=base_retriever, base_compressor=filter
)

# This performs similarity checking between query and chunks in memory
relevant_docs = compression_retriever.invoke("Explain the technical architecture of the API.")

Query-Aware Compression Logic

When we perform contextual compression, the core logic relies on the query itself acting as a lens. Unlike a static filter that might always prioritize certain headers, query-aware compression dynamically adjusts what it deems 'relevant' based on the specific words input by the user. If a user asks about 'security protocols,' the compressor will prioritize segments containing encryption or access control details, even if those segments share the same parent document as unrelated HR policies. The reasoning is that the retrieval process is simply a high-recall search, while the compression process is a high-precision ranking mechanism. By separating these two steps, we gain a massive increase in context window efficiency because we only send the model exactly what is needed for the prompt, rather than blindly flooding it with potentially distracting, albeit semantically similar, boilerplate text from retrieved files.

# Chaining multiple compressors for optimized results
from langchain.retrievers.document_compressors import DocumentCompressorPipeline

# Sequence: Filter by embedding distance, then shorten with LLM extraction
filter_step = EmbeddingsFilter(embeddings=embeddings, similarity_threshold=0.8)
llm_step = LLMChainExtractor.from_llm(llm)

# Combine them into a single pipeline
pipeline = DocumentCompressorPipeline(transformers=[filter_step, llm_step])

# Apply the pipeline to the retriever
optimized_retriever = ContextualCompressionRetriever(
    base_retriever=base_retriever, base_compressor=pipeline
)

docs = optimized_retriever.invoke("What are the project timelines?")

Handling Noise with Redundancy Filters

Redundancy is a silent killer of model accuracy. Often, a vector database will return three different chunks that contain essentially the same information, merely phrased differently or indexed from repeating sections. If you pass all three to a language model, you occupy valuable context space and risk the model becoming stuck in a repetitive loop. Contextual compression allows for the implementation of redundancy filters that compute the mutual information or semantic similarity between candidates, keeping only the most representative or unique snippets. This reasoning is based on the information bottleneck principle: we want to maximize the information content per token supplied to the model. By pruning duplicates, we allow the LLM to process a denser, more diverse set of data, which invariably results in a more nuanced and accurate answer for the end-user without requiring larger, costlier context windows.

# Removing duplicate information to save context space
from langchain.retrievers.document_compressors import EmbeddingsRedundantFilter

# Define a filter to remove redundant chunks
redundant_filter = EmbeddingsRedundantFilter(embeddings=embeddings)

# Pipeline: Remove duplicates first, then perform LLM extraction
pipeline = DocumentCompressorPipeline(
    transformers=[redundant_filter, llm_step]
)

# Initialize with the combined pipeline
final_retriever = ContextualCompressionRetriever(
    base_retriever=base_retriever, base_compressor=pipeline
)

# Final results contain unique, highly relevant segments
results = final_retriever.invoke("Summary of project objectives")

Evaluating Performance and Trade-offs

Implementing contextual compression is a balance between retrieval latency and output quality. Every stage of compression adds time to the initial retrieval phase, as each compressor must process the documents before they are passed downstream. It is vital to perform offline evaluations to determine if the gain in answer precision justifies the millisecond-latency cost added to the pipeline. For real-time applications, consider using lighter compressors like embedding-based filters rather than full LLM extraction. The goal is to establish a threshold where the cost of the compressor is significantly lower than the cost of token-heavy generation, while simultaneously ensuring that the retrieval process remains fast enough for the user experience. By iteratively testing with varying thresholds, you can tune the system to find the optimal 'Goldilocks' zone for your specific document collection size and complexity.

# Monitoring compression time for performance tuning
import time

start_time = time.time()

# Execute retrieval and compression
results = final_retriever.invoke("Performance metrics for Q4")

end_time = time.time()

# Log metrics to evaluate the impact on latency
print(f"Compression pipeline took {end_time - start_time:.2f} seconds")
print(f"Retrieved {len(results)} compressed document segments.")

Key points

  • Contextual compression functions by filtering and extracting relevant content after the initial retrieval phase.
  • This technique helps mitigate the problem of context window overflow by passing only high-signal information to the language model.
  • Developers can use LLM-based extractors to trim irrelevant text from retrieved document segments.
  • Embedding-based filters offer a faster alternative to LLM extraction by using semantic similarity to prune segments.
  • Redundancy filters are essential to prevent the model from receiving duplicate data that wastes token space.
  • Compression pipelines can be combined to form multi-stage workflows that filter and refine documents sequentially.
  • Implementing these tools requires a careful trade-off analysis between increased latency and improved answer quality.
  • Query-aware compression ensures that the filtering logic remains dynamic and responsive to specific user requests.

Common mistakes

  • Mistake: Expecting contextual compression to reduce the size of the original vector database. Why it's wrong: Compression happens at the retrieval stage, not the storage stage. Fix: Understand that your original documents remain indexed in their entirety; compression only filters the context provided to the LLM.
  • Mistake: Over-compressing documents by setting the top_k parameter too low. Why it's wrong: You risk losing critical semantic nuances that the LLM needs to construct an accurate answer. Fix: Experiment with top_k to balance context relevance with token budget.
  • Mistake: Using a base retriever that provides poor semantic relevance. Why it's wrong: Compression cannot salvage irrelevant documents; if the base retriever fetches the wrong content, the compressor will either keep garbage or remove the actual answer. Fix: Ensure the base retriever is performing well before adding a compression layer.
  • Mistake: Misinterpreting the role of the 'Document Compressor'. Why it's wrong: Users often think the compressor summarizes documents; however, it usually filters or rearranges them based on relevance. Fix: Use a 'ContextualCompressionRetriever' specifically for pruning, and separate summarization chains for distilling information.
  • Mistake: Forgetting that LLM-based compressors (like LLMChainExtractor) add latency. Why it's wrong: Passing documents through an LLM to decide relevance is significantly slower than using an embedding-based filter. Fix: Only use heavy LLM-based compressors when accuracy is more critical than speed.

Interview questions

What is the primary purpose of Contextual Compression in LangChain?

The primary purpose of Contextual Compression in LangChain is to optimize the information retrieval process by filtering out irrelevant parts of retrieved documents. When dealing with large datasets, retrieving full documents often leads to noise, increasing costs and confusing the Large Language Model. By applying a 'BaseCompressor' to the retrieved documents, we extract only the most pertinent information, ensuring the context provided to the LLM is focused, concise, and highly relevant to the user query.

How does the Contextual Compression Retriever differ from a standard VectorStoreRetriever?

A standard VectorStoreRetriever simply performs a similarity search to return the top-k most relevant document chunks based on embedding proximity. In contrast, the Contextual Compression Retriever acts as a wrapper around that base retriever. It takes those initial documents and passes them through a compression step, which could involve re-ranking or extraction. This allows the system to refine the output dynamically, ensuring that the documents passed to the LLM are not just topically similar, but also contextually meaningful and compact.

Can you explain how to implement Contextual Compression using the LLMChainExtractor in LangChain?

To implement `LLMChainExtractor`, you first define your base retriever and a language model. You then initialize the compressor using `LLMChainExtractor.from_llm(llm)`. This compressor iterates through each retrieved document and uses the LLM to extract only the portions relevant to the user's query. The final implementation looks like this: `compression_retriever = ContextualCompressionRetriever(base_compressor=compressor, base_retriever=retriever)`. It is highly effective for reducing context bloat, though it does increase the number of API calls significantly.

Compare the use of LLMChainExtractor versus EmbeddingsFilter for Contextual Compression.

The `LLMChainExtractor` focuses on rewriting or trimming content based on semantic relevance via an LLM, which is thorough but computationally expensive. Conversely, `EmbeddingsFilter` operates by calculating the embedding similarity between the query and each document segment, dropping those that fall below a specific relevance threshold. `EmbeddingsFilter` is significantly faster and cheaper because it avoids generative calls, making it ideal for large-scale production environments where latency is a concern, whereas `LLMChainExtractor` provides higher precision for complex, nuanced queries.

When would you choose DocumentCompressorPipeline over a single compressor in LangChain?

You choose the `DocumentCompressorPipeline` when a single compression technique is insufficient to achieve the desired output quality. For example, you might want to first filter out irrelevant documents using an `EmbeddingsFilter` to reduce volume, and then use `EmbeddingsRedundantFilter` to remove duplicate information. By chaining these, you create a multi-stage refinement process. This allows you to optimize for both latency and accuracy, ensuring the final context provided to the LLM is both minimized in length and high in information density.

How do you handle potential latency issues when using complex Contextual Compression pipelines?

Latency is a major trade-off in Contextual Compression, especially with generative extractors. To mitigate this, you should prioritize non-generative compressors like `EmbeddingsFilter` or `EmbeddingsRedundantFilter` as the initial stage in your pipeline to drastically prune the document set before any LLM processing occurs. Additionally, consider using smaller, faster models for the compression step. By keeping the document count sent to the LLM as low as possible through these early-stage filters, you minimize the overall time-to-first-token while maintaining high contextual accuracy for the final response.

All LangChain interview questions →

Check yourself

1. What is the primary benefit of using a Contextual Compression Retriever compared to a standard Vector Store Retriever?

  • A.It increases the storage density of the vector index.
  • B.It filters retrieved documents to include only content relevant to the specific user query.
  • C.It automatically summarizes every document in the database.
  • D.It forces the LLM to ignore all retrieved context.
Show answer

B. It filters retrieved documents to include only content relevant to the specific user query.
Contextual compression focuses on refining retrieved results so the LLM only gets the most relevant information. Option 0 is wrong because compression occurs at query time, not storage. Option 2 is wrong because compression is about filtering, not summarization. Option 3 is wrong because the goal is to enhance, not hinder, context.

2. If you implement an LLMChainExtractor as your compressor, what is the main trade-off you introduce into your application?

  • A.Reduced accuracy in final output.
  • B.Significant decrease in memory usage on the vector database.
  • C.Increased latency due to additional calls to the LLM.
  • D.Incompatibility with standard LangChain retrievers.
Show answer

C. Increased latency due to additional calls to the LLM.
An LLMChainExtractor requires the LLM to process each retrieved document to decide if it is relevant, which adds overhead. Option 0 is wrong as it potentially increases accuracy. Option 1 is wrong as it does not affect database storage. Option 3 is wrong because it is specifically built for LangChain retrievers.

3. When configuring a retriever with a compressor, why might you choose a simple 'EmbeddingsFilter' over an 'LLMChainExtractor'?

  • A.To achieve faster query performance.
  • B.To perform complex semantic reasoning on every retrieved chunk.
  • C.To eliminate the need for any embedding model.
  • D.To increase the number of tokens sent to the LLM.
Show answer

A. To achieve faster query performance.
EmbeddingsFilter uses vector similarity scores, which are computationally cheap compared to passing text through an LLM. Option 1 is wrong because LLMChainExtractor does the reasoning. Option 2 is wrong because embedding models are required. Option 3 is wrong because the goal of compression is usually to reduce tokens.

4. A user asks a query, and your retriever returns 10 chunks. Your compressor is set to filter these. What happens if the compressor removes 7 of those chunks?

  • A.The system throws an error because the document count changed.
  • B.Only the remaining 3 chunks are passed to the LLM for the final generation.
  • C.The original 10 chunks are still sent to the LLM.
  • D.The vector store automatically deletes the 7 chunks.
Show answer

B. Only the remaining 3 chunks are passed to the LLM for the final generation.
The contextual compression retriever acts as a wrapper that passes only the relevant subset to the final processing step. Option 0 is false; this is the intended workflow. Option 2 is false as the compressor specifically restricts the list. Option 3 is false as compression does not modify the underlying data store.

5. Which scenario best justifies the use of a Contextual Compression Retriever?

  • A.When the user query is very short and generic.
  • B.When your vector store contains only a few documents.
  • C.When retrieving large documents that contain both relevant and irrelevant sections.
  • D.When you want to bypass the need for an LLM entirely.
Show answer

C. When retrieving large documents that contain both relevant and irrelevant sections.
Compression is most valuable when retrievers return long chunks with noise, as it cleans the context for the LLM. Option 0 is not a specific driver for compression. Option 1 is wrong as small databases usually don't require complex filtering. Option 3 is wrong because compression is designed to improve the context passed to an LLM, not remove the LLM.

Take the full LangChain quiz →

← PreviousRetrieval ChainsNext →LangChain Agents Overview

LangChain

24 lessons, free to read.

All lessons →

Track your progress

Sign in to mark lessons done, score quizzes and keep notes.

Open in the app