ReMe 深度解析:面向 AI 智能体的模块化记忆管理工具包设计与实现
摘要:ReMe 是由 AgentScope 团队开发的开源智能体记忆管理工具包,采用模块化设计将记忆管理视为智能体任务而非简单数据存取。其架构分为用户入口层、应用编排层、记忆代理层和基础设施层,核心创新在于记忆代理层由 LLM 驱动的智能流程实现。基础数据结构 MemoryNode 通过 when_to_use 字段实现检索意图与存储内容的解耦,类比图书馆索引卡机制,有效解决了传统向量检索中语义鸿
导读:在大语言模型驱动的 AI 智能体中,“记忆”(Memory)是决定智能体能否跨会话学习、长期适应用户需求的关键能力。ReMe(Remember Me, Refine Me)是由 AgentScope 团队开源的一款面向智能体的模块化记忆管理工具包,其核心理念是将记忆管理本身视为一项智能体任务,而非简单的数据存取操作。本文将从架构设计、记忆分类体系、核心数据模型、执行引擎及工程实现等多个维度,对 ReMe 的记忆体设计进行深度剖析。
一、设计背景与核心理念
在传统的 RAG(Retrieval-Augmented Generation)系统中,“记忆"通常等价于"向量检索”——将文本切块、生成嵌入向量、存入向量数据库。然而,对于一个真正具备持续学习能力的 AI 智能体而言,记忆问题远比简单的文本检索复杂得多。
ReMe 的设计者借鉴了认知科学中人类记忆的分层模型,将智能体的记忆体系形式化为:
Agent Memory = Long-Term Memory + Short-Term Memory
= (Personal + Procedural + Tool) Memory + Working Memory
其中:
- 长期记忆持久化存储在向量数据库中,跨会话保留——类似于人类的"显式记忆";
- **短期记忆(工作记忆)**管理当前会话上下文,通过压缩与卸载机制防止 token 溢出——类似于人类的"工作记忆容量"。
这种分层模型并非概念上的简单对齐,而是深入到代码层面的系统性设计,指导着整个工具包的架构组织。
二、整体架构概览
ReMe 的系统架构可以划分为四个清晰的层次:
┌──────────────────────────────────────────────────────────────┐
│ 用户入口层 │
│ ReMe / ReMeApp(Python API & CLI) │
├──────────────────────────────────────────────────────────────┤
│ 应用编排层 │
│ Application → ServiceContext → Flow 编排引擎 │
├──────────────────────────────────────────────────────────────┤
│ 记忆代理层 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ ReMeSummarizer│ │ ReMeRetriever│ │ 统一调度器 │ │
│ └──────┬───────┘ └──────┬───────┘ │ DelegateTask │ │
│ │ │ └──────────────┘ │
│ ┌──────┴──────────────────┴──────┐ │
│ │ PersonalSummarizer/Retriever │ │
│ │ ProceduralSummarizer/Retriever│ │
│ │ ToolSummarizer/Retriever │ │
│ └────────────────────────────────┘ │
├──────────────────────────────────────────────────────────────┤
│ 基础设施层 │
│ BaseVectorStore │ BaseEmbeddingModel │ BaseLLM │ FileStore │
│ (Local/ChromaDB/Elasticsearch) │
└──────────────────────────────────────────────────────────────┘
这一分层设计有几个值得关注的特点:
- 记忆代理层是整个系统的核心创新——记忆的提取与检索不是硬编码的规则管线(pipeline),而是由 LLM 驱动的 ReAct Agent 自主完成的智能流程;
- 基础设施层采用抽象接口模式(
BaseVectorStore、BaseLLM等),支持多种后端实现,保证了系统的可插拔性; - Flow 编排引擎通过操作符组合语法(
>>顺序执行、|并行执行),支持灵活的工作流定义。
三、核心数据模型:MemoryNode
MemoryNode 是 ReMe 整个记忆体系的基础数据结构,其字段设计反映了团队对"一条记忆应该包含什么信息"的深入思考:
class MemoryNode(BaseModel):
memory_id: str # 基于 content 的 SHA-256 哈希自动生成(前 16 位)
memory_type: MemoryType # 枚举:personal / procedural / tool / history 等
memory_target: str # 记忆所属目标(用户名、任务名、工具名)
when_to_use: str # 关键字段:描述"何时应使用此记忆"
content: str # 记忆的实际内容
message_time: str # 产生该记忆的消息时间戳
ref_memory_id: str # 关联的原始历史记忆 ID
time_created: str # 创建时间
time_modified: str # 最后修改时间(content/when_to_use 变更自动更新)
author: str # 生成该记忆的 LLM 模型名称
score: float # 相关性/重要性评分
vector: list[float] # 向量嵌入
metadata: dict # 扩展元数据
3.1 when_to_use —— 检索意图与存储内容的解耦
when_to_use 是 MemoryNode 中最具设计巧思的字段。要理解它解决了什么问题,需要先看清传统向量检索的一个固有缺陷。
传统向量检索的困境
在常规 RAG 系统中,一条知识被存进向量库时,系统直接把知识内容本身转成向量嵌入。检索时,拿用户的查询和这些向量做相似度匹配。
然而,用户提问的方式和知识本身的表述方式,往往存在巨大的词汇和语义鸿沟。
举个具体例子。假设记忆库里存了这样一条记忆:
记忆内容(content):“在 AppWorld 中执行多步任务时,应先通过 list_apis 获取接口列表,再逐个用 check_params 验证约束,最后用 chain_call 构造调用链。成功率可提升约 15%。”
这条记忆非常有价值。但用户实际的提问可能是这样的:
“我要在 AppWorld 里完成一个需要多个 API 配合的任务,怎么规划?”
对比来看——用户的查询说的是"怎么规划"、“API 配合”;而记忆内容说的是"list_apis"、“check_params”、“chain_call”、“成功率 15%”。一个是提问语气,一个是技术操作步骤。虽然语义上相关,但向量相似度可能并不高,检索很容易漏掉。
when_to_use 的解决思路
when_to_use 的核心思路可以概括为:给每条记忆额外贴一张"使用场景标签"。这张标签描述的不是记忆的内容,而是"这条记忆适合在什么场景下被使用?"
对于前面的例子,加上 when_to_use 后就变成了:
| 字段 | 内容 |
|---|---|
when_to_use(场景标签) |
“当需要在 AppWorld 中规划多 API 协作的任务执行策略时” |
content(实际内容) |
“应先通过 list_apis 获取接口列表,再用 check_params 验证…” |
现在检索时,系统是拿用户的查询和 when_to_use 做向量匹配,而不是和 content 匹配。when_to_use 的措辞天然更接近"用户会怎么问这个问题",匹配度会高得多。匹配上之后,系统再把 content(具体操作步骤)返回给用户。
图书馆索引卡的比喻
可以把这个设计想象成图书馆的索引卡系统:
┌──────────────────────────────────────────────┐
│ 索引卡(when_to_use) │
│ ────────────────────────────── │
│ "当读者想了解二战期间太平洋战场的 │
│ 转折点时,推荐查阅此书" │
│ │
│ 实际书籍(content) │
│ ────────────────────────────── │
│ "1942年6月,中途岛海战爆发。美军凭借 │
│ 情报优势,成功伏击日军四艘航母... │
│ 此战被视为太平洋战争的转折点..." │
└──────────────────────────────────────────────┘
- 读者(用户)走进图书馆问"我想了解太平洋战争的关键节点"
- 图书管理员翻的是索引卡(
when_to_use),不是逐页搜索书的正文 - 找到匹配的索引卡后,再把对应的书(
content)递给读者
更多实际场景示例
场景 1:工具记忆
when_to_use: "当需要搜索最新新闻,且关键词包含中文时"
content: "使用 search_news 工具,设置 lang=zh-CN,max_results=10。
避免使用 web_search,因为它对中文关键词的召回率只有 40%。"
如果没有 when_to_use,系统用 content 做检索——但 content 里全是参数名(lang=zh-CN)和技术细节,跟用户"帮我搜下关于 xxx 的中文新闻"的措辞完全不同,很可能检索不到。
场景 2:程序性记忆
when_to_use: "当遇到 API 调用超时错误时"
content: "采用指数退避重试策略:初始等待 1s,每次翻倍,最多重试 3 次。
如果 3 次都失败,检查网络连通性后切换到备用 endpoint。"
用户的查询可能是"API 超时了怎么办"——跟 when_to_use (“当遇到 API 调用超时错误时”) 高度匹配,但跟 content(“指数退避”、“翻倍”、“备用 endpoint”)的相似度就低得多。
代码层面的实现
在底层实现中,to_vector_node() 方法根据 when_to_use 是否为空,决定向量数据库实际存储哪段文本做检索:
def to_vector_node(self) -> VectorNode:
if self.when_to_use:
# 👈 有 when_to_use 时:
# 向量数据库里存的"搜索内容" = when_to_use(用于检索匹配)
# 实际的 content 被放进 metadata 里"藏"起来
vector_content = self.when_to_use
metadata["content"] = self.content
else:
# 没有 when_to_use 时:退化为普通 RAG 模式
# 直接用 content 做检索
vector_content = self.content
return VectorNode(
vector_id=self.memory_id,
content=vector_content, # 这个字段会被转成向量嵌入用于检索
vector=self.vector,
metadata=metadata, # content 可能就藏在 metadata 里
)
用一张表格概括两种模式的区别:
| 模式 | 向量数据库拿什么做检索 | 检索命中后返回什么 |
|---|---|---|
有 when_to_use |
when_to_use 的向量嵌入 |
metadata["content"](真正的记忆内容) |
无 when_to_use |
content 的向量嵌入 |
content(内容即检索锚点,传统 RAG 模式) |
一句话总结:when_to_use 让"怎么找到这条记忆"和"这条记忆的实际内容"成为两段独立的文本。检索用前者,返回用后者。 这就是"检索意图与存储内容解耦"的含义。
3.2 自动化的 ID 生成与变更追踪
MemoryNode 还内置了两个自动化机制:
- 基于内容的 ID 生成:
memory_id通过SHA-256(content)的前 16 位自动生成,确保相同内容的记忆具有稳定且唯一的标识; - 变更时间的自动追踪:当
content或when_to_use字段被修改时,__setattr__拦截器自动更新time_modified并重新计算memory_id。
这些细节虽小,但在大规模记忆管理中,它们有效避免了内容重复存储和手动维护时间戳的负担。
四、四种记忆类型详解
4.1 个人记忆(Personal Memory)
个人记忆用于捕获用户的偏好、习惯与个性化上下文,是实现"千人千面"交互的基础。
ReMe 在个人记忆的处理上采用了独特的两阶段设计(Two-Phase):
阶段一(S1 — Memory Phase):细粒度记忆片段的提取与去重。
对话轨迹 → 创建记忆草稿 → 向量检索历史相似记忆 → 对比去重 → 添加新记忆
这一阶段的核心工具是 add_draft_and_retrieve_similar_memory,它在一个原子操作中完成"草拟 + 检索"两件事——先为每条新记忆创建草稿,然后立即检索向量库中的相似历史记忆。LLM Agent 随后对比草稿与历史记忆,仅将不冗余的新记忆持久化。
在提示词工程上,ReMe 明确要求使用真实名称(如"Bob 喜欢喝咖啡")而非泛指(如"用户喜欢喝咖啡"),以保持记忆的具体性和可区分度。
阶段二(S2 — Profile Phase):结构化用户画像的更新。
读取现有用户画像 → 分析最新对话 → 更新/添加 Profile 键值对
与细粒度记忆片段不同,用户画像采用结构化的 key-value 形式(如 name: Bob、occupation: engineer、preference: dark mode),提供更加稳定、全局性的用户特征描述。
这种"片段记忆 + 结构化画像"的双轨管理,使得系统既能在语义层面进行模糊匹配(通过向量检索),也能在结构层面进行精确查询(通过画像键值)。
4.2 程序性记忆(Procedural / Task Memory)
程序性记忆的目标是从任务执行轨迹中提取可复用的"How-To"知识。这在认知科学中对应"程序性记忆"——即"知道怎么做"的知识。
ReMe 的 ProceduralSummarizer 通过提示词工程,引导 LLM 从执行轨迹中聚焦于五类知识的提取:
| 知识类型 | 提取模板 | 示例 |
|---|---|---|
| 成功策略 | “When doing X, approach Y works well because…” | 在导航设置页面时,先获取 session token 效果更好 |
| 失败模式 | “Avoid doing X when Y because it leads to…” | 避免在未验证权限时直接调用写入 API |
| 最佳实践 | “Always check X before doing Y to ensure…” | 执行删除操作前务必确认备份存在 |
| 工作流模式 | “The optimal sequence for X is: step1 → step2 → step3” | 任务规划的最优顺序是:分析→分解→验证→执行 |
| 问题-解决方案对 | “When encountering X issue, the solution is Y” | 遇到 API 超时时,采用指数退避重试 |
提示词中有一个值得注意的设计原则:明确区分"事实"与"程序"。系统要求跳过纯事实描述(如"任务 X 的成功率是 72%“),只保留可操作的程序性知识(如"当成功率低于预期时,增加验证步骤可提升 6%”)。
在实验验证方面,ReMe 在 Appworld 环境上使用 Qwen3-8B 模型进行了评测。引入任务记忆后,Pass@4 指标从 0.3285 提升至 0.3631(+3.46%);在 BFCL-V3 工具调用任务中,Pass@4 从 0.5955 提升至 0.6577(+6.22%)。
4.3 工具记忆(Tool Memory)
工具记忆是 ReMe 的一个差异化特性,旨在解决一个被广泛忽视的问题:智能体的工具使用效率是否可以通过历史经验持续优化?
ToolSummarizer 不仅记录工具调用的成功/失败结果,还综合考量多个维度:
- 成功率统计:该工具的历史成功比例;
- 调用耗时:平均响应时间;
- Token 成本:每次调用的 token 开销;
- LLM-as-Judge 评估:由 LLM 对工具调用的成功/失败进行定性分析;
- 参数优化模式:从历史成功调用中学习最优参数配置。
工具记忆的产出是一份持续更新的"工具使用指南"——它不是静态的 API 文档,而是基于真实调用数据不断演化的活文档。
在工具记忆基准测试中,引入记忆后工具选择的成功率提升了约 14.88%(从 0.672 到 0.772),这一改进完全来自数据驱动的参数优化和工具选择策略。
4.4 工作记忆(Working Memory)
工作记忆解决的是长运行智能体面临的实际问题:当对话或任务执行链超出 LLM 的上下文窗口时,如何在保留关键信息的前提下控制 token 消耗?
ReMe 提出了 消息卸载与重载(Message Offload & Reload) 机制,由三个核心组件协作实现:
(1)FbContextChecker — 上下文检查器
该组件持续监控消息序列的 token 总量。当总量超过 context_window_tokens - reserve_tokens 的阈值时,触发压缩流程。
其切割算法具备分裂轮次感知能力(Split Turn Detection)。在典型的对话中,一个完整"轮次"由 User → Assistant 组成。当切割点恰好落在轮次中间(如 User → Assistant → [CUT] → Assistant → User)时,系统不会简单地截断,而是将该轮次分为两部分分别生成摘要,以避免信息丢失。
(2)FbCompactor — 上下文压缩器
接收需要压缩的消息,通过 LLM 生成结构化的摘要。当存在分裂轮次时,它会生成两部分摘要:
- History Summary:历史对话的整体摘要;
- Turn Context:被分裂轮次的前缀部分的摘要。
(3)Reload 工具
当智能体需要回顾已卸载的内容时,可通过 grep_working_memory(全文搜索)和 read_working_memory(精确读取)按需重新载入相关信息。
这一设计的灵感来源于 OpenClaw 项目,在 ReMe 的终端 AI 聊天助手 ReMeCli 中得到了端到端的落地验证。
五、执行引擎:ReAct Agent 与任务委派
5.1 ReAct 循环
ReMe 的记忆代理构建在 ReAct(Reasoning + Acting) 模式之上。BaseReact 类实现了经典的推理-行动交替循环:
async def react(self, messages, tools):
for step in range(max_steps):
# Reasoning: LLM 决定下一步行动
assistant_message, should_act = await self._reasoning_step(messages, tools)
if not should_act:
break # 无工具调用请求,任务完成
# Acting: 并行执行工具调用,收集结果
tool_results = await self._acting_step(assistant_message, tools)
messages.extend(tool_results)
类层次结构为 BaseOp → BaseReact → BaseMemoryAgent → 具体 Summarizer/Retriever。每一层添加特定的记忆上下文(如 memory_type、memory_target、retrieved_nodes 等),形成了一个从通用到特化的继承链。
5.2 DelegateTask — 多记忆类型的统一调度
在实际场景中,一次对话可能同时涉及多个用户、多个任务类型的记忆操作。DelegateTask 工具实现了一次分析、多路分发、并行执行的调度模式:
LLM 分析输入 → 确定需要处理的 memory_target 列表
│
┌───────┼───────┐
▼ ▼ ▼
Personal Procedural Tool
Agent Agent Agent
│ │ │
└───────┼───────┘
▼
汇总各 Agent 结果
DelegateTask 内部维护了一个 MemoryType → BaseMemoryAgent 的映射字典。当接收到包含多个 memory_target 的任务列表时,它并行启动对应的 Agent 实例,待所有 Agent 执行完成后汇总结果。
上游的 ReMeSummarizer 和 ReMeRetriever 通过提示词工程引导 LLM 判断"当前上下文应该分派给哪些 memory_target",从而实现了一个由 LLM 驱动的动态路由机制。
六、记忆工具层的工程实现
6.1 Draft → Retrieve → Deduplicate 模式
记忆写入遵循"先草拟、后确认"的策略,由 AddAndRetrieveSimilarMemory 工具实现:
- Draft 阶段:LLM 从对话轨迹提取候选记忆片段,每条包含
message_time和memory_content; - Retrieve 阶段:以每条草稿的内容为查询,批量检索向量库中的相似历史记忆;
- Deduplicate 阶段:LLM 对比草稿与历史记忆,仅将真正新颖的内容持久化。
这种模式有效避免了传统 RAG 系统中常见的信息冗余问题;通过让 LLM 而非固定规则来判断"是否冗余",系统具备了更精细的语义去重能力。
6.2 MemoryHandler 的批量搜索与混合检索
MemoryHandler 封装了向量数据库的 CRUD 操作,并在此基础上实现了混合检索模式。当设置 hybrid_threshold 参数时,系统执行以下流程:
- 对所有查询文本获取向量嵌入;
- 分别执行多路向量搜索并去重;
- 计算每条结果与所有查询的平均余弦相似度;
- 按阈值过滤,按分数降序排列。
这确保了检索结果不偏向某一个查询,而是与查询集的整体语义保持较高的一致性。
6.3 更新策略:Delete + Insert
MemoryHandler 的记忆更新采用 Delete + Insert 策略而非原地修改。当需要更新一条记忆时,系统先通过 vector_store.get() 获取原始 VectorNode,转换为 MemoryNode 修改字段,再删除旧节点、插入新节点。
这一策略的原因在于:更新 content 后 memory_id 会变化(因其基于内容哈希),同时向量嵌入也需要重新生成。直接替换比原地修改在语义一致性上更可靠。
七、提示词工程的设计哲学
ReMe 的每个 Memory Agent 都配备了独立的 YAML 提示词文件,这些文件的设计遵循几个原则:
原则一:角色定义明确化。 每个 Agent 被赋予清晰的角色身份。例如,ReMeSummarizer 的系统提示为"You are a Memory Orchestrator responsible for routing memory summarization tasks to specialized agents",强调其"调度者"而非"执行者"的定位。
原则二:工具使用约束严格化。 在 DelegateTask 的提示词中,系统通过大量加粗文本和 CRITICAL 标记,反复强调 memory_target 必须精确匹配预定义列表中的值,不得从上下文内容中"发明"新的 target 名称。
原则三:决策流程步骤化。 个人记忆的提示词将写入流程拆分为显式的 Step 1(创建草稿 + 检索相似记忆)和 Step 2(对比后添加新记忆),每一步的输入、输出和判断条件都有明确说明。
原则四:可选的思维链注入。 BaseMemoryTool 支持 enable_thinking_params 参数。当启用时,工具调用的 JSON Schema 中会自动注入一个 thinking 字段,要求 LLM 在填写参数之前先输出其推理过程。
八、Flow 编排与操作符组合
ReMe 的 BaseFlow 类提供了工作流编排能力,而 BaseOp 类支持操作符重载语法:
# 顺序执行
pipeline = step1 >> step2 >> step3
# 并行执行
parallel = branch_a | branch_b | branch_c
# 混合组合
workflow = (preprocess >> (analyze | summarize)) >> postprocess
这种设计使得复杂的记忆处理管线可以通过声明式语法灵活组合,同时保持了代码的可读性。
九、与传统 RAG 方案的对比
| 维度 | ReMe | 传统 RAG |
|---|---|---|
| 记忆类型 | 四种细分类型(个人 / 程序性 / 工具 / 工作) | 通常仅支持文档检索 |
| 写入方式 | LLM Agent 自主提取 + 语义去重 | 直接文本分块存储 |
| 检索锚点 | when_to_use(检索意图与内容解耦) |
内容本身 |
| 用户画像 | 结构化 Profile + 细粒度片段 | 通常不涉及 |
| 上下文管理 | Offload/Reload + Split Turn 检测 | 简单截断或滑动窗口 |
| 工具选择优化 | 基于历史表现的数据驱动策略 | 通常不涉及 |
| 冗余控制 | Draft → Retrieve → Deduplicate 流程 | 通常不涉及 |
| 执行模式 | ReAct Agent(推理 + 行动交替) | 固定管线(Pipeline) |
十、总结
ReMe 的核心创新在于将记忆管理从传统的"被动存取"升级为由 LLM 驱动的**"主动理解、智能提取、语义去重、分类存储"的智能化流程**。其设计中几个值得深入学习的技术亮点包括:
when_to_use解耦设计——将检索意图与实际内容分离,解决了"内容语义 ≠ 检索意图"的经典问题;- Two-Phase 个人记忆——将细粒度记忆片段与结构化用户画像双轨管理,兼顾语义灵活性与结构精确性;
- Agent-as-Memory-Manager——用 ReAct Agent 替代硬编码规则执行记忆操作,天然具备泛化能力;
- Draft-Retrieve-Deduplicate——"先草拟后确认"的写入策略,以 LLM 的语义理解能力解决冗余控制问题;
- Split Turn 感知压缩——工作记忆的上下文压缩不仅考虑 token 预算,还尊重对话的逻辑边界。
对于正在构建 AI 智能体系统的开发者而言,ReMe 提供了一个经过 Appworld、BFCL-V3、FrozenLake 等多个环境验证的、可插拔的记忆管理方案。其开源地址为 https://github.com/agentscope-ai/ReMe,基于 Apache 2.0 协议发布。
参考文献:
- ReMe GitHub 仓库:https://github.com/agentscope-ai/ReMe
- 程序性记忆论文:https://arxiv.org/abs/2512.10696
- ReMe 文档:https://reme.agentscope.io
更多推荐



所有评论(0)