Agent 智能体的核心技术不是工具——而是上下文管理
为什么压缩是 Agent 系统的"元机制",而工具只是确定性的基础设施
一、一个被误导的行业焦点
过去两年,Agent 框架的竞赛似乎围绕着一个指标展开:谁集成了更多工具。从代码解释器到浏览器控制,从数据库查询到图像生成,工具数量成了衡量 Agent "能力"的显性标准。
但这种视角忽略了一个根本事实:工具调用本身是确定性的。read_file读第 35 行就是第 35 行,grep匹配到的结果就是那些结果,执行bash命令的返回码不会撒谎。工具是基础设施,是手脚,是感官——它们本身不产生智能。
真正让 Agent 从"单次问答"进化为"长期工作"的,是上下文管理。而在上下文管理的所有技术中,压缩(Compaction)是唯一的元机制——它决定 Agent 记得什么、忘记什么,从而决定 Agent 能工作多久、工作质量如何。
二、上下文管理:Agent 的"操作系统"
如果把 Agent 比作计算机,上下文窗口就是 RAM——有限、昂贵、高速。文件系统和向量数据库是磁盘——无限、廉价、慢速。Agent 的长期工作能力,不取决于它有多少工具,而取决于它的"操作系统"如何管理这块 RAM。
2.1 没有上下文管理,Agent 是金鱼
一个 128K 上下文的 Agent,面对一个 10MB 的代码仓库,如果不做上下文管理,它只能:
- 读几个文件就触顶
- 忘记用户最初的约束
- 重复读取同一个函数
- 在循环中耗尽 token 预算
工具再多也没用——它根本"记不住"自己用过什么工具。
2.2 上下文管理的七层技术栈
业界处理上下文超限的方法可以归纳为七层:
| 层级 | 技术 | 作用域 | 确定性 |
|---|---|---|---|
| L1 | 硬截断(Truncate) | 工具返回时 | 100% 确定 |
| L2 | 分页读取(Pagination) | 工具设计时 | 100% 确定 |
| L3 | 去重(Deduplication) | 工具返回时 | 100% 确定 |
| L4 | 引用替换(Reference) | 轮次内 | 100% 确定 |
| L5 | 外化记忆(Offload) | 跨轮次 | 100% 确定 |
| L6 | 结构化压缩(Compaction) | 跨轮次/轮次内 | 概率性 |
| L7 | 上下文重置(Reset) | 跨轮次 | 100% 确定 |
L1-L5 和 L7 都是工程手段,是确定性的基础设施。只有 L6——结构化压缩——是智能行为,因为它需要判断"什么重要、什么可以丢"。
三、压缩:唯一的"元机制"
3.1 为什么压缩是"元"的
工具调用修改的是外部世界(文件系统、数据库、API),压缩修改的是 LLM 自己的"大脑状态"。
- 工具调用错了→ 系统返回错误,LLM 收到反馈,下一轮回正。这是可逆的。
- 压缩错了→ 关键信息被静默丢弃,LLM 真诚地相信自己记得的是对的,然后基于错误的记忆继续走向深渊。这是不可逆的。
压缩是 Agent 系统中唯一一个 LLM 修改自己未来输入上下文的环节。它像人脑的海马体——在睡眠时决定什么进入长期记忆,什么被遗忘。海马体坏了,再聪明的大脑也会变成痴呆。
3.2 压缩失败的复利效应
工具调用的错误是单点失败:
read_file 报错 → 重试 → 成功 → 继续压缩错误是复利失败:
Round 1: 压缩时丢了"用户说不要用全局变量" Round 2: Agent 用了全局变量(约束已丢) Round 3: 压缩时丢了"全局变量已被使用" Round 4: Agent 在全局变量上继续堆代码 ... Round N: 代码变成 spaghetti,Agent 不知道为什么会这样每一轮压缩都在前一轮的"失真记忆"上继续失真。最终,上下文与真实世界严重脱节,而 LLM 对此毫无察觉。
四、架构分界:自主域 vs 控制域
理解 Agent 系统,必须划清一条界线:
LLM 自主域:工具选什么、参数怎么填、先读后改还是先 grep——这是 LLM 的"思维自由",系统只提示,不控制。
算法控制域:压缩怎么做、什么能丢什么不能丢、丢完后对不对——这是系统的"工程责任",必须算法级保障。
4.1 工具调用属于自主域
LLM 决定调用read_file还是grep,决定offset=35还是offset=230,这是它的 reasoning 能力。系统不应该干预——即使它选错了,错误也是显式的:
LLM: read_file("auth.ts", offset=99999) → 越界 系统: 返回错误 "offset out of range" LLM: 重新计算,read_file("auth.ts", offset=1)自主域允许试错,因为错误可被感知、被反馈、被修正。
4.2 压缩必须属于控制域
压缩不能交给 LLM 自由发挥,因为它的失败是静默的、不可逆的。
一个正确的压缩系统应该这样工作:
- 算法决定丢什么:系统按硬规则分类消息(不可丢 / 可摘要 / 可丢弃)
- LLM 只负责填充:按给定的 Schema,把"可摘要"的消息提炼成结构化摘要
- 算法校验结果:检查不可丢项是否还在、修改过的文件是否保留、未解决的错误是否丢失
- 校验失败则阻断:不是让 LLM “重新试试”,而是回退到 checkpoint 或拒绝压缩
五、压缩的工程实现:三层防线
5.1 第一层:硬规则(系统决定,LLM 不可违背)
COMPACTION_RULES={"immutable":[# 绝对不可丢弃"user_constraints_last_5_turns","files_modified_in_this_session","unresolved_errors","unresolved_contradictions"],"summarizable":[# 可摘要,但路径/行号必须保留"files_read_but_not_modified","tool_outputs_already_consumed"],"droppable":[# 可直接丢弃"file_unchanged_records","superseded_code_versions"]}这些规则不是 prompt 里的"建议",而是系统对消息数组的直接操作。LLM 的权限边界非常清晰:它可以决定key_findings怎么写,但不能决定files_modified是否保留。
5.2 第二层:Schema 填充(LLM 执行,结构约束)
LLM 的任务不是"自由写摘要",而是按算法给定的输入,生成结构化输出:
{"preserved":[{"type":"file_modified","path":"src/auth.ts","lines":[35,50]},{"type":"user_constraint","text":"不要用全局变量"}],"summarized":{"files_read":[{"path":"src/auth.ts","key_findings":"实现了登录逻辑","lines":[35,230]}]}}Schema 强制保留定位信息(路径、行号),只压缩语义内容(代码细节)。这避免了"摘要写得很通顺,但丢了关键行号"的问题。
5.3 第三层:一致性校验(算法验证,不经过 LLM)
defverify_compaction(original,compressed):# 修改过的文件不能丢modified=get_modified_files(original)assertall(fincompressed.files_writtenforfinmodified)# 未解决的错误不能丢unresolved=get_unresolved_errors(original)assertall(eincompressed.issues.errorsforeinunresolved)# 不能产生幻觉(新信息必须能在原文中找到)forfindingincompressed.summarized.files_read:assertfinding.pathinoriginal_file_paths校验失败的处理不是"让 LLM 重新压缩",而是回退到更保守的策略或拒绝压缩直接报错。因为压缩是单向门,一旦错了就没有挽回余地。
六、主流项目的佐证
这个论点不是理论推演,各主流项目的架构选择都在印证它:
Claude Code:压缩是黑盒,但行为暴露硬规则
Claude Code 的自动 compaction 不可定制,但从其可观测行为可以反推出明确的优先级:
- P0(绝不丢弃):
CLAUDE.md、最近修改的文件路径、未解决的错误 - P1(尽量保留):最近 3-5 轮对话、用户明确约束
- P2(可丢弃):已消费的 Read 结果、
File unchanged记录 - P3(优先丢弃):早期探索路径、已解决的错误、被覆盖的代码版本
Anthropic 把压缩策略硬编码,说明他们认为这太重要,不能交给 LLM 随意决定。
CoordClaw:绕过压缩,用外化替代
CoordClaw 的选择更极端——如果压缩太危险,那就避免需要压缩。
- 一轮生命周期极短(T1-T5 标准动作),自然不需要轮次内压缩
- 跨轮次不保留对话历史,只保留工作日志
- 工作日志是确定性的结构化输出,不是 LLM 生成的摘要
这相当于把"压缩"这个概率性问题,转化成了"结构化输出"这个确定性问题。
OpenCode:把压缩交给社区探索
OpenCode 提供experimental.session.compacting钩子,但默认不实现。这暴露了一个事实:Even 核心团队也认为"最佳压缩策略"还没有共识。社区插件(DCP、mnemosyne)各自探索不同的压缩方案,恰恰说明压缩是 Agent 系统中最开放、最关键的问题域。
七、给架构师的结论
设计 Agent 系统时,请遵循这条公理:
凡是不可逆的操作,必须落在算法控制域;凡是可逆的操作,可以交给 LLM 自主域。
| 操作 | 可逆性 | 所属域 |
|---|---|---|
| 工具调用(读/写/搜) | ✅ 可逆(可重试/可回滚) | 自主域 |
| 代码编辑 | ✅ 可逆(git 可恢复) | 自主域 |
| 任务拆分 | ✅ 可逆(可重新规划) | 自主域 |
| 上下文压缩 | ❌不可逆(原始信息已丢) | 控制域 |
| 记忆外化 | ❌不可逆(写入即事实) | 控制域 |
工具设计是确定性的基础设施——它解决"能不能做"的问题。工具调用是 LLM 的自主行为——它解决"怎么做"的问题。但压缩是 Agent 的元认知机制——它解决"还记得什么"的问题。
一个 Agent 可以只有三个工具(读、写、搜),只要它的压缩策略正确,就能维护百万行代码。反之,一个 Agent 有三十个工具,如果压缩策略错误,读五个文件就会忘记用户是谁。
Prompt 指导思维,算法保障底线。压缩就是底线。
