导读:在大语言模型驱动的 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)                            │
└──────────────────────────────────────────────────────────────┘

这一分层设计有几个值得关注的特点:

  1. 记忆代理层是整个系统的核心创新——记忆的提取与检索不是硬编码的规则管线(pipeline),而是由 LLM 驱动的 ReAct Agent 自主完成的智能流程;
  2. 基础设施层采用抽象接口模式(BaseVectorStoreBaseLLM 等),支持多种后端实现,保证了系统的可插拔性;
  3. 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 还内置了两个自动化机制:

  1. 基于内容的 ID 生成memory_id 通过 SHA-256(content) 的前 16 位自动生成,确保相同内容的记忆具有稳定且唯一的标识;
  2. 变更时间的自动追踪:当 contentwhen_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: Boboccupation: engineerpreference: 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_typememory_targetretrieved_nodes 等),形成了一个从通用到特化的继承链。

5.2 DelegateTask — 多记忆类型的统一调度

在实际场景中,一次对话可能同时涉及多个用户、多个任务类型的记忆操作。DelegateTask 工具实现了一次分析、多路分发、并行执行的调度模式:

LLM 分析输入 → 确定需要处理的 memory_target 列表
                    │
            ┌───────┼───────┐
            ▼       ▼       ▼
       Personal  Procedural  Tool
       Agent     Agent       Agent
            │       │       │
            └───────┼───────┘
                    ▼
              汇总各 Agent 结果

DelegateTask 内部维护了一个 MemoryType → BaseMemoryAgent 的映射字典。当接收到包含多个 memory_target 的任务列表时,它并行启动对应的 Agent 实例,待所有 Agent 执行完成后汇总结果。

上游的 ReMeSummarizerReMeRetriever 通过提示词工程引导 LLM 判断"当前上下文应该分派给哪些 memory_target",从而实现了一个由 LLM 驱动的动态路由机制


六、记忆工具层的工程实现

6.1 Draft → Retrieve → Deduplicate 模式

记忆写入遵循"先草拟、后确认"的策略,由 AddAndRetrieveSimilarMemory 工具实现:

  1. Draft 阶段:LLM 从对话轨迹提取候选记忆片段,每条包含 message_timememory_content
  2. Retrieve 阶段:以每条草稿的内容为查询,批量检索向量库中的相似历史记忆;
  3. Deduplicate 阶段:LLM 对比草稿与历史记忆,仅将真正新颖的内容持久化。

这种模式有效避免了传统 RAG 系统中常见的信息冗余问题;通过让 LLM 而非固定规则来判断"是否冗余",系统具备了更精细的语义去重能力。

6.2 MemoryHandler 的批量搜索与混合检索

MemoryHandler 封装了向量数据库的 CRUD 操作,并在此基础上实现了混合检索模式。当设置 hybrid_threshold 参数时,系统执行以下流程:

  1. 对所有查询文本获取向量嵌入;
  2. 分别执行多路向量搜索并去重;
  3. 计算每条结果与所有查询的平均余弦相似度
  4. 按阈值过滤,按分数降序排列。

这确保了检索结果不偏向某一个查询,而是与查询集的整体语义保持较高的一致性。

6.3 更新策略:Delete + Insert

MemoryHandler 的记忆更新采用 Delete + Insert 策略而非原地修改。当需要更新一条记忆时,系统先通过 vector_store.get() 获取原始 VectorNode,转换为 MemoryNode 修改字段,再删除旧节点、插入新节点。

这一策略的原因在于:更新 contentmemory_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 驱动的**"主动理解、智能提取、语义去重、分类存储"的智能化流程**。其设计中几个值得深入学习的技术亮点包括:

  1. when_to_use 解耦设计——将检索意图与实际内容分离,解决了"内容语义 ≠ 检索意图"的经典问题;
  2. Two-Phase 个人记忆——将细粒度记忆片段与结构化用户画像双轨管理,兼顾语义灵活性与结构精确性;
  3. Agent-as-Memory-Manager——用 ReAct Agent 替代硬编码规则执行记忆操作,天然具备泛化能力;
  4. Draft-Retrieve-Deduplicate——"先草拟后确认"的写入策略,以 LLM 的语义理解能力解决冗余控制问题;
  5. 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
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