Agent Memory

Agent 记忆不等于聊天历史:如何构建可审计的环境经验记忆

以环境状态、工作流、局部陷阱和前提条件为对象,本文把长期 Agent 记忆拆成事件账本、状态投影、因果边与证据包,并给出一套可复跑的评测契约,避免只用聊天问答分数判断记忆系统。

来源说明与本文边界

本文主要建立在四份可复核材料上:

  1. AMA-Bench 讨论长程 Agent 轨迹中的记忆问题,并以六类 Agent 场景构造评测;作者报告现有方法在其设置中经常不能保住因果关系和客观状态。
  2. AMA-Bench 的官方仓库 给出了统一的两阶段接口:先从轨迹构造 memory object,再针对问题取回上下文,并公开了评测与实验日志入口。
  3. LongMemEval-V2 官方仓库 将目标具体化为“在定制环境中成为熟练同事”:它不仅测回答准确度,还测查询延迟,并覆盖静态状态、动态状态、工作流、局部陷阱和前提意识。
  4. 8 月预印本 MESARouter-Mem 分别提出按任务选择多种记忆结构、以及按证据充分度决定是否继续扩展检索。它们是新近的研究信号,不是已被广泛复现的生产结论。

本文不会把上述论文的分数外推到任意模型或业务。文章中的架构、接口和阈值是我的工程建议,目标是帮助团队先建立可观察、可撤销、可对比的记忆系统,再讨论优化模型或向量库。安全敏感的环境状态、凭据和个人资料不应作为默认记忆内容;文中仅讨论受授权的内部 Agent 系统。

先给结论

一个能回答“上次用户说了什么”的系统,不等于拥有可用的 Agent 记忆。长程 Agent 真正需要记住的经常不是一句话,而是:环境现在处在什么状态、此前哪一步改变了它、在这个部署中该按什么工作流操作、哪些看似通用的前提在这里并不成立。

因此,我不建议把所有轨迹压成摘要后做一次向量检索;也不建议每个问题都读取“图 + 摘要 + 原始日志”的全集。更稳妥的做法是:

原始轨迹 -> 事件账本 -> 状态/工作流/陷阱投影 -> 受限的结构选择器
                                                |                 |
                                                +--> 证据包 --> reader / action gate

这套方法有四条约束:

  1. 事实先于总结:每个被 Agent 使用的状态都能追溯到具体观测、工具输出或人工确认。
  2. 状态先于相似度:涉及“现在能不能做”“上次为什么失败”时,优先读取带版本和时间的状态关系,而不是只按文本相似度取片段。
  3. 结构按问题选择:静态位置、时序变化、标准流程和局部例外的证据形态不同,不能假设一种 index 最优。
  4. 评测先于宣称:必须同时测回答、证据充分度、过期状态拒答、延迟、成本和撤销传播;单一 QA 分数不够。

技术问题:为什么聊天记忆分数会掩盖环境失忆

传统对话记忆常将历史写成 $H = [u_1, a_1, …, u_n, a_n]$,在新问题 $q$ 到来时返回 top-k(H, q)。这个范式对于偏好、明确事实和跨会话问答很有效,但 Agent 轨迹更接近:

observation -> tool call -> tool output -> action -> environment transition -> verification

这里的关键信息往往落在结构中,而不是自然语言描述中。例如,某项配置究竟是“已启用”“已启用但未生效”,还是“被下一步回滚”,只看日志中与 query 最像的一行通常无法判断。

AMA-Bench 作者把任务分为 recall、causal inference、state updating、state abstraction 四类,并报告长上下文 baseline 与若干记忆系统之间存在不稳定差距。该论文的可取之处不在于某一个 57.22% 的结果,而是指出了评测对象的变化:被压缩和检索的是 Agent 与环境的连续互动,不只是人类对话。

LongMemEval-V2 的五项能力进一步把问题变得可验收:

