Prompt 没变,为什么 Agent 还是会跑偏?

刚开始使用 Claude Code 和 Codex 时,我很少主动切换会话。同一个会话已经积累了项目结构、任务背景和此前的决策;开启新会话意味着 Agent 需要重新探索仓库,我也需要重新说明目标与约束。为了避免这些重复工作,我更倾向于在一个长会话里连续完成相关任务。

这种做法在短任务里通常没有明显问题。但任务变长以后,搜索结果、工具返回、中间结论和历史指令会不断进入上下文。Agent 可能重复已经完成的探索,忽略较早的目标,或者继续使用已经失效的中间结果。

有一次,我让同一个 Agent 连续完成三个阶段:先探索仓库,再依据已有约束实现功能,最后回到这些约束审查改动。任务期间,上下文两次触发压缩。压缩后,Agent 仍在调用工具和处理代码,最终却没有完成整条任务链:局部动作继续发生,探索、实现和评审不再围绕同一组约束推进。

Agent 三阶段任务链在上下文压缩后失焦
Agent 三阶段任务链在上下文压缩后失焦

这段经历不能证明压缩就是跑偏的根因。当时没有保存完整 trace,也无法确认哪条信息在什么时候丢失。它能说明的只有一点:Prompt 不是决定 Agent 行为的全部输入。随着任务推进,工具结果会增加,中间结论会失效,原始目标也可能在摘要中被改写。即使 Prompt 没变,模型每一轮实际看到的信息集合也可能已经不同。

Anthropic 在 Effective context engineering for AI agents 中将 Prompt Engineering 与 Context Engineering 区分开来:前者关注如何编写和组织指令;后者关注推理发生时,哪些信息进入模型,包括系统指令、工具定义、消息历史和外部资料。

本文沿用这个边界。Prompt 规定 Agent 应该怎样行动;Context Engineering 管理它此刻能够依据什么行动。问题因此不再是“怎样写一段更强的 Prompt”,而是:

当信息不会全部、永久、同等可信地留在窗口里,系统应该如何决定它的进入、更新和退出?

先分清四种东西

在本文中,“上下文”专指某一轮推理实际发送给模型的信息。系统保存过、模型曾经看过,或者未来能够检索到的内容,都不自动属于当前上下文。

MemGPT 展示了在窗口与外部存储之间移动信息的分层方式;CoALA 则区分工作记忆与多种长期记忆。下面的四分法不是这些研究提出的标准,而是本文为了讨论真实任务中的读写和失效规则采用的工程分类。

类型回答的问题典型内容主要规则
行为契约(Prompt contract)Agent 应该怎样行动任务目标、权限、工具规则、交付与验收要求相对稳定,由任务发起者维护,变更应显式发生
工作上下文Agent 此刻做到哪里当前阶段、待办、未解问题、近期结论任务内高频更新,在阶段切换或任务结束时整理
检索证据本轮判断依据什么代码、配置、测试输出、文档、API 与网页内容按需获取,保留来源和时间,必要时重新查询
长期记忆什么值得在未来任务中复用用户偏好、稳定项目约束、已确认决策和经验跨任务保存,有范围,可更新、撤销和删除
Prompt contract、工作上下文、检索证据与长期记忆的四类运行时角色
Prompt contract、工作上下文、检索证据与长期记忆的四类运行时角色

这里区分的是运行时角色,不是四个必须独立部署的存储区。同一条信息可以改变角色,但不能因为“出现过”就自动获得更高权威。

例如,Agent 从仓库读取一份测试配置时,它首先是检索证据;当这份配置影响当前方案时,相应结论进入工作上下文;只有在多次核验后确认它是跨任务稳定的仓库约束,它才可能成为长期记忆。如果负责人进一步把它写入开发规范,它才成为后续任务的行为契约。

这也解释了两个常见误区。第一,被长期保存不等于应该在每轮注入。代码库、来源库和事件账本都可以长期存在,但仍应按需读取。第二,来自过去不等于值得成为长期记忆。失败调用、被推翻的方案和未经核验的模型总结属于任务历史,不应因为便于检索就继续影响未来任务。

分层不是为了概念整齐。只有先知道信息承担什么角色,系统才能决定它应该常驻、按需读取、暂时保留,还是跨任务治理。

上下文窗口不是仓库

更长的上下文窗口当然有价值。它能减少硬截断,让模型一次接收更多代码、文档和历史。但窗口容量只回答“最多能输入多少”,不保证模型能在需要时找到并正确使用其中的信息。

三组研究从不同角度给出了行为层证据:

