Agent Memory:从上下文管理到长期记忆运行时

从上下文、会话状态和原始轨迹出发,系统拆解 Agent 的分层记忆、写入、加载、整理、多 Agent 共享、安全治理与评测。


完成一次回答并不难。让 Agent 在几天、几个月甚至跨多个任务后,仍然记得用户偏好、项目决定和过去的执行经验,问题就复杂得多。

假设用户在一次会话里提出:

我们的 Java 项目统一使用构造器注入,后续生成的代码都遵守这条约定。

只要这句话还在当前对话中,模型通常能够照做。换一个 session,或者旧消息因为上下文压缩被移走后,这条约定可能不再可见。将全部历史消息重新发送给模型可以暂时缓解问题,但随着会话增多,输入成本、噪声和隐私风险都会持续上升。

这类跨时间的信息管理由 Agent Memory 负责。它从交互中选择有长期价值的信息,将其保存、整理和索引,并在后续任务中按权限和相关性重新加载。

从运行时角度看,Memory 横跨当前上下文、会话状态、原始轨迹、长期知识和任务系统,并包含两条持续运行的数据链路:

写入链路:交互 → 抽取 → 校验 → 去重/更新 → 持久化 → 建索引

加载链路:请求 → 查询理解 → 权限过滤 → 检索 → 重排 → 上下文注入

这两条链路决定了 Agent 会记住什么、会忘掉什么,以及某段旧信息能否影响今天的行动。

本文以通用记忆架构为主线。learn-claude-code 和 AgentScope Java 用于说明两种实现尺度:前者展示一个可逐行阅读的最小闭环,后者展示记忆进入长运行、多会话和分布式 Agent Runtime 后需要补齐的工程边界。

本文源码基线:

  • AgentScope Java:c32de52228297bd46249f285318ffe3578561f73
  • learn-claude-code:0dcafa2ae053a1ddd6a72f265431104b08a5aa13

1. Agent Memory 的历史定位

1.1 对话历史解决了最早的连续性问题

早期对话系统通常把历史消息拼接到下一次模型请求中。对大模型应用而言,这种做法直接、有效:

system prompt
+ 第 1 轮 user / assistant
+ 第 2 轮 user / assistant
+ 当前 user message
→ 下一次模型调用

只要历史没有超出上下文窗口,模型就能引用前文。这里的 history 更接近短期工作记录。它没有判断哪些信息值得长期保存,也没有解决跨 session 共享、事实更新和按需检索。

随着 Agent 开始调用工具,历史消息中又出现了 Tool Call、Tool Result、代码、日志和外部页面。上下文增长速度明显快于普通聊天。一条命令输出就可能占用数万 token,完整保留所有历史逐渐变得不可行。

1.2 RAG 扩展了模型可访问的外部知识

Retrieval-Augmented Generation 把文档放入外部知识库,根据当前问题检索相关片段,再把结果交给模型。它解决了模型参数之外的知识访问问题,也形成了成熟的切分、索引、召回与重排体系。

Memory 系统会复用这些检索技术,但两者关注的数据来源不同。RAG 的语料通常由组织或应用预先提供,例如产品文档、合同和代码库;Memory 则持续从 Agent 与用户、工具和环境的交互中产生。它还要处理用户作用域、session、时间变化、事实纠正、删除请求和写入权限。

1.3 长上下文扩大了容量,没有消除选择问题

模型上下文窗口不断扩大后,应用可以一次传入更多历史。容量提升很有价值,但大量低相关信息仍会占用注意力和输入成本。旧工具结果、重复对话和已失效事实混在一起时,模型还可能引用错误版本。

Anthropic 在 context engineering 的工程实践中,将上下文视为有限的注意力预算,并建议对长任务使用 compaction、结构化笔记和子 Agent 隔离。Effective context engineering for AI agents

因此,长上下文与外部记忆通常配合使用:context 保留当前任务最有用的信息,外部存储保留可按需取回的历史与知识。

1.4 Agent 研究把记忆扩展为主动管理机制

近几年的代表性工作分别推动了几个方向:

  • Generative Agents 保存完整经历,根据相关性、近因性和重要性取回记忆,并通过 reflection 形成更高层认识。Generative Agents
  • Reflexion 将任务反馈转化为语言反思,写入 episodic memory,用于下一次尝试。Reflexion
  • MemGPT 参考操作系统的分层内存,在有限上下文与外部存储之间搬运信息,形成 virtual context。MemGPT
  • CoALA 从认知架构角度统一描述工作记忆、长期记忆、内部动作和外部动作。CoALA

这些工作让 Memory 从被动保存聊天记录,逐步演进为 Agent 可以读写、整理和反思的运行时能力。

