Skip to content

Agent Memory 不只是 RAG:基于 Anthropic Dreaming 的开源实现与三种 Runtime 落地方案

Published: at 12:00 AM

Agent Memory 不只是 RAG:基于 Anthropic Dreaming 的开源实现与三种 Runtime 落地方案

Anthropic 在 Components of Agent Memory 中描述了一种长期记忆设计:Agent 在工作时留下原始记录,从经历中提炼值得保留的知识;记忆积累到一定规模后,后台过程重新检查旧记录,纠正错误、合并重复项、建立更高层的知识表示,并为下一次 Session 准备少量相关上下文。

这篇文章容易让人联想到 Claude Code 的 Auto Memory,但真正值得研究的是记忆如何形成和维护,而不是某个产品使用了什么私有文件路径或 Prompt。要实现这种架构,不需要反向工程 Claude Code。 公开的 Session 生命周期 API、可持久化的事件记录、一个独立 Memory Store 和后台 LLM Worker 已经能覆盖主要机制。只有在目标是复制 Claude Code 未公开的内部选择策略时,才有必要做黑盒行为对比;这并不是构建可移植 Memory Engine 的前提。

本文重点讨论三个 Runtime:GitHub Copilot SDK、LangChain DeepAgents,以及 pi.dev / pi-mono。三者的会话管理方式不同,但可以通过统一的 Memory Contract 接入同一套记忆服务。先给出结论:

推荐的方向不是为三个 Runtime 分别实现三套 Memory Engine,而是先实现一个共享的 Memory Core,再分别编写 Runtime Adapter。这样可以复用记忆模型、检索、整理、评估、删除和审计逻辑,避免某个框架的内部数据格式成为长期架构依赖。

1. Anthropic 的设计到底解决什么问题

传统 RAG 会把文档切块、建立索引,再在请求时检索相关内容。它能让模型找到已有信息,却不会自动判断一次长任务中哪些经历值得保留,也不会主动验证旧结论是否已经失效。对话摘要同样不够:它主要压缩当前上下文,目标是让当前 Session 继续运行,不是建立一个能够跨 Session 演化的知识体系。

Anthropic 将 Agent Memory 分成四层:

组件保存什么主要用途
Transcripts用户消息、Agent 回复、工具调用及结果等原始事件保留经历与证据,支持回溯
Memory Notes从经历中提取的偏好、事实、决策、规则和教训保存以后可能再次用到的信息
Memory RepresentationsWiki 页面、主题索引、实体关系和链接组织持续增长的知识,支持关联检索
Injected Context每次 Session 开始时预加载的少量内容让 Agent 一开始就掌握相关背景

四层职责不同。原始 Transcript 应尽量保留来源,不应直接全部塞进 System Prompt;Memory Notes 是经过提炼的候选知识,但仍可能出错;Wiki 或知识图谱帮助 Agent 找到关联信息,却不应成为脱离来源的第二套事实源;Injected Context 则是按当前任务和 Token 预算挑选出来的视图,不是完整知识库。

1.1 Dreaming 是记忆维护过程,不是定时摘要

文章最有价值的部分是 Dreaming。Agent 在工作时可以记录显著事件,但它未必能立即判断一条信息未来是否重要,也可能把当时的猜测误当成事实。Dreaming 让后台任务重新检查历史记录,以来源证据校正旧笔记,合并重复项,识别冲突和过时内容,再生成适合未来检索的知识表示。

Anthropic 的示例包括:Agent 根据任务反馈修正此前回答错误的规则;在 Pokémon 长任务中,根据历史 Transcript 纠正错误的地点记忆;以及把大量内部文档组织成相互链接的 Wiki 页面。它们对应不同的记忆维护动作:纠错、经验归纳、知识组织和上下文准备。

这并不意味着每次 Session 结束都必须运行一次昂贵的总结。整理任务可以由事件数量、时间窗口、重复召回、错误反馈或队列积压触发;一次任务也可以只处理某个 User、Project 或 Agent 的部分记忆。关键是把它设计成可重试、可追踪、可评估的后台工作,而不是放在交互式请求的关键路径里。

1.2 不要把 Compaction 当成 Long-term Memory

Compaction 解决的是当前上下文窗口接近上限时,如何压缩旧消息并继续当前任务。Long-term Memory 解决的是多个 Session 之间,哪些知识应该留下、如何纠错和如何再次使用。

两者会共享原始对话材料,但生命周期不同。Pi 官方文档也把 Compaction 和 Branch Summarization 作为会话上下文管理机制:它们会保存摘要和保留边界,但这不等于一套完整的跨 Session 知识维护系统。参见 Pi Compaction Reference。

2. 可以直接使用的开源实现

目前没有一个开源项目能在所有 Runtime 上原样提供 Anthropic 的全部四层能力。更实际的做法是选用已有项目中成熟的部分,再补齐缺失的 Runtime Adapter 或后台 Worker。

