智能体记忆管理与上下文压缩详解
智能体记忆管理与上下文压缩详解
前言
如果你用过AI编程助手,一定遇到过这样的场景:Agent吭哧吭哧改了半小时代码,修了好几个Bug,结果聊到后面它突然问:"话说,你一开始到底让我干什么来着?“更气人的是,因为Context太长、Token开销爆炸,之前处理的信息被无情丢弃,Agent瞬间"失忆”。
这不是某个模型的问题,而是所有依赖LLM(大语言模型)的AI Agent系统必须面对的上下文天花板。与Agent的每一次互动——无论是用户的新指令,还是它调用工具得到的结果,都会塞进同一个上下文中,导致其无限膨胀。
因此,Context Engineering(上下文工程)应运而生。如果说Prompt Engineering解决的是"怎么跟模型说话",那Context Engineering解决的则是"给模型看什么、怎么看"。
一、AI Agent的"记忆负担"从何而来?
1.1 上下文膨胀公式
为了更精确地理解这个问题,我们不妨先看一个简单的数学表示。
假设 xtx_txt 是第 ttt 轮的用户输入(或外部工具返回的信息),yty_tyt 是语言模型在第 ttt 轮产生的输出,而 CtC_tCt 代表到第 ttt 轮时累积的上下文历史。那么在第 t+1t+1t+1 轮,最直接的做法是:
Ct+1=Ct∪{xt+1}∪{yt}C_{t+1} = C_t \cup \{x_{t+1}\} \cup \{y_t\}Ct+1=Ct∪{xt+1}∪{yt}
也就是说,每一轮交互都意味着把新输入和模型输出追加到已有上下文中。在初始阶段,C0=∅C_0 = \emptysetC0=∅。随着 ttt 不断增大,CtC_tCt 的大小理论上会无限增长。当Token数量攀升到数万甚至数十万时,每次推理都要让模型"通读"全部历史——这不仅是计算资源的灾难,也是模型性能的灾难。
1.2 为什么不能放任不管?
把过长的上下文直接塞给模型,会产生至少三个层面的问题:
| 问题 | 描述 |
|---|---|
| 成本失控 | Token就是钱。按API定价计算,一个十万Token的上下文每次交互都要重新处理全部历史,费用会随对话轮次指数级攀升。 |
| 性能衰减 | LLM在超长上下文中的注意力会逐渐分散。研究表明,当上下文超过一定长度后,模型对中间位置信息的回忆准确率显著下降。 |
| 效率下降 | 更长的上下文意味着更长的首Token延迟,用户体验从"实时响应"变成"等待加载"。 |
因此,我们需要一套系统化的方法——Context Engineering的核心行为可以用一个函数 fff 来概括:
Ct+1=f(Ct,xt+1,yt)C_{t+1} = f(C_t, x_{t+1}, y_t)Ct+1=f(Ct,xt+1,yt)
这个 fff 不是简单地"全盘追加",而是一系列加工手段的集合:可能包括裁剪、摘要、替换、外部存储等策略。
二、记忆管理架构:短期 vs 长期
2.1 记忆类型三分法
LangChain的记忆实现参考了COALA论文的记忆分类学,将智能体记忆分为三大类:
| 记忆类型 | 描述 | 用途 | 实现方式 |
|---|---|---|---|
| 短期记忆(Short-term) | 会话历史,仅在单次对话中有效 | 跟踪当前任务进度、对话上下文 | 内存中的状态、会话历史 |
| 长期记忆(Long-term) | 跨会话持久化的知识 | 存储用户偏好、累积知识、研究进展 | 文件系统、数据库 |
| 程序性记忆(Procedural) | 规则、技能、指令 | 指导Agent如何执行任务 | Skills文件、策略文件 |
2.2 Deep Agents的四层记忆模型
Deep Agents是LangChain 2026年推出的下一代智能体框架,它的记忆管理非常有特点:
- 线程级记忆(Thread-scoped):默认情况下,记忆只在同一个线程内有效,切换线程后记忆会丢失。
- Agent级记忆:跨用户共享,让Agent逐步建立自己的身份、知识和偏好。
- 用户级记忆:每个用户隔离的记忆空间。
- 组织级记忆:整个组织共享的只读记忆(策略、最佳实践等)。
2.3 实现:文件系统抽象
Deep Agents的一个核心设计是"把记忆当文件"——所有的记忆都通过文件系统来管理。这有几个重要的好处:
- 简单性:LLM天生就懂文件系统操作,不需要学习新的工具。
- 可移植性:可以轻松切换底层存储(本地文件系统、Postgres、S3等)。
- 透明度:人可以直接阅读和编辑记忆文件。
from deepagents import create_deep_agent
from deepagents.backends.filesystem import FilesystemBackend
agent = create_deep_agent(
memory=["~/.deepagents/AGENTS.md"], # 记忆文件
skills=["/skills/user/"], # 技能文件
backend=FilesystemBackend(root_dir="/", virtual_mode=True),
)
三、上下文压缩策略详解
Context Engineering有四大策略:Write(持久化)、Select(检索)、Compress(压缩)、Isolate(隔离)。我们重点来看压缩策略。
3.1 策略一:LLM摘要(Summary)
核心做法:当上下文超出预设阈值时,调用语言模型对历史对话进行摘要,用结构化的总结替换掉冗长的原始消息记录。
优点:
- 信息密度高:模型能提炼出对话中的关键意图、已完成的任务、下一步计划等核心要素。
- 语义压缩而非机械截断:相比简单地从头部删除旧消息,摘要保留了语义连贯性。
缺点:
- 额外开销:摘要本身也需要一次LLM调用,消耗额外的Token。
- 信息丢失风险:摘要过程中无法避免细节的损失。如果模型把某句关键的参数配置"归纳"掉了,后续任务就可能出错。
Deep Agents的实现:
当上下文超过模型窗口的85%时,Deep Agents会:
- 调用LLM生成结构化的摘要(包含会话意图、已创建的产物、下一步计划)
- 用摘要替换工作内存中的完整对话历史
- 同时把原始完整消息写入文件系统作为记录
# 摘要提示词示例(简化版)
system_prompt = """请生成这段对话的摘要,包含以下信息:
1. 当前会话的目标和意图
2. 已完成的工作和创建的文件
3. 下一步计划
4. 重要的约束条件和决定
用简洁、结构化的方式表达。"""
3.2 策略二:观察屏蔽(Observation Masking)
核心做法:将工具调用返回的大量输出内容(如读取的文件全文、Shell命令结果、API响应等)替换成一个简短的占位标记,例如 [这里曾经有工具输出,如需查看请检索]。
在具体实现中,Observation Masking通常遵循一个简单的逻辑:将完整输出存入外部存储,分配唯一ID,然后在上下文中替换为 [Obs:{ref_id} elided] 这样的模板。
为什么这招管用?
因为在Agent的轨迹中,工具输出往往是最"膨胀"的部分——实测数据显示,工具输出通常占上下文的70%以上,而真正需要在后续步骤中参考的往往只是其中很小的一部分。
Deep Agents的两种卸载策略:
| 卸载类型 | 触发时机 | 内容 | 阈值 |
|---|---|---|---|
| 大工具结果卸载 | 每次工具调用后 | 大工具响应内容 | >20,000 tokens |
| 大工具输入卸载 | 上下文超阈值时 | 旧的写入/编辑参数 | 上下文85%阈值 |
示例:
原始:
├─ User: 读取 /src/config.py
├─ Assistant: [调用 read_file]
└─ Tool: [3000行代码内容]
卸载后:
├─ User: 读取 /src/config.py
├─ Assistant: [调用 read_file]
└─ Tool: [文件已保存至 /memory/obs_123.txt,前10行预览: ...]
3.3 策略三:记忆淘汰(Memory Eviction)
核心问题:当上下文窗口满了,你决定删除什么、保留什么?
一个简单的策略是:递归摘要。将旧对话压缩成摘要,保留核心信息,丢弃细节。
但这里有个问题:压缩多少才合适?压缩太多,关键信息会丢失;压缩太少,空间仍然紧张。
实践建议:保留70%的信息密度是一个不错的平衡点——既能保持上下文的连续性,又能显著节省空间。
另一种策略:基于时间/重要性的淘汰
不是所有记忆都同等重要。可以给不同类型的记忆设置不同的TTL(生存时间):
| 记忆类型 | TTL建议 | 原因 |
|---|---|---|
| 用户偏好 | 永久 | 需要长期记住用户习惯 |
| 任务状态 | 会话结束后删除 | 只在当前任务中有用 |
| 操作日志 | 30天 | 可能需要调试历史问题 |
3.4 策略四:上下文隔离(Isolation)
核心思想:使用子Agent来隔离不同类型的工作,只有结果返回给主Agent。
例如:
- 主Agent需要分析一个大代码库
- 它不应该把整个代码库都加载到自己的上下文中
- 而是创建一个子Agent,让子Agent专门处理代码分析
- 子Agent可以有自己的上下文管理策略
- 最后只把分析结果返回给主Agent
这种方式可以有效防止主上下文被非必要的细节污染。
四、Deep Agents实战:代码示例
4.1 基础记忆配置
from deepagents import create_deep_agent
from deepagents.backends import StoreBackend
from langgraph.store.postgres import PostgresStore
from langgraph.checkpoint.postgres import PostgresSaver
DB_URI = "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"
with (
PostgresStore.from_conn_string(DB_URI) as store,
PostgresSaver.from_conn_string(DB_URI) as checkpointer,
):
store.setup()
checkpointer.setup()
agent = create_deep_agent(
backend=lambda rt: StoreBackend(rt),
store=store,
checkpointer=checkpointer,
skills=["/skills/user/"],
memory=["/memory/AGENTS.md"],
)
# 第一个对话
config = {
"configurable": {"thread_id": "thread-1"},
"metadata": {"assistant_id": "my-assistant"},
}
agent.invoke(
{"messages": [{"role": "user", "content": "记住:我喜欢简洁的回答。"}]},
config,
)
# 切换到新线程,但记忆依然存在
config2 = {
"configurable": {"thread_id": "thread-2"},
"metadata": {"assistant_id": "my-assistant"},
}
result = agent.invoke(
{"messages": [{"role": "user", "content": "你还记得我的偏好吗?"}]},
config2,
)
print(result) # 会记得用户喜欢简洁回答
4.2 自定义上下文压缩
如果Deep Agents的默认压缩策略不满足需求,你可以自定义:
from langchain_core.messages import BaseMessage, SystemMessage
from typing import List
def custom_compression_strategy(
messages: List[BaseMessage],
max_tokens: int
) -> List[BaseMessage]:
"""自定义压缩策略示例"""
# 1. 保留最新的10条消息
recent_messages = messages[-10:]
# 2. 对更早期的消息进行摘要
if len(messages) > 10:
# 这里可以调用LLM生成摘要
summary_text = generate_summary(messages[:-10])
summary_message = SystemMessage(content=f"对话摘要:{summary_text}")
return [summary_message] + recent_messages
return recent_messages
# 在创建Agent时使用
agent = create_deep_agent(
# ...
compression_strategy=custom_compression_strategy,
)
五、常见问题与最佳实践
5.1 什么时候用什么策略?
| 场景 | 推荐策略 |
|---|---|
| 工具输出特别大 | Observation Masking(卸载) |
| 对话历史很长,但需要保持连贯性 | LLM Summary(摘要) |
| 有些记忆明显不再需要 | Memory Eviction(淘汰) |
| 不同任务的上下文容易混淆 | Context Isolation(隔离) |
5.2 如何决定保留多少上下文?
一个经验法则是:
- 永远保留系统提示词:这是Agent的核心指令。
- 保留最近的N轮对话:通常5-20轮比较合适。
- 保留关键决策点:比如用户明确的需求变更、重要的工具调用结果。
- 可以摘要的尽早摘要:不要等上下文满了才想起来压缩。
5.3 如何调试记忆问题?
如果Agent出现"失忆"或"混淆",可以检查:
- 压缩是不是太激进了:检查摘要是否丢失了关键信息。
- 记忆文件是否正确保存:确认持久化层工作正常。
- 上下文窗口设置是否合理:是不是给模型塞了太多内容。
- 使用LangSmith追踪:查看完整的上下文变化轨迹。
六、总结
Context Engineering和记忆管理是构建生产级Agent系统的关键。核心思想是:
- 不要把所有东西都塞给模型:每一个Token都应该有它存在的理由。
- 分层管理记忆:短期、长期、程序性记忆各司其职。
- 让Agent自己管理记忆:给Agent读写记忆文件的能力,而不是硬编码规则。
- 监控和调试:使用LangSmith等工具观察上下文的变化。
随着Agent任务变得越来越复杂,这些技术只会变得更加重要。希望这篇文章能帮助你构建出更"聪明"、更"持久"的AI Agent系统!
参考资料
- LangChain Deep Agents文档:https://docs.langchain.com/oss/python/deepagents/
- Context Management for Deep Agents:https://www.langchain.com/blog/context-management-for-deepagents
- Context Engineering深度拆解:https://m.toutiao.com/group/7642192312428167716/
- Active Context Compression论文:https://www.arxiv.org/pdf/2601.07190
更多推荐
所有评论(0)