阶段主要做法解决的问题仍然存在的限制
对话 history拼接历史消息当前会话连续性容量、成本、跨会话
RAG检索外部文档参数外知识访问交互记忆、时间与用户作用域
长上下文一次输入更多历史减少截断注意力稀释、无关信息
Agent Memory主动写入、整理、检索和遗忘长期连续性与经验复用一致性、安全、评测

2. 什么是 Agent Memory

可以把 Agent Memory 定义为:

Agent Runtime 对历史交互、环境观察、用户信息和执行经验进行选择性持久化、组织、检索与更新的能力。

这个定义包含几个限制。

这里的存储具有选择性。原始对话和工具轨迹可以完整归档,但长期记忆只保留后续任务仍可能使用的信息。

每条记忆还要落到明确的作用域。个人偏好通常属于 user,一次任务的进度属于 session 或 task,团队运行手册属于 project。作用域错误会导致数据串扰。

长期信息本身也会变化。偏好可能更新,项目事实可能失效,旧经验可能被新实践取代,因此存储层必须支持版本、有效时间、删除和重新整理。

外部网页、旧会话和子 Agent 产出的文本都可能进入记忆库,这些内容不会自动获得指令权限,也不能覆盖 system policy 或当前用户授权。

2.1 Memory 与相邻概念的边界

概念保存内容典型生命周期默认进入模型上下文吗
Context当前请求、近期消息、工具结果、召回资料一次模型调用
Session State消息、摘要、计划、权限、任务状态一个 session部分进入
Transcript完整消息与工具轨迹长期归档否,按需检索
Long-term Memory筛选后的偏好、事实、经验、规则跨 session选择性进入
RAG Knowledge预先提供的外部知识随知识库版本选择性进入
Task State状态、依赖、结果、完成条件一个任务按运行需要进入

Context 是模型此刻能看到的输入集合。Memory 是构造这份输入时可以使用的信息来源之一。

Session State 让同一个会话能够恢复。它除了对话,还可能包含 Plan Mode、todo、权限和工具状态;Long-term Memory 则负责跨会话的知识连续性。

Transcript 追求保真,Memory 追求复用价值。一次排障轨迹可以完整保存在 transcript 中,经过验证的根因和处理方法再晋升为长期记忆。至于 background job 的 RUNNINGCOMPLETEDFAILED,它们具有明确状态转换,应该进入任务仓库,而不是只写进自然语言摘要。

3. 为什么 Agent 需要记忆

3.1 跨会话连续性

个人助理需要记住用户的语言、格式和工作偏好;编码 Agent 需要记住仓库的构建命令、模块边界和验证方式;客服 Agent 需要知道用户之前的问题与处理结果。

这些信息如果每次都由用户重新提供,Agent 只能完成一次性问答,无法形成稳定的长期协作关系。

3.2 长任务的上下文管理

代码迁移、技术调研和生产排障可能跨越几十次模型调用。完整历史会持续膨胀,最终需要摘要、落盘或重新开一个 context window。

长任务需要两类持久信息:

  • 用于继续执行的状态,例如当前目标、已修改文件、剩余步骤和后台任务;
  • 用于未来复用的知识,例如仓库测试命令、失败原因和验证过的解决方案。

前者属于 session/task state,后者可以沉淀为长期记忆。

3.3 个性化

同一个问题对不同用户可能有不同答案。有人偏好简短结论,有人需要完整推导;有人使用 Java 17,有人受限于 Java 8;某个团队要求所有数据库变更经过人工确认。

个性化信息具有用户或组织作用域。记忆系统需要在检索前完成权限过滤,不能先跨租户搜索,再依赖模型自行忽略不该看到的内容。

3.4 经验复用

Agent 在工具环境中会产生大量执行轨迹。成功轨迹可以转化为案例,失败轨迹可以转化为反思,经过验证的反思还可以晋升为程序记忆或 Skill。

这类能力让 Agent 在不更新模型参数的情况下,利用过去的工作结果改进后续决策。

3.5 上下文成本

每次模型调用都重新发送全部记忆,会把长期存储成本转换为持续的 token 成本。更合理的方式是保留小型目录、profile 或摘要,根据当前请求逐步加载正文。

记忆系统追求的目标是用少量高相关内容支撑当前推理。

4. Agent Memory 的组成

一个完整实现通常包含五个运行组件和一套元数据。

                    ┌────────────────────┐
Conversation ──────►│   Memory Writer    │
Tool / Event        │ extract + validate │
                    └─────────┬──────────┘
                    ┌────────────────────┐
                    │    Memory Store    │
                    │ facts / episodes  │
                    └─────────┬──────────┘
                 ┌────────────┴────────────┐
                 ▼                         ▼
       ┌──────────────────┐      ┌──────────────────┐
       │   Consolidator   │      │    Retriever     │
       │ merge / version  │      │ search / rerank  │
       └────────┬─────────┘      └────────┬─────────┘
                │                         ▼
                └──────────────► Context Injector
                                         Model