2.1 OpenClaw:最接近完整 Dreaming 流程的参考实现

OpenClaw Memory 和 Dreaming 值得作为架构参考。它没有把所有长期记忆塞进一份不断增长的摘要,而是按生命周期使用不同文件:

OpenClaw 的 Dreaming 分为 Light、REM 和 Deep 三个阶段。Light 整理近期信号并去重,REM 归纳主题和重复模式,这两个阶段不直接改写长期的 MEMORY.md。Deep 阶段才对候选项评分和校验,并在通过门槛后执行新增、合并或替代。它会重新读取来源文件,避免把已经删除或变更的旧片段写回。

这套实现还有几项值得借鉴的保护措施:记忆条目保留来源引用;不可信来源会在进入整理模型前被过滤;候选项需要通过分数、重复召回次数和不同查询数等门槛;更新前保存旧文件,整理结果可以检查;历史记忆回放也会避免把已经注入过的记忆再次当成新的经历。

OpenClaw 还提供从 Claude Code 自动记忆目录导入 Markdown 文件的功能,但不会把 Claude Code 的完整 Session Transcript 一并导入。这说明可以复用公开的文件约定和迁移入口,不需要反向工程 Claude Code 的运行时。

2.2 Pi 生态:从轻量 Markdown 到完整睡眠周期

Pi 的社区实现最丰富,且与 TypeScript Runtime 的结合比较直接。

pi-hermes-memory:适合先验证日常自动学习

仓库:chandra447/pi-hermes-memory

截至 2026-11-12,Pi Package Catalog 显示的版本为 0.11.0。扩展提供长期事实、User Profile、项目记忆、历史 Session 搜索和可复用 Skill。它会在一定数量的 Turn 或 Tool Call 后进行后台 Review,也会识别用户纠正并保存相关信息。历史对话索引用 SQLite FTS5,记忆本身仍可落在 Markdown 文件中。

它将全局信息与 Project 信息分开,程序性经验保存为 Pi 原生 SKILL.md。默认的 Policy-only 模式不会把整份记忆文件都注入 Prompt,而是给 Agent 一套检索规则,需要时再调用 memory_search。这比每轮加载越来越大的 MEMORY.md 更可控。

边界也需要看清:后台 Review 仍然需要 LLM 调用;历史 Session 必须先建立索引;SQLite FTS5 主要解决关键词检索,不能等同于时间知识图谱或完整语义检索。对中文检索,项目文档指出 Trigram 对一两个字符的查询不友好。它是一个可直接使用的本地记忆扩展,但不是 Anthropic Dreaming 的完整复刻。

安装:

pi install npm:pi-hermes-memory

pi-sleep-agent:最值得研究的 Dreaming 实现

仓库:aydinnyunus/sleep

Sleep 把记忆生命周期拆成三层:T0 是会话中捕获的临时观察,T1 是经过整理但仍待确认的候选记忆,T2 是已经晋升的长期记忆。它提供自动捕获、预算控制、候选整理、夜间 Dream Cycle、记忆卫生检查和 Replay。

Nightly Cycle 会执行四项工作:Mine 从多次 Session 中找重复偏好;Consolidate 把近期 T0 观察提炼为 T1 候选;Hygiene 检查陈旧、未使用或积压的候选;Replay 对相同任务分别使用和不使用候选记忆,比较结果是否改善。

这个设计有两个值得参考的地方。第一,候选记忆和永久记忆分开管理,避免一次 LLM 误判直接污染长期知识。第二,项目会度量记忆是否有帮助,而不是只统计写入了多少条。

它的默认治理更严格:新的永久记忆需要通过 promote 显式批准。这个门槛适合高风险或证据不足的场景,但不宜无条件推广到所有个人偏好和普通项目经验。项目还需要模型调用和平台调度器来运行夜间流程。公开支持列表包括 Pi、Claude Code、Gemini、Cursor、OpenCode、Windsurf、Codex 和 Cline 等;当前列表没有 GitHub Copilot SDK,因此不能直接视为 Copilot 插件。

Pi 安装入口:

pi install npm:pi-sleep-agent

pi-memory:轻量的 Markdown 方案

仓库:jayzeng/pi-memory

它将长期记忆、每日日志和待办 scratchpad 保存为普通 Markdown 文件,核心能力不依赖额外数据库。可选的 QMD 提供 BM25、语义和混合搜索。对于希望直接查看、编辑并通过 Git 管理记忆的个人开发者,这是一个轻量起点。

但它的定位仍以文件记忆和检索为主。其仓库中的 pi-dream Consolidation PR 在本次核对时仍是开放状态,不能把未合并的提案当成已经发布的稳定能力。

pi-cognee:知识图谱方向

仓库:kerryhatcher/pi-cognee

这是一个社区维护的 Pi 扩展,通过 Cognee 提供持久化知识图谱记忆。它可以选择在进程内使用 TypeScript SDK,也可以连接远端 Cognee MCP Server,提供 Remember、Recall、Forget 和 Dataset 管理等操作。

