Agent上下文管理概述-1

agent在运行过程中一般会去将历史、任务状态、记忆等内容进行“选择拼接”去构成完整的上下文(context)去交给大模型处理,其中就有几点设计理念需要考虑:1、大模型推理过程中都会选择kv-cache去加速推理,那么如何保证对话过程中cache命中高加快推理,亦或者说频繁的工具调用/文本内容如何拼接到上下文中;2、模型输入上下文总是有限的如何保证在超出上限之后对上下文压缩,而且压缩还不能都是关键信息;3、过长的上下文反而会去影响到模型的效果[1],因此这些都是上下文管理需要考虑的事情。

上下文组织

claude code/codex/pi agent等在构建上下文中一般选择的架构组织方式是:静态提示词+ 动态提示词,其中静态提示词指的一般是不会改变的提示词,如系统提示词、工具介绍等,而动态提示词 指的是经常发生改变内容,比如用户内容输入等,在上文组织过程中以pi agent为例介绍其组织过程,在第一次启动之后其context为:

AgentContext
├── systemPrompt
│    ├── Pi Base System Prompt
│    ├── Tool 使用说明
│    ├── Guidelines
│    ├── APPEND_SYSTEM.md
│    ├── AGENTS.md / CLAUDE.md
│    ├── Available Skills Index
│    └── Current Working Directory
├── messages
│    └── []
└── tools
     ├── read schema
     ├── bash schema
     ├── edit schema
     ├── write schema
     └── extension tool schemas...

这里 messages 还是空的,因为用户还没有真正开始对话;但 System Prompt 和可用 Tool 已经准备完成。Pi 会在启动阶段读取 AGENTS.mdCLAUDE.md 等项目规则,同时扫描当前可用的 Skills(Skill 并不会在启动时把完整内容全部塞进 Context 一般只会保存 description、name、path)。不断对话过程中 用户输入问题->调用工具 此过程会将工具结果 补充到对话结尾

上下文压缩

因为模型上下文是有限的因此不对对话过程中就需要去对内容进行压缩保证后续对话进行,对于普通对话压缩可能就是对上下文进行简短即可,但是对于Agent而言上下文不是一篇单纯的长文本,而是一个持续变化的运行状态。压缩策略也比较多一般而言压缩需要同时考虑:速度、效率(压缩后内容尽可能少的占用token),从两个角度出发了解上下文压缩策略:

常用Agent架构中使用的压缩策略

工业 Agent 很少使用 LLMLingua 式 Token 分类器直接压缩整个对话。更常见的是“确定性选择与清理 + 专用 LLM 生成任务交接摘要 + 原始尾部 + 外部可恢复状态”。

1、Pi Agent 上下文压缩策略

Pi 的自动触发条件是:\(\text{ContextTokens}> \text{ContextWindow}-\text{ReserveTokens}\) 其中 reservetokens 表示给下一次模型输出和工具执行留空间,除此之外在压缩过程中假如对话窗口为 SystemPrompt+Context_i 其中 Context_i可能就是表示一轮对话结果(User+Assistance+toolCall+toolResult 其中 toolCall 表示调用工具名称、 toolResult 表示对应的工具返回的结果)在Pi的第一轮压缩过程中它会先从最新消息往前扫描,尽量找到一段近期上下文(比如检索到 i=2 那么就会将前两轮对话历史结果进行压缩)在压缩过程中通过提示词引导进行“结构化压缩”比如说:

## Goal
当前任务最终目标

## Constraints & Preferences
用户约束、技术限制、不允许执行的操作

## Progress
### Done
已经完成的工作

### In Progress
当前正在做什么

### Blocked
被什么问题阻塞

## Key Decisions
关键决策,以及做出决策的原因

## Next Steps
后续动作

## Critical Context
关键报错、实体、路径、函数名、API和数据结构

## Read Files
已经读取的文件

## Modified Files
已经修改的文件

压缩后不是将内容进行删除只是以后不再发送给 LLM。官方文档明确描述为:追加 CompactionEntry,然后下一轮根据 firstKeptEntryId 重建 Context,所以下一轮对话内容为:SystemPrompt+CompactionSummary+Context_j。在后续n轮压缩过程中 会直接把 上一轮的压缩内容和最近需要压缩的内容一起进行压缩。

从上面过程可以发现Pi里面压缩比较简单粗暴并没有去区分tool result直接通过一个prompt进行全部压缩

2、Open Code 上下文压缩策略

OpenCode 比 Pi 更复杂一点,因为它实际上有两个完全不同的 Context Reduction 层:Tool Result PruningConversation Compaction第一层对工具调用结果进行压缩(也容易理解在coding任务中一个 grep的工具调用,返回的结果可能就有上千行内容这些内容不是都有用的,只需要对他们进行标记后续去掉就行)在对话中一轮工具调用可能是 Assistance+ toolCall+ toolResult+ Assistance OpenCode 会把旧 Tool Result 标记为 compacted,其中它主要压掉的是 Tool Result 的 output,而不是直接把整个 Tool Call + Assistant 消息链都删掉。 换言之在进行对话过程中 toolResult 是有生命周期的,这一层没有去调用llm处理,在代码里面会对旧 completed tool parts 上标记 compacted,而不是调用 LLM 改写这些结果。