4.1 Memory Writer

Writer 决定当前交互是否产生了值得保存的信息。它可能由用户显式触发,也可能在回合结束、任务完成或 compaction 前自动运行。

LLM 适合做语义抽取和初步分类,确定性程序负责 schema、权限、敏感字段、作用域和幂等校验。

4.2 Memory Store

Store 保存记忆记录和原始证据。后端可以是文件、关系数据库、KV、对象存储、向量库或知识图谱。复杂系统通常组合多个后端,不要求一个数据库承担所有职责。

4.3 Consolidator

Consolidator 处理重复、冲突、过期和容量限制。它把细粒度记录合并为稳定的长期视图,也负责保留版本和推进处理水位。

Consolidation 属于有损操作。生产实现需要快照、版本或可重放事件源,避免一次错误整理永久破坏记忆。

4.4 Retriever

Retriever 根据当前请求查找候选记忆。常见信号包括关键词、向量相似度、实体、时间范围、来源权威、任务状态和最近使用频率。

召回结果还需要去重与 rerank。低于阈值时返回空结果,是正常行为。

4.5 Context Injector

Injector 把最终选择的记忆放入模型上下文,并明确它们的来源、时间和优先级。它要控制 token 预算,避免记忆挤占当前请求、工具说明和必要的近期消息。

4.6 记忆元数据

一条生产级记忆通常需要以下字段:

{
  "id": "mem_01...",
  "namespace": ["tenant", "user", "project"],
  "kind": "semantic | episodic | procedural | prospective",
  "content": "用户在 Java 项目中偏好构造器注入",
  "source": {
    "session_id": "s-123",
    "message_ids": ["m-8", "m-9"],
    "actor": "user"
  },
  "valid_time": {"from": "2026-08-30", "to": null},
  "recorded_at": "2026-08-30T12:00:00Z",
  "confidence": 0.95,
  "authority": "user_explicit",
  "status": "active",
  "supersedes": null,
  "tags": ["java", "style"],
  "embedding_version": "v3"
}

source 保留证据来源,namespace 决定可见范围,valid_time 表示事实在现实世界中的有效期,recorded_at 表示系统何时记录它。supersedes 用于表达新版本对旧版本的替代关系。

5. 一条记忆如何运行

下面用用户偏好为例,走完整个生命周期。

5.1 产生

用户在会话中说:

我们的 Java 项目统一使用构造器注入,后续生成的代码都遵守这条约定。

这句话首先进入 conversation。此时它只是当前上下文中的一条用户消息。

5.2 抽取

Writer 判断这是一条稳定、未来可复用的项目约定,生成候选记录:

{
  "kind": "semantic",
  "scope": "persistent",
  "content": "该 Java 项目统一使用构造器注入",
  "authority": "user_explicit"
}

同一个会话里如果还有一句这次不要新建文件,抽取器应将它标为 current task,而不是 persistent。

5.3 校验

确定性代码检查:

  • 当前调用是否有权写入该 namespace;
  • schema 是否完整;
  • 内容是否包含密钥或禁止保存的 PII;
  • scope 是否允许跨会话;
  • source message 是否真实存在;
  • 幂等键是否已经处理。

LLM 输出通过这些检查后,才能进入持久化阶段。

5.4 去重与冲突处理

系统查找同主题记录。如果旧记录已经表达相同事实,可以跳过或增加证据引用。如果旧记录写着该项目使用字段注入,则新旧内容发生冲突。

冲突处理不应简单地用最后写入覆盖一切。系统需要比较 authority、时间和作用域,并保留 supersedes 关系。

5.5 持久化与索引

通过校验的记录写入主存。随后更新全文、向量或图索引。主存写入和索引更新之间需要 outbox、重试或重建机制,防止主记录已经成功但检索索引仍然缺失。

5.6 整理

后台任务会把相近记录合并,清理重复描述,更新旧版本并控制长期视图大小。原始事件和历史版本继续用于审计与重建。

5.7 检索与注入

未来用户要求生成一个 Spring Service。Retriever 根据 Java、依赖注入和当前 project namespace 找到这条记忆,经权限过滤和重排后,把它作为项目背景注入 context。

模型最终看到的内容可以是:

<memory_context>
Source: project/user memory
Recorded: 2026-08-30
- 该 Java 项目统一使用构造器注入。
</memory_context>

5.8 更新与遗忘

用户后来将约定改为框架生成类允许字段注入。系统新建记录并结束旧记录的有效期,同时保留历史。

用户要求删除记忆时,需要同时处理主存、索引、缓存和派生视图。Context eviction 只表示不再放进当前 prompt,不等于合规删除。

6. 如何对记忆分类

记忆可以从时间范围和内容用途两个维度分析。

6.1 按时间范围分类

