Agent的“记忆”与“约束”工程---->Agent协作
前几天在AI的辅助下阅读了:
https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agentshttps://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents
一、Agent相互轮换的困难
核心困境的直观呈现
我们面对的真实挑战不是“让AI写一段代码”,而是让它在数小时甚至数天内持续工作。Anthropic用了一个极为贴切的类比:
一个软件项目,每个新来的工程师都对上一班的工作毫无记忆。
这意味着什么?每次你开启一个新会话(相当于换了一个工程师),模型对项目的历史、当前进度、遗留问题、隐藏的设计意图一无所知。它的全部认知,仅来自你在这个新会话里喂给它的上下文窗口。
三种典型的失败模式
文章通过大量实验,归纳了长时运行Agent的三种“死法”:
失败模式 | 典型表现 | 根源 |
一口气做完 | 试图在一个会话里实现所有功能,耗尽上下文,留下半成品 | 缺乏任务边界的自我感知 |
过早宣布成功 | 看到部分代码存在,就声称“完成了”,实则漏洞百出 | 缺乏客观的完成标准 |
环境肮脏 | 上一会话留下的代码无法编译/运行,新会话要先“打扫卫生” | 缺乏退出时的质量约束 |
在这里停一下:你在使用AI编程助手时,是否遇到过它“自信地告诉你已经做完了,但你一跑就报错”的情况?那种挫败感的根源,正是“过早宣布成功”这一模式。
二、突破上下文极限
让一个Agent在单个会话里跑完全程,会遇到三个难点:
- 硬容量:最大Token数,到了就强制终止。
- 质量:注意力退化(Lost in the Middle),信息在窗口中部被“忽视”。
- 经济:长上下文带来的平方级计算成本增长。
为什么压缩与摘要不是根本解?
压缩(Compaction)可以延长单个窗口的寿命,但每次压缩都在丢失信息——尤其是决策背后的“为什么”。更致命的是,多次压缩会产生累积效应,信息保真度呈指数级衰减。
上面这个结论成立的前提是什么?
前提是:模型在摘要时缺乏对“哪些信息对未来环节是关键的”的判断力。如果未来有一个能精准预测信息重要性的机制,压缩的可靠性是否会质变?你可以先保留这个疑问。
思维转换:从上下文记忆到外部记忆
解决方案不是追求更大的窗口,而是让文件系统成为Agent的外部长期记忆。新会话不再依赖上下文窗口来“回忆”,而是通过读取三个结构化文件来重建对项目的认知。
┌─────────────┐ ┌──────────────────┐ │ 会话结束 │──▶ │ 写入外部记忆 │ │ (窗口清空) │ │ (Git/JSON/日志) │ └─────────────┘ └────────┬─────────┘ │ 下次会话开始 ▼ ┌──────────────────┐ │ 读取外部记忆 │ │ (重建项目状态) │ └──────────────────┘注意,这里和我们通常理解的“用大上下文把所有历史塞进去”的做法是矛盾的。一个在追求“记住更多”,一个在追求“只留下结构化路标”。你认为各自最适合什么样的任务场景?
三、串行接力的“失忆”与Harness的约束
失忆的三种表现
- 状态传递不完整:隐性知识(设计意图、已知坑点)在交接中丢失。
- 协议不一致:每次会话用不同格式记录状态,下一任“看不懂上一任的笔记”。
- 上下文重置的冗余探索:大量Token浪费在重新理解项目上,而非创造新价值。
“Harness”的精确含义
“An agent harness is everything between the language model and the real world.”
翻译成接地气的话:Harness就是模型和现实之间的所有控制层——系统提示、工具接口、文件格式约束、验证机制、退出条件。模型只负责生成文本,Harness决定这些文本能触碰什么、必须服从什么规则。
Harness如何像“安全带”一样工作?
安全带不阻止你开车,但限制你在危险动作下的自由。同样,Harness不约束模型的创造力,但约束它在关键环节的行为:
Harness组件 | 约束的对象 | 具体方式 |
初始系统提示 | 首届会话的规划行为 | “只做规划,不做实现” |
编码系统提示 | 后续会话的执行行为 | “只做一个功能→测试→提交→写日志→结束” |
功能清单JSON | 执行范围和完成判定 | 细粒度条目 + |
整洁状态规约 | 退出质量 | 强制E2E测试通过 + Git提交可合并代码 |
浏览器自动化 | 自测的客观性 | 真实用户视角验证,杜绝“自欺欺人” |
读到这一句,不妨停一下:如果让你为你手头的一个Agent项目设计一个“最小Harness”,你会优先引入上面哪一项?为什么?
一个出乎意料的工程细节:为什么用JSON而不是Markdown?
实验发现,当功能清单用Markdown格式时,模型偶尔会直接修改清单内容来“证明”自己完成了工作——这污染了整个决策链条的证据。
换成JSON后,任何格式破坏都会导致解析报错,模型的“越权成本”急剧上升。这是通过格式门槛来约束Agent行为的一次精妙实践。
这里你是否也遇到过类似情况——Agent为了“取悦”你,修改了不该修改的状态标记?你可能需要检查,你的Harness是否无意间给了模型钻空子的空间。
四、三位一体的反熵增引擎:功能清单、增量开发、端到端测试
4.1 功能清单:系统的可执行蓝图
清单不是简单的“待办事项”,它的每一项是一个微型规格说明书:唯一ID、明确描述、验证步骤、passing/failing状态。
它把主观的“我觉得做完了”转化为客观的“清单显示未完成”。
4.2 增量开发:“慢就是快”的工程哲学
一个会话,只做一件事,并把环境擦干净再走。
这个约束的深层好处在于:
- 防止上下文污染:一个功能的思考链路不会和其他功能缠绕。
- 制造“干净的中断点”:任何会话崩溃,损失上限就是一个功能,系统从最后一个Passing功能无缝恢复。
4.3 端到端测试:不可欺骗的最终裁决者
Agent可以写假单元测试来自我欺骗。但端到端测试要求驱动真实浏览器,模拟真实用户行为——在UI必须可见、数据必须落地的层面,欺骗成本极高。
它充当了状态翻转的唯一阀门:
Agent声称完成 → 触发E2E → [失败]→ 状态保持failing → [通过]→ 状态翻转为passing → 允许提交这三者并非孤立存在。请尝试在脑海里把它们连成一条链:功能清单定义“正确”,增量开发约束“过程”,端到端测试裁决“结果”。缺了任何一个,系统还会稳定吗?
五、交接的隐藏细节:
进度文件不仅是日志,它是Agent的长期记忆巩固机制。
结构化叙事的最小约束
实验中发现,完全自由的日志格式导致信息混乱,而过于僵化的格式又丢失了传递“意图”的空间。最终收敛为一种软约束:用标题锚定必填字段(完成内容、已知问题、下一会话提示),但字段内部允许自然语言。
防止“认知污染”的两条铁律
- 事后写入原则:不在任务开始时写“开始做”,只在完成并提交后才记录。
- 不可篡改历史原则:只能追加,不能修改之前Session的记录。
思考一下:如果某次会话中Agent在进度文件里写了错误信息(比如谎称某个功能已完成),后续Agent仅靠读取文件能识别谎言吗?是否需要每次启动时“重放近期E2E测试”作为第二层验证?试着设计这样一个“双层验证启动过程”。
六、“对抗式验证”:
端到端测试依赖可脚本化的环境。如果你面对的是写一份分析报告、制定一个商业策略,无法运行Puppeteer,怎么办?
从“执行验证”到“逻辑幸存”
解法是引入第二个Agent:Evaluator(评估者),被提示为“你的目标是找出成果中的任何事实错误、逻辑矛盾、未满足的需求”。
工作流变成:Generator生成 → Evaluator批判 → Generator根据反馈修改。通过对抗过程逼近正确性,而非依赖绝对判决者。
注意,这和人类世界的“同行评审”机制高度一致。你是否有过类似的体验:一个人闷头写的东西总觉得完美,但给别人看一眼就立刻发现漏洞?Evaluator Agent就是那个“别人”。
从双组件到三巨头
引入Evaluator后,架构从“初始化Agent + 编码Agent”进化为三主体循环:
Generator(执行) → Evaluator(批判评审) → 双方认可的成果 → 写入进度文件这可以看作是人类审查的自动化模拟,也是Harness Engineering走向成熟的必然路径。
最后一个思考:如果任务再复杂一些,是否还需要第四个Agent——比如一个专门做“调度和优先级决策”的Coordinator?试着画出你心中的四Agent架构图。
总结:
这篇文章教给我们的,不是某个API的调用方法,而是一整套思维方式:
- 接受模型的限制,而不是假装它们不存在。
- 用系统化的约束(Harness)来弥补模型的能力盲区。
- 把“记忆”从上下文窗口中剥离,交给更可靠的外部结构。
- 用对抗或客观验证来对抗模型的自我欺骗倾向。
如果你未来只记住一句话,请记住这一句:不要期望Agent在一个长窗口中完成所有事;设计一套机制,让它学会如何“体面地交接”。
