第37集:上下文压缩与过滤,防止超长上下文
章节标题
上下文压缩与过滤技术详解
核心知识点讲解
什么是上下文压缩?
上下文压缩是一种技术,用于减少传递给语言模型的文本量,同时保留最相关的信息。这对于处理长文档或多轮对话尤为重要,因为语言模型的上下文窗口是有限的。
为什么需要上下文压缩?
- 模型限制:大多数语言模型都有上下文窗口限制(如GPT-3.5为4k token,GPT-4为8k/32k token)
- 成本优化:更长的上下文意味着更高的API调用成本
- 性能提升:更短、更相关的上下文可以提高模型的响应速度和质量
- 避免信息过载:过多的信息可能导致模型忽略关键细节
上下文压缩的主要技术
1. 文本摘要
- 提取式摘要:从原始文本中提取关键句子和短语
- 生成式摘要:生成原始文本的浓缩版本
- 混合式摘要:结合提取和生成技术
2. 相关性过滤
- 基于相似度的过滤:只保留与查询最相关的文本片段
- 基于重要性的过滤:优先保留包含关键信息的文本片段
- 基于元数据的过滤:根据文档类型、时间等元数据进行过滤
3. 结构化信息提取
- 实体提取:提取人名、地名、组织等实体
- 关系提取:提取实体之间的关系
- 事件提取:提取重要事件和时间线
实用案例分析
案例1:文档问答中的上下文压缩
场景:用户询问关于公司产品的详细信息,系统需要从多个长文档中检索相关信息。
挑战:如果直接将所有相关文档传递给模型,会超出上下文窗口限制。
解决方案:使用上下文压缩技术,只传递最相关的信息给模型。
案例2:多轮对话中的上下文管理
场景:用户与智能体进行长时间对话,讨论多个话题。
挑战:对话历史不断增长,可能超出模型的上下文窗口。
解决方案:定期对对话历史进行总结,只保留重要的信息。
代码示例
示例1:使用LangChain的ContextualCompressionRetriever
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import LLMChainExtractor
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.document_loaders import TextLoader
from langchain.text_splitter import CharacterTextSplitter
# 加载文档
loader = TextLoader("company_handbook.txt")
documents = loader.load()
# 分割文档
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
docs = text_splitter.split_documents(documents)
# 创建向量存储
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(docs, embeddings)
# 创建基础检索器
base_retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
# 创建压缩器
compressor = LLMChainExtractor.from_llm(llm)
# 创建压缩检索器
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=base_retriever
)
# 使用压缩检索器
query = "公司的年假政策是什么?"
compressed_docs = compression_retriever.get_relevant_documents(query)
# 查看压缩后的文档
for i, doc in enumerate(compressed_docs):
print(f"文档 {i+1}:\n{doc.page_content}\n")示例2:使用对话历史总结
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain
from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate
# 初始化模型
llm = OpenAI(temperature=0.7)
# 创建对话历史总结模板
summary_template = """
请总结以下对话历史,提取关键信息和决策:
{conversation_history}
总结:
"""
summary_prompt = PromptTemplate(
input_variables=["conversation_history"],
template=summary_template
)
# 初始化对话记忆
memory = ConversationBufferMemory(
return_messages=True,
max_token_limit=2000 # 设置最大token限制
)
# 创建对话链
conversation_chain = ConversationChain(
llm=llm,
memory=memory,
verbose=True
)
# 模拟多轮对话
def chat_with_agent(user_input):
# 检查对话历史长度
if memory.get_buffer_string().count(' ') > 1500:
# 总结对话历史
conversation_history = memory.get_buffer_string()
summary_chain = LLMChain(llm=llm, prompt=summary_prompt)
summary = summary_chain.run(conversation_history=conversation_history)
# 清空记忆并添加总结
memory.clear()
memory.chat_memory.add_ai_message(f"对话总结:{summary}")
# 继续对话
response = conversation_chain.run(user_input)
return response
# 测试对话
print(chat_with_agent("你好,我想了解一下公司的福利政策。"))
print(chat_with_agent("具体说说医疗保险的覆盖范围。"))
print(chat_with_agent("那养老保险呢?"))
print(chat_with_agent("还有其他福利吗?"))示例3:使用自定义过滤器
from langchain.retrievers import BaseRetriever
from langchain.schema import Document
from typing import List, Optional
class RelevanceFilterRetriever(BaseRetriever):
"""基于相关性分数过滤文档的检索器"""
def __init__(self, base_retriever, threshold=0.7):
self.base_retriever = base_retriever
self.threshold = threshold
def _get_relevant_documents(self, query: str) -> List[Document]:
# 获取基础检索结果
docs = self.base_retriever.get_relevant_documents(query)
# 过滤低相关性文档
# 注意:这里假设文档有metadata中的score字段
# 实际实现中需要根据具体的向量库进行调整
filtered_docs = [
doc for doc in docs
if doc.metadata.get('score', 0) >= self.threshold
]
return filtered_docs
# 使用自定义过滤器
filtered_retriever = RelevanceFilterRetriever(
base_retriever=base_retriever,
threshold=0.65
)
# 测试过滤效果
query = "公司的请假政策"
filtered_docs = filtered_retriever.get_relevant_documents(query)
print(f"过滤后返回 {len(filtered_docs)} 个文档")高级技巧
1. 动态压缩策略
- 基于查询复杂度:复杂查询保留更多上下文,简单查询使用更激进的压缩
- 基于文档类型:不同类型的文档使用不同的压缩策略
- 基于用户偏好:根据用户的历史反馈调整压缩策略
2. 分层压缩
- 第一层:基于向量相似度的初步过滤
- 第二层:基于语义相关性的深度过滤
- 第三层:基于摘要的最终压缩
3. 上下文窗口管理
- 滑动窗口:只保留最近的N轮对话
- 重要性加权:根据内容重要性分配上下文空间
- 自适应窗口:根据对话内容动态调整窗口大小
最佳实践
1. 选择合适的压缩方法
- 对于结构化数据:使用信息提取技术
- 对于非结构化数据:使用摘要技术
- 对于对话历史:使用混合方法
2. 平衡压缩率和信息完整性
- 高压缩率:适合简单问题,响应速度快
- 低压缩率:适合复杂问题,信息更完整
- 动态调整:根据问题类型自动调整
3. 监控和评估
- 压缩率:测量压缩前后的文本长度比例
- 信息损失:评估压缩后信息的完整性
- 响应质量:通过用户反馈评估压缩效果
故障排除
1. 信息丢失问题
- 症状:模型无法回答需要特定细节的问题
- 原因:压缩过度,丢失了关键信息
- 解决方案:降低压缩率,调整过滤阈值
2. 上下文溢出问题
- 症状:仍然超出模型的上下文窗口限制
- 原因:压缩策略不够激进
- 解决方案:增加压缩层级,使用更有效的压缩算法
3. 响应质量下降
- 症状:模型的回答质量明显下降
- 原因:压缩方法不合适,丢失了重要上下文
- 解决方案:尝试不同的压缩方法,调整压缩参数
总结与展望
上下文压缩与过滤是构建高效智能体的关键技术之一,它可以帮助我们:
- 突破模型限制:充分利用有限的上下文窗口
- 提高响应速度:减少模型处理的文本量
- 降低API成本:减少token使用量
- 提升响应质量:让模型专注于最相关的信息
随着模型上下文窗口的不断扩大,上下文压缩技术的重要性可能会有所下降,但它仍然是智能体开发中的重要工具,特别是在处理海量文档或长对话时。
未来,我们可以期待更智能的上下文管理技术,如:
- 自适应压缩:根据内容和查询自动调整压缩策略
- 多模态上下文管理:处理文本、图像、音频等多种形式的上下文
- 上下文预测:预测模型需要哪些上下文信息
这些技术将进一步提升智能体的性能和用户体验,使它们能够处理更复杂的任务和更长的对话。