首页 > AI教程 >AI Agent上下文压缩策略选型详解

AI Agent上下文压缩策略选型详解

来源:互联网 2026-07-23 06:21:09

AIAgent构建中,Context压缩策略选型直接影响长对话任务表现。六大策略包括硬截断、滑动窗口、摘要压缩、关键提取、神经压缩和外部化,各有成本与保真度权衡。选型需匹配任务特征,避免压缩率过高导致核心信息丢失,任务完成率可能下降30%以上。

在 AI Agent 的构建中, Context 压缩是一个常被忽视但至关重要的环节,它直接决定了你的模型在长对话、复杂任务中的表现。本文旨在为你系统梳理 6 大主流压缩策略,从原理、代码实现到成本与保真度,再到最终的生产选型决策,帮助你避开“压缩导致任务崩盘”的陷阱,精准选择最适合你项目的方案。

一、压缩策略选错,代价不仅仅是多花点 Token

在详细介绍具体策略之前,必须先强调一个核心观点:压缩策略选错,不仅会多花 Token,更可能导致任务完成率大幅下降 30% 甚至更高。这并非危言耸听。

长期稳定更新的攒劲资源: >>>点此立即查看<<<

NirDiamant 在基于 LoCoMo benchmark 的对照实验中,模拟了同一段 50 轮的长对话,结果清晰地展示了不同策略的差异:

  • 硬截断:会直接将“用户在第 12 轮提到的忌口信息”砍掉,导致下游 Agent 在推荐餐厅时“踩雷”。
  • 摘要压缩:在 5 轮一次时,任务完成率最高(92%);但 1 轮一摘要时,完成率反而掉到 71%。因为每次摘要都丢掉一些次要细节,累积到最后,核心细节全没了。

这个实验揭示了一个残酷的事实:压缩率高的策略,保真度不一定高,这是新手最容易踩的坑。压缩的本质是“用信息熵换 Token 预算”,不同策略在不同信息分布上表现天差地别。

二、6 大核心概念与“酒店类比”

在深入代码之前,我们先明确 6 种主流策略的定义。为了方便理解,我们用一个“酒店前台帮客人办入住”的类比来帮助记忆:

  • 硬截断 (Truncation):直接砍掉超出 N token 的最早内容,最便宜也最暴力。就像行李员,只看最上面 5 件行李,剩下的直接塞仓库,不管客人是不是需要箱子里的东西。
  • 滑动窗口 (Sliding Window):保留最近 K 轮对话,并保持 K-1 轮重叠,以平衡遗忘与开销。就像酒店只关注你最近 3 次出差带了什么,第 4 次出差的行李就完全被遗忘了。
  • 摘要压缩 (Summarization):使用 LLM 将历史对话生成摘要替换原文,保真度最高,但需要额外的 LLM 调用成本。就像 VIP 客户经理,帮你写一段“客户画像”,下次你再来,他就凭这个画像接待你。
  • 关键提取 (Extraction):用规则或小模型,只抽取实体、数字、决策等关键信息,只保留骨架。就像入住登记表,只填“姓名/身份证/房型”,其他信息都不要。
  • 神经压缩 (LLMLingua):使用小分类模型,逐 token 判断其重要性,再裁剪掉不重要的内容。这是微软 2023 年提出的方法。就像前台配了个 AI 助理,看一眼你的行李,就能判断哪些是“重要物品”,哪些是杂物。
  • 外部化 (Offloading):将大块内容写入磁盘或向量数据库,只在 prompt 中留下引用或检索片段。就像行李太多无法带进房间,前台告诉你“行李可以存放在 031 号柜,凭取件码随时取用”。

选型的关键不是“哪个最好”,而是“哪个最匹配你的行李特征”——你出差带的是书(长文本)还是杂物(多模态),前台给你的方案完全不同。

三、代码实现:6 种策略逐个上手

以下 6 段代码均可独立运行,逻辑均从 Agent_Memory_Techniques 和 mem0ai/mem0 项目中提炼,可直接复制到你的项目中使用

3.1 硬截断(最便宜)

# 硬截断:保留 System + 最近 N 条消息
from langchain_core.messages import SystemMessage, BaseMessage
def hard_truncate(messages: list[BaseMessage], max_messages: int = 20) -> list[BaseMessage]:
    """保留 SystemMessage 不动 + 最近 max_messages 条"""
    system = [m for m in messages if isinstance(m, SystemMessage)]
    others = [m for m in messages if not isinstance(m, SystemMessage)]
    return system + others[-max_messages:]

适用:闲聊机器人、简单客服问答。禁用:用户偏好、关键决策类对话。

3.2 滑动窗口(任务流友好)

