第十七章 长期记忆系统从 LongTermMemory 到 workspace/MEMORY.md 中间件驱动1.x 的io.agentscope.core.memory.LongTermMemory及其子类LongTermMemoryBase/Mem0LongTermMemory等在 2.0.0-RC2 中被标记为Deprecated(forRemoval true)。原因硬编码知识库接口很难适配不同业务——什么是长期是按主题、按时间、还是按人2.0 把这件事下沉到workspace 文件中间件业务方自己决定写什么、何时写、写到哪。本章先给出 1.x 旧 API 的最小示例仅供维护老项目参考再讲 2.0 推荐的新做法。17.1 1.x LongTermMemory 旧 API 回顾import io.agentscope.core.memory.LongTermMemory; import io.agentscope.core.memory.LongTermMemoryBase; import io.agentscope.core.memory.Mem0LongTermMemory; import io.agentscope.core.ReActAgent; public class Chapter17_LegacyLongTerm { public static void main(String[] args) { LongTermMemory memory new Mem0LongTermMemory( https://api.mem0.ai, System.getenv(MEM0_API_KEY)); ReActAgent agent ReActAgent.builder() .name(assistant) .sysPrompt(你是一个长期记忆助理。) .model(model()) .longTermMemory(memory) // 1.x .build(); } }2.0 仍可编译ReActAgent.longTermMemory(...)在 RC2 中标记为Deprecated(forRemoval true)编译通过但会告警。17.2 2.0 推荐的两层记忆结构workspace下两份文件按不同时间粒度组织workspace/ ├── MEMORY.md # 长期稳定的事实笔记 └── memory/ ├── 2026-06-05.md # 当天事件流 ├── 2026-06-06.md └── 2026-06-07.md17.2.1MEMORY.md跨会话、跨天都会用到的稳定信息——# MEMORY.md ## 用户偏好 - 常驻城市杭州 - 时区UTC8 - 语言中文 ## 长期目标 - 2026 年内学完 Rust17.2.2memory/YYYY-MM-DD.md按天追加的事件流——# 2026-06-07 ## 14:32 - 用户问今天杭州天气回答22~28℃局部多云建议带伞 ## 15:01 - 用户问我常驻哪里agent 从 MEMORY.md 知道杭州回答一致17.3 两个核心中间件io.agentscope.core.middleware提供两个跟记忆直接相关的中间件中间件何时触发副作用CompactionMiddleware上下文 token 超过阈值早期消息压成摘要写进MEMORY.mdMemoryFlushMiddleware每轮 call 结束把该记的刷到当日memory/YYYY-MM-DD.md17.3.1 CompactionMiddlewareCompactionMiddleware由HarnessAgent在build()时根据MemoryConfig自动挂载业务方通过.memory()配置即可HarnessAgent agent HarnessAgent.builder() ... .memory(MemoryConfig.builder() .compactionEnabled(true) // 超过 token 阈值自动压缩 .build()) .build();CompactionMiddleware工作流程监听每轮 call 结束检查当前上下文 token 数超过阈值 → 把较旧的消息让 LLM 总结成几行摘要写进MEMORY.md的近期摘要小节旧消息从AgentState移除17.3.2 MemoryFlushMiddlewareimport io.agentscope.harness.agent.middleware.MemoryFlushMiddleware; HarnessAgent agent HarnessAgent.builder() ... .memory(MemoryConfig.builder().build()) // 默认开启每轮结束后冲刷记忆 .build();MemoryFlushMiddleware由 HarnessAgent 根据MemoryConfig自动挂载业务方无需手动构造。工作流程监听每轮 call 结束让 LLM 决定本轮是否有值得长期记的事事实、用户偏好变化等如果有 → 追加到当日memory/2026-06-07.md没有 → 跳过不写注意每日事件文件不保证一定生成。LLM 可能判断当前对话太短、没有新信息从而跳过写入。另外 MemoryFlush 是异步回调进程如果在 flush 完成前退出文件也来不及创建。实际使用中建议关注MEMORY.mdCompactionMiddleware 写入更稳定memory/*.md作为辅助查看。17.3.3 完整配置HarnessAgent agent HarnessAgent.builder() .name(assistant) .sysPrompt(...) .model(model()) .workspace(Path.of(./workspace)) .memory(MemoryConfig.builder().build()) // 默认开启 MemoryFlushMiddleware .build(); // CompactionMiddleware 按 CompactionConfig 挂载17.4 主 agent 自动读 / 写 MEMORY.md主 agent 在每轮推理时会自动读MEMORY.md顶部几行作为已知背景。如果你想agent 决定更新 MEMORY.md——给它ToolTool(name update_long_term_memory, description 更新长期记忆) // 注册为 agent 可调工具 public String updateMemory( ToolParam(name section) String section, // 比如 用户偏好 ToolParam(name content) String content) { // 比如 默认中文回答 Path memFile Path.of(./workspace/MEMORY.md); // 记忆文件固定路径 String existing memFile.toFile().exists() ? Files.readString(memFile) // 已有记忆 → 追加 : # MEMORY.md\n\n; // 新文件 → 创建 String updated existing \n## section \n- content \n; Files.writeString(memFile, updated); // 写回文件 return memory updated; }把工具通过Toolkit注册进 agentLLM 看到用户说以后请默认中文回答会自己调这个工具写进 MEMORY.md下次对话时立刻生效。17.5 完整可运行示例这个例子在演示什么一个 agent 同时运行三种记忆机制各管各的时间粒度CompactionMiddleware.compaction()配置上下文太长时自动压缩旧消息为摘要写进MEMORY.mdMemoryFlushMiddleware.memory().flushEnabled(true)配置每轮对话结束自动提取重要信息写进memory/YYYY-MM-DD.mdUpdateMemoryToolTool工具agent 在对话中主动决定这件事值得长期记住自己调工具写MEMORY.md三者互补Compaction 管上下文压缩被动、MemoryFlush 管日常记录自动、UpdateMemoryTool 管即时决策主动。public class Chapter17_MemoryStack { public static void main(String[] args) { DashScopeChatModel model DashScopeChatModel.builder() .apiKey(System.getenv(DASHSCOPE_API_KEY)) .modelName(qwen-plus) .build(); // 1. 手工更新记忆的工具agent 主动决策记住这个 Toolkit toolkit new Toolkit(); toolkit.registerTool(new UpdateMemoryTool()); // 2. agent三层记忆机制一起跑 HarnessAgent agent HarnessAgent.builder() .name(assistant) .sysPrompt(你是一个有长期记忆的助理。用户告诉你的事如果需要长期记住调用 update_long_term_memory 工具写下来。) .model(model) .workspace(Path.of(./workspace)) .toolkit(toolkit) .compaction(CompactionConfig.builder().build()) // 上下文太长时自动压缩 .memory(MemoryConfig.builder().build()) // 默认 flush每轮结束自动冲刷 .build(); // 3. 多轮对话同一个 session演示跨轮记忆 RuntimeContext ctx RuntimeContext.builder() .sessionId(user-9527-2026-06-07) .userId(9527) .build(); // Round 1agent 记住用户基本信息 agent.call(List.of(new UserMessage(user, 我叫小李住在杭州。)), ctx).block(); // Round 2agent 应该能从记忆里调出信息 agent.call(List.of(new UserMessage(user, 我叫什么住在哪)), ctx).block(); // Round 3agent 主动调 update_long_term_memory 写偏好 agent.call(List.of(new UserMessage(user, 以后请用中文回答。)), ctx).block(); } }跑完你会看到workspace/MEMORY.md— CompactionMiddleware 写的摘要 UpdateMemoryTool 写的用户偏好ToolParam缺了这里会是 nullworkspace/memory/—不一定生成。MemoryFlushMiddleware 的写入取决于 LLM 判断是否值得记以及异步回调是否执行完毕Round 2 时 agent 能从上下文记忆里知道小李 / 杭州Round 3 时 agent 主动调工具把中文回答偏好写进 MEMORY.md17.6 最小迁移清单1.x LongTermMemory → 2.0 文件记忆1.x 用法2.0 等价LongTermMemory.retrieve(query)业务方手写Tool调自家向量库或 subagent 用grep_filesLongTermMemory.record(messages)MemoryFlushMiddlewareupdate_long_term_memory工具LongTermMemoryBase子类业务方自己实现Toolagent.longTermMemory(memory)workspace/MEMORY.mdmemory/*.md 中间件17.7 本章小结1.xLongTermMemory在 2.0 中被标记为弃用未来会移除。2.0 用MEMORY.mdmemory/YYYY-MM-DD.md两层文件做长期记忆。CompactionMiddleware自动压缩 MemoryFlushMiddleware自动写入。业务方可以手写Tool让 agent 主动更新 MEMORY.md。