要注意两点:它不是 Cognee 官方团队维护的 Pi 插件;在 SDK 模式下,实体抽取、Embedding 和搜索补全使用扩展单独配置的模型与服务,并不会自动复用 Pi 当前的模型调用。另外,官方集成文档说明 Recall 并非每轮自动执行,Agent 需要主动调用 cognee_recall。因此它更适合承担 Memory Representations 和关系检索,不是完整的自动记忆生命周期。

2.3 其他可以作为 Memory Engine 的项目

如果目标是给多个 Runtime 提供共享记忆服务,可以研究以下项目:

项目主要方向更适合的场景
Mem0记忆提取、实体关联、检索和多种 SDK 集成需要应用侧 Memory API,且能接受其 OSS 与托管能力之间的差异
HindsightRetain、Recall、Reflect,多策略检索和记忆页面希望把记忆作为独立服务,通过 SDK、MCP 或 HTTP 接入多个 Agent
Cognee从文本、文档和对话生成知识图谱与向量表示企业知识关系、文档和多源资料的图结构检索
Graphiti带时间信息的知识图谱事实会随时间变化,需要追踪实体关系及其有效时间
Letta可自我编辑记忆的 Stateful Agent Runtime希望把记忆管理直接放进 Agent Runtime,而不是只引入独立记忆库

这里要区分 Memory Engine 与 Agent Runtime。Mem0、Hindsight、Cognee 和 Graphiti 更适合提供记忆存储、整理或检索能力;Letta 则把 Agent 本身也纳入框架。它们都不能仅凭某一项功能就被视为完整复制 Anthropic 的架构。

3. 三种 Runtime 的接入方式

三个框架的关键差异在于:事件从哪里来、上下文在哪个时点注入、后台整理由谁触发。它们不需要拥有相同的 Session 数据格式,但应映射到同一个内部事件和记忆模型。

3.1 GitHub Copilot SDK:用 Hooks 和事件流接入独立 Memory Core

Copilot SDK 的定位是把 Copilot CLI 的 Agent Runtime 嵌入应用,而不是提供一套长期记忆数据库。SDK 已有正式的 Session Hooks 和 Streaming Events API,足以建立 Memory Adapter。

可用接口包括:

SDK 接口在记忆系统中的职责
onSessionStart在新建或恢复 Session 时检索记忆,通过 additionalContext 注入
onUserPromptSubmitted / onUserPromptTransformed观察用户输入及其最终变换形式
onPostToolUse / onPostToolUseFailure补充工具执行成功或失败的信息
onAgentStop观察顶层 Agent 自然停止,可用于完成检查
onSessionEnd释放资源、完成最终 Flush 或提交后台整理任务
Streaming Events捕获完整用户消息、Assistant 消息、工具调用和工具结果

SDK 的事件流区分已持久化事件与 ephemeral 事件。完整消息、工具执行结果等可以写入 Session Event Log;增量 Delta 和部分进度事件不会作为持久记录在恢复时重放。Memory Adapter 应以完整事件为主要输入,避免把每个 Token Delta 都送入长期记忆提取。

一个接入轮廓如下。代码展示的是边界位置,事件归一化、Scope 解析和持久化由应用自己的 Memory SDK 负责:

import { CopilotClient } from "@github/copilot-sdk";

const client = new CopilotClient();

const session = await client.createSession({
  streaming: true,
  hooks: {
    onSessionStart: async () => {
      const context = await memory.prepareContext({
        userId,
        projectId,
        agentId,
        runtime: "copilot-sdk",
      });

      return { additionalContext: context.text };
    },
  },
});

session.on((event) => {
  // Delta / progress 不进入长期记忆;完整事件交给持久化队列。
  if (event.ephemeral) return;

  if (isMemoryEvent(event.type)) {
    void memoryEventWriter.enqueue({
      runtime: "copilot-sdk",
      sessionId: session.sessionId,
      event,
    });
  }
});

这里有三个实现要求。

第一,区分会话恢复与长期记忆恢复。 Copilot SDK 会保留 Session 的对话、工具状态和规划上下文,能够通过 Session ID 恢复原会话。这是 Session Persistence,不是跨 Session 的 User Memory 或 Project Memory。应用应继续维护自己的事件索引和记忆 Scope,不能只依赖 CLI 的 Session 文件来实现跨用户检索。

第二,事件写入不要阻塞 Agent 主循环。 Streaming Handler 应尽快把标准化事件放入可靠队列或本地 Outbox,再由后台 Worker 写入数据库和执行提取。若记忆服务不可用,主 Agent 应继续正常工作;事件可以按重试策略补写,但不能因为 Memory Store 故障而阻塞用户任务。