# 滑动窗口:保留最近 K 轮(user+assistant 算一轮)
def sliding_window(messages: list[BaseMessage], window_size: int = 10) -> list[BaseMessage]:
    """以“轮”为单位保留,user+assistant 算 1 轮"""
    system = [m for m in messages if isinstance(m, SystemMessage)]
    others = [m for m in messages if not isinstance(m, SystemMessage)]
    # 按 2 条消息一轮倒推
    kept = others[-(window_size * 2):] if len(others) > window_size * 2 else others
    return system + kept

适用:固定长度的多步任务(数据分析、报表生成)。禁用:长程依赖任务(用户 12 轮前提到的信息要用到第 30 轮)。

3.3 摘要压缩(保真度最高)

# 摘要压缩:Anchored Iterative Summarization(Anthropic 推荐结构)
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, RemoveMessage
SUMMARY_PROMPT = """请将以下对话压缩成结构化摘要,每个 section 一行,不要超过 250 字:
- INTENT: 用户核心目标
- DECISIONS: 已做出的关键决定
- FACTS: 重要的实体/数字/事实
- OPEN: 未解决的问题
对话:
{messages}
"""
def summarize_compress(messages: list[BaseMessage], threshold: int = 12) -> dict:
    if len(messages) <= threshold:
        return {"messages": messages, "summary": ""}
    # 保留 System + 最近 2 轮,其余送 LLM 摘要
    system = [m for m in messages if isinstance(m, SystemMessage)]
    to_compress = [m for m in messages if not isinstance(m, SystemMessage)][:-2]
    recent = [m for m in messages if not isinstance(m, SystemMessage)][-2:]
    llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
    text_blob = "n".join(f"{m.type}: {m.content[:300]}" for m in to_compress)
    summary = llm.invoke(SUMMARY_PROMPT.format(messages=text_blob)).content
    # 显式删除被压缩的消息
    return {
        "messages": system + recent + [SystemMessage(content=f"历史摘要:{summary}")]
                              + [RemoveMessage(id=m.id) for m in to_compress if hasattr(m, "id")],
        "summary": summary,
    }

适用:长对话、连续多任务。成本:每次压缩 1 次 LLM 调用,建议使用 gpt-4o-miniclaude-haiku-4-5,可节省 80% 成本。

3.4 关键提取(PII/合规场景必选)

# 关键提取:只保留实体/数字/决策的骨架
import re
from typing import TypedDict
class ExtractedFacts(TypedDict):
    entities: list[str]      # 人名/地名/产品名
    numbers: list[str]       # 金额/日期/百分比
    decisions: list[str]     # 用户做出的决定
def extract_key_facts(messages: list[BaseMessage]) -> ExtractedFacts:
    """规则版:正则抽实体/数字/LLM 抽决策"""
    text = "n".join(m.content for m in messages if hasattr(m, "content"))
    # 实体:简单正则(生产换 GLiNER / 小模型)
    entities = list(set(re.findall(r"[A-Z][a-zA-Z]{2,}", text)))[:20]
    # 数字:金额/日期
    numbers = re.findall(r"d{1,3}(?:,d{3})*(?:.d+)?|d{4}-d{2}-d{2}", text)[:20]
    # 决策:用小模型判断
    llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
    decisions_prompt = (
        f"从下面对话中提取用户做出的关键决定,每条一行,无决定返回'NONE':n{text[:2000]}"
    )
    decisions_text = llm.invoke(decisions_prompt).content
    decisions = [d.strip() for d in decisions_text.split("n") if d.strip() and d.strip() != "NONE"]
    return {"entities": entities, "numbers": numbers, "decisions": decisions}
# 用法:把 facts 拼到 system prompt 里
def build_prompt_with_facts(messages, facts: ExtractedFacts) -> str:
    fact_str = (
        f"已知实体:{', '.join(facts['entities'][:10])}n"
        f"关键数字:{', '.join(facts['numbers'][:10])}n"
        f"用户决定:{'; '.join(facts['decisions'][:5])}"
    )
    return fact_str

适用:合规要求高(PII 不能进 LLM)、长文本检索任务。优势:提取后内容从 10K token 压缩到 ~200 token,几乎是无损的。

3.5 神经压缩(LLMLingua-2)

# 神经压缩:Microsoft LLMLingua-2,逐 token 分类
# pip install llmlingua
from llmlingua import PromptCompressor
def neural_compress(prompt: str, target_ratio: float = 0.4) -> str:
    """target=0.4 表示压缩到原文 40%"""
    compressor = PromptCompressor(
        model_name="microsoft/llmlingua-2-bert-base-multilingual-cased-meetingbank",
        device_map="cpu",  # GPU 改 "cuda"
    )
    result = compressor.compress_prompt(
        prompt,
        rate=target_ratio,
        force_context_tokens=["IMPORTANT", "DECISION", "USER_PREF"],
    )
    return result["compressed_prompt"]