研究主要比较可以支持的结论不能直接推出
Lost in the Middle在多文档问答和键值检索中改变目标信息的位置许多受测模型的表现会随目标位置变化,中间位置常更弱所有模型都看不见中间信息
Context Length Alone Hurts LLM Performance Despite Perfect Retrieval在五个模型的数学、问答和代码任务中保持目标可访问,同时增加输入长度检索成功后,输入长度仍可能影响后续使用所有任务都会按同一幅度单调下降
Context Rot在十八个长上下文模型上改变长度、位置、干扰项和输入组织方式额外内容及语义相近的干扰项可能增加区分难度已经确认某一种统一内部机制

这些研究的任务、模型和控制变量不同,不能拼成一条关于所有长上下文模型的普遍定律。它们共同反驳的是更弱、也更实用的假设:只要信息仍在窗口里,模型就会同样稳定地使用它。

这也不意味着上下文越短越好。窗口过短会遗漏必要约束,压缩过度会同时删除结论和依据。需要优化的不是长度本身,而是当前任务中的信息密度。Anthropic 将这个方向概括为寻找足以支持当前行为的高信号信息集合;本文把它进一步表述为“最小充分工作集”。

在这个工作集中,每条信息都需要回答两个问题:为什么现在要进入,为什么现在还应留下。暂时不需要但能够重新获得的信息可以保留索引;已经失效的信息则应退出,而不是与当前事实并排堆放。

把上下文窗口当仓库与作为最小充分工作集的对比
把上下文窗口当仓库与作为最小充分工作集的对比

一旦不再把窗口当仓库,“记住”就不能只意味着保存。它还必须包括提炼、校验、取回、修订和退出。

让记忆拥有生命周期,而不是保留聊天记录

把每轮消息、工具调用和模型回复按时间保存下来,完成的是 trace 留存。完整 trace 对复盘和审计有价值,但它同时包含事实、猜测、失败尝试、重复内容和只在当时有效的状态。直接对它做向量检索,只是让旧对话有机会重新进入上下文,并没有把旧对话变成可信的长期资产。

PlugMem 提供了一种具体做法:先保留情景记录,再从中提取事实命题和可复用流程,并让抽象知识能够回到原始经历。A-MEM 则把记忆组织成带有上下文描述、关键词、标签和链接的笔记,并允许新记忆更新既有条目的属性。这些研究支持“原始经历需要经过提炼”和“记忆对象可以演进”,但没有共同提出完整的来源、权限和撤销标准。

为了把这些能力放进真实系统,本文建议让一条长期记忆经历以下生命周期:

采集 trace → 提炼候选 → 校验 → 带属性写入 → 按需检索 → 修订 → 退出使用

假设 Agent 在仓库中看到 pnpm-lock.yaml,运行 pnpm test 成功,又在 CI 中看到 pnpm 命令。这些首先是三条带来源的任务事件。系统可以据此提出候选记忆:“该仓库根目录使用 pnpm,默认测试入口是 pnpm test。”

候选不等于事实。一次命令成功可能发生在子目录,lockfile 也可能是迁移遗留。写入前还需要检查 package.json、脚本、当前分支和 CI 配置,并明确结论适用于整个仓库还是某个 workspace。

一条可维护的记忆至少需要三组属性:

属性组典型内容解决的问题
内容与检索内容、类型、关键词、关联项记住了什么,之后如何找到
来源与权限原始证据、写入者、创建时间、访问边界为什么可以相信,谁可以读写
生命周期适用范围、最近校验时间、状态、失效条件、替代关系它现在是否仍有资格影响 Agent

其中,适用范围、失效条件和替代关系是本文的工程建议,不是上述论文共同采用的标准字段。

当仓库迁移到 npm 时,系统不应简单追加一条相反记忆。新配置可以产生一个新版本,原来的 pnpm 记忆则标记为 superseded。如果只发现配置发生变化、尚未完成核验,可以先标为 stale 并暂停注入。如果后来确认它来自错误分支、越过权限边界或本来就不应持久化,则应标为 revoked,停止使用,并检查它是否已经影响其他笔记或任务。

过期、被替代、撤销和物理删除不是同一个动作。Microsoft 的 Agent 记忆安全指导把记忆同时视为高价值数据和影响 Agent 行为的控制层,要求写入门禁、来源标记、身份隔离、检索时的新鲜度检查,以及可追踪的创建、读取、更新和删除记录。它是一份安全治理建议,不是记忆效果实验;但它说明,一条能够跨会话影响行为的信息,不能只由模型自由写入。

Agent 长期记忆从 trace 到退出使用的生命周期
Agent 长期记忆从 trace 到退出使用的生命周期