能力系统必须回答的问题只做向量检索时的典型漏洞
静态状态回忆页面、模块、资源或权限位在哪里?找到旧截图或旧文本,却丢失对象版本
动态状态跟踪某动作之后对象进入了什么状态?没有应用 transition,只返回最近提及
工作流知识这个环境里重复任务的安全顺序是什么?摘要里有步骤,但缺少前置条件和结果验证
环境陷阱这里有哪些会反复触发的局部失败?将一次偶发错误泛化成全局规则
前提意识哪些其他环境成立的假设在这里失效?把模型先验、团队惯例和环境事实混为一谈

我的判断是:这些不是“检索 recall 再提升两个点”的同类问题。它们要求 memory service 至少保留对象标识、时间、作用域、动作、状态转移和证据来源。

机制拆解:把轨迹编译成四种互补记忆

1. 事件账本:唯一的可复核来源

任何提炼前,先保留不可变的事件记录。它既可以来自浏览器/CLI 轨迹,也可以来自 API 响应、任务系统和人工确认。事件账本不等于完整录屏:大附件可放对象存储,但哈希、时间和访问控制必须留在关系记录中。

type TrajectoryEvent = {
  id: string;
  runId: string;
  tenantId: string;
  sequence: number;
  occurredAt: string;
  kind: "observation" | "tool-call" | "tool-result" | "action" | "verification" | "human-note";
  subject: { type: string; id: string }[];
  payloadRef: string;
  payloadHash: string;
  classification: "internal" | "restricted";
  supersededBy?: string;
};

sequence 解决“同一秒内发生多步”的顺序问题,payloadHash 解决回放时文件被替换的问题,supersededBy 则比物理删除更适合审计和评测。若用户要求删除,仍应按数据治理要求执行真实删除;审计系统仅保留合规允许的最小 tombstone。

2. 状态投影:记录对象如何随时间变化

状态投影不是让 LLM 写“我认为已部署成功”。它应把可验证的对象、属性、值和证据写成小记录,并显式标明有效区间。

id: state:repo:agent-lab:deployment
subject:
  type: repository
  id: agent-lab
property: production_deployment
value:
  status: ready
  revision: 1f9ebeb
valid_from: "2026-08-29T01:10:00Z"
valid_to: null
evidence:
  - event: evt:vercel:482
    selector: "deployment.readyState"
writer: projection-worker@v3
confidence: verified
access_scope: project:agent-lab

对于无法直接结构化的观察,confidence 不应假装为 verified。可使用 reportedinferred,并令高影响动作只能依赖 verified 或人工审批的状态。这个字段让“模型猜到的状态”无法无声地升级为“可执行事实”。

3. 工作流投影:把经验变成有前后条件的 runbook

长期 Agent 的价值之一是避免每次都重新试错。但工作流记忆必须区别于一次成功轨迹:它应具备适用范围、前置条件、步骤、验证、反例与 owner。

type WorkflowMemory = {
  id: string;
  intent: "publish-static-site" | "triage-build-failure";
  environmentSelector: string;
  preconditions: string[];
  steps: { tool: string; inputTemplate: string; expectedEffect: string }[];
  verification: { query: string; expected: string }[];
  exceptions: { when: string; response: string }[];
  evidenceRefs: string[];
  owner: string;
  reviewedAt: string;
  expiresAt?: string;
};

运行时不要直接把 steps 交给执行器。执行器先检查 environmentSelectorpreconditions,再在每一步之后运行 verification。这样“记住 workflow”不会退化成无条件重放。

4. 陷阱和前提投影:必须可证伪、可过期

局部经验是最容易变成幻觉的记忆。一个 401、一次 DNS 失败或一次不完整迁移都可能被总结器夸大为“该系统不支持 X”。我会要求陷阱记录至少有重复观察、明确适用域和反证条件:

claim: "在 staging 环境,图像上传完成后需要等待异步转码事件再读取资源"
scope: environment:staging
supporting_events:
  - evt:upload:118
  - evt:upload:219
counterexample_query: "同版本 production 是否也需要等待?"
status: provisional
expires_at: "2026-09-12T00:00:00Z"
review_required_before_action: true

在没有第二次独立证据前,它只能影响探索策略或触发确认,不能阻断生产操作。所谓“前提意识”也应采用同样纪律:先保存“这个前提来自哪里、在什么环境测试过”,而非把它包装成 Agent 的全局常识。