层次生命周期典型内容常见实现
工作记忆一次模型调用当前请求、近期观察、工具结果context window
会话记忆一个 session消息、滚动摘要、计划、权限StateStore、checkpoint
跨会话记忆多个 session偏好、稳定事实、可复用经验文件、SQL、向量、图
团队记忆多 Agent、多用户、多项目规范、运行手册、验证过的经验共享知识库、artifact
参数记忆模型版本生命周期模型内化的知识与行为预训练、微调

MemGPT 的 virtual context 可以理解为在工作记忆与外部长期存储之间进行受控换入和换出。MemGPT

6.2 按内容用途分类

CoALA 与 LangGraph 的记忆文档使用了接近认知科学的分类。CoALALangGraph Memory Overview

类型保存内容Agent 示例常见表示
Semantic 语义记忆事实与概念用户偏好、项目约束profile、事实集合、知识图谱
Episodic 情景记忆经历与行动轨迹一次成功排障过程事件、案例、session log
Procedural 程序记忆做事规则编码规范、部署步骤prompt、Skill、policy、代码
Prospective 前瞻状态未来要完成的事deadline、回访、目标task、goal、scheduler

语义记忆回答已知什么,情景记忆回答以前发生过什么,程序记忆回答应该怎样做。

一次成功轨迹不能直接晋升为程序记忆。它可能只是偶然成功。更稳妥的流程是:

episode
  → reflection candidate
  → test / evaluator / human review
  → validated procedure
  → Skill or policy

Reflexion 展示了如何用语言反思改进下一次尝试;工程系统还需要为反思增加验证和版本管理。Reflexion

前瞻信息有明确的触发时间和完成状态,更适合 task 或 scheduler。把周五复查迁移结果只存成一条自然语言事实,系统不会因此在周五自动执行。

7. Agent Memory 的分层存储设计

短期、中期和长期记忆在读取频率、数据形态和一致性要求上差异很大。生产系统通常把它们放入不同的数据平面。

7.1 短期记忆存什么

短期记忆是当前模型调用直接使用的工作集,通常包括:

  • 当前用户请求;
  • 最近若干轮消息;
  • 模型尚未消费的 Tool Result;
  • 当前目标和必要计划;
  • 本轮检索出的少量外部知识与长期记忆;
  • system policy 与可用工具说明。

它位于 context window 中,读取不需要额外 Tool Call。代价是每次推理都会重复发送,并受到模型上下文上限约束。

短期层追求高相关和立即可用。大型日志、完整 transcript、全部用户历史都不适合常驻。

7.2 中期记忆存什么

中期记忆覆盖一个 session 或长任务,负责中断恢复和跨多次模型调用的连续性。典型内容包括:

  • 完整或增量消息历史;
  • compaction summary;
  • 当前计划与 active goal;
  • todo 和任务依赖;
  • 权限规则与已激活工具;
  • 后台任务状态和最终结果;
  • checkpoint、interrupt 和恢复元数据;
  • 指向大型 Tool Result 与 artifact 的路径。

这类数据适合进入 StateStore、关系数据库、KV 或 append-only session log。

Session State 与自然语言长期记忆应保持分离。RUNNING 任务需要原子状态更新,权限规则需要确定性判断,不能依赖模型从 MEMORY.md 中自行推断。

7.3 长期记忆存什么

长期层保存跨 session 仍然有用的信息:

  • 用户明确表达的稳定偏好;
  • 项目架构、环境和业务约束;
  • 经过验证的执行经验;
  • 历史事件与重要决定;
  • 可复用的 procedure、example 和 Skill;
  • 记忆来源、有效时间、版本和删除状态。

长期层读取频率低于 context,生命周期更长。它需要支持搜索、更新、归档和用户删除。

7.4 原始轨迹放在哪里

原始 transcript 与长期记忆属于不同数据产品。Transcript 应尽量保留原始 user、assistant、Tool Call 和 Tool Result,用于:

  • 审计一次操作是怎样发生的;
  • 从摘要中找回被省略的细节;
  • 重新运行记忆抽取与索引构建;
  • 训练或评估 retrieval、reflection 和 Skill;
  • 调查记忆投毒与错误写入。

原始轨迹通常写入 JSONL、事件流或对象存储,不默认进入 prompt。

7.5 存储映射

数据层典型内容推荐存储加载方式主要要求
Context当前请求、近期消息、检索结果模型请求内存每次调用直接输入token 预算、顺序正确
Session State摘要、计划、权限、任务Redis、MySQL、JSON state按 user/session 恢复一致性、并发隔离
Transcript原始消息和工具轨迹JSONL、对象存储、日志系统session search、审计保真、保留策略
Curated Memory偏好、事实、经验Markdown、SQL、文档库常驻摘要或按需读取版本、来源、更新
Retrieval Indextext/vector/graph 索引搜索引擎、向量库、图数据库query、filter、rerank新鲜度、可重建
Task Repositorytask、result、checkpointSQL、KV、任务系统Runtime 自动恢复状态机、幂等

