微信扫码
添加专属顾问
我要投稿
Agent框架中的上下文管理策略解析:如何让AI在长对话中保持专注与高效? 核心内容: 1. 上下文工程的定义与重要性:优化token效用对抗注意力涣散 2. 多轮对话中的关键挑战:工具调用导致的历史记录膨胀问题 3. 应对"上下文腐败"的三大技术原理与工程实践方案
“上下文工程”简单来说,就是在一些LLM的约束下(如上下文窗口大小、注意力长度的限制),优化上下文token的效用,从而持续获得理想输出的工程实践。
一个好的context engineering追求用最少的、信号最强的token集合,最大化期望输出的概率。
因为随着Agent不断地运行,会不断产生新的数据,这些数据可能对下一轮推理有用,因此必须放到上下文中,作为模型的“短期记忆”。Context engineering就是从不断变化的信息中,精心挑选出能放入有限上下文窗口的内容。
如果说之前早期的"Prompt Engineering"适用于单轮文本生成任务;那么"Context Engineering"就适用于需要多轮推理、长时间运行的智能体,需管理不断演变的上下文状态。
Agent 每调用一次工具,就会返回一个工具的Observation,这个结果会被追加到聊天记录中。生产环境中的 Agent 可能会进行长达数百轮的对话,因此随着时间的推移,历史记录message会越来越长。
而且,工具调用的Observation经常会特别长:比如模型执行 Read 命令,读取了一个文件的部分内容;或者执行Bash命令,输出了很长的log。这样长的Observation不断地拼接到上下文message中,最后很有可能超过了模型最长能够接受的上下文长度(比如128K~1M)。
虽然现在的LLM能够接受越来越长的序列了,但它们和人类一样,会随着上下文增长而出现注意力涣散的现象,模型准确回忆信息的能力会下降,而且推理也会变慢。
这种现象被称为 “上下文腐败”(context rot)。虽然不同模型的性能下降曲线不同,但所有模型都存在这个特点,会在远低于能接受的最长序列长度时就出现context rot的现象(比如最长支持1M token,但是在200K token的时候就已经开始context rot了)。
导致这种现象的原因包括:
在上面一节我们说到,上下文工程的指导原则是:找到能够最大化期望输出概率的最小高信号 token 集合。那么这一部分,我们就介绍一些常见的上下文管理方式。
因为上下文窗口是有限的,所以我们不能将所有信息都塞进 Agent 的短期记忆(上下文窗口)中,而应将其“卸载”到外部存储,并在需要时精确检索。这种思想催生了多种工程实践,下面会结合 Manus、Cursor、Claude Code 等案例进行详细介绍。
一种最粗暴的解决上下文过长的方式当然就是截断了。在处理过长的输出(如Shell 命令结果、MCP 返回)时,如果简单采取截断的方式,就可能导致关键信息永久丢失掉。
例如,一个报错信息可能在日志的末尾,截断后 Agent 就无法定位到问题;或者一个长列表的中间几行恰好包含了我们需要的重要线索,截断后就无法获取到了。
更根本的问题是:Agent 需要基于所有先前状态预测下一步动作,但我们无法提前预判哪个观察结果在十步之后会变得至关重要。所以,任何不可逆的压缩都带有风险。
Manus提出了一个核心理念:将文件系统视为终极上下文。这是因为文件系统天然具有无限容量、持久化、可随机访问的特性,Agent 可以像人类一样通过路径、文件名、时间戳等元数据来组织信息。最关键的是,这样的压缩策略是可逆的。例如:
这种“可逆压缩”确保上下文长度缩减的同时,信息并未真正丢失——它们只是被卸载到文件系统中了,随时可以重新加载进来。
同时,Manus 也鼓励 Agent 主动将中间结果写入文件。例如,执行一个复杂查询后,将结果保存到output.log,然后上下文中只保留一句话:“结果已写入 /tmp/query_result.json”。Agent 后续可以通过head、tail、grep等命令渐进式地查看,或一次性读取整个文件。这种方式既减少了上下文占用,又保留了完整信息。
很多早期的Agent 都依赖于 RAG(Retrieval-Augmented Generation)这样的"推理前检索(pre-inference retrieval)"方式来获取信息——预先对知识库的文本进行向量化,然后在推理前预先检索相关片段。但 Manus、Claude Code 这些新锐 Agent Scaffold 则采取了不同的方法:弱化甚至抛弃 RAG,转而让 LLM 自己生成搜索命令,像人类一样主动探索大文件或者代码库,即"just-in-time"检索。
为什么要弱化RAG呢?
因为RAG包含的组件太多,太复杂了。我们要考虑的事情也会变得很复杂,某个步骤稍有差池,就会导致整体的效果受影响。
比如说,如何对代码进行合理分块,按函数、按行数,还是按语法结构?选择什么embedding模型、如何计算相似度?而且,对于一些非结构化数据比如PDF、图片,RAG 很难有效处理。
如果说我们从Claude Code等SOTA的agent框架中学到了什么的话,那就是"Keep Things Simple, Dummy":我们要让框架的逻辑尽可能地简单,把有难度的事情交给模型本身。
"Just-in-time"检索的优势:以Claude Code为例
Claude Code 在处理大型的数据时,会生成一些复杂的 Bash 命令进行查询(如ripgrep、jq、find等),利用自己对代码的深刻理解,使用精细而复杂的正则表达式定位相关代码块,无需将大段大段的数据加载进上下文。这也模仿了人类认知,我们不会去记忆所有的信息,但是我们知道什么时候该查找、知道去哪里查找信息。这种方式的优势在于:
“他们沉回了来时的大地。而他们的孩子则被留了下来,对黄金时代—— 时间出现前的那个时代,只剩模糊的记忆。” ——《人之涛》,刘宇昆
当上下文窗口即将被填满,且没有办法进一步做紧凑化的时候,我们不得不采用另一种手段:摘要化。这是一种有损压缩,它会将对话历史浓缩成一段摘要,从而释放空间。
但这必然会导致信息的模糊和丢失——模型无法再访问精确的代码行或工具输出的完整内容。因此,摘要化只能作为上下文窗口即将耗尽时的最后手段。
而且摘要化必须设计得可恢复,让 Agent 在必要时也能找回丢失的细节。具体的做法就是将完整的对话历史dump到一个持久化文件中。
这个文件就像一个黑匣子,保留了所有原始消息、工具调用及其结果。Agent 在后续交互中,如果发现摘要中缺少关键细节,可以通过工具(如grep、Read)主动检索这个历史文件,找回丢失的信息。
为了确保模型能够平滑地继续工作,通常还会保留最后几次完整的工具调用及其结果。这样,模型可以清楚地知道自己从何处中断,保持风格和语气的连贯性,避免因上下文重置而失忆。
那么,什么时候使用紧凑化,什么时候使用摘要化呢?
我们应当优先采用可逆的紧凑化策略(如将大文件输出写入磁盘,只保留路径)。但当已经无法再紧凑化,而且上下文也确实即将耗尽时,再使用带备份的摘要化——完整dump聊天记录,然后再摘要,这样就可以让有损的压缩变得可恢复。
这两种手段结合起来,Agent 理论上就能够处理无限长的任务,而无需无限大的上下文窗口,同时保留了关键信息,在有限中创造无限。
下面以Claude Code的压缩流程为例,介绍压缩的过程。
有两种情况会触发Claude Code的压缩机制:
这里给出我在Claude Code中对话时实际运行了/compact命令之后的打屏输出。具体的内容因为涉及到我的聊天记录,所以脱敏了。只看整个skeleton就可以了解它的工作原理。
Compact summary
⎿ This session is being continued from a previous conversation that ran out of context. The summary below covers the earlier portion of the
conversation.
Summary:
1. Primary Request and Intent:
用户在上一轮对话(xxxx)结束后,对 xxxx产生了好奇,连续提出了四个问题:
......
2. Key Technical Concepts:
......
3. Files and Code Sections:
......
4. Errors and fixes:
xxxx, 我把它们搞混了
5. Problem Solving:
......
6. All user messages: ###用户所有的input
......
7. Pending Tasks:
- 无明确待办任务。上一轮关于xxxx的任务已完成,用户表示会验证,但尚未反馈验证结果。
8. Current Work:
最后回答的问题是:xxxx。
回答要点:
......
9. Optional Next Step:
无明确下一步任务。用户的问题已全部回答,且没有新的待办事项。如有需要可继续讨论 xxx,或回到上一轮的 xxx
文档验证工作。
If you need specific details from before compaction (like exact code snippets, error messages, or content you generated), read the full
transcript at: /root/.claude/projects/PATH/SESSION_ID.jsonl 在返回摘要文本后,用这段摘要替换掉旧的对话历史,腾出空间。
而且我们从这个摘要的输出就可以看到,原始对话被完整地保存到了本地的JSONL文件/root/.claude/projects/PATH/SESSION_ID.jsonl 中。如果需要精确的代码片段或变量名,Agent完全可以按照摘要的提示,主动读取 JSONL 文件中的相应部分来“回忆”。这种方式虽然牺牲了即时访问的便利性,但确保了信息的可恢复性。
面对一个复杂的任务,我们可以将任务分解,然后由主智能体协调多个专门化的子智能体(sub-agents)来处理具体任务。
每个子智能体都拥有干净的上下文窗口,并且运行在自己的上下文窗口中,拥有独立的system prompt和工具使用权限(tool access)。主Agent会根据每个subagent的功能描述,自动决定什么时候该把什么任务委托(delegate)给哪个合适的 subagent。当然,用户也可以直接显式地要求使用特定的subagent。使用这种多智能体架构的几个好处是:
重点就在于,每个 subagent 拥有完全独立的上下文窗口,不会与主对话共享历史。subagent 看不到主对话的早期交互(除非显式传递信息),主对话的上下文也不会被 subagent 的输出污染。
举个例子,有一些会产生大量输出的任务(如运行测试、获取文档、处理日志),那么主agent就把它委托给 subagent,让详细的输出留在 subagent 上下文中,只将摘要返回主对话,从而保持主对话上下文的简洁。
下面是多智能体架构的一些分类:
按运行模式分:
按调用关系分:
下面以Claude Code为例,介绍subagent运行的示例。
Claude Code 采用了极简的设计,这正是“Keep Things Simple, Dummy”的典范。当遇到需要分解为多个子任务的情况时,主 Agent 生成自己的克隆作为子 Agent。
但是,每个子 Agent不能再派生出更多子 Agent,这是为了避免无限递归和过度复杂的嵌套。每个子 Agent 执行完毕后,其结果以工具响应的形式返回主历史,主 Agent 继续推进。
此外,主 Agent 内部有一个TODO list,跟踪需要完成的子任务,始终关注着最终目标。
下面是一个调用subagent的例子。
我输入的问题:
"分析 opencode 项目的 agent 系统:找出关键入口文件、核心数据结构、以及 sub-agent 是怎么被调用的,用中文写一份分析报告存到/tmp/agent-analysis.md"
之后,主Agent调用了一个General-Purpose的subagent,这个subagent在后台运行:
● Agent(Analyze opencode agent system)
⎿ Backgrounded agent ##表明是后台运行的subagent
⎿ Prompt: ###给subagent的任务描述
请分析 opencode 项目的 agent 系统实现。
具体要做的事:
1. 用 Glob 找到 agent 相关的源文件(在 packages/opencode/src/ 下)
2. 用 Grep 搜索关键词:subagent、spawn、AgentTool 等,找到 sub-agent 调用
3. 读取核心文件,理解:
- agent 的入口在哪里
- agent 的核心数据结构是什么
- sub-agent 是如何被创建和管理的
4. 把分析结果用中文写成一份报告,存到 /tmp/agent-analysis.md
报告格式:
opencode Agent 系统分析
1. 关键文件列表
2. 核心数据结构
3. Sub-agent 调用机制
4. 与 Claude Code 的对比推测
请认真探索,不要只猜测,要基于实际读到的代码。
● Agent 已在后台运行,实时看它的进度:
之后,它就在后台运行了,这个任务会触发:Glob → Grep → 多次 Read → Write 等多种工具。过了大概两分钟后它就完成了,于是屏幕上出现:
● Agent "Analyze opencode agent system" completed之后,就可以看到它已经把报告帮助咱们生成好了。
Transformer 模型在生成每个 token 时,需要计算所有之前 token 的Key和Value向量,用于注意力机制的计算,这些 KV 向量就构成了上下文的状态。KV Cache就是将这些中间计算结果保存下来,当后续请求包含相同的前缀时,可以直接复用,避免重复计算。
名词解释:Prefill(预填充)
Prefilling 是只在生成第一个输出 token 之前,模型对所有输入 token 进行并行处理的阶段。在这个阶段,模型计算每个输入 token 的Key和Value向量,构建用于后续解码的 KV Cache。
Agent 的工作流程通常是多轮工具调用的重复:用户输入 → agent 根据当前上下文选择一个动作 → 执行动作,得到Observation → 将Observation追加到上下文,继续下一轮
随着轮次增加,上下文不断增长,而每次模型的输出长度相对较短。有统计表明,平均Agent的输入输出 token 比高达 100:1。这意味着每轮推理的大部分时间都花在处理历史上下文上。如果没有缓存,每次请求都要从头计算所有历史 token,导致延迟、成本剧增。
KV Cache能复用相同prefix的结果。例如在多轮对话中,前几轮的历史内容可以作为prefix被缓存,后续请求只需处理新追加的内容。这样,即使上下文很长,TTFT(Time to First Token,首字延迟)仍能保持很低。而且命中缓存和未命中缓存的调用成本也相差甚远:以 Claude Sonnet 为例,缓存的输入 token 价格为 0.30 美元/百万 token,而未缓存的则高达 3 美元/百万 token,相差 10 倍!
缓存命中要求新请求的前缀与已缓存的内容完全一致,哪怕多一个空格、换行符,或者 JSON 键的顺序不同,都会导致缓存失效。所以,我们要做的就是保持prompt prefix的绝对稳定。
自动缓存就是在请求顶层添加一个cache_control字段,系统就会自动将最后一个可缓存的内容块作为缓存断点,将整个请求prefix(从开头到该块)存入缓存。默认的缓存生命周期是5 分钟,每次命中缓存会自动刷新,延长 5 分钟。当然,通过付费也可以将这个时间延长到1小时。
{ "cache_control": { "type": "ephemeral", "ttl": "1h" } }配置自动缓存的方式很简单,就像这样:
curl https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-opus-4-6",
"max_tokens": 1024,
"cache_control": {"type": "ephemeral"}, ###放在这里
"system": "You are a helpful assistant that remembers our conversation.",
"messages": [
{"role": "user", "content": "My name is Alex. I work on machine learning."},
{"role": "assistant", "content": "Nice to meet you, Alex! How can I help with your ML work today?"},
{"role": "user", "content": "What did I say I work on?"}
]
}'除了自动缓存之外,我们还可以对稳定性极高的内容(如system prompt、工具定义)使用显式断点,确保它们被缓存。Claude Code允许最多4 个缓存断点(包括显式和自动缓存)。自动缓存会占用其中一个槽位,因此如果已经设置了4 个显式断点,自动缓存将失败。
自动缓存和显示缓存的结合示例如下:
{
"model": "claude-opus-4-6",
"max_tokens": 1024,
"cache_control": { "type": "ephemeral" }, // 自动缓存
"system": [
{
"type": "text",
"text": "You are a helpful assistant.",
"cache_control": { "type": "ephemeral" } // 显式断点
}
],
"messages": [{ "role": "user", "content": "What are the key terms?" }]
}成、文本摘要、智能问答、聊天机器人、机器翻译、知识图谱、推荐系统、计算广告、招聘信息、求职经验分享等,欢迎关注!加技术交流群请添加AINLP小助手微信(id:ainlp2),备注工作/研究方向+加群目的。53AI,企业落地大模型首选服务商
产品:场景落地咨询+大模型应用平台+行业解决方案
承诺:免费POC验证,效果达标后再合作。零风险落地应用大模型,已交付160+中大型企业
2026-04-18
设计行业的“棺材板”,要被Claude Design盖上了?
2026-04-18
设计圈的 Claude Code 时刻来了
2026-04-18
OpenAI Codex重大更新:第二个Claude Code已经来了
2026-04-18
Claude Design 发布:设计的新时代
2026-04-17
Anthropic自己承认了:1M上下文是个伪命题,上下文的锅得自己背!
2026-04-17
Claude 4.7 正式发布!更强但中国用户更难
2026-04-17
赛博鸡生蛋,7小时用Claude Vibe Coding一个Mini-Claude
2026-04-17
Claude Opus 4.7 发布,全网最详细解读
2026-01-24
2026-04-15
2026-01-23
2026-01-26
2026-03-31
2026-03-13
2026-01-21
2026-02-14
2026-02-03
2026-02-03