Appearance
长期记忆
长期记忆(Long-Term Memory, LTM)让 Agent 跨会话记住用户偏好、历史事实与项目背景。AgentScope 2.x 把长期记忆实现为中间件而非独立模块——三套后端(AgenticMemory / Mem0 / ReMe)都以 Middleware 形式挂载,通过 on_system_prompt / on_reply / on_reasoning 等 hook 介入 Agent 执行链,或通过 list_tools() 向 Agent 暴露 search_memory / add_memory 等工具。
版本基线:本文以 Python
agentscope 2.0.4(PyPI,2026-07-07)为准,要求 Python>=3.11。 导入路径为from agentscope.middleware import AgenticMemoryMiddleware, Mem0Middleware, ReMeMiddleware(源:src/agentscope/middleware/__init__.py)。三套后端分别在 2.0.3 / 2.0.4 引入:Mem0Middleware随 2.0.3 发布(PR #1775,2026-06-17),AgenticMemoryMiddleware与ReMeMiddleware随 2.0.4 发布(PR #1927 / #1972,2026-06-30 / 2026-07-02)。官方文档站的 Long-Term Memory 页面可能滞后于源码,尤其ReMeMiddleware的文档页尚未收录——以源码与 release notes 为准。
为什么需要长期记忆
Agent 的上下文(Context)是单会话内的消息历史,会话结束即消亡。但很多场景需要跨会话延续:
- 用户偏好:用户在上一轮说"我用 TypeScript",下一轮 Agent 应记住这一点。
- 历史事实:用户上周提过项目截止日期,今天 Agent 应仍能引用。
- 反馈记忆:用户纠正过"不要在回复末尾加总结",Agent 应在未来所有会话遵守。
这些信息的共同特征是:不属于当前会话的上下文,但需要在未来的会话中被检索并注入。长期记忆 中间件负责在每轮 reply 前检索相关记忆、注入上下文,并在 reply 后把新的可持久事实写回存储。
前端类比
如果你来自前端,长期记忆就是 IndexedDB 或 localStorage:
Context(上下文)像 React 组件的 state——组件卸载(会话结束)即销毁,只在本会话内有效。- 长期记忆像 IndexedDB——跨页面刷新(跨会话)持久化,下次打开时主动查询并加载。
MEMORY.md索引文件(AgenticMemory 后端)像 localStorage 的 key 列表——先读索引知道有哪些 记忆条目,再按需读取具体内容。search_memory工具像 IndexedDB 的索引查询——Agent 主动发起检索,按相关性取回记忆。
AgentScope 原生语义:长期记忆不是把整个会话历史存盘(那是 Context 压缩与卸载的职责),而是 由 LLM 或中间件从会话中抽取可持久事实(用户偏好、项目决策、外部引用指针),写入独立存储, 在后续会话中按需检索注入。它与 IndexedDB 的区别在于:写入什么、何时检索由 LLM 决定 (agentic 模式)或由中间件自动完成(static_control 模式),而非应用代码显式控制。长期记忆存的是 "关于用户的可复用事实",不是"会话日志"。
三后端对比
三套长期记忆中间件共享同一套 hook 机制,但存储形式、检索方式与引入版本不同:
| 中间件 | 后端 | 存储形式 | 引入版本 | 检索方式 | 写入方式 | 适用场景 |
|---|---|---|---|---|---|---|
AgenticMemoryMiddleware | Markdown 文件 | MEMORY.md 索引 + 话题文件 | 2.0.4 | LLM 选择相关文件 + 异步注入 | Agent 用 Write 工具主动写 | 无外部依赖、可读可审阅、本地优先 |
Mem0Middleware | mem0 | 向量数据库(Qdrant) | 2.0.3 | 向量相似度搜索 | 中间件自动写回 / Agent 调工具 | 已有 mem0 基础设施、需要语义检索 |
ReMeMiddleware | ReMe | 记忆卡片(文件库) | 2.0.4 | 关键词 / 向量搜索 | 中间件自动写回(所有模式) | 需要 ReMe 工具链、自动记录优先 |
关键差异:
- AgenticMemory:LLM 自主决定何时保存什么,Agent 通过
Read/Write/Edit工具直接操作 Markdown 文件。中间件不提供list_tools()(返回空列表),只负责注入记忆指令与异步检索。 - Mem0:支持三种控制模式(
static_control/agent_control/both)。static_control模式 下中间件全自动检索与写回,Agent 完全不感知;agent_control模式下暴露search_memory/add_memory工具由 Agent 按需调用。 - ReMe:写入在所有模式下都是自动的(reply 后通过
auto_memory任务写回),mode参数只 控制检索方式。agent_control/both模式暴露memory_search工具,但没有add_memory工具——Agent 不需要手动写记忆。
AgenticMemoryMiddleware
AgenticMemoryMiddleware 是文件式长期记忆:记忆以 Markdown 文件存储在工作目录下,MEMORY.md 作为 索引,每个话题是独立的 .md 文件。中间件通过 on_system_prompt 把记忆指令与 MEMORY.md 快照注入 系统提示词,通过 on_reply + on_reasoning 异步检索相关记忆文件并注入上下文。Agent 自己用 Write / Read / Edit 工具管理记忆文件——中间件不代劳写入。
构造参数(源:_longterm_memory/_agentic_memory/_middleware.py):
| 参数 | 类型 | 默认值 | 作用 |
|---|---|---|---|
workdir | str | 必填 | Agent 工作目录,记忆文件存放根目录 |
memory_dir | str | "Memory" | 记忆子目录名(MEMORY.md 与话题文件存于此) |
parameters | Parameters | None | None | 用户可调参数(见下表) |
backend | BackendBase | None | LocalBackend() | 存储后端,可换 Docker / E2B 实现远程隔离 |
Parameters 主要字段:
| 字段 | 类型 | 默认值 | 作用 |
|---|---|---|---|
memory_max_tokens | int | 4000 | MEMORY.md 注入系统提示词的最大 token 数 |
retrieval_async | bool | True | 是否在 reply 期间异步检索相关记忆文件 |
retrieval_model | ChatModelBase | None | None | 检索用的 LLM,None 时复用 Agent 自身模型 |
retrieval_max_tokens_per_md | int | 2000 | 单个记忆文件注入上下文的最大 token 数 |
retrieval_max_files | int | 200 | 检索时最多扫描的记忆文件数 |
Agent 自己写记忆:
AgenticMemoryMiddleware不覆盖list_tools(),因此await mw.list_tools()返回空列表。Agent 需要通过Toolkit(tools=[Read(), Write(), Edit()])获得文件操作工具,靠中间件 注入的系统提示词指令自主决定何时写记忆文件。这是"agentic"的真正含义——LLM 自主管理记忆。
python
import asyncio
import os
from agentscope.agent import Agent
from agentscope.credential import DashScopeCredential
from agentscope.message import UserMsg
from agentscope.middleware import AgenticMemoryMiddleware
from agentscope.model import DashScopeChatModel
from agentscope.tool import Edit, Read, Toolkit, Write
async def main() -> None:
mw = AgenticMemoryMiddleware(workdir="./my_agent_workspace")
agent = Agent(
name="memory_agent",
system_prompt="你是一个严谨的中文助手,会主动记住用户的偏好与事实。",
model=DashScopeChatModel(
credential=DashScopeCredential(
api_key=os.environ["DASHSCOPE_API_KEY"],
),
model="qwen-plus",
),
middlewares=[mw],
# Agent 用 Read/Write/Edit 工具自主管理 Markdown 记忆文件
# mw.list_tools() 返回空列表,不需要合并
toolkit=Toolkit(tools=[Read(), Write(), Edit()]),
)
# 第一轮:Agent 会用 Write 工具把用户偏好写入 Memory/ 目录
result = await agent.reply(
UserMsg(name="user", content="我喜欢用 TypeScript 写前端,后端用 Python。"),
)
print(result.get_text_content())
# 第二轮(可以是新会话):中间件会从 MEMORY.md 索引中检索相关记忆
result = await agent.reply(
UserMsg(name="user", content="帮我写一个 hello world。"),
)
print(result.get_text_content())
asyncio.run(main())运行后,./my_agent_workspace/Memory/ 目录下会出现 MEMORY.md 索引文件和若干话题 .md 文件 (如 user_role.md),每个文件带 frontmatter(name / description / type),可以被人类直接 阅读与编辑。下次启动 Agent 时,中间件会读取这些文件并注入上下文。
Mem0Middleware
Mem0Middleware 基于 mem0 后端,支持向量相似度检索。它有三种 控制模式与三种构造路径。
构造参数(源:_longterm_memory/_mem0/_middleware.py):
| 参数 | 类型 | 默认值 | 作用 |
|---|---|---|---|
user_id | str | 必填 | mem0 的用户命名空间,隔离不同用户的记忆 |
client | AsyncMemory | AsyncMemoryClient | None | None | 预构建的 mem0 客户端,给定后优先使用,忽略其余后端参数 |
chat_model | ChatModelBase | None | None | mem0 提取记忆用的 LLM(无 client 时必填) |
embedding_model | EmbeddingModelBase | None | None | mem0 向量化记忆用的嵌入模型(无 client 时必填) |
mem0_config | MemoryConfig | None | None | 自定义 mem0 配置(向量库 / 历史 DB 等) |
mode | Literal["static_control", "agent_control", "both"] | "both" | 控制模式(见下文) |
agent_id | str | None | None | 更细粒度的命名空间,按 Agent 隔离记忆 |
top_k | int | 5 | 每次检索返回的最大记忆数 |
threshold | float | None | None | 最小相似度阈值,None 由 mem0 决定 |
scope_search_by_agent | bool | True | True 时搜索同时按 user_id + agent_id 过滤 |
await_write | bool | True | True 时写回操作内联等待;False 时后台异步写 |
三种控制模式:
| 模式 | 自动检索 | 自动写回 | 暴露工具 | 适用 |
|---|---|---|---|---|
static_control | 是 | 是 | 无 | Agent 不感知记忆,中间件全自动 |
agent_control | 否 | 否 | search_memory + add_memory | Agent 自主决定何时检索与写入 |
both(默认) | 是 | 是 | search_memory + add_memory | 既有自动兜底,又有按需工具 |
python
import asyncio
import os
from agentscope.agent import Agent
from agentscope.credential import DashScopeCredential
from agentscope.embedding import DashScopeEmbeddingModel
from agentscope.message import UserMsg
from agentscope.middleware import Mem0Middleware
from agentscope.model import DashScopeChatModel
from agentscope.tool import Toolkit
async def main() -> None:
chat_model = DashScopeChatModel(
credential=DashScopeCredential(
api_key=os.environ["DASHSCOPE_API_KEY"],
),
model="qwen-plus",
)
embedding_model = DashScopeEmbeddingModel(
credential=DashScopeCredential(
api_key=os.environ["DASHSCOPE_API_KEY"],
),
model="text-embedding-v3",
)
mw = Mem0Middleware(
user_id="alice",
chat_model=chat_model,
embedding_model=embedding_model,
mode="both", # 既自动检索/写回,又暴露 search_memory / add_memory 工具
)
agent = Agent(
name="memory_agent",
system_prompt="你是一个严谨的中文助手。",
model=chat_model,
middlewares=[mw],
toolkit=Toolkit(tools=await mw.list_tools()),
)
result = await agent.reply(
UserMsg(name="user", content="记住我喜欢用 TypeScript 写前端。"),
)
print(result.get_text_content())
asyncio.run(main())mem0 依赖:
Mem0Middleware需要mem0包的异步客户端(AsyncMemory或AsyncMemoryClient)。安装方式:pip install "agentscope[mem0]"或pip install mem0。同步客户端Memory/MemoryClient不被支持,中间件会抛TypeError。embedding_model的向量维度必须与 mem0 向量库匹配——默认 Qdrant 期望 1536 维(DashScopetext-embedding-v3默认输出 1024 维, 需确认 mem0 配置中的向量维度一致)。
ReMeMiddleware
ReMeMiddleware 基于 ReMe 工具链——一个构建在 AgentScope 之上的文件式记忆工具包。与 Mem0 不同,ReMe 被内嵌进进程运行(无需单独启动服务),且写入在 所有模式下都是自动的:reply 结束后中间件自动把本轮对话增量通过 ReMe 的 auto_memory 任务写回。 mode 参数只控制检索方式。
构造参数(源:_longterm_memory/_reme/_middleware.py):
| 参数 | 类型 | 默认值 | 作用 |
|---|---|---|---|
workspace_dir | str | ".reme" | ReMe 工作区(记忆库)目录 |
config | str | "default" | ReMe 配置名或路径(默认用 ReMe 内置的 default.yaml) |
parameters | Parameters | None | None | 用户可调参数(见下表) |
Parameters 字段:
| 字段 | 类型 | 默认值 | 作用 |
|---|---|---|---|
chat_model | ChatModelBase | None | None | 注入 ReMe 的 LLM,None 时 ReMe 用自身配置的凭据 |
embedding_model | EmbeddingModelBase | None | None | 注入 ReMe 的嵌入模型,给定后启用向量检索;否则仅关键词检索 |
mode | Literal["static_control", "agent_control", "both"] | "both" | 检索模式(写入始终自动) |
top_k | int | 5 | 每次检索返回的最大记忆数 |
三种模式(只影响检索,写入始终自动):
| 模式 | 自动检索注入 | 暴露工具 | 自动写回 |
|---|---|---|---|
static_control | 是 | 无 | 是 |
agent_control | 否 | memory_search | 是 |
both(默认) | 是 | memory_search | 是 |
无
add_memory工具:ReMe 的设计哲学是"写入全自动"——reply 结束后中间件自动提取本轮对话 增量并写回,Agent 不需要(也不能)手动添加记忆。list_tools()在agent_control/both模式 下只返回memory_search工具。
python
import asyncio
import os
from agentscope.agent import Agent
from agentscope.credential import DashScopeCredential
from agentscope.message import UserMsg
from agentscope.middleware import ReMeMiddleware
from agentscope.model import DashScopeChatModel
from agentscope.tool import Toolkit
async def main() -> None:
chat_model = DashScopeChatModel(
credential=DashScopeCredential(
api_key=os.environ["DASHSCOPE_API_KEY"],
),
model="qwen-plus",
)
mw = ReMeMiddleware(
workspace_dir="./my_reme_workspace",
parameters=ReMeMiddleware.Parameters(
chat_model=chat_model,
mode="both", # 自动检索 + 暴露 memory_search 工具;写入始终自动
),
)
agent = Agent(
name="memory_agent",
system_prompt="你是一个严谨的中文助手。",
model=chat_model,
middlewares=[mw],
toolkit=Toolkit(tools=await mw.list_tools()),
)
result = await agent.reply(
UserMsg(name="user", content="记住我下周五要做项目演示。"),
)
print(result.get_text_content())
# 显式关闭内嵌的 ReMe 应用(AgentScope 不管理中间件生命周期)
await mw.close()
asyncio.run(main())ReMe 依赖:
ReMeMiddleware需要reme-ai包。安装方式:pip install "agentscope[reme]"或pip install reme-ai。未安装时构造不会报错,但首次使用 (_build_app)会抛ImportError。session_id从agent.state.session_id实时读取(不存储在 中间件上),因此同一个中间件实例可被多个 Agent 安全共享,各会话写入互不干扰。
文档滞后说明:
ReMeMiddleware在 2.0.4 才引入(PR #1972,2026-07-02),截至本文写作时 官方文档站的 Long-Term Memory 页面可能尚未收录 ReMe 相关内容。以上构造参数与模式说明均来自源码_longterm_memory/_reme/_middleware.py,请以源码与 release notes 为准。
与短期上下文、RAG 的区别
长期记忆容易与上下文压缩(Context)和检索增强生成(RAG)混淆,三者职责不同:
| 维度 | 长期记忆(LTM) | 上下文压缩(Context) | RAG |
|---|---|---|---|
| 生命周期 | 跨会话持久 | 单会话内 | 按需检索,不持久 |
| 存储内容 | 关于用户的可复用事实 | 当前会话的消息历史 | 外部知识库的文档片段 |
| 数据来源 | Agent 自身对话中提取 | Agent 自身对话历史 | 外部文档 / 数据库 |
| 注入方式 | 中间件 hook 或工具调用 | 上下文压缩 / 卸载 | 中间件 on_reasoning 注入 |
| 典型场景 | "记住我用 TypeScript" | "长任务上下文超限,压缩保留摘要" | "检索公司 Wiki 中的 API 文档" |
一句话区分:
- LTM 是自身记忆——Agent 记住"关于用户的事",跨会话复用(本页)。
- Context 是会话内压缩——同一会话中上下文太长时压缩保留摘要,会话结束即消亡 (见 上下文管理)。
- RAG 是外部知识——从外部知识库检索文档注入上下文,不是 Agent 自己的记忆 (见 RAG)。
与 1.x Memory 的关系
AgentScope 1.x 有一个独立的 agentscope.memory 模块,提供 InMemoryMemory / PermanentMemory 等 记忆实现,ReActAgent 通过 long_term_memory 参数直接挂载。2.x 把 agentscope.memory 模块完全 移除,长期记忆以中间件形式回归,通过 middlewares=[mw] 装配。
两者的关键区别:
| 维度 | 1.x agentscope.memory | 2.x LTM Middleware |
|---|---|---|
| 形态 | 独立模块,Agent 直接持有 | 中间件,包裹 Agent 执行链 |
| 装配 | ReActAgent(long_term_memory=...) | Agent(middlewares=[mw]) |
| 检索 | Agent 内部固定逻辑 | hook 介入 / 工具暴露,模式可选 |
| 后端 | 内置 SimpleMemory / PermanentMemory | AgenticMemory / Mem0 / ReMe 三后端 |
| 写入 | record_to_memory 固定三档回退 | 由 LLM 自主 / 中间件自动 / Agent 工具 |
⚠️ 1.x 仅作历史对照:
from agentscope.memory import ...在 2.x 已不可用,会抛ImportError。1.x 的ReActAgent.long_term_memory参数也已移除。迁移到 2.x 时,把原long_term_memory替换为对应的中间件装配,详见 1.x 到 2.x 迁移。
常见错误
- 误用
agentscope.memory:1.x 的agentscope.memory模块在 2.x 已移除。导入from agentscope.memory import ...会抛ImportError。2.x 的长期记忆在agentscope.middleware下,导入from agentscope.middleware import AgenticMemoryMiddleware等。 - LTM 与 Context 混淆:长期记忆跨会话持久,存储"关于用户的可复用事实";Context 是单会话内的 消息压缩,会话结束即消亡。不要用 LTM 替代 Context 压缩,也不要指望 Context 能跨会话记住用户。
- AgenticMemory 忘了给文件工具:
AgenticMemoryMiddleware不提供list_tools()(返回空列表), Agent 需要通过Toolkit(tools=[Read(), Write(), Edit()])获得文件操作工具才能写记忆文件。如果只 传toolkit=Toolkit(tools=await mw.list_tools()),Agent 没有文件工具,无法保存任何记忆。 - Mem0 extra 未装:
Mem0Middleware需要mem0包的异步客户端。未安装时构造不会立即报错,但 首次使用会抛ImportError。安装pip install "agentscope[mem0]"。 - ReMe extra 未装:
ReMeMiddleware需要reme-ai包。未安装时首次使用会抛ImportError,提示pip install "agentscope[reme]"。 - Mem0 嵌入维度不匹配:
embedding_model的向量维度必须与 mem0 向量库配置一致。默认 Qdrant 期望 1536 维,如果嵌入模型输出不同维度(如 DashScopetext-embedding-v3默认 1024 维),需要在mem0_config中调整向量库维度或选择匹配的嵌入模型,否则写入或检索会报错。 - ReMe 期望手动写记忆:ReMe 的
list_tools()只返回memory_search,没有add_memory工具。 写入由中间件在 reply 后自动完成。如果代码中尝试调用add_memory,会找不到该工具。
先修
下一步
- Agent Skills - 把可复用能力封装为 Skill,与长期记忆互补