第三,选择正确的整理边界。 session.idle 是一个 ephemeral 事件,适合通知应用“当前暂时没有继续执行”;onSessionEnd 适合清理 Session 资源或触发最终 Flush。不要把 onAgentStop 当成唯一的最终整理信号,因为顶层 Agent 停止和 Session 生命周期结束不是同一个概念。

因此,Copilot SDK 不需要反向工程 CLI。应用只需利用公开事件建立自己的事件日志,用 onSessionStart 做 Context Injection,再由后台 Worker 完成跨 Session 的记忆整理。若要让多个 Copilot Session 共享同一用户或项目记忆,Scope 和权限必须由应用层明确控制。

参考:Copilot SDK Hooks、Session Lifecycle、Streaming Events、Session Persistence。

3.2 LangChain DeepAgents:把 Store 和 Consolidation Agent 分开

DeepAgents 已将长期记忆做成框架的一等能力。默认的 Thread State 和 Checkpointer 保存当前 Thread 的执行状态;跨 Thread 的长期记忆使用 LangGraph Store。Memory 以文件形式暴露给 Agent,Agent 可以读取或修改这些文件,Backend 决定实际存储在哪里。

这和 Anthropic 的 Memory Notes、Injected Context 有直接对应关系:

官方示例将 /memories/ 映射到 StoreBackend,并将临时工作文件保留在 StateBackend。生产环境应使用持久化 Store,而不是 InMemoryStore。Namespace 必须使用经过认证的稳定身份,不能把模型生成的名称或显示标签当作用户 ID。

from deepagents import create_deep_agent
from deepagents.backends import CompositeBackend, StateBackend, StoreBackend

# durable_store 由应用提供,例如持久化的 LangGraph Store。
agent = create_deep_agent(
    model="anthropic:claude-sonnet-5",
    memory=["/memories/USER.md", "/memories/PROJECT.md"],
    skills=["/skills/"],
    backend=CompositeBackend(
        default=StateBackend(),
        routes={
            "/memories/": StoreBackend(
                namespace=lambda rt: (
                    rt.server_info.user.identity,
                    "memory",
                )
            ),
            "/skills/": StoreBackend(
                namespace=lambda rt: (
                    rt.server_info.assistant_id,
                    "skills",
                )
            ),
        },
    ),
    store=durable_store,
)

DeepAgents 的 Background Consolidation 不是安装后自动运行的后台服务。 官方文档给出的做法是部署一个独立 Consolidation Agent。它通过自定义工具查询最近的对话,将关键事实和已有 Memory 合并,再写回长期 Store。随后通过 LangGraph SDK 创建 Cron Job 定期运行。

官方示例使用每六小时一次的 Cron,并让 Consolidation Agent 查询最近六小时的对话。两个时间窗口必须一致,否则会重复处理或漏掉中间的会话。自托管部署也可以使用外部调度器触发这个 Agent,但要保留同样的 Lookback 和幂等规则。

LangMem 可以提供记忆处理组件。它的 create_memory_store_manager 用于从消息中提取和更新记忆,ReflectionExecutor 可以延迟和合并后台处理任务。它适合放在提取与反思阶段,不需要再复制一份长期存储。如果已经使用官方 Consolidation Agent,就应明确谁是唯一写入者,避免 LangMem 和另一整理 Agent 并发改写同一份 Memory。

可参考:DeepAgents Memory、DeepAgents Context Engineering、DeepAgents GitHub、LangMem GitHub。

如果新 Agent 已经基于 LangGraph / DeepAgents 开发,优先使用它原生的 StoreBackend、Memory Files 和独立 Consolidation Agent。不要为了得到一个记忆库而再包一层 Mem0;只有需要跨多个不同 Runtime 共享同一记忆服务时,才把 Memory Core 外置。

3.3 pi.dev / pi-mono:利用 Extension 生命周期做自动捕获和注入

Pi 是三个 Runtime 中最容易做本地快速试验的一个。Extension 可以在消息、工具、Agent 和 Session 生命周期的不同阶段运行逻辑,并能通过 before_agent_start 修改当前 System Prompt 或插入一条发给模型的自定义消息。

几个事件尤其重要:

Pi Extension Event适用用途
session_start初始化数据库、索引或 Session 级资源
before_agent_start在每次 Agent Run 开始前准备相关记忆
message_end捕获最终的用户、Assistant、工具结果和自定义消息
tool_execution_end记录工具执行的最终结果,包括失败的调用
agent_settled判断本轮运行以及可能的自动恢复、排队工作都已结束
session_before_compact在 Compaction 前保留会话交接信息
session_shutdown关闭 Session 级资源

一个重要细节是:agent_end 只代表一轮低层 Agent Run 结束,Pi 之后仍可能自动重试、执行 Compaction 或处理排队消息。要触发“当前 Agent 已经不会自动继续”的后台任务,应使用 agent_settled。另一个细节是 message_end 是最终消息事件,适合捕获完整记录;逐 Token 的 message_update 不应直接进入长期记忆。