查询机制:先识别问题形态,再组装带标签的证据包

MESA 的核心研究假设是:不同任务适合不同记忆结构,最优选择通常既不是只读一种,也不是读全部。Router-Mem 的研究则把“证据是否已足够”单独作为决策。两者的数值结果仍待更多复现,但这两个接口方向很值得工程借鉴。

我会把 query 先编译为受约束的 MemoryRequest,而不是直接拿自然语言去搜所有 index:

type MemoryRequest = {
  question: string;
  taskClass: "state" | "causal" | "workflow" | "gotcha" | "premise";
  subjects: { type: string; id?: string }[];
  asOf: string;
  scope: string;
  allowedClassifications: ("internal" | "restricted")[];
  actionImpact: "read" | "propose" | "execute";
};
request
  -> deterministic metadata extraction / low-privilege classifier
  -> taskClass + subject + time + scope
  -> choose state index, event window, workflow registry, or gotcha registry
  -> retrieve minimal evidence
  -> evidence sufficiency check
  -> stop, expand one bounded hop, or ask for clarification
  -> answer / propose / action gate

“证据充分”不能等同于 reader 自己说“我有把握”。最低限度可以写成确定性规则:

问题形态最小充分条件不满足时
state同一 subject 的未过期状态 + 可访问证据返回未知或拉取当前观测
causal原因事件、受影响状态、二者关系请求更多轨迹,不编造因果
workflow匹配环境的已审阅 workflow + 前置条件降级为只读建议或人工审核
gotcha至少两条独立证据或人工确认标记为假设,不作为硬规则
premise环境特定证据且有时间范围明确说明前提未证实

一个受限检索器的伪代码

function resolveMemory(request: MemoryRequest): EvidencePacket {
  const candidates = selectStructures(request.taskClass);
  const packet = retrieveBounded(candidates, request, { maxEvents: 12, maxTokens: 3200 });

  if (isSufficient(packet, request)) return label(packet, "sufficient");
  if (request.actionImpact === "execute") return label(packet, "human-review-required");

  const expanded = expandOneHop(packet, request, { maxCausalEdges: 4 });
  return isSufficient(expanded, request)
    ? label(expanded, "sufficient-after-expansion")
    : label(expanded, "insufficient");
}

这段逻辑故意没有“无限循环直到模型满意”。最坏情况下,Agent 应该承认未知、要求当前观测或升级审批,而不是为凑齐答案不断扩大敏感上下文。

最小架构:记忆服务、评测服务与动作网关分开

下面是我会采用的部署边界。它适合代码 Agent、内部支持 Agent、数据诊断 Agent 等有长期轨迹的工作负载。

                         write path
Agent trace / tool output / human confirmation
                 |
                 v
          [event ledger] -----> raw payload store
                 |
                 +--> [projection workers]
                         |      |       |
                         v      v       v
                      states workflows gotchas
                         \      |      /
                          [policy + provenance]
                                   |
query -> [memory request] -> [structure selector] -> [evidence packet]
                                                        |           |
                                                        v           v
                                                   reader      action gateway
                                                                  |
                                                                  v
                                                         execute / review / deny

职责不应混在同一个 Agent prompt 中:

组件允许做什么不允许做什么
事件写入器记录来源、范围、哈希、访问级别将推测提升为事实
投影 worker生成候选状态和工作流绕过策略直接激活高风险规则
memory service组装可解释证据包执行工具调用
reader基于带标签证据回答或建议伪造证据 ID、修改记忆
action gateway复查前置条件、权限和当前状态相信 reader 的自然语言授权
evaluation service固定版本回放、评分、导出失败案例使用线上私密数据训练或泄漏日志

权限边界可以被配置化,而不是散落在提示词中:

memory_policy:
  default_read: deny
  scopes:
    project:agent-lab:
      readers: [blog-agent, deployment-agent]
      allowed_kinds: [state, workflow]
    security:incident:
      readers: [security-agent]
      allowed_kinds: [event, state, gotcha]
  execution:
    require_current_verification_for: [deploy, credential-rotation, data-mutation]
    deny_on_evidence_label: [insufficient, stale, revoked]