# 用法
raw_prompt = "用户在前 20 轮的对话内容..." * 50
compressed = neural_compress(raw_prompt, target_ratio=0.3)
print(f"原 {len(raw_prompt)} → 压 {len(compressed)} chars")

适用:超长 prompt(>50K)、RAG 多文档合并。优势:压缩率 60-80% 时仍保留关键信息,比纯摘要快 10 倍。:首次使用需要下载 ~500MB 模型。

3.6 外部化(RAG-offload)

# 外部化:大块内容写向量库,prompt 里只留检索片段
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import FAISS
from langchain_core.documents import Document
import uuid
class ContextOffloader:
    """把长内容外置,prompt 只引用 ref_id"""
    def __init__(self):
        self.embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
        self.store = FAISS.from_texts(["__init__"], self.embeddings)
        self.ref_map: dict[str, str] = {"__init__": "__init__"}
    def offload(self, content: str, metadata: dict | None = None) -> str:
        """返回 ref_id,content 写进向量库"""
        ref_id = f"ctx_{uuid.uuid4().hex[:8]}"
        self.store.add_texts(
            [content],
            metadatas=[{**({"ref_id": ref_id}), **(metadata or {})}],
        )
        self.ref_map[ref_id] = content
        return ref_id
    def retrieve(self, ref_id: str, query: str, k: int = 3) -> str:
        """按 query 检索回最相关片段(不是原文)"""
        if ref_id not in self.ref_map:
            return ""
        full = self.ref_map[ref_id]
        # 把引用内容加进去检索
        tmp_store = FAISS.from_texts([full], self.embeddings)
        docs = tmp_store.similarity_search(query, k=k)
        return "n".join(d.page_content for d in docs)
# 用法
offloader = ContextOffloader()
ref = offloader.offload("(此处是 10K 字的会议记录...)")
# 后续调用 LLM 时 prompt 里只写:[REF:ref_id] 查"用户决策"
# LLM 触发工具调用时再 retrieve()

适用:工具返回超长内容(如 PDF 解析、日志文件)、文档处理任务。优势:上下文永远不爆,引用解耦。

四、常见问题与避坑指南

即使选对了策略,在实际工程中,仍会遇到一些常见问题。以下是 4 个最常见的坑及其解决方案:

坑 1:摘要触发时机太晚

现象:等到 prompt_too_long 报错才触发压缩。

根因:触发时,状态已经污染,回滚困难,并且此时调用 LLM 已经失败。

解法在 prompt 占用率达到 60% 时就主动进行压缩,80% 时强制压缩(与 Anthropic Claude Code 的内部建议一致)。

坑 2:神经压缩将关键决策一起砍掉

现象:LLMLingua 压缩后,用户说“以后别再给我推 5G 套餐”这条关键决定被删除了。

根因:默认按“通用重要性”进行裁剪,没有保护业务关键词。

解法:在调用时,传入 force_context_tokens=["禁止", "决定", "不", "以后"] 等业务关键词,强制保留

坑 3:摘要破坏了“原子消息组”

现象:第 8 轮的 tool_call 消息还在,但对应的 tool_result 消息被删除了。

根因:按“条数”截断,没有考虑消息组的完整性。

解法必须以 MessageGroup 为单位进行操作(例如将 tool_calltool_result 配对),要么都保留,要么都删除。

坑 4:外部化的“假检索”

现象:将内容 offload 后,retrieve 函数没有使用用户 query 进行匹配,而是将原文一字不差地全部塞回 prompt。

根因:把“外置”当成了“备份”,没有真正执行语义检索。

解法retrieve 函数必须接入用户当前 query 执行 similarity_search,否则外部化就失去了意义。

五、生产选型决策树

面对 6 种策略,到底该如何选择?我们为你提供了一个4 维度决策框架

  1. 内容特征:是结构化(JSON/日志)还是非结构化(自然语言)?
  2. 延迟预算:单次响应必须 < 1s 还是有 3-5s 的余量?
  3. 成本上限:每千次调用预算 1 美元还是 10 美元?
  4. 保真度要求:错了能否容忍(闲聊),还是错了会出问题(医疗/法律)?

根据这 4 个维度打分,可以快速淘汰掉 80% 的不适用方案:

场景 内容 延迟 成本 保真度 推荐策略
闲聊机器人 非结构化 < 1s 极低 硬截断
数据分析助手 半结构化 3s 滑动窗口 + 关键提取
写作/创作 非结构化 5s 摘要压缩(5 轮一次)
合规咨询 结构化 3s 极高 关键提取 + 摘要
长文档 RAG 非结构化 5s 神经压缩 + 外部化
工具调用密集 半结构化 1s 滑动窗口 + 外部化

