第37集:上下文压缩与过滤,防止超长上下文

章节标题

上下文压缩与过滤技术详解

核心知识点讲解

什么是上下文压缩?

上下文压缩是一种技术,用于减少传递给语言模型的文本量,同时保留最相关的信息。这对于处理长文档或多轮对话尤为重要,因为语言模型的上下文窗口是有限的。

为什么需要上下文压缩?

  1. 模型限制:大多数语言模型都有上下文窗口限制(如GPT-3.5为4k token,GPT-4为8k/32k token)
  2. 成本优化:更长的上下文意味着更高的API调用成本
  3. 性能提升:更短、更相关的上下文可以提高模型的响应速度和质量
  4. 避免信息过载:过多的信息可能导致模型忽略关键细节

上下文压缩的主要技术

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. 响应质量下降

  • 症状:模型的回答质量明显下降
  • 原因:压缩方法不合适,丢失了重要上下文
  • 解决方案:尝试不同的压缩方法,调整压缩参数

总结与展望

上下文压缩与过滤是构建高效智能体的关键技术之一,它可以帮助我们:

  1. 突破模型限制:充分利用有限的上下文窗口
  2. 提高响应速度:减少模型处理的文本量
  3. 降低API成本:减少token使用量
  4. 提升响应质量:让模型专注于最相关的信息

随着模型上下文窗口的不断扩大,上下文压缩技术的重要性可能会有所下降,但它仍然是智能体开发中的重要工具,特别是在处理海量文档或长对话时。

未来,我们可以期待更智能的上下文管理技术,如:

  • 自适应压缩:根据内容和查询自动调整压缩策略
  • 多模态上下文管理:处理文本、图像、音频等多种形式的上下文
  • 上下文预测:预测模型需要哪些上下文信息

这些技术将进一步提升智能体的性能和用户体验,使它们能够处理更复杂的任务和更长的对话。

« 上一篇 高级检索技术:多路召回与重排序(Rerank) 下一篇 » 记忆的存储与同步:Redis在智能体中的应用