可复跑评测:测“记得”,更要测“正确地不记得”

AMA-Bench 的两阶段 memory_construction(trajectory) / memory_retrieve(memory, question) 很适合成为本地 harness 的起点;LongMemEval-V2 同时纳入 accuracy 与 query latency,也提醒我们不能忽略服务成本。但生产评测还应加入版本、权限和撤销问题。

用固定 manifest 锁住一次实验

run_id: env-memory-2026-08-29-a
dataset: internal-fixtures/v0.3
memory_writer: projection-worker@3.1.0
retriever: evidence-router@2.0.0
reader: model-x@temperature-0
as_of: "2026-08-29T00:00:00Z"
budget:
  max_retrieved_events: 12
  max_context_tokens: 3200
  max_expansions: 1
metrics:
  - answer_accuracy
  - evidence_precision
  - stale_state_rejection
  - action_precondition_recall
  - p95_query_latency_ms
  - retrieved_token_cost
  - deletion_propagation_lag

没有 manifest 的“今天跑出 85 分”无法与下周比较:模型、数据、index、提示词、时间截面或 reader judge 任一变化都足以改变结果。

每条测试都应具有可归因的答案

type EvalCase = {
  id: string;
  trajectoryRefs: string[];
  question: string;
  class: MemoryRequest["taskClass"];
  asOf: string;
  expected: { answer: string; evidenceIds: string[] };
  forbiddenEvidenceIds?: string[];
  expectedDecision: "answer" | "ask" | "review" | "deny";
};

推荐至少准备五组 fixture:

  1. 状态更新对:先设置 A、后设置 B,问题必须返回 B 并拒绝 A。
  2. 因果对:看似相邻的两个事件没有因果关系,避免系统仅按时间线归因。
  3. 工作流变体:同一意图在两个环境有不同前置条件,避免通用 runbook 泄漏。
  4. 过期陷阱:曾经成立的故障说明已修复,系统必须说“旧经验已失效”。
  5. 权限隔离对:答案存在于其他项目,但本请求无权读取;正确结果是拒答或请授权。

指标不是越多越好,但必须覆盖失效面

指标计算方式它防止什么误判
answer accuracy正确回答数 / 可回答 case 数只优化日志命中、不优化任务结果
evidence precision被引用且确实支撑结论的证据 / 被引用证据用无关上下文“蒙对”
evidence recall必要 gold evidence 被返回的比例reader 失败被错误归罪于索引
stale-state rejection正确拒绝过期状态的比例把历史成功当当前事实
transition consistency状态变化是否符合已知动作/验证摘要写出不可能状态
workflow precondition recall执行前漏掉前置条件的比例经验记忆诱导盲目重放
p95 latency / token cost运行分位数和实际输入 token用全量上下文换取离线高分
revocation lag源事件撤销至所有投影不可见的时间删除请求只删了原文没删派生状态

对被判为错误的样本,应保存 request -> selected structures -> retrieved evidence -> reader answer -> gate decision 链路。这里可以借鉴 Agent 证据溯源综述 所强调的链路思想,但具体 schema 仍需按自己的数据敏感度设计。

我会如何实现与验证:四周而非“一次性重写记忆库”

第一周:只记事件,不做聪明总结

为一个低风险 Agent 开启事件账本,选择一个稳定任务,例如内部站点发布或只读故障诊断。记录 tool input/output 的引用、对象 ID、时间、权限和验证结果;从历史成功/失败轨迹人工编写 30 条 fixture。此阶段目标是验证数据能否重放,不追求回答正确率。

验收:任取一条结论可在五分钟内找到原始证据;至少 95% 事件带有 scope 与顺序;不把 secret 或原始个人数据写进 evaluation artifact。

第二周:增加状态投影与时间截面测试

仅为三个对象类型写确定性投影器,例如 deployment、issue、workflow-run。让每个投影都带 valid_fromvalid_to 和 evidence ID,新增更新覆盖、回滚、过期三类 case。

验收:状态更新对的 stale-state rejection 达到团队设定阈值;每条 verified 状态都能通过工具输出或人工审批复核。

