链载Ai

标题: 上下文压缩:RAG信息提炼的核心步骤 [打印本页]

作者: 链载Ai    时间: 昨天 20:58
标题: 上下文压缩:RAG信息提炼的核心步骤

ingFang SC", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);visibility: visible;">


-正文-

RAG传统方法检索的文档块常含无关噪声,受限于LLM上下文窗口。上下文压缩技术旨在解决此问题,它处理检索到的文档,只保留与查询最相关内容。这能减少上下文长度,去除噪声,提高信息密度,使提供给LLM的上下文更短、更干净,提升回答质量和效率。

  • 1. 上下文压缩是什么
  • 2. 使用LangChain的实现
    • 2.1 代码(基于ContextualCompressionRetriever)
    • 2.2 运行日志
    • 2.3 Langchain压缩器的其他实现

-- 领取学习资料大礼包,见文末

在RAG中,你要问一个问题,一个简单直接的方法是将文档分割成大小相等的块,然后把这些块的向量嵌入存储到向量数据库中。

当用户提问时,系统会对问题进行嵌入,再在向量数据库中进行相似度搜索,找到最相关的文档块(文本块),然后将它们附加到大模型的提示中。

image-20250504120823987

这种方式的一个问题是,当你把数据导入到向量数据库时,通常并不知道未来会用什么具体的查询来检索这些文档块。

这意味着,当获得用户的具体问题并检索到一个块时,即便这个块中有一些相关内容,也很可能包含一些无关的内容。

这会造成一些困扰:

  1. 这些与用户问题相关性不高、甚至完全不相关的文档片段就像“噪声”,会干扰 LLM,导致它生成不准确或偏离主题的回答。

  2. LLM通常有其能够处理的最大输入长度(称为上下文窗口)。如果我们检索到的文档片段太长,超过了 LLM 的上下文窗口,我们就无法将所有相关信息都提供给模型。

上下文压缩就是为了解决这些问题而诞生的,通过“先检索再压缩”,它会对每条初步检索到的文档块进行筛选或提取,只留下与当前查询最相关的内容,然后再送入生成环节

1. 上下文压缩是什么

上下文压缩是指在将检索到的原始文档块提供给LLM之前,对其进行处理和精简的技术。其主要目标是:

通过上下文压缩,我们可以向 LLM 提供一个更短、更干净、信息密度更高的上下文,从而提高生成回答的质量和效率。

“压缩”在这里既指对单个文档块内容的压缩,也指整体过滤掉不相关的文档。

image-20250504151941273


2. 使用LangChain的实现

LangChain引入了DocumentCompressor抽象,使您能够在检索到的文档块上运行compress_documents(documents: List[Document], query: str)

其核心思想很简单:不是立即将检索到的文档块原样返回,我们可以通过给定问题对文档块进行压缩,只返回相关信息。

image-20250504121118757

压缩的目标是让传递给 LLM 的信息变得更为相关。这样一来,你也可以传递更多信息给 LLM,因为在最初的检索环节,你可以专注于召回率(比如增加返回的文档块数量),由压缩来处理精确度。

缺点就是需要根据检索到的文档块数量进行额外的 API 调用,这会增加应用的成本和延迟。

2.1 代码(基于ContextualCompressionRetriever)

初始化环境和导入文件:

完成基础设置,使用LangChain的TextLoader加载文本,为后续的文本处理做准备。

# 导入操作系统相关功能模块
importos

# 导入Chroma向量数据库相关模块
fromlangchain_chromaimportChroma
# 导入OpenAI聊天模型和嵌入模型相关模块
fromlangchain_openaiimportChatOpenAI, OpenAIEmbeddings

# 设置OpenAI API密钥
OPENAI_API_KEY ='hk-iwtbie191e427'
# 将API密钥设置为环境变量
os.environ['OpenAI_API_KEY'] = OPENAI_API_KEY

### 1.导入文件 ##########################################################################

# 导入文本文档加载器
fromlangchain_community.document_loadersimportTextLoader
# 导入递归字符文本分割器
fromlangchain.text_splitterimportRecursiveCharacterTextSplitter

# 创建文本加载器实例,加载西游记文本文件
loader = TextLoader(file_path="../../data/西游记1.txt", encoding='utf-8')
# 加载文档内容到内存
data = loader.load()

# 打印加载的文档数量
print(f'一共{len(data)}个文档')
# 打印第一个文档的字符数
print(f'一共{len(data[0].page_content)}个字符')

文本分块和向量嵌入:

实现RAG系统的两个核心步骤:文本分块和向量嵌入。

