Agent 原理(十四):Agent 跑得越久,为什么反而越容易变笨?真正要解决的,是“怎么忘”
做 Agent 做到一定阶段,你迟早会撞上一堵墙。
而且这堵墙很容易被误判。
刚开始,我们写的 Agent 非常自然:
用户说一句话。
模型思考。
调用工具。
工具返回结果。
再把结果塞回 Context。
模型继续思考。
再调用工具。
再塞回 Context。
于是整个系统一直沿着一个非常简单的逻辑运行:
发生过什么,就继续记住什么。
任务只有十几轮的时候,这种设计几乎完美。
可一旦 Agent 真正开始工作半小时、一个小时,甚至跨天运行,问题马上就来了。
它读过几十份文件。
调用过上百次 Tool。
查过网页。
运行过代码。
遇到过报错。
修改过 Plan。
调用过 Subagent。
做过 Verification。
等待过人工审批。
用户中间又修改过几次要求。
如果所有这些东西都继续放进 Context,那么你很快会发现一个很反直觉的现象:
Agent 明明掌握的信息越来越多,表现却可能越来越差。
很多人看到这里,第一反应是:
“Context Window 不够大。”
然后开始寻找 128K、200K、1M Context 的模型。
但真正理解 Agent 以后,你会发现:
这不是问题的本质。
因为哪怕明天模型拥有 1000 万 Token 的 Context,你依然不应该把过去发生的一切全部塞给它。
真正的问题是:
一个 Agent 到底应该记住什么,又应该忘掉什么?
这就是 Context Compression。
原来的课程设计把这一课概括成“Agent 工作几十分钟为什么不会爆 Context”,对应实验就是实现一个 Conversation Compressor。
但这一课如果只是教你“聊天太长就总结一下”,其实远远不够。
我们真正要解决的是一个更底层的问题:
一个长期运行的 Agent,怎样在丢掉大量历史细节以后,仍然知道自己是谁、正在做什么、已经做到哪里,以及下一步应该干什么。
这才是第十四课。
先别想 Agent,想象你自己工作了一整天
假设今天老板给你一个任务:
分析 20 家供应商报价,最后选出综合条件最优的一家。
上午九点,你开始工作。
第一家报美元。
第二家报人民币。
第三家按一打报价。
第四家按件报价。
有人 FOB。
有人 CIF。
有人没写运费。
有人 MOQ 不满足。
有人包装是一箱 24 件,有人是一箱 30 件。
于是你开始整理 Excel、发邮件、查汇率、统一计价单位、计算运费。
中间还发现一个公式写错了。
修正。
重新计算。
Supplier H 不满足 MOQ,排除。
Supplier C 少了 Freight,等待回复。
Supplier B 和 Supplier D 最接近,又继续比较付款条件。
下午三点,老板过来问:
“现在做到哪里了?”
你会怎么回答?
正常人绝对不会说:
“早上 9:02,我打开了第一个 Excel。”
“9:04,我点击了 Sheet2。”
“9:06,我发现 D17 是空值。”
“9:08,我第一次运行公式失败。”
“9:09,我重新运行。”
……
这些事情确实都发生过。
但它们已经没有资格继续占据你现在的工作记忆。
你真正会说的大概是:
已经完成 18 家供应商的标准化。
Supplier H 因 MOQ 不满足已经排除。
Supplier C 还缺运费数据。
目前 Supplier B 综合成本最低,Supplier D 第二。
但由于 Supplier C 数据还没补齐,现在还不能做最终推荐。
注意这里发生了什么。
过去六个小时,也许发生了几百个动作。
你没有全部忘记。
原始 Excel 还在。
邮件还在。
计算表还在。
但你的大脑已经把它们压缩成了几个真正影响下一步工作的状态。
这就是 Context Compression 最自然的理解。
不是“把字数变少”。
而是:
把已经发生的大量过程,重新表达成现在做下一步决定真正需要的信息。
一个非常关键的误区:Context 不是 Agent 的数据库
我们在前面的课程里已经把几个东西拆开了。
Context 是:
这一轮模型现在能够看到什么。
State 是:
当前任务已经进行到哪里。
Memory 是:
过去发生过什么,或者过去知道什么。
Skill 是:
遇到这类问题时,已经总结出来应该怎么做。
这几个概念原本就是课程中必须刻意区分的核心。
到了第十四课,还要再加一个东西:
Evidence。
也就是:
这个结论的原始证据到底在哪里。
比如采购案例中:
Supplier C Freight = 0.48 USD / piece。
这是一个事实。
这个事实可能进入 State。
但它的 Evidence 是:
Supplier C 发来的原始邮件。
现在模型不一定每一轮都要重新读取那封完整邮件。
可如果之后有人问:
“0.48 是从哪里来的?”
系统必须能够找回来。
这就是为什么:
“模型现在看不到”与“系统已经丢失”完全不是一回事。
这是理解长期 Agent 的第一道分水岭。
最原始的 Agent 为什么迟早会出问题?
我们最开始写 Agent Loop 时,通常会有一个messages。
每发生一件事情,就往里面 append。
概念上大概是:
messages = [] while True: response = llm(messages, tools) if response.tool_calls: for call in response.tool_calls: result = execute_tool(call) messages.append({ "role": "tool", "content": result }) else: return response.text这段代码没有错。
事实上,理解 Agent 最好的方式之一,就是先亲手写出这样一个 Loop。
问题出现在:
我们把messages同时当成了运行历史、任务状态、记忆系统和模型 Context。
刚开始它们看起来是一回事。
运行久以后,就完全不是了。
比如 Agent 工作一小时以后,messages里面可能已经有:
用户原始要求。
第一次 Plan。
第二次 Plan。
一个已经被推翻的旧 Plan。
20 个文件的原始内容。
12 个搜索结果。
3 次失败搜索。
6 个 Python Error。
几十次 Tool Call。
几百 KB 的日志。
两个 Subagent 的完整报告。
用户中途修改过的要求。
已经解决的问题。
已经失效的假设。
等待审批的 Action。
Verification 结果。
然后下一轮模型调用时,你把这一切重新发送给模型。
表面上:
“模型什么都知道。”
实际上:
你正在让模型重新阅读一大堆已经失去决策价值的信息。
这不是记忆力强。
这是没有信息管理。
Context Window 越大,为什么还是解决不了这个问题?
这里特别容易走错方向。
假设今天模型可以处理 128K Token。
有人说:
“那我换一个 1M Context 模型不就行了?”
当然会缓解容量问题。
但只解决了最浅的一层。
因为 Context 的真正成本不只是:
“装不装得下”。
还有另外一个更重要的问题:
哪些信息值得模型重新关注?
假设你现在真正需要解决的问题只有:
Supplier C 的运费数据什么时候回来。
结果模型同时看到:
70 轮以前已经修好的 Python Error。
Supplier A 的完整报价。
Supplier D 的完整报价。
十几个已经失效的搜索结果。
第一次 Plan。
第二次 Plan。
第三次 Plan。
五千字 Subagent 报告。
已经通过 Verification 的旧步骤。
这些内容都是真的。
但它们此时已经是噪声。
这就是 Context Engineering 最反直觉的地方:
给模型更多信息,有时候不是在帮助模型,而是在降低信噪比。
所以一个好的 Context 系统追求的从来不是:
“我能不能把所有东西都塞进去。”
而是:
这一轮决定,到底必须让模型看到什么?
课程最早在讲 Context Engineering 时其实已经留下了这个问题:哪些内容应该 permanent,哪些应该 retrieved,哪些应该 summarize,哪些应该直接丢弃。
第十四课就是开始真正给这个问题一个工程答案。
真正的转折:Context 应该是“临时构造”的
这一点如果想通,你对 Agent 的理解会发生一次明显升级。
早期 Agent 很容易这样理解:
Context = Conversation History
但成熟 Agent 应该是:
Context = Runtime 为当前这一轮决策临时构造出来的工作视图。
注意“临时构造”。
真实系统里可能保存着非常多东西:
用户 Goal。
当前 Task State。
完整 Session History。
Memory。
Skills。
原始文件。
Tool Results。
Subagent Results。
Audit Log。
Approval State。
环境状态。
但它们没有任何理由全部进入同一次 LLM Call。
Runtime 应该根据:
“现在准备做什么”
来选择:
“现在应该给模型看什么”。
所以到了这里,Context 已经不应该被理解成一个永远增长的数组。
更准确地说:
Context 是一个函数。
CONTEXT 的工程化理解
真实系统状态→Runtime 按当前任务选择信息→Working Set→LLM 做下一步决定
它接收当前系统状态,
然后生成:
这一轮模型需要看到的 Working Set。
Working Set:这是理解 Context Compression 最值得掌握的词
操作系统不会要求所有数据永远待在 CPU Cache。
数据库也不会为了执行一个查询,把整个数据库加载进内存。
Agent 同样不应该把所有历史永远放在 Context。
它真正需要的是:
Working Set。
也就是:
当前完成下一步工作真正需要的信息集合。
比如采购任务现在做到:
Supplier C 的 Freight 还没有拿到。
此时 Working Set 可能只需要:
用户最终目标。
“数据不完整不能推荐”的约束。
当前 Plan。
其他 19 家供应商已经完成标准化的事实。
Supplier C 的结构化报价。
缺失字段:Freight。
当前 Next Action:获取 Freight。
这时候需要把另外 19 家完整 PDF 再读一次吗?
不需要。
需要把两个小时前那个已经解决的 Python Stack Trace 放进来吗?
不需要。
需要把第一次失败的 Search Result 放进来吗?
也不需要。
这就是 Working Set。
而真正优秀的 Context Manager,本质上一直在回答:
什么应该进入当前 Working Set?
然后很多人会犯第二个错误:让 LLM “总结一下全部历史”
这是最常见的 Context Compression 实现。
历史太长。
于是:
“请总结以上聊天,保留重要内容。”
得到一段 Summary。
然后删掉前面的消息。
Demo 里看起来特别漂亮。
生产环境却隐藏着一个很大的风险。
因为:
Summary 是有损压缩。
比如原始资料是:
Supplier B 单价 8.42 USD / piece。
Freight 0.36。
MOQ 12,000。
Payment 60 days。
Supplier D 单价 8.31。
Freight 0.61。
MOQ 10,000。
Payment 30 days。
Compressor 最后总结成:
“Supplier B 和 Supplier D 报价接近,目前 Supplier B 综合条件略好。”
这句话完全没有错。
但半小时以后如果 Agent 需要重新分析:
为什么 B 比 D 好?
运费到底差多少?
Payment Terms 是否参与过判断?
你会突然发现:
Summary 已经不够了。
于是这一课必须建立第二个极其重要的概念:
Summary 不是 Evidence。
Summary 是为了帮助模型继续思考。
Evidence 是为了让系统能够重新证明:
“这个结论为什么成立。”
这两个东西不能混。
真正成熟的 Compression,第一步甚至不是“总结”
而是:
分层。
我们把 Agent 运行过程中产生的信息重新分一下。
第一类:
不能轻易丢掉的任务状态。
例如:
Goal。
Constraint。
当前 Plan。
Completed。
Pending。
关键 Decision。
Approval State。
Verification State。
这些东西如果消失,Agent 很可能直接改变行为。
第二类:
可以压缩的执行历史。
例如:
打开过哪些文件。
中间跑过哪些命令。
已经解决的报错。
旧的搜索过程。
已经结束的局部讨论。
这些东西不需要永远逐条占据 Context。
第三类:
原始 Evidence。
PDF。
Excel。
邮件。
数据库结果。
完整 Tool Output。
它们不需要每轮可见,但必须能够重新 Retrieve。
第四类:
长期知识。
Memory。
Skill。
这些甚至不一定属于当前任务,要按需加载。
所以一个成熟 Agent 的 Context,最后往往更接近:
Pinned Information + Current State + Compressed History + Recent Turns + Retrieved Evidence
CURRENT CONTEXT
Pinned Information + Current State + Compressed History + Recent Turns + Retrieved Evidence
而不是:
Everything That Ever Happened
这已经不是一个 Summary 技巧。
这是信息架构。
什么信息最不能被压错?
真正做 Agent 时,我会特别盯下面几类。
第一类是 Goal 和 Constraint。
比如用户说:
“数据不完整之前不要做最终推荐。”
如果 Compressor 最后只留下:
“目标是找出最优供应商,目前 Supplier B 最便宜。”
Agent 很可能下一步直接推荐 Supplier B。
程序不会报错。
模型也不会觉得自己做错了。
但原任务已经被 Compression 改写。
这类错误非常危险。
因为它不是 Crash。
它会继续正常运行。
第二类是已经确定的关键 Decision。
比如前面已经决定:
所有价格统一成USD / piece。
结果压缩以后只剩:
“已经完成价格标准化。”
过一会 Agent 重新处理一份数据时改成USD / dozen。
前半段一个标准。
后半段一个标准。
每一步单独看似乎都合理。
最终结果却已经不可比。
第三类是 Pending。
假设:
Supplier C 缺 Freight。
Supplier F 的 Packaging Basis 待确认。
如果 Summary 写成:
“报价标准化基本完成。”
这个“基本完成”特别危险。
Agent 很可能直接进入下一阶段。
所以长期 Agent 里一个非常重要的原则是:
Completed 和 Pending 必须结构化分开。
不要让一句“差不多完成了”承担系统状态。
第四类是失败产生的新约束。
比如某个数据源已经证实过期。
某个接口权限不足。
某条路径已经尝试并证明不可行。
Stack Trace 可以不留。
但:
这条路已经验证不能走
这件事情可能必须保留。
否则 Agent 过 30 轮以后又重新踩一次。
第五类是 Governance。
上一课最核心的变化,就是 Planning 得出的动作不再天然拥有执行权。Agent 只是提出“我认为应该做什么”,系统还需要检查权限和风险,再决定允许、审批还是禁止。
所以如果当前 Action 正在:
等待采购负责人审批,
Compression 以后千万不能只剩:
“采购流程正在进行。”
系统至少应该知道:
审批的是哪个 Action。
哪张 PO。
什么金额。
为什么需要审批。
当前状态。
谁批准了没有。
否则 Context Compression 可能直接把上一课建立的安全边界压没。
到这里,就该引入真正比 Summary 更重要的东西:Checkpoint
我觉得理解 Checkpoint 最简单的方法,不是想 Agent。
想游戏。
你玩一个 RPG 玩了六个小时。
存档时,游戏会保存什么?
不会保存过去六小时屏幕上出现过的每一帧。
它保存的是:
你现在在哪里。
多少级。
有什么装备。
哪些任务完成了。
哪些任务还没完成。
哪些门已经打开。
哪个 Boss 已经打死。
当前世界状态是什么。
下一次重新打开游戏,
系统加载这些状态。
你就能继续玩。
Agent 一样。
如果它工作了两个小时,
真正值得保存的不是:
两小时完整聊天。
而是一个能够继续任务的 Checkpoint。
例如:
checkpoint = { "goal": ..., "constraints": ..., "plan": ..., "completed": ..., "pending": ..., "decisions": ..., "critical_facts": ..., "evidence_refs": ..., "approval_state": ..., "verification_state": ..., "next_action": ... }看到这里,你应该会有一个很明显的恍然大悟:
长期 Agent 真正依赖的不是“更长的聊天记录”,而是“可恢复的任务状态”。
更关键的是:这些东西很多根本不应该交给 LLM 总结
这是从 Demo 走向工程系统特别重要的一步。
假设系统明明已经知道:
amount = 184300
为什么还要把整个对话交给 LLM,再让它总结成:
“这是一笔比较大的订单。”
系统明明知道:
approval_state = waiting_for_procurement_director
为什么还要让模型总结成:
“目前还在等待审批。”
系统明明已经拿到:
Supplier C Freight = 0.48 USD / piece。
为什么让它依赖一段自然语言 Summary 保存?
能结构化保存的东西,
就直接结构化保存。
这条原则我会写得非常重:
能 deterministic 保存的事实,不要交给 LLM 猜回来。
金额。
日期。
ID。
数量。
版本。
状态。
审批结果。
计算结果。
关键字段。
这些都应该属于 Structured State。
LLM Compressor 更适合做什么?
它更适合处理:
“前面这几十轮到底发生了什么?”
“为什么最后选择这种做法?”
“哪些尝试失败过,不值得重复?”
“还有什么异常值得后面注意?”
也就是说:
Structured State 保存事实。
Summary 保存语义。
Evidence 保存依据。
Compression 的三层分工
| Structured State | 保存事实 |
| Summary | 保存语义 |
| Evidence | 保存依据 |
这三层一分开,Context Compression 才真正开始可靠。
现在我们可以真正设计一个 Compressor 了
假设这次继续沿用整门课的风格,不上 LangChain,不上 LangGraph。
直接自己写。
原课程本身也是这样安排的:一路从01_llm.py演进到14_context_compression.py,最终再去理解完整 Agent Harness。
先把 State 从 messages 中独立出来:
from dataclasses import dataclass, field @dataclass class TaskState: goal: str constraints: list[str] = field(default_factory=list) plan: list[str] = field(default_factory=list) completed: list[str] = field(default_factory=list) pending: list[str] = field(default_factory=list) decisions: list[str] = field(default_factory=list) evidence_refs: list[str] = field(default_factory=list) approval_state: str = "not_required" verification_state: str = "not_started"这段代码并不复杂。
但它做了一件特别重要的事情:
以前 Agent 想知道自己做到哪里,
只能回头翻聊天记录。
现在:
Runtime 自己知道。
接下来生成 Checkpoint:
def build_checkpoint(state: TaskState): return { "goal": state.goal, "constraints": list(state.constraints), "plan": list(state.plan), "completed": list(state.completed), "pending": list(state.pending), "decisions": list(state.decisions), "evidence_refs": list(state.evidence_refs), "approval_state": state.approval_state, "verification_state": state.verification_state, }然后才轮到 LLM。
而且它只负责压缩真正非结构化的旧历史:
def compress_history(llm, old_messages): prompt = """ Compress the old execution history. Preserve: - important conclusions - unresolved issues - reasons behind key decisions - failed approaches that should not be repeated Do not invent facts. Do not replace structured task state. """ return llm(prompt, old_messages)这时候系统的逻辑已经完全变了。
不是:
“让 LLM 帮我记住整个世界。”
而是:
“Runtime 保存确定状态,LLM 只帮我压缩难以结构化的语义。”
旧历史是不是全部压掉?
也不是。
这里还有一个很容易忽略的细节:
Recent Turns 通常应该暂时保留原文。
例如刚刚发生:
Agent:
Supplier C 一箱 24 件,对吗?
用户:
不是,最新版改成 30 件。
Agent:
收到,我重新计算。
这三句刚刚发生。
它们具有非常强的局部相关性。
如果马上压成:
“Packaging Basis 已更新。”
大意虽然对,
但原始纠正关系已经弱了一层。
因此比较成熟的 Context 通常会保留:
Pinned Context。
Checkpoint。
Compressed Old History。
Recent Turns。
Retrieved Evidence。
比如可以简单切:
def split_history(messages, recent_count=8): if len(messages) <= recent_count: return [], messages return messages[:-recent_count], messages[-recent_count:]旧的压。
新的暂时不压。
这就非常接近人类工作记忆的方式。
然后最核心的函数,不再是messages.append()
而是:
build_context()。
例如:
def build_context( system_prompt, checkpoint, compressed_history, recent_messages, retrieved_evidence, ): context = [] context.append({ "role": "system", "content": system_prompt, }) context.append({ "role": "system", "content": f"CURRENT TASK STATE:\n{checkpoint}", }) context.append({ "role": "system", "content": f"OLD HISTORY SUMMARY:\n{compressed_history}", }) context.extend(retrieved_evidence) context.extend(recent_messages) return context这里是第十四课真正值得记住的代码变化。
前十三课你可能一直在:
messages.append(...)
到了第十四课,真正应该开始思考的是:
这一轮到底怎样 Build Context。
Agent 的历史第一次和 Agent 的当前 Context 正式分家。
但这还不够:Compression 本身也要 Verification
这一点非常容易被忽略。
大家对 Compressor 有一种莫名其妙的信任:
“它只是总结而已,能有什么问题?”
当然会有问题。
它也是模型。
模型就可能:
漏掉。
误解。
改写。
把不确定写成确定。
把 0.48 写成 0.84。
把 Pending 写成 Completed。
把“不能执行”压成“暂未执行”。
所以我们前面学过的 Verification,在这里又回来了。
假设压缩前系统知道:
Goal。
Constraint。
Pending。
Approval State。
压缩以后应该检查:
这些关键语义还在不在。
最朴素的版本甚至可以这样:
def verify_checkpoint(before, after): assert before["goal"] == after["goal"] assert set(before["constraints"]).issubset( set(after["constraints"]) ) assert set(before["pending"]).issubset( set(after["pending"]) ) assert ( before["approval_state"] == after["approval_state"] )真实系统当然会比这个复杂。
但真正重要的是这个思想:
Compressor 不是天然可信模块。
它也应该接受检查。
这就是为什么前面的课不是一节一节孤立存在。
Planning。
Verification。
Memory。
Skill。
Learning。
Governance。
到了长期 Agent 以后,它们开始互相咬合。
再往深一点:Context Compression 其实很像“编译”
这个比喻特别好用。
假设 Agent 原始历史是:
打开 Excel。
读取 Sheet1。
D 列解析失败。
发现是百分号格式。
标准化。
重新计算。
结果异常。
检查公式。
重新运行。
Verification Passed。
这是一串非常低层的执行 Trace。
未来 Agent 真正需要知道的可能只是:
Supplier A 已完成 GM 计算。
GM = 31.4%。
原始百分比字段已标准化。
Verification Passed。
Evidence = calculation_run_17。
也就是说:
几十个低层事件,
被“编译”成一个高层状态。
这就是优秀 Compression 真正做的事情。
不是单纯:
短一点。
而是:
表达层级更高。
原始操作留在底层。
当前决策只看抽象后的结果。
需要检查时,再下钻到 Evidence。
这和成熟软件系统的思想其实非常接近。
信息其实应该有自己的生命周期
一份数据进入 Agent 后,不应该永远以同一种形态存在。
比如一份 80 页 PDF。
刚开始处理时:
可能需要全文。
接着找到相关章节以后:
只需要几个关键段落。
结构化提取以后:
只需要字段。
形成结论以后:
只需要 Summary。
再往后:
甚至 Context 里面只需要一个 Evidence Reference。
于是它可能经历:
Raw → Relevant Extract → Structured Fact → Summary → Reference Only。
Context Lifecycle
Raw→Relevant Extract→Structured Fact→Summary→Reference Only
需要核查时,再 Retrieve 原始 Evidence。
后来如果需要核查:
再 Retrieve 原始 Evidence。
这就是 Context Lifecycle。
一旦开始这样理解 Agent,
你就不会再问:
“这份文件要不要一直留在 Prompt?”
你会问:
它现在处于哪个生命周期阶段?
这个问题高级得多。
Compression 应该什么时候发生?
最简单的方法是:
Token 快满了再压。
例如:
used_tokens > budget * 0.75
第一版完全可以这么写。
但生产系统最好不要只看 Token。
因为还有一种非常自然的 Compression 时机:
任务阶段结束。
比如:
20 份文件全部提取完成。
一个 Subagent 已经返回。
某一个 Plan 阶段完成。
一次 Verification 通过。
系统准备等待人工审批。
当前任务准备暂停。
这些时刻天然形成一个语义完整的边界。
在这里创建 Checkpoint,比做到半截突然因为 Token 不够而硬压,通常可靠得多。
所以真正好的 Compression Trigger 往往有两种:
一种是:
Resource Boundary
Context 快超预算了。
另一种是:
Semantic Boundary
一个完整工作阶段刚刚结束。
RESOURCE BOUNDARY
Context 快超预算了
典型触发:Token 使用接近预算上限。
SEMANTIC BOUNDARY
一个完整工作阶段刚刚结束
例如提取完成、Subagent 返回、Verification 通过或准备等待审批。
两种一起用,才像真正的 Runtime。
这时候,你会发现 Compression 和 Retrieval 根本分不开
如果一段历史离开 Context,
下一句话一定应该问:
“那以后需要的时候怎么找回来?”
如果回答是:
“找不回来了。”
那不叫 Compression。
那只是删除。
成熟系统会把信息从:
Always Visible
变成:
Available When Needed
原始文件可以进 Artifact Store。
Tool Result 可以进 Session Store。
结构化状态进 Task State。
审批信息进 Audit / Governance State。
长期事实以后可能进入 Memory。
可复用做法经过 Learning Loop 后可能进入 Skill。
然后当前 Context 只拿真正相关的一小部分。
所以:
Compression 回答:
什么应该暂时离开 Context?
Retrieval 回答:
什么现在应该重新回来?
Compression回答:什么应该暂时离开 Context?
Retrieval回答:什么现在应该重新回来?
只有这两件事同时存在,才叫 Context Management。
现在重新跑一次完整采购案例
用户要求:
分析 20 家报价。
统一币种、单位、MOQ、贸易条款、运费、税费、Packaging Basis。
数据不完整之前不能最终推荐。
Agent 开始读取。
Supplier 1。
Supplier 2。
Supplier 3。
……
做到第 12 家。
已经产生大量 Tool Calls。
这时候做一次 Checkpoint:
Goal:
比较 20 家供应商并形成采购建议。
Constraint:
数据完整前不能最终推荐。
Completed:
12 / 20。
Normalization:
USD / piece。
Excluded:
Supplier H,因为 MOQ 不满足。
Pending:
Supplier C Freight Missing。
Evidence:
supplier_table_v2,以及 12 份原始报价引用。
然后:
前面那些打开文件、解析字段、修复格式、重新读取的过程,
可以退出 Active Context。
Agent继续处理 13 到 20。
第二个阶段结束。
再次 Checkpoint:
Completed:
20 / 20。
Pending:
Supplier C Freight。
Current Best Candidate:
Supplier B。
Final Recommendation:
Not Allowed Yet。
最后一句特别重要。
因为它不是“信息”。
它是:
当前行动边界。
Supplier C 数据回来。
系统 Retrieve Supplier C Record。
更新 Freight。
重新计算。
Supplier B 最优。
Verification 通过。
然后进入上一课 Governance。
订单金额超过阈值。
需要采购负责人审批。
Agent 停下来。
人批准。
执行。
再 Verification ERP 状态。
完成。
整个任务可能经历 200 次 Tool Call。
但最后一轮模型根本没有必要重新看那 200 次过程。
它只需要看到:
Goal。
Constraint。
当前 State。
最终计算结果。
关键 Evidence。
Approval State。
这就是一个真正可以长期工作的 Agent。
现在我们终于能回答:Context Compression 到底解决了什么?
表面上:
它解决 Token。
实际上:
它解决的是长期 Agent 的连续性。
如果 Agent 的全部“自我”都存在聊天记录里面,
那每运行一轮,
过去就越来越重。
而当我们把:
State 独立出来。
Evidence 独立出来。
Memory 独立出来。
Skill 独立出来。
Audit 独立出来。
Conversation History 可以 Compression。
Context 每一轮重新 Build。
Agent 才终于从:
一直拖着整个过去往前走
变成:
保存过去,但只携带现在需要的部分。
这已经是两个完全不同的系统。
更深的一层:Context Compression 其实是 Persistence 的前置条件
假设 Agent 今天运行了两小时。
已经完成:
数据收集。
清洗。
分析。
现在等待采购负责人批准。
你关掉程序。
第二天再打开。
如果系统只能保存 Conversation History,
恢复任务意味着:
重新塞入昨天两小时的聊天。
很笨。
真正成熟的系统应该:
Load Checkpoint。
恢复 Goal。
恢复 Plan。
恢复 Completed / Pending。
恢复 Decision。
恢复 Approval State。
重新 Retrieve 当前需要的 Evidence。
Build 一个新的 Context。
继续执行。
这时候 Agent 才真正拥有:
Resume。
而 Resume 一旦成立,
后台任务。
跨 Session。
Scheduler。
长时间运行。
甚至跨天 Agent,
才真正有了基础。
所以 Context Compression 并不是一个 Prompt 小技巧。
它实际上已经开始进入:
Agent Runtime Design。
最后再说一个特别容易混淆的问题:Compression 不是 Memory
比如这次任务里:
Supplier B 当前报价最低。
这是当前 Task State。
一个月以后价格变了。
如果你因为这句话出现在 Compression Summary 里面,就写进长期 Memory:
“Supplier B 是最便宜供应商。”
那未来一定出问题。
再比如:
“比较供应商报价之前,需要统一 Packaging Basis。”
这更接近 Skill。
而:
“Supplier C 当前 Freight 是 0.48”
只是这一次任务的事实。
所以永远不要把:
Compression Summary、
Memory、
Skill
看成同一个东西。
它们解决的是三个不同时间尺度的问题。
Compression:
当前任务继续跑,哪些历史可以变薄。
Memory:
未来任务可能重新需要哪些过去信息。
Skill:
以后遇到这类任务,应该怎样做得更好。
把时间尺度想清楚,很多 Agent 架构自然就不会乱。
如果这一课你真正学懂,脑子里应该发生一个变化
以前看到 Agent,你可能想:
“它有一个很长的 Messages 数组。”
以后应该变成:
系统拥有一个很大的真实状态世界。
里面有:
State。
Memory。
Skills。
Evidence。
Artifacts。
History。
Governance。
Audit。
Environment。
但每一轮 LLM Call 前,Runtime 都会问一句:
“为了现在这个决定,我到底应该让模型看到什么?”
然后才构造 Context。
这就是 Context Engineering 从:
“Prompt 怎么写”
真正走向:
“Runtime 怎么设计”。
第十四课真正值得带走的,不是“总结历史”
我会把这一课最后压成三个判断。
第一个:
Context 不是历史。
历史可以非常长。
Context 只是现在的工作视图。
第二个:
Compression 不是删除。
信息可以离开 Context,但重要状态和 Evidence 必须仍然存在,而且能找回来。
第三个:
长期 Agent 的核心能力不是永远记住,而是知道什么现在必须记住,什么可以暂时忘掉,以及什么时候重新想起来。
最早的 Agent 是这样工作的:
发生什么,
append。
再发生什么,
再 append。
一直把整个过去背在身上。
而真正成熟的 Agent 会逐渐变成:
真实世界不断产生事件。
Runtime 更新 State。
Evidence 被保存。
旧历史被压缩。
当前相关信息被 Retrieve。
Context 被重新构造。
LLM 做下一步决定。
再进入下一轮。
长期 Agent 的运行闭环
真实世界不断产生事件 → Runtime 更新 State → Evidence 被保存 → 旧历史被压缩 → 当前相关信息被 Retrieve → Context 被重新构造 → LLM 做下一步决定 → 再进入下一轮
所以第十四课表面上讲的是:
Context Compression。
但它真正教的是另一件更重要的事情:
一个 Agent 只有学会“正确地忘记”,才有资格开始长期工作。
因为智能从来不只是:
记得越多越好。
真正的智能,是到了这一刻,
你仍然知道:
什么最重要。
