在基于 RAG (Retrieval-Augmented Generation,检索增强生成) 的项目中,上下文压缩是一个至关重要的环节。它旨在解决当文档过长,分割成块后可能丢失重要上下文信息,导致检索结果不连贯或无意义的问题。本文将深入探讨 RAG 管道中上下文压缩的重要性,并结合 Langchain 和 Azure AI Search,详细介绍如何通过不同的压缩器来优化检索效果,提升大语言模型 (LLM) 的响应质量。
RAG 的核心挑战:检索质量与上下文完整性
RAG 的基本流程是将大型文档分割成更小的块,并将这些块索引到向量存储中。当用户提出查询时,系统会检索最相关的文档块,然后将这些文档块与原始问题一起传递给 LLM,以生成类似人类的响应。这个过程的核心在于检索环节,检索到的文档质量直接决定了最终生成的答案质量。
然而,当文档篇幅较长时,简单的切块策略可能会导致关键信息的丢失。例如,如果一个段落的关键论点被分割到不同的文档块中,那么在检索过程中就可能无法完整地检索到这些信息,从而导致 LLM 无法生成准确的答案。这就是 RAG 系统面临的检索质量与上下文完整性之间的挑战。单纯依靠提示词工程,难以从根本上解决检索质量的问题。问题的本质在于,检索阶段没有正确识别和选择相关信息。
上下文压缩:RAG 管道中的关键环节
上下文压缩正是为了解决上述挑战而提出的。它充当检索和生成之间的中间层,负责过滤或总结检索到的文档,仅保留与用户问题真正相关的内容。换句话说,上下文压缩的目标是减少语义噪声,降低发送给 LLM 的 token 数量,并保留原始文档中的关键推理和核心内容。
想象一下,用户询问关于“如何利用 Azure AI Search 优化电商网站的搜索功能”的问题。传统的检索器可能会返回多个包含“Azure AI Search”、“电商”、“搜索”等关键词的文档,但这些文档可能包含大量与用户问题无关的信息,比如 Azure AI Search 的定价、部署方式等。而经过上下文压缩后,系统只会保留与电商网站搜索功能优化相关的部分,例如,使用 Azure AI Search 的语义搜索功能提升商品排序、利用自定义实体识别提取商品属性等。
Langchain 中的 ContextualCompressionRetriever
Langchain 提供了对上下文压缩的天然支持,它通过 ContextualCompressionRetriever
类来实现这一策略。ContextualCompressionRetriever
将一个基础检索器 (base retriever) 与一个压缩组件 (compression component) 结合在一起。Langchain 文档中列出的压缩器主要有以下几种:
- LLMChainExtractor: 使用 LLM 读取每个文档,并仅提取与问题最相关的内容。这种方法的优点是精度较高,但缺点是成本较高且速度较慢。
- LLMChainFilter: 决定整个文档是否与用户的问题相关。它充当一个二元分类器,根据 LLM 的评估结果,保留或丢弃整个文档。相比于
LLMChainExtractor
,它的成本更低,速度更快,但精度可能会有所降低。 - EmbeddingsFilter: 使用嵌入向量比较用户问题与文档之间的相似度。它仅返回最相似的文档。其精度取决于嵌入向量的质量和配置的相似度阈值。
结合 Azure AI Search 和 Langchain 实现上下文压缩
以下将通过具体的代码示例,展示如何结合 Azure AI Search 和 Langchain,利用不同的压缩器来实现上下文压缩。
首先,需要初始化 Azure AI Search 检索器,并配置 LLM 和嵌入模型。
from langchain_community.retrievers import AzureAISearchRetriever
from langchain_openai import AzureChatOpenAI, AzureOpenAIEmbeddings
# 基础检索器配置
baseretriever = AzureAISearchRetriever(
content_key="chunk_content",
service_name="your-azure-ai-search-service-name",
api_key="your-azure-ai-search-api-key",
top_k=10,
index_name="your-index-name"
)
# LLM 配置
llm = AzureChatOpenAI(
azure_deployment="your-gpt-deployment-name",
api_version="2024-08-01-preview",
api_key="your-azure-openai-api-key",
azure_endpoint="your-azure-openai-endpoint",
temperature=0,
)
# 嵌入模型配置
embeddings = AzureOpenAIEmbeddings(
azure_deployment="your-embedding-deployment-name",
api_key="your-azure-openai-api-key",
azure_endpoint="your-azure-openai-endpoint",
)
使用 LLMChainExtractor
LLMChainExtractor
使用 LLM 来提取文档中与用户问题最相关的部分。以下代码展示了如何使用 LLMChainExtractor
来构建 ContextualCompressionRetriever
。
from langchain.retrievers.document_compressors import LLMChainExtractor
from langchain.retrievers import ContextualCompressionRetriever
# 创建 LLMChainExtractor 实例
_extractor = LLMChainExtractor.from_llm(llm)
# 创建 ContextualCompressionRetriever 实例
compression_retriever = ContextualCompressionRetriever(
base_compressor=_extractor,
base_retriever=baseretriever,
)
# 执行检索
compressed_docs = compression_retriever.invoke("用户的问题")
在这个例子中,compressed_docs
变量将包含经过 LLM 提取后,与用户问题最相关的文档片段。例如,如果用户提问“Azure AI Search 在电商搜索中的优势是什么?”,则 compressed_docs
中将包含关于语义搜索、自定义实体识别等优势的文档片段,而其他无关的信息则会被过滤掉。
使用 LLMChainFilter
LLMChainFilter
使用 LLM 来判断整个文档是否与用户问题相关。以下代码展示了如何使用 LLMChainFilter
来构建 ContextualCompressionRetriever
。
from langchain.retrievers.document_compressors import LLMChainFilter
# 创建 LLMChainFilter 实例
_filter = LLMChainFilter.from_llm(llm)
# 创建 ContextualCompressionRetriever 实例
compression_retriever = ContextualCompressionRetriever(
base_compressor=_filter,
base_retriever=baseretriever,
)
# 执行检索
compressed_docs = compression_retriever.invoke("用户的问题")
与 LLMChainExtractor
不同,LLMChainFilter
会直接保留或丢弃整个文档。例如,如果用户提问“Azure AI Search 的定价策略是怎样的?”,则只有包含定价信息的文档才会被保留,而其他文档则会被过滤掉。
使用 EmbeddingsFilter
EmbeddingsFilter
使用嵌入向量来比较用户问题与文档之间的相似度。以下代码展示了如何使用 EmbeddingsFilter
来构建 ContextualCompressionRetriever
。
from langchain.retrievers.document_compressors import EmbeddingsFilter
# 创建 EmbeddingsFilter 实例
_embedding = EmbeddingsFilter(embeddings=embeddings, similarity_threshold=0.7)
# 创建 ContextualCompressionRetriever 实例
compression_retriever = ContextualCompressionRetriever(
base_compressor=_embedding,
base_retriever=baseretriever,
)
# 执行检索
compressed_docs = compression_retriever.invoke("用户的问题")
在这个例子中,similarity_threshold
参数控制了文档被保留的最低相似度。只有与用户问题相似度高于 0.7 的文档才会被保留。这种方法的优点是速度快,成本低,但精度可能不如 LLMChainExtractor
高。
上下文压缩的实际应用与效果评估
上下文压缩在 RAG 管道中具有广泛的应用场景。例如,在处理大型法律文档时,可以使用 上下文压缩来提取与特定案件相关的条款和案例。在处理金融报告时,可以使用 上下文压缩来提取与特定公司或行业相关的财务数据。
为了评估上下文压缩的效果,可以采用以下指标:
- 检索准确率: 检索到的文档与用户问题的相关程度。
- 生成答案的质量: LLM 生成的答案的准确性、完整性和流畅性。
- LLM 的 token 使用量: 经过上下文压缩后,发送给 LLM 的 token 数量是否减少。
- 响应时间: 从用户提问到 LLM 生成答案的总时间。
通过对这些指标的对比分析,可以有效地评估不同上下文压缩方法的优劣,并选择最适合特定应用场景的方法。
上下文压缩的局限性与未来发展
虽然上下文压缩是一种有效的 RAG 优化方法,但它并非万能的。其有效性很大程度上取决于数据的性质、文档的类型和结构、以及使用的语言等因素。此外,上下文压缩通常需要与其他策略相结合才能发挥最大的作用。
例如,在处理结构化数据时,可以结合关键词提取、实体识别等技术来提高上下文压缩的精度。在处理非结构化数据时,可以结合文档摘要、主题建模等技术来提高上下文压缩的效率。
未来,上下文压缩的发展方向将主要集中在以下几个方面:
- 自适应压缩: 根据不同的用户问题和文档类型,自动选择最合适的压缩方法。
- 多模态压缩: 同时考虑文本、图像、音频等多种信息模态,实现更全面的上下文压缩。
- 轻量级压缩: 开发更高效、更轻量级的压缩算法,以降低计算成本和延迟。
结论
在 RAG 管道中,确保模型接收到的信息是真正相关的,对于生成更准确、更快速、更经济的响应至关重要。过长或分割不良的文档会损害上下文,直接影响输出质量。上下文压缩方法是一种有前景的解决方案,它允许在信息到达模型之前,将长文档选择或转换为更简洁和相关的信息。通过 Langchain 和 Azure AI Search 的结合,我们可以灵活地应用不同的压缩器,优化 RAG 管道的检索效果,从而提升 LLM 的应用价值。但需要注意的是,上下文压缩并非一蹴而就的解决方案,需要不断地测试、迭代和评估,才能找到最适合特定应用场景的方法。