Assistant: 我先搜索 refresh_token。 
ToolCall: grep -R "refresh_token" src/ 
ToolResult: src/auth/token.py:... src/auth/service.py:... src/api/login.py:... ...... 几千行内容 
Assistant: 主要逻辑位于 src/auth/token.py,接下来读取该文件。

----- 进行 tool result pruning -----
Assistant: 我先搜索 refresh_token。 
ToolCall: grep -R "refresh_token" src/ 
ToolResult: [旧 output 已从 Active Context 中移除] 
Assistant: 最终定位到 src/auth/token.py

OpenCode 并不知道某条 Tool Result 在语义上“已经没用了”。它采用的是一种更工程化的启发式规则:近期工具输出优先保护,较老、已完成、非保护类型的工具输出,在累计超过一定 Token 预算以后直接从 Active Context 中驱逐。

第二层对内容进行压缩这个过程和上面的Pi的处理类似通过提示词进行结构化压缩,不过对话过程中进行压缩之后为了保证agent loop不被打断,会将对话重新拼接到压缩后内容后面(Summary + RecentContext

3、其他闭源压缩策略

Manus 强调将网页、文件、中间结果和长 Tool Output 写入文件系统,只在活动 Context 中保留路径、URL、对象 ID 和简要结论。Todo / Plan 会不断重写到最近上下文,形成 Goal Recitation[2]。Langchain DeepAgents会先把大型 Tool Result 写入文件,并在 Context 中留下文件指针和预览;必要时再对旧历史生成 Summary,同时保留完整 Transcript 作为 Canonical Record[3]

学术领域中常用的压缩策略

个人认为工程化实践上可以重点关注两点策略:1、离散文本与token级压缩 以及 2、面向Agent运行轨迹压缩

1、离散文本与token级压缩

推荐直接使用: https://github.com/microsoft/LLMLingua

这一类方法最终仍然输出可读文本,对于原始输入文本 \(X=[x_1,x_2,\ldots,x_n]\) 通过压缩器为每个 Token 或文本片段决定 \(z_i\in\{0,1\}\) 最终保留:\(X'=\{x_i\mid z_i=1\}\)(一般希望\(\max_{X'}\operatorname{Utility}(X',Q) \quad \text{s.t.} \quad |X'|\le B\) 其中 \(B\) 为压缩后的token budget)。
在论文 Selective Context[4]LLMLingua-2[5]处理思路类似都是 对不重要token进行过滤只将重要token进入模型推理 ,以selective context为例其处理思路很简单对于较长的prompt直接计算 \(I(x)=-\log P(x)\) 其中 \(x\) 表示较长的prompt而 \(P\) 对应一个较小的模型而后对计算结果计算阈值过滤即可达到压缩目的(实际过程llm类似prefill处理prompt这样就可以得到每一个token的logits然后阈值过滤即可)下图红色表示保留文本

image.png585

这一类方法最大的优点是兼容闭源模型;最大的局限是:Token 级“相关性”不等于 Agent 状态级“未来效用”。

2、latent压缩

将长 Context 编码 \(X\in\mathbb{R}^{n\times d}\) 压缩为:\(Z=C_{\phi}(X)\in\mathbb{R}^{m\times d},\quad m\ll n\) 其目标不是生成可读摘要,而是让下游模型能够从少量隐状态中恢复任务所需信息,简而言之将中间状态结果进行压缩处理,不过对于这种策略需要考虑一点就是LLM都是自回归的,就需要考虑如何将内容进行拼接输入。比如在论文ICAE[6]直接将原本的大模型推理过程Context+Prompt+Deocer 替换为 Context+Summary在经过一次编码后在将 Summary+Prompt 就行decoder处理。训练分为两部分预训练和微调,预训练阶段文本复写任务通过MemoryTokens去复写后面内容(相当于截断文本而后decoder通过MemoryTokens去复写出来)以及 文本续写任务(或者说“问题回答”)。在后续论文500xCompressor[7]中处理思路类似不过将 embeding 替换为 KV-value去实现压缩。

image.png772

3、面向Agent运行轨迹压缩

主要是将 \(t\) 轮中对话结果进行压缩比如说 \(S_t=\{G,C,P,D,E,F,A_t,O_t\}\) 其中内部就是Agent不同运行轨迹状态因此轨迹压缩的目标是构造 \(\hat{S}_t=C(S_{1:t})\)MEM1[8]在处理React每一步推理过程中进行“微压缩”然后进入下一轮的推理在react中每一轮推理都会直接将上一轮内容叠加到状态里面进行推理。