7.6 后端怎样选择

表示方式优点局限适合场景
Markdown/JSON 文件透明、可 diff、Agent 易读取并发和规模有限coding agent、个人助理
KV/SQL结构明确、过滤和事务能力好语义召回需额外索引profile、状态、事实表
全文检索精确词与可解释性好同义表达召回较弱代码、日志、实体名
向量库模糊语义召回较强时间、否定和版本关系较弱大量非结构化经历
知识图谱关系与多跳查询强抽取和维护成本高人物关系、项目依赖
混合存储能综合多种信号系统复杂度较高生产级长期助手

向量库是 retrieval index 的一种实现。主记录仍然需要明确的 namespace、版本、来源和删除状态。

Mem0 在 LoCoMo 上比较了基础记忆和图记忆,并报告图结构对复杂关系有增益,相比全上下文方案还能减少延迟和 token 成本。这些结果来自作者实验,落地前需要使用业务数据复验。Mem0

A-MEM 借鉴 Zettelkasten,为新记忆生成上下文描述、关键词与标签,并与旧记录建立连接;新信息也可能触发历史记录更新。A-MEM

8. Agent Memory 的写入与加载机制

记忆系统的技术实现可以从一次 Runtime 调用展开。

Request arrives
  → restore session state
  → assemble base context
  → retrieve relevant long-term memory
  → model reasoning / tool loop
  → persist session state and transcript
  → extract memory candidates
  → consolidate and maintain indexes

8.1 写入发生在什么时候

生产系统常见四个写入入口。

用户显式写入

用户明确说请记住时,可以在请求热路径调用 memory_save。系统应即时校验作用域和敏感信息,并返回保存结果。

这种写法新鲜度最高,也最容易让用户理解。但它会增加当前请求延迟。

回合结束抽取

Agent 返回最终答案后,从本轮 conversation 中抽取持久候选。抽取可以异步执行,避免阻塞主响应。

适合写入隐含偏好、稳定项目事实和本轮新获得的经验。系统需要避免重复抽取同一消息窗口。

Compaction 前 flush

Context compaction 会用摘要替换旧消息。摘要面向当前任务,不一定保留所有跨会话事实。因此在压缩前,可以先从即将被移出的前缀中 flush 长期记忆。

后台 consolidation

后台任务周期性合并新记录与长期视图,处理重复、冲突、过期和容量限制。它还可以归档旧 daily log、清理 session transcript,并重建索引。

LangGraph 将长期记忆写入分为 hot path 与 background 两类。二者在延迟、新鲜度和故障处理上各有取舍。LangGraph Memory Overview

8.2 写入流水线

Conversation / Event
  → Candidate Extraction
  → Scope Classification
  → Schema & Security Validation
  → Deduplication / Conflict Detection
  → Versioning
  → Durable Write
  → Index Update

Candidate Extraction 可以使用 LLM,从自然语言中提取自包含事实。输出只是一组候选。

Scope Classification 判断内容属于 current turn、session、user、project 还是 global。当前任务临时限制不进入跨会话记忆。

Schema & Security Validation 由程序完成,包括字段完整性、namespace 权限、PII 规则、来源存在性和内容长度。

Deduplication 可以使用精确键、文本归一化、embedding 和实体匹配。Conflict Detection 识别同一 subject 与 predicate 下相互矛盾的 value。

Versioning 为记录分配版本、valid time 和 supersedes 关系。

Durable Write 先写权威主存。Index Update 通过 outbox 或异步事件更新全文、向量和图索引。索引应可以从主存重建。

8.3 一个可落地的写入策略

下面的伪代码展示 LLM 与确定性规则的分工:

def write_memories(messages, runtime_context):
    candidates = extractor.extract(messages)

    accepted = []
    for candidate in candidates:
        if candidate.scope not in {"user", "project", "global"}:
            continue
        if not schema_validator.valid(candidate):
            continue
        if not acl.can_write(runtime_context, candidate.namespace):
            continue
        if pii_policy.prohibited(candidate.content):
            continue

        existing = store.find_subject(candidate.namespace, candidate.subject)
        decision = conflict_resolver.resolve(existing, candidate)
        if decision.action == "skip":
            continue

        record = versioner.apply(candidate, decision)
        store.put(record)
        outbox.publish("memory.updated", record.id)
        accepted.append(record)

    return accepted

这段流程允许模型做语义判断,但不允许模型自行决定越权写入、删除或提高记忆 authority。

8.4 会话状态怎样加载

新请求到达后,Runtime 先根据 (userId, sessionId) 加载 Session State。典型顺序如下:

resolve user + tenant + session
  → acquire per-session gate
  → load AgentState / checkpoint
  → restore messages + summary
  → restore plan + task + permission state
  → attach state to RuntimeContext