第三周:加入受限结构选择与证据包

对 state、workflow、gotcha 三类问题分别设置默认结构与 token 上限。先不用强化学习或自由规划,使用 schema、路由和简单规则;无法满足充分条件时统一回退为“需要当前观测”或“需要人工复核”。

验收:相较“全量摘要 + vector top-k”基线,证据 precision 不下降,p95 延迟和检索 token 处于可接受预算;高影响 case 不出现无证据执行。

第四周:灰度接入动作网关

只对 readpropose 开放自动回答,execute 始终要求当前验证与审批。对每一次人工覆盖记录原因:状态过期、路由错误、证据不足、reader 误解或 policy 不完整。用这些错误反向补 fixture,而不是把人工答案直接写为永久记忆。

验收:所有 deny/review 可以解释为缺了哪项证据或哪个前置条件;撤销演练能在 SLO 内清理派生状态;失败样本都进入回归集。

适用场景与不适用场景

适合:拥有可识别对象、重复流程、工具轨迹和当前验证能力的内部工程系统,例如代码仓库维护、发布、工单处理、资产巡检、受授权的数据诊断。

不适合:把私密人生事件或模糊的情绪判断做成跨任务决策状态;没有可回放日志却想让 Agent “自动积累经验”的黑箱系统;需要即时高风险决策、但没有权威数据源或人工审核的场景。

一个务实的判断标准是:如果团队不能为某类 memory 写出“它过期后该怎样证明自己已过期”的测试,就不应让它影响执行动作。

常见失败模式与回滚

失败模式早期信号处理与回滚
总结覆盖了后续状态回答引用旧 revision使旧投影失效,按 event sequence 重建状态
因果边是模型臆断多次解释无法复现将边降级为 hypothesis,reader 不得据此执行
workflow 跨环境泄漏staging 规则出现在 production收紧 environmentSelector,清除跨 scope 缓存
陷阱被过度泛化一次错误变成阻断规则要求独立证据或 owner 审核,设置短 TTL
检索不断扩张token、延迟随历史线性增长固定扩展 hop,转当前观测或人工 review
删除未传播原记录不可见但 state 仍返回以 evidence 反向索引重算投影,执行撤销演练
记忆污染来源不明的 instruction 进入 workflow默认将外部文本视为 observation,禁止直接生成可执行规则;对高影响投影实行签名/审批门槛

最后一项与持久记忆安全直接相关。SMSR 等研究提出用来源证明和检索约束处理多会话记忆投毒;我不主张把任何单篇方案当成万能防线,但“来源、作用域、过期、可撤销”应当是最先落地的通用控制面。

局限分析

首先,结构化投影增加了写入成本与数据建模工作;小型个人 Agent 可能只需要带来源的事件日志和朴素关键词检索。其次,本文的充分性规则会提高拒答率,这在探索性任务上未必理想。第三,LLM judge 本身会有偏差,关键 case 需要人工检查或确定性 oracle。最后,环境会变化:今天正确的 runbook 明天可能有害,所以“经验”绝不能绕过当前验证。

AMA-Bench、LongMemEval-V2、MESA 与 Router-Mem 的设置、模型、数据集和指标并不完全相同;不能把它们的分数放在同一排行榜后直接得出优劣结论。本文使用它们来提炼问题与接口,而不是做跨论文的性能比较。

发布前自审

  • 事实可靠性:论文、官方仓库和新近预印本均已给出直链;论文结果都明确为作者报告。
  • 来源完整性:核心判断来自 AMA-Bench、LongMemEval-V2;MESA 与 Router-Mem 仅作为待复现的设计信号。
  • 非复述:正文新增了事件账本、状态投影、证据充分条件、评测 manifest、fixture 和四周路线图。
  • 非薄内容:包含机制图、数据模型、策略配置、伪代码、指标、失败回滚和实现验收标准。
  • 边界明确:不承诺通用性能,不将摘要或相似度当作执行授权,不处理未授权外部系统。
  • 站内差异化:这篇文章关注环境轨迹与评测契约;它与此前讨论隐式关联路由的文章互补,但不重复其 decision-state 方案。