最小的 Context Injection 可以这样做:

pi.on("before_agent_start", async (event, ctx) => {
  const result = await memory.recall({
    query: event.prompt ?? "",
    cwd: event.systemPromptOptions.cwd,
    userId,
    projectId,
    maxTokens: 1200,
  });

  if (!result.text) return;

  return {
    message: {
      customType: "agent-memory",
      content: result.text,
      display: false,
    },
  };
});

捕获则由 message_end 和 tool_execution_end 完成;后台整理可以先入队,在 agent_settled 后触发轻量任务,再由独立调度器运行 Nightly Dream。对于资源较重的 Worker,应在 session_start 后初始化,并在 session_shutdown 时关闭。

Pi 的 Extension API 足以实现这些能力,不需要改动 pi-mono 内核。它的 SessionManager 管理当前会话和分支,扩展可以读取 Session Messages,或将独立的记忆事件写入自己的 Store。

实际选型上,我会先把 pi-hermes-memory 作为本地自动学习的参考实现,再研究 pi-sleep-agent 的候选记忆和 Dream Cycle。不要把两个扩展未经隔离就同时安装到同一套工作流里:它们都可能捕获事件、提取经验并维护记忆,重复写入会使冲突处理和质量评估更难判断。更好的方式是先分别运行和评估,再决定将哪些模块纳入自己的 Memory Engine。

参考:Pi Extensions、Pi SDK、Pi Compaction。

4. 推荐架构:一个 Memory Core,三个 Runtime Adapter

Memory 能力拆成两部分:Runtime Adapter 负责接入各自框架;Memory Core 负责事件归一化、记忆提取、整理、检索和生命周期管理。后台 Dream Worker 与 Agent 的交互循环独立运行。

统一 Agent Memory 架构:三个 Runtime Adapter 接入共享 Memory Core

图源:HTML 版本。

这张图表达的是逻辑职责,不要求第一版就部署成多个服务。个人开发环境可以是一个 Node.js 进程加 SQLite;团队环境可以将 API、Worker 和数据库分开运行。拆分应由并发、故障隔离和部署边界驱动,而不是先假设需要微服务。

4.1 Core 的职责和最小接口

Memory Core 先只需要五类能力:

接口作用典型调用方
appendEvent追加原始事件,保证可追溯和幂等Runtime Adapter
extractCandidates从一段经历中抽取有来源的候选记忆Capture Worker
consolidate去重、纠错、冲突处理和表示构建Dream Worker
recall根据任务、Scope 和预算检索记忆Runtime Adapter
forget退休或删除记忆,并同步清除派生索引管理工具 / 用户

可以把它暴露为 TypeScript SDK、HTTP API 和 MCP Server。Pi 与 Copilot SDK 直接使用 TypeScript Client;DeepAgents 可以使用 Python Client 或 MCP。对外协议应当保持简单,不要让每个 Runtime 都了解 SQLite 表结构或 Dream Worker 的内部实现。

第一版可以先放在一个仓库、一个进程中,按职责划分目录,不必一开始就拆成独立服务:

memory/
  contracts/          # Event、Candidate、Record、Scope、SourceRef
  core/               # append、recall、forget、candidate lifecycle
  store-sqlite/       # SQLite + FTS5 repository
  retrieval/          # keyword / semantic / temporal ranking
  dream-worker/       # extract、validate、consolidate、replay
  adapters/
    copilot-sdk/      # Session Hooks + streaming events
    pi/               # Extension lifecycle
    deepagents/       # Python / HTTP / MCP bridge
  eval/               # gold tasks、replay reports、regression tests

Runtime Adapter 应只依赖稳定的 Contract,不应直接访问数据库表。核心事件契约可以保持很小:

type RuntimeKind = "copilot-sdk" | "deepagents" | "pi";

interface MemoryEvent {
  id: string;                 // source event id,作为幂等键
  runtime: RuntimeKind;
  sessionId: string;
  scope: {
    userId: string;
    projectId?: string;
    agentId?: string;
  };
  type: "user_message" | "assistant_message" | "tool_result" | "correction";
  occurredAt: string;
  source: {
    ref: string;              // transcript / tool event locator
    trust: "user" | "tool" | "assistant" | "external";
  };
  payload: unknown;
}

interface MemoryService {
  appendEvent(event: MemoryEvent): Promise<void>;
  prepareContext(input: {
    scope: MemoryEvent["scope"];
    query: string;
    tokenBudget: number;
  }): Promise<{ text: string; recordIds: string[] }>;
  enqueueConsolidation(input: {
    scope: MemoryEvent["scope"];
    throughEventId?: string;
  }): Promise<{ jobId: string }>;
  forget(input: {
    scope: MemoryEvent["scope"];
    recordIds: string[];
  }): Promise<{ accepted: boolean }>;
}

