agent 开发中,压缩历史信息常用策略

在 Agent 开发中,**历史信息压缩(History Compression)**是一个核心问题,因为:

  1. Context Window 有限:即使是 GPT-4 Turbo(128K)或 Claude 3(200K),也无法无限容纳历史对话。
  2. 成本高昂:每次请求都携带完整历史会导致 Token 消耗指数级增长。
  3. 噪音干扰:冗长的历史会稀释关键信息,降低 Agent 的决策质量。

以下是工业界和学术界常用的历史信息压缩策略,从简单到复杂分为 5 个层级:


一、基础策略:截断与采样 (Truncation & Sampling)

适用于简单场景,不需要额外模型。

1. 滑动窗口 (Sliding Window)

  • 原理:只保留最近的 N 轮对话。

  • 实现

    python 复制代码
    def sliding_window(history, max_turns=5):
        return history[-max_turns:]
  • 优点:简单高效。

  • 缺点:丢失早期关键信息(如用户在第1轮说的任务目标)。

2. 首尾保留 (Head-Tail Retention)

  • 原理:保留最开始的 K 轮(任务描述)+ 最近的 M 轮(当前上下文)。

  • 实现

    python 复制代码
    def head_tail_retention(history, head=2, tail=3):
        if len(history) <= head + tail:
            return history
        return history[:head] + history[-tail:]
  • 优点:保证任务目标不丢失。

  • 缺点:中间的推理链条可能丢失。

3. 关键轮次采样 (Key Turn Sampling)

  • 原理:只保留包含关键信息的轮次(如用户提问、工具调用结果、决策点)。

  • 实现 :基于规则过滤。

    python 复制代码
    def filter_key_turns(history):
        key_turns = []
        for turn in history:
            if turn['role'] == 'user':  # 用户输入
                key_turns.append(turn)
            elif 'tool_call' in turn:  # 工具调用
                key_turns.append(turn)
            elif turn.get('is_decision', False):  # 标记为决策点
                key_turns.append(turn)
        return key_turns

二、语义压缩:摘要与重写 (Summarization)

使用 LLM 对历史进行语义压缩,保留核心信息。

1. 递归摘要 (Recursive Summarization)

  • 原理:每隔 N 轮,用 LLM 将这 N 轮对话总结成一段话。

  • 实现

    python 复制代码
    def recursive_summarize(history, llm, chunk_size=5):
        if len(history) <= chunk_size:
            return history
        
        # 对前 chunk_size 轮进行摘要
        chunk = history[:chunk_size]
        summary_prompt = f"请将以下对话总结为一段话:\n{chunk}"
        summary = llm.generate(summary_prompt)
        
        # 用摘要替换原始对话
        compressed = [{"role": "system", "content": f"历史摘要: {summary}"}]
        compressed.extend(history[chunk_size:])
        
        return recursive_summarize(compressed, llm, chunk_size)
  • 优点:大幅减少 Token 数。

  • 缺点:摘要可能丢失细节,且增加了额外的 LLM 调用成本。

2. 渐进式压缩 (Progressive Compression)

  • 原理:越久远的历史,压缩得越狠。

  • 策略

    • 最近 3 轮:保留原文。
    • 3-10 轮:提取关键句。
    • 10 轮以上:只保留摘要。
  • 实现

    python 复制代码
    def progressive_compress(history):
        recent = history[-3:]  # 最近3轮原文
        middle = extract_key_sentences(history[-10:-3])  # 中间提取关键句
        old_summary = summarize(history[:-10])  # 早期摘要
        return [old_summary] + middle + recent

三、结构化压缩:状态追踪 (State Tracking)

不存储完整对话,而是维护一个结构化的状态对象

1. 槽位填充 (Slot Filling)

  • 原理:将对话信息抽取为 Key-Value 对。

  • 示例

    python 复制代码
    # 原始对话
    history = [
        {"role": "user", "content": "我要买iPhone 15 Pro Max 256GB 黑色"},
        {"role": "assistant", "content": "好的,正在为您查询..."},
        {"role": "user", "content": "价格在8000以内"}
    ]
    
    # 压缩为状态
    state = {
        "product": "iPhone 15 Pro Max",
        "storage": "256GB",
        "color": "黑色",
        "price_limit": 8000
    }
  • 优点:极致压缩,只保留决策所需的关键信息。

  • 缺点:需要预定义槽位模式,不适合开放域对话。