核心原则单一策略永远不够用。生产项目通常是“截断/滑窗打头阵,摘要兜底,必要时外部化”的分层组合。Claude Code 的 5 层压缩(microcompact → autocompact → context collapse → 工具结果截断 → 子袋里隔离)就是这一思路的体现。

六、6 大策略 + 决策总表

策略 核心思路 成本 保真度 延迟 适用场景 不适用场景
硬截断 砍最早 N 条 0 0 闲聊/简单问答 关键决策类
滑动窗口 保留最近 K 轮 0 0 固定长度任务流 长程依赖
摘要压缩 LLM 生成摘要 1-3s 长对话/多任务 极高频调用
关键提取 抽实体/数字/决定 低-中 0.5-1s 合规/检索 自由对话
神经压缩 小模型逐 token 评分 1-2s 超长 prompt 强实时
外部化 内容外置+检索 中-高 0.5-1s 工具结果超长 短上下文

决策树简化版

你的上下文有多长?
├─ < 4K  → 不用压缩(裸跑)
├─ 4K-32K → 滑动窗口(最稳)
├─ 32K-100K → 摘要压缩(5 轮一次)
├─ 100K-500K → 神经压缩(LLMLingua)
└─ > 500K → 外部化 + RAG 检索
你的内容能丢吗?
├─ 闲聊可丢 → 硬截断 / 滑窗
├─ 决策不能丢 → 关键提取 + 摘要
└─ 合规不能错 → 关键提取 + 外部化

七、面试官还会追问什么?

为了帮助你更深入地理解,我们准备了一些常见的高频追问:

追问 1:神经压缩和摘要压缩的本质区别?

→ 摘要压缩是“生成式”(LLM 重新组织语言),神经压缩是“判别式”(小模型给每个 token 打分)。摘要压缩保留语义完整,神经压缩保留关键 token。在 1M token 场景下,优先选择神经压缩(速度快),在 100K 以内,优先选择摘要(更准确)。

追问 2:为什么不直接用 1M 窗口模型,省得压缩?

→ 有三个问题:
1. Context Rot:Chroma 2025 年实验表明,GPT-4o 的准确率从 98% 掉到 64%。
2. 成本线性增长:输入 1M token 的单次调用成本比 32K 贵 30 倍。
3. 延迟:1M token 推理的首 token 延迟高达 5-10s。
大窗口不是银弹,分层管理才是关键。

追问 3:怎么评估压缩策略好不好?

→ 主要看三个指标:
1. 任务完成率(最终结果对不对)。
2. Token 压缩率(节省了多少费用)。
3. 关键信息保留率(重要决策是否还在)。

追问 4:长期记忆和压缩的边界?

→ 长期记忆是“跨会话”的,且存储的是“用户画像/历史决策”类信息,存放在向量库中;压缩是“单会话内”的瘦身,存放于当前 prompt。一个跨会话的事实(比如用户忌口)应该走长期记忆,不应该每次会话都从历史里压缩出来。

追问 5:实时性要求极高(< 100ms)时怎么压缩?

→ 放弃所有涉及 LLM 调用的策略,只使用硬截断 + 滑动窗口 + 关键提取(规则版)。规则版提取(如正则、小分类器)能在 10ms 内完成 10K 文本的实体抽取。

八、总结:压缩策略是“分层 + 组合”,不是“选一个”

Context 压缩不是“挑一个最好的策略”问题,而是“按场景组合分层”问题:

  • 默认起手式:滑动窗口 + 关键提取(绝大多数项目够用)。
  • 长对话必加:摘要压缩(5 轮一次,不要每轮都压)。
  • 超长 prompt:神经压缩(LLMLingua-2 是事实标准)。
  • 工具结果超长:外部化(写向量库 + 检索回注)。
  • 合规场景:关键提取 + 长期记忆,不要让 PII 走 LLM。

读完这篇,你应该能:

  • 说出 6 种主流策略的代码实现(不是只背概念)。
  • 用 4 维度决策框架为你的项目选型。
  • 避开 4 个常见坑(时机、关键词、原子组、假检索)。

最后一个问题留给你:你的项目现在 prompt 平均有多长?有没有跑过 token 占用率统计?哪一段最值得压缩?

侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述

热游推荐

更多
湘ICP备14008430号-1 湘公网安备 43070302000280号
All Rights Reserved
本站为非盈利网站,不接受任何广告。本站所有软件,都由网友
上传,如有侵犯你的版权,请发邮件给xiayx666@163.com
抵制不良色情、反动、暴力游戏。注意自我保护,谨防受骗上当。
适度游戏益脑,沉迷游戏伤身。合理安排时间,享受健康生活。