同一个 session 的并发请求需要串行化或使用乐观锁,防止两个调用基于相同旧状态分别写回,造成消息和任务状态丢失。

原始 transcript 不默认整份加载。Runtime 可以根据 session ID 查询最近消息,或在模型主动调用 session_search 时读取相关片段。

8.5 长期记忆怎样加载

长期记忆的读取通常分成六步:

Current Request
  → Query / Entity / Time Extraction
  → Namespace & Permission Filter
  → Keyword / Vector / Graph Retrieval
  → Deduplication & Rerank
  → Token-budget Packing
  → Context Injection

Query Extraction 从当前请求和最近几轮对话中提取查询词、实体与时间范围。类似那次故障是怎么解决的查询,需要同时识别故障实体和历史时间。

Namespace Filter 在检索前限制 tenant、user、project 和 agent 范围。这是数据访问边界。

Retrieval 可以并行执行关键词、BM25、向量和图查询。最近 session、活跃 task 和用户 profile 也可以作为独立召回源。

Rerank 综合多个信号:

rank_score =
  α × semantic_relevance
+ β × keyword_relevance
+ γ × recency_decay
+ δ × importance
+ ε × source_authority
+ ζ × task_state_match
- η × contradiction_penalty
- θ × staleness_penalty

Token-budget Packing 根据输入预算选择最终记录。它应优先保留当前请求、system policy、未消费 Tool Result,再为长期记忆分配剩余预算。

Context Injection 为每条记忆添加来源和时间,并声明这些内容是 reference data。当前用户请求与系统策略具有更高优先级。

8.6 常驻加载与按需加载

规模较小的 user profile 或 curated summary 可以常驻 system prompt,使常用偏好无需每轮调用检索工具。

详细事实、daily log 和 session transcript 更适合按需读取。常见方式包括:

  • Runtime 在模型调用前自动检索并注入;
  • 模型调用 memory_search 搜索候选;
  • 模型使用 memory_get 读取特定文件或行范围;
  • 模型使用 session_search 回查完整轨迹。

自动检索延迟更稳定,模型主动检索更灵活。复杂系统通常同时提供两种方式。

8.7 冲突记忆怎样进入上下文

系统发现同一用户的两条偏好互相冲突时,可以:

  • 根据 valid time 选择当前有效版本;
  • 根据 source authority 选择用户显式陈述;
  • 将冲突记录一起交给模型并标注不确定性;
  • 对高风险场景询问用户;
  • 低于可信阈值时不注入。

检索阶段需要支持 abstention。错误记忆常常比没有记忆更危险。

8.8 learn-claude-code:最小写入与加载闭环

s09_memory/code.py 使用 .memory/MEMORY.md 作为目录,每条记忆保存在独立 Markdown 文件中。类型包括 userfeedbackprojectreference

should_store_memory() 对候选执行持久性检查:

  • scope == persistent
  • 类型属于允许集合;
  • name、description 和 body 完整;
  • 不包含当前会话、当前任务、暂时等临时语义;
  • 与已有名称、描述或正文不重复。

对应源码为 s09_memory/code.py:108-139

加载采用目录选择与正文读取两步。select_relevant_memories() 读取最近 3 条用户消息,让轻量模型从目录中最多选择 5 条记录;调用失败时退化为关键词匹配。load_memories() 再读取正文,总召回量限制为 20,000 字符。对应源码为 s09_memory/code.py:253-329

当 Agent 停止工具调用后,extract_memories() 从最近消息抽取候选。记忆达到 10 条后,consolidate_memories() 合并重复和过期信息,最多保留 30 条。替换失败时,代码使用快照恢复旧文件。对应源码为 s09_memory/code.py:386-537, 720-747

这套实现没有分布式锁、版本、时间有效性和租户隔离,但完整展示了 select、load、extract、filter 和 consolidate。

8.9 AgentScope Java:生产 Runtime 中的记忆

AgentScope Java 2.0 将会话状态和工作区长期文件放在两个数据平面:

AgentStateStore
└── (userId, sessionId)
    ├── conversation context
    ├── compaction summary
    ├── permissions
    ├── plan state
    ├── todo/task context
    └── tool context

