跳转至

第 11 步:记忆系统

这一节学什么

  • 短期状态和长期记忆的区别
  • 正常的 Agent memory 接法是什么
  • 本地轻量 memory 和语义 memory 的边界
  • 长期记忆最小写入、检索和跨会话读取链路

你会运行什么

示例代码:

  • examples/getting_started/step_11_memory.py

运行:

python examples/getting_started/step_11_memory.py

这一节先建立心智模型

这一步会先用一个完全本地可跑的例子,把长期记忆最小链路走通。

你应该先区分:

  • messages、done、final_answer、rounds 这类字段,属于一次运行内的工作状态
  • memory 属于跨运行、跨会话保留的信息

这个例子里最重要的点是:

  • 第一段 agent session 会把用户偏好写进长期记忆
  • 第二段会启动一个新的 agent session
  • 新 session 不再依赖上一轮 messages,而是通过 memory_search 取回刚才写入的内容

核心概念

Memory 不是 messages 的替代品。

更准确地说:

  • messages 负责当前对话和当前推理链路
  • memory 负责长期保留、后续检索和按需注入

这一层通常要回答三个问题:

  • 记录什么
  • 什么时候检索
  • 检索结果怎么回到 prompt 或状态里

在 Jianmu 里,getting started 这一节先走最简单也最正常的接法:

  • 长期记忆先暴露成 tool
  • ReAct agent 在需要时自己调用 memory_add / memory_search

这比“手写 actions 去调 tool”更接近正常使用方式。

这个示例做了什么

这个步骤故意不用外部向量库或外部记忆服务,而是走最小本地链路:

  • SimpleStore:本地 JSON 存储
  • MemoryService:带命名空间的长期记忆门面
  • create_memory_tools(...):把长期记忆暴露成 memory_add / memory_search
  • create_react_node(...):把 memory tools 接进一个普通 ReAct agent

也就是说,这一步重点不是“高级语义记忆”,而是先看清框架里的标准接法:

  1. 准备一个长期记忆 backend
  2. 用 MemoryService 给它加上 namespace
  3. 把 memory 包装成 tool
  4. 让 agent 在对话里自行决定什么时候写入、什么时候检索

为什么示例里要显式新建第二个 session

这是为了避免一个常见误解:

  • 如果你在同一个 agent session 里先说“记住 X”,下一轮再问“你记得什么”,模型很可能直接靠当前 messages 回答
  • 这时即使没有真正命中 long-term memory,表面上也像“记忆生效了”

所以这个步骤故意在写入后重新创建一个 fresh agent:

  • 旧的会话历史不再参与回答
  • 这时还能答出来,才说明确实走到了长期记忆链路

为什么这里用规范化文本

SimpleStore 是一个轻量本地 backend,不是语义检索系统。

它更适合这种写法:

  • communication style: concise technical explanations
  • preferred language: Python

也就是把长期信息存成短而明确的字段式文本。

这样做的目的不是追求“最好看的 memory 数据”,而是让你先看清:

  • memory 是怎么接进 agent 的
  • agent 是怎么通过 tool 写入和读取它的

如果你希望更自然的语义召回,比如:

  • 用户随便说一段话
  • 后面用另一种表达方式也能稳稳搜回来

那就不该继续强化 SimpleStore,而应该换成语义 memory backend。

SimpleStore 和语义 memory 的边界

可以先这样理解:

  • SimpleStore
  • 适合 getting started、本地调试、最小链路验证
  • 检索更接近关键词匹配
  • Mem0LongTermMemory
  • 适合需要语义提取、语义检索、长期偏好归纳的场景

如果你想看语义 memory 的完整示例,继续看:

  • examples/memory/mem0_long_term_demo.py

常见误区

这一节最容易踩的坑主要有三个:

  1. 把 messages 当成 memory

  2. 在同一个 session 里,模型可能直接靠当前对话历史回答

  3. 这时即使没有真正命中 long-term memory,看起来也像“它记住了”

所以验证长期记忆时,最好像这个示例一样新建一个 fresh session。

  1. 用 SimpleStore 期待语义召回

  2. SimpleStore 更接近轻量关键词检索

  3. 它不适合“用户换一种说法也能稳稳搜回来”的场景

如果你需要这种能力,应该换成 Mem0LongTermMemory 之类的语义 memory backend。

  1. 不做 namespace 隔离

  2. 如果 demo、测试、真实会话共用同一个 store 和 namespace

  3. 旧数据就可能串进新结果

这会让你很难判断当前这次 memory 行为到底是不是正确的。

运行后观察什么

  • 第一段 session 里,agent 会调用 memory_add
  • 第二段 fresh session 里,agent 会调用 memory_search
  • memory_search 会把命中的长期记忆格式化成文本结果
  • messages 里会出现 memory tool 的 observation
  • 同一个 MemoryService 既可以被代码直接调用,也可以被包装成 Agent 可用工具
  • 示例默认会生成新的 namespace,避免旧 demo 数据污染当前结果

你可以特别关注这几个现象:

  • 第二段回答前是否真的出现了 memory_search(...)
  • memory_search observation 里是否真的返回了你刚写入的字段式文本
  • Raw stored records 是否和当前 namespace 对应

这一步学完后应该会什么

你应该能回答:

  • 为什么 memory 不等于聊天历史
  • MemoryService 在主路径里扮演什么角色
  • 怎样把长期记忆以 tool 的形式接进 Agent
  • 为什么验证 long-term memory 时要区分“同 session 记得”与“跨 session 还能取回”
  • 什么时候应该从 SimpleStore 升级到语义 memory backend

下一步