appendEvent 必须按来源事件 ID 幂等;prepareContext 必须从可信身份推导 Scope,并返回实际注入的 Record ID,便于之后判断一条记忆是否真的被使用;enqueueConsolidation 只创建后台任务,不让 Runtime 等待整理完成。forget 的执行结果还需要确认主记录和所有派生索引已同步清理,不能只返回“已收到请求”。

语言边界也要提前决定。Copilot SDK 和 Pi 都可以直接用 TypeScript Adapter;DeepAgents 是 Python Runtime。如果 Memory Core 先用 TypeScript 实现,DeepAgents 应通过 HTTP 或 MCP 调用契约,而不是复制一份 Python 数据模型后直接读写 SQLite 表。只有当团队决定以 Postgres 作为共享存储,并且愿意承担跨语言 schema migration 的责任时,才考虑让不同语言的 Repository 直接操作同一套表结构。

4.2 数据模型:保留来源、时间和作用范围

记忆系统至少需要区分 Event、Candidate 和 Memory Record 三种对象。

MemoryEvent 是追加式原始事件。 它记录事件 ID、Runtime、User、Project、Agent、Session、事件类型、发生时间、来源位置和必要的 Payload 引用。不要把工具调用失败、用户纠正和 Agent 最终回答简单合并成一段文本;这些事件的证据强度和用途不同。

MemoryCandidate 是尚未晋升的知识主张。 它保存提炼后的内容、类别、Scope、置信度、来源 Event ID、提取时间、所描述事实的有效时间、冲突关系和处理状态。Candidate 可以被拒绝、合并或等待进一步证据。

MemoryRecord 是当前可检索的长期知识。 它应保留来源引用、更新时间、有效时间和生命周期状态。旧事实不一定要物理覆盖;例如某个服务原来运行在 us-east-1,后来迁移到 eu-central-1,应把旧记录标记为已被替代,并保留它曾经有效的时间范围。

记忆类别可以从五种开始:Profile / Preference、Episodic、Semantic、Procedural 和 Decision。它们分别用于用户偏好、任务经历、稳定事实、操作经验和已经做出的设计或业务决策。当前执行计划和临时任务状态应留在 Session 或 Workflow State 中,不能都当成长久知识保存。

4.3 存储先简单,按检索需求逐步增加组件

第一版建议以 SQLite 或 PostgreSQL 为核心:

不要在第一版同时引入 SQLite、PostgreSQL、Qdrant、Neo4j 和独立搜索服务。它们会让事件一致性、删除语义、迁移和备份复杂化。一个数据库先承担事件、结构化记忆和关键词检索,已经足够验证架构。

5. Dream Worker:如何把经历变成可信的长期记忆

Dream Worker 不应只是一个“把最近 N 条消息总结成 Markdown”的 Prompt。它需要一个有状态的处理流程。

Dreaming 流程:从原始事件、候选事实到校验、合并、晋升和效果评估

图源:HTML 版本。

5.1 捕获与提取分开

Capture 只负责忠实地保留事件,不急于判断其长期价值。提取器可以在 Session Idle 后或达到事件阈值时工作,将可能值得保留的内容转成 Candidate。例如,用户明确说“这个仓库统一使用 pnpm”,它可能成为 Project Convention;工具连续失败后 Agent 找到可复现的修复方法,可能成为 Procedural Memory;用户纠正了某项偏好,则可能替换旧的 Profile 值。

不能把每次工具输出都变成记忆。大多数中间结果只是当前任务状态,未来可以重新从源码、数据库或工具中获得。优先保留那些无法轻易重建、而且对以后决策确实有价值的信息。

5.2 校验来源和时间冲突

候选事实应当能回到原始事件或外部来源。对于 Project Convention,检查它是否与仓库配置或实际命令冲突;对于技术结论,检查是否有成功执行记录;对于用户偏好,用户直接表达的证据应高于模型根据行为推断的偏好。

时间也不能只保存一个 created_at。至少要区分:

例如,今天记录的一条历史信息可能描述的是两年前的架构状态。若只根据记录时间排序,Agent 就会把旧事实误认为当前事实。

对于冲突候选,优先生成 Supersedes 或时间版本关系,而不是直接覆写。找不到足够证据时,保留冲突状态或拒绝晋升。记忆越重要,来源和时间约束越不能省略。

5.3 自动晋升,不要让人工审核成为默认阻塞点

pi-sleep-agent 的 T0/T1/T2 和人工 Promote 很适合做高风险记忆的治理参考,但个人助理或普通工程 Agent 不应让每条候选都等待人工确认。可以使用按风险分类的策略:

候选类型默认策略
用户明确表达的稳定偏好满足 Scope 和隐私规则后自动更新,保留用户原话来源
可复现的项目命令和工具用法有成功执行证据后自动晋升
单次任务得到的技术推断暂存为 Candidate,等待重复证据或后续验证
与已有记忆冲突的事实标记 Supersedes / Conflict,必要时进一步验证
涉及权限、金融操作、隐私或安全策略的指令不作为自动授权依据;遵守系统策略和审批边界