长期记忆因此不是一段“以后可能有用”的聊天摘录。它是从证据中提炼、经过准入判断、带着适用范围进入系统,并能被修订、暂停和撤销的对象。

三种运行时策略:压缩、按需检索与任务笔记

生命周期解决一条信息如何成为长期记忆。长任务还需要处理另一个问题:在任务尚未结束时,如何维持连续性、取得证据并保住全局状态。

压缩、按需检索和任务笔记处理的是三类不同问题:

策略主要对象作用主要风险
压缩已进入窗口的消息、工具结果和中间过程保留任务如何走到当前阶段丢失细节或形成摘要漂移
按需检索代码、文档、网页、数据库和原始日志在需要时回到当前证据漏召回、索引过期和运行时延迟
任务笔记当前目标、阶段、决策、未解问题和证据入口让全局状态跨越窗口重置固化错误状态,增加维护成本

Anthropic 将压缩定义为用高保真摘要重新开始上下文,并建议优先保住架构决策、未解决问题和实现细节。压缩适合回答“任务怎样走到了这里”,不适合替代仍需精确核验的原始配置和日志。

对于能够从外部可靠恢复的信息,更合适的是即时检索。上下文先保留文件路径、查询和链接,Agent 到达需要证据的步骤时再读取具体内容。这样可以减少无关输入并提高信息新鲜度,但会把成本转移到查询规划、工具调用和 I/O。

任务笔记则是工作上下文的外部表示。它可以记录“当前正在实现,评审尚未完成”,但这仍是单个任务内的状态,不应自动进入长期记忆。LangChain 的当前文档也按作用范围区分 thread-scoped short-term memory 与跨 session 的 long-term memory。

回到开头的 explore、generate 和 review 任务,一种可验证的组合是:contract 固定三个阶段和验收要求;任务笔记记录当前阶段、未解问题及仍需执行的 review;探索期间读取的代码和测试输出留在原位置,笔记只保存路径和结论;历史接近窗口上限时,压缩保留已完成工作和已排除方案;压缩后,Agent 先重读任务笔记,再沿证据入口打开当前文件。

压缩、按需检索与结构化笔记组成的 Agent 运行时管线
压缩、按需检索与结构化笔记组成的 Agent 运行时管线

Anthropic 建议清理久远的原始工具结果;Manus 的工程复盘则强调保留失败动作和反馈,以免重复错误。两者可以通过区分“失败的语义”和“失败的全部输出”来调和:任务笔记保留错误签名、失败结论和已排除方向,完整日志退出窗口但保留路径。这个组合是本文的策略判断,不是两篇来源已经验证的统一答案。

我没有保存开头任务的完整 trace,也没有用这套组合重跑,因此不能宣称它已经解决了跑偏问题。它只是把问题变成了可验证对象:压缩后阶段是否仍完整,任务状态能否恢复,关键结论能否回到原始证据,失败方向是否会被重复执行。

放回已有项目:保存什么,给 Agent 看什么

真实项目往往已经保存了大量信息。关键问题不是还要不要保存,而是其中多少应该进入下一轮推理。

Harness 提供了最完整的实现证据。按照固定版本的 Task Repository 设计,一个任务目录同时包含 events.jsonlstate.yamlruns/ 和四份语义文档。它们描述同一项任务,却服务于不同目的:

  • runs/ 保存经过脱敏的原始执行日志;
  • events.jsonl 用于重放和审计;
  • state.yaml 是可重建的机器状态;
  • brief.mddecisions.mdevidence.mdhandoff.md 面向任务理解与交接。

文件系统仓储实现会检查事件版本,再写入账本、状态投影、语义文档和独立执行日志。恢复任务时,WorkOrder 实现只向新执行者提供四份语义文档和已记录产物,不把事件账本、状态投影或原始日志直接注入上下文。

为核对当前实现,我在该固定 commit 上复跑了语义文档、跨宿主恢复、原始日志保留和 WorkOrder 四组测试,共十九项,全部通过。测试能够证明分层存储、恢复上下文和保留策略按代码执行,不能证明这种设计已经提高真实生产任务的成功率。

语义文档也不能自动成为比原始证据更高权威的事实。当前 evidence.md 是从角色提交内容投影出的证据摘要,生成路径没有强制每一项都携带独立来源对象和验证者。因此它更适合被定位为证据索引:帮助下一位执行者知道去哪里检查,而不是替代工作区文件和测试输出。任务文档只有经过显式审批才会被提升到项目文档目录,原始运行记录不会自动升级为长期记忆。