首先使用RecursiveCharacterTextSplitter将长文本分割成500字符的小块,然后使用嵌入模型将这些文本块转换为向量表示,并存储到Chroma向量数据库中,为后续的相似度搜索做准备。

### 2.文件分块 ###########################################################################

# 创建文本分割器实例,设置块大小和重叠量
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
# 对文档进行分割
splits = text_splitter.split_documents(data)
# 打印分割后的文本块数量
print(f'一共{len(splits)}个块')

### 3.文本快嵌入 ##########################################################################
# 创建OpenAI嵌入模型实例
embeddings = OpenAIEmbeddings(model="text-embedding-3-large", base_url="https://api.openai-hk.com/v1")

# 导入文件操作工具
importshutil

# 检查chroma_db目录是否存在,存在则删除
ifos.path.exists("./chroma_db"):
shutil.rmtree("./chroma_db")

# 创建Chroma向量数据库实例
vectordb = Chroma.from_documents(
documents=splits, # 使用分割后的文档
embedding=embeddings, # 使用OpenAI嵌入模型
persist_directory="./chroma_db"# 设置持久化目录
)

相关阅读:基于文本结构分块 - 文本分块(Text Splitting),RAG不可缺失的重要环节

定义基础检索器:

实现基础的向量检索功能。创建一个基于相似度搜索的检索器,设置返回前3个最相关的文档块。执行检索后,初始化一个LLM实例,为后续的文本生成做准备。

### 4.定义基础检索器 #################################################################
# 设置查询问题
query ="孙悟空和谁打过架?"

# 设置检索返回结果数量
top_k =3
# 创建基础检索器
retriever = vectordb.as_retriever(
search_type='similarity', # 使用相似度搜索
search_kwargs={"k": top_k} # 设置返回结果数量
)
# 执行检索
docs = retriever.invoke(query)

# 打印基础检索结果
print("===基础检索=========")
fordocindocs: # 遍历搜索结果
print(doc)
print("--------------------------")
print("====================================")

# 创建OpenAI聊天模型实例
llm = ChatOpenAI(
model="gpt-4.1-nano", # 使用gpt-4o-mini模型
temperature=0, # 设置温度为0
base_url="https://api.openai-hk.com/v1"# 指定API端点
)

相关阅读:Top-K Similarity Search:精准提取RAG系统中最相关的知识

定义基础压缩器LLMChainExtractor:

这段代码实现了文档压缩功能,是上下文压缩的核心部分。导入了ContextualCompressionRetriever和LLMChainExtractor模块,使用之前初始化的语言模型创建了一个文档压缩器。压缩器对之前检索到的文档块进行处理,提取与问题相关的内容,去除无关信息。

### 5.定义基础压缩器 #################################################################
# 导入上下文压缩检索器和文档压缩器
fromlangchain.retrieversimportContextualCompressionRetriever
fromlangchain.retrievers.document_compressorsimportLLMChainExtractor
# 创建LLM文档压缩器
compressor = LLMChainExtractor.from_llm(llm)
# 对检索结果进行压缩
docs = compressor.compress_documents(documents=docs, query=query)

# 打印压缩后的结果
print("===压缩=========")
fordocindocs: # 遍历搜索结果
print(doc)
print("--------------------------")
print("====================================")

定义上下文检索器ContextualCompressionRetriever和问答链:

将前面定义的基础压缩器和基础检索器整合成一个上下文压缩检索器,实现了"先检索再压缩"的完整流程。创建一个ContextualCompressionRetriever实例,使用该检索器执行查询。

最后,创建一个RetrievalQA问答链,将语言模型和压缩检索器结合起来,执行问答。

### 5.定义上下文检索器 #################################################################
# 创建上下文压缩检索器
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor, # 设置基础压缩器
base_retriever=retriever # 设置基础检索器
)

# 使用压缩检索器执行检索
compressed_docs = compression_retriever.invoke(query)

# 打印压缩检索器的结果
print("===压缩检索器=========")
fordocincompressed_docs: # 遍历搜索结果
print(doc)
print("--------------------------")
print("====================================")

# 导入检索问答链
fromlangchain.chainsimportRetrievalQA

# 创建检索问答链实例
qa = RetrievalQA.from_chain_type(
llm=llm, # 设置语言模型
retriever=compression_retriever, # 设置检索器
return_source_documents=True# 设置返回源文档
)
# 执行问答
result = qa.invoke(query)
# 打印问答结果
print(result['result'])
print(result)

2.2 运行日志

  1. 基础检索部分展示了针对查询"孙悟空和谁打过架?"返回的3个原始文档块
  2. 压缩部分展示了经过LLM压缩器处理后的结果,可以明显看到:






欢迎光临 链载Ai (https://www.lianzai.com/) Powered by Discuz! X3.5