人工确认应该用于真正影响权限、风险或业务责任的事项,而不是用来弥补记忆系统无法处理不确定性的设计缺陷。需要审批的是操作授权,不是每一条知识记录。

5.4 记忆表示与检索索引分开维护

Memory Notes 积累到一定规模后,可以按主题生成 Wiki 页面、实体关系和反向链接。它们是从来源记录生成的派生表示,必须能够重新构建。不要让 Wiki 成为唯一事实来源,否则一次生成错误就可能扩散到所有检索入口。

检索可以逐步引入多种信号:精确关键词、语义相似度、实体关联、时间有效性和 Scope。只有在候选集足够小后才调用 Reranker。对个人偏好和项目约束,可以用很小的固定预算预加载;详细技术资料和历史经历则按任务按需加载。

5.5 评估记忆有没有实际帮助

Replay 不应该只是测试检索接口是否返回了结果。对一组真实任务或经过脱敏的历史任务,分别运行有记忆和无记忆的版本,比较最终任务结果、纠错次数、执行成本和错误类型。

建议至少测量四个指标:

可以把每次 Replay 的结果写入评估日志,用于调整候选晋升门槛和检索策略。不能只追求记忆召回率;一个总能召回但经常过时或越权的记忆系统,并不是更好的记忆系统。

6. 安全、隔离与删除是 Memory Core 的一部分

记忆会长期影响 Agent 的行为,因此 Memory Store 本身也是一个新的输入信任边界。它不应因为内容被保存过,就自动拥有更高的指令优先级。

Scope 隔离要在存储和查询两端执行。 User Memory、Project Memory、Agent Memory 和 Organization Memory 应有明确 Namespace 和 ACL。检索接口必须从可信身份上下文推导 Scope,不允许模型通过工具参数自行扩大检索范围。

Provenance 必须参与可信度判断。 用户直接表达、Agent 已验证的工具结果、外部网页和模型推测不能同等对待。来自不可信文档或外部页面的指令,应作为数据处理,不能因为被提炼进记忆就升级为系统指令。

捕获前还应做 Secret Redaction。OpenClaw 的 Dreaming 文档明确说明,Session Transcript 在进入其整理语料前不会自动做 Secret Masking;不能把“有记忆准入策略”误认为“凭据已经脱敏”。在自己的实现中,应在事件落盘或至少在送入提取模型前扫描 API Key、Token、私钥、Cookie 和连接串,并把原始敏感值留在受控的业务日志系统中,而不是复制进长期 Memory。

忘记需要定义真实删除语义。 对过时内容,可以 Retire 或 Supersede;对用户明确要求删除的个人数据,则需要从主记录、向量索引、图索引、缓存和派生 Wiki 中同步删除,并按照 Transcript 保留策略处理来源记录。仅仅从 MEMORY.md 中删掉一行,而底层 SQLite、向量库和图数据库仍能搜到,不算完成删除。

记忆故障不应阻止 Agent 工作。 检索超时、Worker 崩溃或索引损坏时,Agent 应能够继续使用当前 Session 和正常工具完成任务;后台任务通过 Outbox、幂等事件 ID 和重试处理恢复。对于检索失败,要能区分“没有记忆”和“记忆服务不可用”,不能让两者表现为同一个空结果。

7. 实施路线:先跑通一个 Runtime,再扩展到另外两个

不建议同时把三个 Runtime、三种存储和完整知识图谱全部上线。最短的验证路径如下。

Phase 1:用 Pi 完成端到端试点

先在 Pi 上运行 pi-hermes-memory,观察自动学习、纠错、Session 搜索和 Project Scope 的实际效果。再独立运行 pi-sleep-agent,重点评估它的 T0/T1/T2、夜间整理和 Replay。两个扩展先分别试验,不要同时写入同一个目录。

试点期间定义一小组固定任务:用户偏好跨 Session 复用、项目命令复用、一次错误修复被再次使用、旧配置变更后不再推荐旧命令、用户要求忘记某条个人信息。先验证正确性和隔离,再测量 Token 和 LLM 成本。

验收应使用可重复的任务集,而不是凭几次演示判断效果。至少检查:同一来源事件重复投递不会生成重复记忆;两个 User 或两个 Project 之间没有任何 Scope 泄漏;旧事实被 Supersede 后不再作为当前状态返回;删除后主记录、检索索引和派生页面均不可再召回;记忆注入确实降低了重复纠正或改善任务结果。建议将“跨 Scope 泄漏为零”和“删除后不可召回”设为硬性门槛,Recall Quality 与 Token 成本则在有基线后再优化。

Phase 2:提取共享的 Memory Contract

根据 Pi 试点的真实事件定义统一 TypeScript 数据契约,包括 Event、Candidate、MemoryRecord、Scope、Source Reference 和 Context Plan。第一版用 SQLite + FTS5,提供本地 SDK 和简单 HTTP / MCP API。Dream Worker 使用独立模型调用,不要求与交互式 Agent 使用同一模型。