ZhoMind-v2 展示了同一原则在知识库中的形态。固定版本的检索实现只接受当前发布 generation 的分块;生成 Prompt只包含本轮问题和前三条证据,而不是完整文件。会话消息和 RAG trace 会持久化,但当前生成路径不读取历史消息;记忆 Store 仍为 Runner 内部的内存实现,MemoryWriteNode只记录门禁决定。这可以称为会话记录、证据链和记忆策略脚手架,不能称为已经验证的跨会话长期记忆。本轮没有复跑该项目测试。

TracePress 则只有设计层证据。它的 PRD明确标记为“待技术设计”,要求未来分别管理来源、主张、冲突、审批、任务状态、日志和作者偏好。来源库长期保存完整材料、各角色只读取当前主张所需证据,是设计目标,不是已经实现的能力。

Harness、ZhoMind-v2 与 TracePress 中持久化资产和最小工作集的边界
Harness、ZhoMind-v2 与 TracePress 中持久化资产和最小工作集的边界

三个项目成熟度不同,却指向同一个判断:原始资料因为需要复核而保存,事件账本因为需要恢复和审计而保存,任务摘要因为需要继续工作而进入上下文,长期记忆则需要单独的准入和治理。持久化不是注入的理由。

边界与最小验收:不是所有 Agent 都需要长期记忆

判断是否需要长期记忆,不能只看任务持续多久。一个每天重复的五分钟任务,可能需要跨任务复用偏好;一个持续数小时但不会再次发生的任务,可能只需要当前 session、checkpoint 和任务笔记。

单次格式转换、给定材料摘要、临时分类和一次性检查,通常没有必要先生成长期记忆。OpenAI Agents SDK 的 Memory 文档也允许为 checker、subagent 或低信号的一次性工具 Agent 关闭记忆生成。该功能目前标为 Beta,因此这里只把它作为产品工程例子,而不是普遍定律。

能够从权威来源低成本重新获得、并且持续变化的信息,也不宜保存成长期真相。依赖版本、仓库配置、构建结果和服务状态都可能迅速失效。对这类信息,更适合记住“去哪里查、如何验证”,而不是记住某一时刻的结果。

权限和纠错能力是更硬的准入条件。如果系统不能说明一条记忆来自谁、何时产生、适用于哪个用户或项目,也不能让用户查看、修改和删除,那么即使检索效果不错,也不应让敏感信息和行为偏好跨会话影响 Agent。

长期记忆至少应与两个更轻的基线比较:只保留 session 或 checkpoint,以及在需要时重新查询权威来源。比较时应保持任务集、模型、工具权限和近似预算一致。

最小验收可以从六个问题开始:

维度最小问题建议记录
检索与使用需要的记忆是否被找到,并被正确用于任务?Recall@kNDCG@k、最终任务成功率
过期影响旧信息是否进入候选并改变最终行为?旧版本 top-k 暴露率、最终误用率
纠错发现错误后,多久能停止召回及清理派生影响?纠错耗时、残留影响数
token提取、写入、检索和注入是否值得?每个合格任务的额外 token
时延记忆构建与查询分别增加多少延迟?构建、查询及端到端 P50/P95
人工接管记忆是否把机器成本转移成了人工纠错?每百个任务中的非计划接管次数

LongMemEval同时报告检索指标与最终问答结果,并覆盖知识更新和拒答;MemoryAgentBench进一步测试选择性遗忘,并区分记忆构建与查询时延。表中的具体门槛、P50/P95 和人工接管口径仍是本文给项目评测提出的建议,不是这些论文规定的统一标准。

建设 Agent 长期记忆前的准入条件与最小验收指标
建设 Agent 长期记忆前的准入条件与最小验收指标

正确召回不能被压缩成一个数字。检索层找到记忆,不代表模型会正确使用;最终答案正确,也可能是模型重新查询或依靠其他信息得到的。过期信息同样要拆成两层:旧版本进入候选是检索问题,Agent 最终服从旧版本则是行为问题。

token 更少、响应更快也不能单独证明记忆有效。如果节省了原始资料读取,却增加错误召回和人工接管,系统只是把计算成本转移成了纠错成本。

长期记忆因此不应成为 Agent 的默认基础设施。只有当信息确实需要跨任务复用,重新获得它的成本高于持久化和治理成本,并且系统能够证明任务质量改善、过期影响可控、纠错足够快、成本与时延可以接受时,建设长期记忆才有明确理由。

真正可靠的记忆系统,不是让 Agent 永远不忘,而是让每条被想起的信息仍能回答三个问题:它从哪里来,为什么现在仍然有效,以及谁能够让它被纠正或遗忘。