image837

比如说上图中左下方每一次act推理过程中都会将工具结果等都记录到 IS中,再训练过程中因为每轮都有一个压缩处理因此在计算Attention过程中下一轮的 <IS> 状态“看不到”前面状态结果(2D Attention Mask)。在Context-folding论文[9]论文 ACM[10]中不是讲状态进行总结而是将状态进行“折叠”,比如在Context-folding中:
image665

在上述过程中,工具调用产生的中间结果会被统一“折叠”,仅将 Sub-agent 最终返回的结果保留在主上下文中。该方法采用 Main Agent 与 Sub-agent 协同的架构:前者负责在 Planning State 中进行任务规划与子任务拆解,后者根据规划执行 ReAct 式推理及工具调用。与其他上下文压缩方法的主要区别在于,Sub-agent 的完整执行轨迹不会写入主上下文,只有其最终返回的结果会被保留。而ACM更加简单直接通过两个上下文管理工具,使代理能够模仿人类的记忆机制:manage_context,它将之前的转换压缩成简洁的摘要,并将原始消息卸载到磁盘上的外部文件中;以及 query_memory,它允许代理查询存储的原始消息以精确地检索信息。

4、KV cache压缩

KV Cache Compression 处理的是 Transformer 每层的 Key 和 Value:\(K_l,V_l\),目标是在生成过程中只保留一部分历史 KV:\((K'_l,V'_l)=\operatorname{Select}(K_l,V_l,B_l)\) 其中 \(B_l\) 是第 \(l\) 层的缓存预算,所以说严格意义上不太算是上下文压缩策略,在论文 StreamingLLM [11]中发现使用 windows attention并不能扩展长度(主要测试的是Llama-2模型,发现模型不能很好外推到训练长度之外),但是通过一种现象 attention sink(保留 起始token 的 KV )能够恢复 windows attention 的效果。之所以出现这一现象是因为起始 token 有着更高的注意力分数,即便是当它在语义上已经不重要了(实验直接将其换成 \n 对于表现影响很大)也依然如此。

image773

操作方式也很简单在计算 windows attention 时候将开始的 \(n\) 个token保留即可(测试llama-2中 \(n=4\)

总结

对于 Agent 运行上下文,主要分为两大部分:1、上下文组织,可分为两个阶段:启动时,拼接系统提示词、Skills、工具 Description、必备文件(如 CLAUDE.md)等静态内容;运行时,持续拼接用户消息、Agent 回复、工具调用及工具结果。参考 Pi Agent 的组织方式,可以概括为:静态内容 + 用户消息 + Agent 交互轨迹 + 工具结果2、上下文压缩,考虑到每个 LLM 的上下文窗口都存在上限,因此需要对内容进行压缩。综合工业实践和学术研究,较为稳妥的策略包括:1、选择性保留工具结果,例如 OpenCode 的 Tool Result Pruning 和 Context Folding。Agent 运行过程中,工具结果可能占用大量上下文,并且部分结果会过期、重复或可以重新获取,因此没有必要始终保留在主上下文中;2、基于提示词进行摘要,通过提示词将历史内容压缩为结构化摘要,同时保留任务目标、运行状态、执行进度、关键结论、环境变化、失败尝试和待办事项,具体可以参考 OpenCode、Pi Agent 和 Claude Code 的提示词设计;3、选择性丢弃或 Token 级压缩(谨慎使用),直接丢弃低价值、重复或可恢复的内容,或者通过离散 Token 压缩等算法缩短文本。此类方法只能尽量保持原始语义,无法保证关键信息完全不丢失;4、运行状态压缩(不建议作为应用层首选方案),直接对 KV-cache 等模型运行状态进行压缩或量化。这类方法主要用于降低显存占用和提高推理效率,并不等同于语义层面的上下文压缩。

参考


  1. https://arxiv.org/pdf/2307.03172 ↩︎

  2. https://manus.im/blog/Context-Engineering-for-AI-Agents-Lessons-from-Building-Manus ↩︎

  3. https://docs.langchain.com/oss/python/deepagents/context-engineering ↩︎

  4. https://arxiv.org/pdf/2310.06201 ↩︎

  5. https://arxiv.org/pdf/2403.12968 ↩︎

  6. https://arxiv.org/pdf/2307.06945 ↩︎

  7. https://aclanthology.org/2025.acl-long.1219.pdf ↩︎

  8. https://arxiv.org/pdf/2506.15841 ↩︎

  9. https://arxiv.org/pdf/2510.11967 ↩︎

  10. https://arxiv.org/pdf/2607.23809 ↩︎

  11. http://arxiv.org/abs/2309.17453 ↩︎

posted @ 2026-09-01 21:16  Big-Yellow-J  阅读(43)  评论(0)    收藏  举报
levels of contents