这一阶段先不引入完整知识图谱。只要能稳定完成事件追加、候选提取、证据校验、记忆更新、检索和删除,就已经形成了可供其他 Runtime 复用的 Core。

Phase 3:接入 Copilot SDK

编写 Copilot Adapter:SessionStart 做 Context Injection;Streaming Events 负责完整事件捕获;Session Idle 和 Session End 负责排队、Flush 和后台触发。要特别测试 Session Resume、工具失败、子 Agent 和事件重复投递。

Copilot Adapter 不应直接访问数据库表,而是通过 Memory SDK 调用统一接口。这样以后升级 Copilot CLI 或 SDK 时,变化只会落在 Adapter 层。

Phase 4:接入 DeepAgents

使用 LangGraph Store 保存长期 Memory Files,并定义 User / Project / Agent Namespace。交互过程继续由 Thread Checkpointer 管理。独立 Consolidation Agent 负责跨 Thread 的历史归纳,由 LangGraph Cron 或自托管调度器定期触发。

如果使用 LangMem,就让它承担候选提取和反思的职责;如果使用自定义 Consolidation Agent,就不要再让另一套 Reflection Worker 同时改写同一份 Memory。无论选哪种方式,最终更新都应通过 Memory Core 的写入接口完成。

Phase 5:再考虑图谱和共享服务

当实际查询开始需要跨多个实体、多段关系和时间状态时,再引入 Graphiti 或 Cognee。若团队要求多个 Runtime 共享记忆、统一审计、集中删除和多租户隔离,则把 Memory Core 从本地 SQLite 升级为 Postgres / 独立服务,并提供 HTTP 和 MCP 双入口。

此时再决定是否部署向量数据库、图数据库和独立检索服务。不要在评估前先为它们付出部署、备份、权限和一致性成本。

8. 三种 Runtime 的最终选型建议

维度Copilot SDKDeepAgentspi.dev / pi-mono
原生长期记忆无完整 Memory EngineFilesystem + StoreBackend通过 Extension / 社区 Package
自动捕获Events + HooksAgent State / Tools / MiddlewareExtension Events
上下文注入onSessionStartmemory 文件、Backend、Middlewarebefore_agent_start
后台整理应用自建 WorkerConsolidation Agent + Cron扩展或外部 Dream Worker
可直接参考的项目Memory Core AdapterLangMem、官方 Consolidation 示例pi-hermes-memory、pi-sleep-agent
推荐定位企业应用中的 Runtime Adapter复杂 Agent 的执行和后台管理框架本地试点和快速验证

如果当前主要目标是验证 Memory 机制,先选 Pi。 pi-hermes-memory 能快速验证自动捕获与跨 Session 检索,pi-sleep-agent 能验证候选记忆、后台整理和 Replay。它们提供了真实可运行的实现,不需要先写一套空框架。

如果要把 Memory 做成企业内部公共能力,Copilot SDK 是合适的接入对象。 通过其公开事件和 Hooks 接入统一 Memory API,避免依赖 Copilot CLI 私有文件结构。对于应用已经使用 Copilot SDK 的场景,这条路径改动范围小,也便于和现有审计、身份和业务系统集成。

如果 Agent 本身是基于 LangGraph 构建,优先使用 DeepAgents 的 Store 和 Memory Files。 它的长期记忆与 Thread 状态边界清楚,官方也给出了后台 Consolidation Agent 的落地方式。LangMem 可以按需引入,不必为一个提取函数再建立第二套 Memory Store。

9. 结论:Claude Code 不是必需的研究对象

Anthropic 的文章给出了有价值的记忆组织思路,但它并没有要求开发者复制 Claude Code。当前已经有足够多的公开接口和开源实现,可以分别覆盖事件捕获、长期存储、知识组织、后台 Dreaming 和上下文注入。

从工程上看,最重要的决策不是选哪一种向量库,而是明确谁负责记录原始经历、谁负责把候选知识变成长期记忆、谁能修改记忆、记忆如何回溯来源,以及错误记忆如何被纠正和删除。把这些边界定义清楚后,Copilot SDK、DeepAgents 和 Pi 就只是不同的 Runtime Adapter。

建议先以 Pi 作为验证环境,抽取一套 TypeScript Memory Contract,再把同一个 Core 接入 Copilot SDK;DeepAgents 通过 LangGraph Store 和 Consolidation Agent 接入。第一版只使用 SQLite / FTS5 和一个后台 LLM Worker,等 Replay 证明语义检索或知识图谱确实改善任务结果后,再增加相应组件。这样既能尽快得到可运行系统,也不会把某个 Agent Framework 的内部实现变成长期架构依赖。

参考资料

Anthropic

GitHub Copilot SDK

LangChain

Pi / pi-mono

Memory Engine 与知识图谱