2. 记忆图谱 (Memory Graph)

  • 原理:将对话中的实体和关系构建为知识图谱。

  • 实现

    python 复制代码
    # 使用 Neo4j 或 NetworkX
    graph.add_node("用户", name="张三")
    graph.add_node("商品", name="iPhone 15")
    graph.add_edge("张三", "iPhone 15", relation="想购买")
  • 优点:适合复杂的多轮任务(如客服、推荐)。

  • 缺点:构建和查询成本高。


四、向量化压缩:Embedding + 检索 (RAG-based)

将历史对话存储为向量,按需检索相关片段。

1. 向量数据库 (Vector DB)

  • 原理:每轮对话编码为 Embedding,存入 Faiss/Milvus/Pinecone。

  • 流程

    1. 每轮对话结束后,将对话内容用 text-embedding-ada-002 编码。
    2. 存入向量数据库,附带元数据(时间戳、轮次、是否关键)。
    3. 下次对话时,用当前 Query 检索 Top-K 最相关的历史片段。
  • 实现

    python 复制代码
    from langchain.vectorstores import FAISS
    from langchain.embeddings import OpenAIEmbeddings
    
    # 存储历史
    embeddings = OpenAIEmbeddings()
    vectorstore = FAISS.from_texts(
        texts=[turn['content'] for turn in history],
        embedding=embeddings
    )
    
    # 检索相关历史
    current_query = "用户刚才说的价格是多少?"
    relevant_history = vectorstore.similarity_search(current_query, k=3)
  • 优点:动态压缩,按需加载。

  • 缺点:需要额外的向量存储和检索开销。


五、模型层压缩:长上下文模型 + 注意力机制

利用模型自身的能力处理长文本。

1. 长上下文模型 (Long-Context LLMs)

  • 模型:Claude 3 (200K), GPT-4 Turbo (128K), Gemini 1.5 Pro (1M)。
  • 策略:直接喂入完整历史,让模型自己"压缩"(通过注意力机制)。
  • 优点:不丢失信息。
  • 缺点:成本极高(Claude 3 的 200K 输入约 $10)。

2. 注意力压缩 (Attention Compression)

  • 技术:LongLoRA, FlashAttention, Sparse Attention。
  • 原理:在模型层面优化注意力计算,减少对冗余 Token 的关注。
  • 适用:需要自己训练或微调模型。

六、混合策略:工业界最佳实践

实际项目中,通常组合多种方法:

python 复制代码
class HistoryCompressor:
    def __init__(self, max_tokens=4000):
        self.max_tokens = max_tokens
        self.vectorstore = FAISS(...)  # 向量库
        
    def compress(self, history, current_query):
        # 1. 首尾保留
        head = history[:2]  # 任务描述
        tail = history[-3:]  # 最近对话
        
        # 2. 中间部分向量检索
        middle_history = history[2:-3]
        if middle_history:
            relevant = self.vectorstore.similarity_search(
                current_query, 
                k=2,
                filter={"turns": middle_history}
            )
        else:
            relevant = []
        
        # 3. 组合
        compressed = head + relevant + tail
        
        # 4. Token 超限时强制摘要
        if count_tokens(compressed) > self.max_tokens:
            compressed = self.summarize(compressed)
        
        return compressed

七、总结:选择策略的决策树

场景 推荐策略 原因
短对话(<10轮) 不压缩 / 滑动窗口 成本低,信息损失小
任务型对话(槽位明确) 状态追踪 极致压缩,适合结构化任务
开放域对话(闲聊) 递归摘要 + 向量检索 保留语义,按需召回
多轮复杂推理 首尾保留 + 关键轮次 保证推理链完整
超长上下文(>50轮) 向量检索 + 长上下文模型 动态加载,避免信息过载

核心原则: 压缩不是目的,保留决策所需的最小信息集才是目标。

相关推荐
AngelPP2 小时前
OpenClaw 架构深度解析:如何把 AI 助手搬到你的个人设备上
人工智能
宅小年2 小时前
Claude Code 换成了Kimi K2.5后,我再也回不去了
人工智能·ai编程·claude
九狼2 小时前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS2 小时前
Kimi Chat Completion API 申请及使用
前端·人工智能
天翼云开发者社区3 小时前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤
知识浅谈3 小时前
教你如何用 Gemini 将课本图片一键转为精美 PPT
人工智能
Ray Liang4 小时前
被低估的量化版模型,小身材也能干大事
人工智能·ai·ai助手·mindx
shengjk15 小时前
NanoClaw 深度剖析:一个"AI 原生"架构的个人助手是如何运转的?
人工智能
西门老铁7 小时前
🦞OpenClaw 让 MacMini 脱销了,而我拿出了6年陈的安卓机
人工智能