Workspace / distributed filesystem
├── MEMORY.md                    # 整理后的长期记忆
├── memory/YYYY-MM-DD.md         # 追加式每日账本
├── agents/.../sessions/*.jsonl  # 原始会话轨迹
├── agents/.../tasks/*.json      # 子任务状态/结果
└── large_tool_results/...       # 被卸载的大输出

AgentState(userId, sessionId) 加载和保存。相同会话的并发调用通过 per-session gate 串行,不同会话可以并行。完整生命周期见 docs/v2/en/docs/building-blocks/context.md:33-78, 150-165, 232-268

长期记忆采用两层结构。MemoryFlushManager 将新事实追加到 memory/YYYY-MM-DD.mdMemoryConsolidator 读取水位线之后发生变化的 daily files,与当前 MEMORY.md 合并、去重、更新和裁剪,成功后推进 watermark。

默认情况下,MemoryFlushMiddleware 会在每次 call() 结束后触发 flush,也可以配置为 NEVER 或按时间间隔 THROTTLED。Compaction 前和上下文溢出恢复时仍有各自的 flush 入口。Per-call flush 与 transcript offload 在响应流结束后异步运行,不阻塞主调用返回。

源码入口:

  • MemoryFlushManager.java:40-89:两层职责和默认抽取规则;
  • MemoryFlushManager.java:109-184:读取已有 memory 以避免重复;
  • MemoryFlushManager.java:218-235:向 daily ledger 追加;
  • MemoryConsolidator.java:39-98:增量整理和输出限制;
  • MemoryConsolidator.java:149-219:写入成功后推进水位;
  • MemoryConsolidator.java:359-379:使用 CAS 更新分布式 watermark。

读取侧提供 memory_searchmemory_getmemory_savesession_search。当前 MemorySearchTool 实现的是大小写不敏感的关键词扫描,并未使用 embedding。对应源码为 MemorySearchTool.java:28-89MemoryGetTool.java:25-82MemorySaveTool.java:27-86SessionSearchTool.java:34-100

AgentScope core 中旧的 MemoryInMemoryMemoryLongTermMemory 在 2.0 已标记废弃。新代码的会话上下文位于 AgentState.getContext(),跨会话记忆由 Harness 工作区管线或应用层实现。版本边界见 Memory.java:22-37LongTermMemory.java:64-70context.md:208-210

9. 工程实践中的边界与风险

9.1 Compaction 与长期记忆

Compaction 处理 context depth:旧对话前缀被摘要,最近尾部保持原文。Tool Result eviction 处理 context width:单条大结果写入文件,context 中只保留预览和路径。

learn-claude-code s08 把超过 30,000 字符的 Tool Result 写入 .task_outputs/tool-results/。已经被模型消费的旧结果可以缩成文件指针,模型尚未读取的新结果受到保护。相关源码为 s08_context_compact/code.py:288-379, 412-456

AgentScope 默认 eviction 阈值是 80,000 字符,保留首尾各约 2,000 字符,完整结果写入 large_tool_results/execute 没有排除在 eviction 外,因为 shell 输出可能很大。源码见 ToolResultEvictionConfig.java:20-74

Compaction summary 服务于当前任务,长期 memory 服务于跨会话复用。压缩前 flush 可以把即将离开 context 的长期事实先写入 memory。

9.2 工具调用边界不能被切断

Assistant Tool Call 与后续 Tool Result 通过 ID 配对。Compaction 如果从两者中间切开,模型 API 可能拒绝请求,模型也可能看到孤立结果。

安全切点需要识别连续 Tool Result,并向前找到发出对应 Tool Call 的 assistant message。AgentScope 的 ConversationCompactor.findSafeCutoffPoint() 采用了这类处理。

9.3 任务状态独立于自然语言记忆

Plan、todo、permission、active goal 和 background task 具有各自状态机。它们应独立持久化,并在 session 恢复时加载。

AgentScope 的 compaction 只修改 conversation list,不处理 Plan Mode、todo、权限和后台子任务。后台结果会在下一次 reasoning 前通过 system reminder 推回父 Agent,任务记录本身保存在独立 repository。

9.4 并发写入与整理

并发问题主要出现在三个位置:

  • 同一 session 的两个请求同时修改 AgentState;
  • 多个 Writer 重复处理相同消息窗口;
  • 多个 Consolidator 同时重写 curated memory。

常见控制方式包括 per-session gate、idempotency key、乐观版本、CAS、watermark、分布式锁和 append-only event log。

Consolidation 只有在新视图成功持久化后才能推进 watermark。失败时保留旧视图和待处理事件,下一次继续重试。

9.5 多 Agent 记忆作用域

范围适合保存的内容写入策略
Agent Private局部尝试、专家工作记录子 Agent 自主管理
Parent Session当前计划、依赖、子任务结果Runtime 管理
User用户偏好、跨会话事实用户或受控 Writer
Project / Team架构决定、运行手册、验证经验Curator 或审核晋升
Global通用策略与公共 Skill高门槛发布流程

子 Agent 通常拥有独立 context,只把结论、证据和 artifact 返回父 Agent。共享范围越大,错误记忆的影响面越大,写入门槛也应提高。

AgentScope 的 ISOLATED workspace 为子 Agent 提供独立空间;SHARED 模式直接使用父 workspace。persistSession(true) 才会根据 (parentSessionId, agentId, label) 复用子 Agent 历史。相关说明见 docs/v2/en/docs/harness/subagent.md:99-105, 192-205

团队级长期记忆可以采用晋升流程:

Subagent observation
  → candidate artifact
  → evidence verification
  → parent / curator review
  → project memory

9.6 Memory Poisoning

长期记忆会让一次恶意输入跨 session 存活。网页或工具结果中的注入指令可能诱导 Writer 保存伪造事实,未来又被 Retriever 召回。

2026 年的预印本研究了 sleeper memory poisoning 和来源洗白:外部内容经过总结、可信工具回显或伪造佐证后,可能获得更高的表面可信度。这些结论仍需持续复核,但攻击链可以直接用于系统测试。Hidden in MemorySecuring LLM-Agent Long-Term Memory Against Poisoning

基础防线包括:

  • 在写入时绑定不可丢失的来源;
  • 用户陈述、可信工具和网页内容使用不同 authority;
  • 召回文本明确标记为 reference data;
  • 记忆内容不能自动获得 Tool 权限;
  • 授权过滤采用 fail closed;
  • 高风险动作重新检查当前授权;
  • 记录 write、retrieve、inject 和 act 的完整链路。

9.7 隐私与删除

记忆系统保存的是长期用户数据,需要支持查询、导出、更正和删除。删除流程应覆盖:

authoritative store
  → text index
  → vector index
  → graph edges
  → cache
  → derived profile / summary

备份和审计数据的处理取决于组织政策和法规要求。系统需要明确 retention、加密、访问日志和管理员权限。

9.8 如何评测

只评最终回答,无法判断错误发生在写入还是检索。评测可以分四层:

问题指标示例
写入该保存的是否写入,不该保存的是否挡住precision、recall、PII leakage
整理更新、冲突、过期是否正确update accuracy、stale rate、consolidation loss
检索相关记忆能否在预算内取回Recall@k、MRR、nDCG、abstention F1
下游记忆是否改善任务answer accuracy、task success、latency、token cost

LoCoMo 的对话最长约 35 个 session,平均 300 turns,评测问答、事件总结和多模态长对话。LoCoMo

LongMemEval 包含 500 个问题,覆盖信息抽取、多 session 推理、时间推理、知识更新和 abstention。LongMemEval

业务评测还应加入事实纠正、删除请求、跨租户访问、工具轨迹、后台任务恢复和 memory poisoning。基线至少包括无记忆、全量历史、滚动摘要、关键词、向量与混合检索。

10. 总结

Agent Memory 是 Runtime 中的信息生命周期管理能力。它持续回答六个工程问题:

  1. 哪些信息值得保存;
  2. 信息属于 turn、session、user、project 还是 global;
  3. 何时同步写入,何时异步抽取或 consolidation;
  4. 当前请求应该加载哪些记录;
  5. 新旧事实冲突时如何更新、降权或失效;
  6. 哪个用户或 Agent 有权读取和修改。

短期记忆位于 context,负责当前推理。中期记忆位于 StateStore、checkpoint 和 session log,负责会话与任务恢复。长期记忆位于 curated store 与检索索引,负责跨 session 的事实、偏好和经验复用。

写入链路需要 LLM 与确定性程序配合:模型理解语义,程序控制 scope、schema、权限、PII、幂等与版本。加载链路先做 namespace 过滤,再执行多路召回、重排和 token packing,最终以带来源的参考数据进入 context。

learn-claude-code 展示了这套机制的最小闭环:文件存储、目录选择、持久性过滤、抽取和 consolidation。AgentScope Java 则补上了 AgentState、双层长期记忆、compaction、session transcript、并发隔离、分布式 watermark 和 Agent-controlled memory tools。

当 Agent 开始长期运行并参与真实业务后,Memory 的质量还取决于来源、时间、权限、状态恢复、冲突处理、删除能力和安全审计。它们共同决定一条旧信息能否被可靠地用于今天的行动。

参考资料

  1. MemGPT: Towards LLMs as Operating Systems
  2. Generative Agents: Interactive Simulacra of Human Behavior
  3. Reflexion: Language Agents with Verbal Reinforcement Learning
  4. Cognitive Architectures for Language Agents (CoALA)
  5. A Survey on the Memory Mechanism of Large Language Model based Agents
  6. Evaluating Very Long-Term Conversational Memory of LLM Agents (LoCoMo)
  7. LongMemEval: Benchmarking Chat Assistants on Long-Term Interactive Memory
  8. Mem0: Building Production-Ready AI Agents with Scalable Long-Term Memory
  9. A-MEM: Agentic Memory for LLM Agents
  10. LangGraph Memory Overview
  11. LangGraph Add Memory
  12. Anthropic: Effective context engineering for AI agents
  13. AgentScope Java GitHub
  14. AgentScope Java Workspace and Memory
  15. Hidden in Memory: Sleeper Memory Poisoning in LLM Agents
  16. Securing LLM-Agent Long-Term Memory Against Poisoning

评论