ARTICLE · 992340
《AI Agent 核心技术解析》|CHAPTER 05 Memory
INSHOCKING · ENGINEERING NOTE
IN / FEATURE SIGNAL
Agent Memory:从上下文管理到长期记忆运行时
《AI Agent 核心技术解析》 CHAPTER 05
AI AGENT · MEMORY · CONTEXT ENGINEERING · AGENT RUNTIME
inShocking
让 agent完成一次回答并不难,调LLM 返回一次 output 就好了。
但是让 Agent 在几天、几个月甚至跨多个任务后,仍然记得用户偏好、项目决定和过去的执行经验,问题就会变得复杂起来。
假设用户在一次会话里提出:
IN / SOURCE SIGNAL
我们公司内部 Java 项目统一使用自研框架注解,不要使用 springboot 的。后续生成的代码都遵守这条约定。
只要这句话还在当前对话中,模型通常能够照做。换一个 session,或者旧消息因为上下文压缩被移走后,这条约定可能不再可见。将全部历史消息重新发送给模型可以暂时缓解问题,但随着会话增多,输入成本、噪声和隐私风险都会持续上升。
这类跨时间的信息管理由 Agent Memory 负责。它从用户对话交互中选择有长期价值的信息,将其保存、整理和索引,并在后续任务中按权限和相关性重新加载。
从运行时角度看,Memory 横跨当前上下文、会话状态、原始轨迹、长期知识和任务系统,并包含两条持续运行的数据链路:
写入链路:交互 → 抽取 → 校验 → 去重/更新 → 持久化 → 建索引加载链路:请求 → 查询理解 → 权限过滤 → 检索 → 重排 → 上下文注入这两条链路决定了 Agent 会记住什么、会忘掉什么,以及某段旧信息能否影响今天的行动。
本文以通用记忆架构为主线。learn-claude-code 和 AgentScope Java 用于说明两种实现尺度:前者展示一个可逐行阅读的最小闭环,后者展示记忆进入长运行、多会话和分布式 Agent Runtime 后需要补齐的工程边界。
本文源码:
AgentScope Java: https://java.agentscope.io/v1/zh/docs/task/memory.html
learn-claude-code: https://learn.shareai.run/zh/s09/
IN / SIGNAL 01
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 可以读写、整理和反思的运行时能力。
IN / SIGNAL 02
2. 什么是 Agent Memory
可以把 Agent Memory 定义为:
IN / SOURCE SIGNAL
Agent Runtime 对历史交互、环境观察、用户信息和执行经验进行选择性持久化、组织、检索与更新的能力。
这个定义包含几个限制。
这里的存储具有选择性。原始对话和工具轨迹可以完整归档,但长期记忆只保留后续任务仍可能使用的信息。
每条记忆还要落到明确的作用域。个人偏好通常属于 user,一次任务的进度属于 session 或 task,团队运行手册属于 project。作用域错误会导致数据串扰。
长期信息本身也会变化。偏好可能更新,项目事实可能失效,旧经验可能被新实践取代,因此存储层必须支持版本、有效时间、删除和重新整理。
外部网页、旧会话和子 Agent 产出的文本都可能进入记忆库,这些内容不会自动获得指令权限,也不能覆盖 system policy 或当前用户授权。
2.1 Memory 与相邻概念的边界
Context 是模型此刻能看到的输入集合。Memory 是构造这份输入时可以使用的信息来源之一。
Session State 让同一个会话能够恢复。它除了对话,还可能包含 Plan Mode、todo、权限和工具状态;Long-term Memory 则负责跨会话的知识连续性。
Transcript 追求保真,Memory 追求复用价值。一次排障轨迹可以完整保存在 transcript 中,经过验证的根因和处理方法再晋升为长期记忆。至于 background job 的 RUNNING、COMPLETED 和 FAILED,它们具有明确状态转换,应该进入任务仓库,而不是只写进自然语言摘要。
IN / SIGNAL 03
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 或摘要,根据当前请求逐步加载正文。
记忆系统追求的目标是用少量高相关内容支撑当前推理。
IN / SIGNAL 04
4. Agent Memory 的组成
一个完整实现通常包含五个运行组件和一套元数据。
┌────────────────────┐Conversation ──────►│ Memory Writer │Tool / Event │ extract + validate │ └─────────┬──────────┘ ▼ ┌────────────────────┐ │ Memory Store │ │ facts / episodes │ └─────────┬──────────┘ │ ┌────────────┴────────────┐ ▼ ▼ ┌──────────────────┐ ┌──────────────────┐ │ Consolidator │ │ Retriever │ │ merge / version │ │ search / rerank │ └────────┬─────────┘ └────────┬─────────┘ │ ▼ └──────────────► Context Injector │ ▼ Model4.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 用于表达新版本对旧版本的替代关系。
IN / SIGNAL 05
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 memoryRecorded: 2026-08-30- 该 Java 项目统一使用构造器注入。</memory_context>5.8 更新与遗忘
用户后来将约定改为框架生成类允许字段注入。系统新建记录并结束旧记录的有效期,同时保留历史。
用户要求删除记忆时,需要同时处理主存、索引、缓存和派生视图。Context eviction 只表示不再放进当前 prompt,不等于合规删除。
IN / SIGNAL 06
6. 如何对记忆分类
记忆可以从时间范围和内容用途两个维度分析。
6.1 按时间范围分类
MemGPT 的 virtual context 可以理解为在工作记忆与外部长期存储之间进行受控换入和换出。MemGPT
6.2 按内容用途分类
CoALA 与 LangGraph 的记忆文档使用了接近认知科学的分类。CoALA、LangGraph Memory Overview
语义记忆回答已知什么,情景记忆回答以前发生过什么,程序记忆回答应该怎样做。
一次成功轨迹不能直接晋升为程序记忆。它可能只是偶然成功。更稳妥的流程是:
episode → reflection candidate → test / evaluator / human review → validated procedure → Skill or policyReflexion 展示了如何用语言反思改进下一次尝试;工程系统还需要为反思增加验证和版本管理。Reflexion
前瞻信息有明确的触发时间和完成状态,更适合 task 或 scheduler。把周五复查迁移结果只存成一条自然语言事实,系统不会因此在周五自动执行。
IN / SIGNAL 07
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 存储映射
7.6 后端怎样选择
向量库是 retrieval index 的一种实现。主记录仍然需要明确的 namespace、版本、来源和删除状态。
Mem0 在 LoCoMo 上比较了基础记忆和图记忆,并报告图结构对复杂关系有增益,相比全上下文方案还能减少延迟和 token 成本。这些结果来自作者实验,落地前需要使用业务数据复验。Mem0
A-MEM 借鉴 Zettelkasten,为新记忆生成上下文描述、关键词与标签,并与旧记录建立连接;新信息也可能触发历史记录更新。A-MEM
IN / SIGNAL 08
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 indexes8.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解析用户+租户+会话→ 获取会话级门控→ 加载 AgentState / 检查点→ 恢复消息 + 摘要→ 恢复计划 + 任务 + 权限状态→ 将状态附加到 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当前请求→ 查询/实体/时间提取→ 命名空间与权限过滤器→ 关键词/向量/图谱检索→ 去重与重排序→ Token 预算打包→ 上下文注入
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_penaltyToken-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 文件中。类型包括 user、feedback、project 和 reference。
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 contextWorkspace / 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.md;MemoryConsolidator 读取水位线之后发生变化的 daily files,与当前 MEMORY.md 合并、去重、更新和裁剪,成功后推进 watermark。
默认情况下,MemoryFlushMiddleware 会在每次 call() 结束后触发 flush,也可以配置为 NEVER 或按时间间隔 THROTTLED。Compaction 前和上下文溢出恢复时仍有各自的 flush 入口。Per-call flush 与 transcript offload 在响应流结束后异步运行,不阻塞主调用返回。
读取侧提供 memory_search、memory_get、memory_save 和 session_search。当前 MemorySearchTool 实现的是大小写不敏感的关键词扫描,并未使用 embedding。
AgentScope core 中旧的 Memory、InMemoryMemory 和 LongTermMemory 在 2.0 已标记废弃。新代码的会话上下文位于 AgentState.getContext(),跨会话记忆由 Harness 工作区管线或应用层实现。版本边界见 Memory.java:22-37、LongTermMemory.java:64-70 和 context.md:208-210。
IN / SIGNAL 09
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 通常拥有独立 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 memory9.6 Memory Poisoning
长期记忆会让一次恶意输入跨 session 存活。网页或工具结果中的注入指令可能诱导 Writer 保存伪造事实,未来又被 Retriever 召回。
2026 年的预印本研究了 sleeper memory poisoning 和来源洗白:外部内容经过总结、可信工具回显或伪造佐证后,可能获得更高的表面可信度。这些结论仍需持续复核,但攻击链可以直接用于系统测试。Hidden in Memory、Securing 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 如何评测
只评最终回答,无法判断错误发生在写入还是检索。评测可以分四层:
LoCoMo 的对话最长约 35 个 session,平均 300 turns,评测问答、事件总结和多模态长对话。LoCoMo
LongMemEval 包含 500 个问题,覆盖信息抽取、多 session 推理、时间推理、知识更新和 abstention。LongMemEval
业务评测还应加入事实纠正、删除请求、跨租户访问、工具轨迹、后台任务恢复和 memory poisoning。基线至少包括无记忆、全量历史、滚动摘要、关键词、向量与混合检索。
IN / SIGNAL 10
10. 总结
Agent Memory 是 Runtime 中的信息生命周期管理能力。它持续回答六个工程问题:
哪些信息值得保存; 信息属于 turn、session、user、project 还是 global; 何时同步写入,何时异步抽取或 consolidation; 当前请求应该加载哪些记录; 新旧事实冲突时如何更新、降权或失效; 哪个用户或 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 的质量还取决于来源、时间、权限、状态恢复、冲突处理、删除能力和安全审计。它们共同决定一条旧信息能否被可靠地用于今天的行动。
IN / SIGNAL 11
参考资料
MemGPT: Towards LLMs as Operating Systems Generative Agents: Interactive Simulacra of Human Behavior Reflexion: Language Agents with Verbal Reinforcement Learning Cognitive Architectures for Language Agents (CoALA) A Survey on the Memory Mechanism of Large Language Model based Agents Evaluating Very Long-Term Conversational Memory of LLM Agents (LoCoMo) LongMemEval: Benchmarking Chat Assistants on Long-Term Interactive Memory Mem0: Building Production-Ready AI Agents with Scalable Long-Term Memory A-MEM: Agentic Memory for LLM Agents LangGraph Memory Overview LangGraph Add Memory Anthropic: Effective context engineering for AI agents AgentScope Java GitHub AgentScope Java Workspace and Memory Hidden in Memory: Sleeper Memory Poisoning in LLM Agents Securing LLM-Agent Long-Term Memory Against Poisoning
⚡inShocking
quiet interface / sudden insight
IN / END OF SIGNAL