Skip to content

Agent Memory 不只是 RAG:从 Anthropic Dreaming 到可落地的开源架构

Published: at 12:00 AM

Agent Memory 不只是 RAG:从 Anthropic Dreaming 到可落地的开源架构

Agent 连续工作几天、跨多个 Session 修改同一个项目时,真正有用的往往不是上一轮对话的摘要,而是已经确认的项目约定、用户纠正过的错误、做过的决策,以及这些信息后来有没有变化。

把历史消息放进向量库,只解决了“以后能不能搜到”;把对话压缩成摘要,只解决了“当前 Session 怎样继续”。两者都没有回答:哪些经历值得留下,旧事实何时失效,错误记忆怎样纠正,以及下一次任务应该注入多少背景。

Anthropic 的 Components of Agent Memory 把这些问题放进同一套记忆生命周期里,Dreaming 是其中最值得关注的部分。本文先拆解这套机制和现有开源实现,再给出一套可接入 Copilot SDK、DeepAgents 与 Pi 的共享架构。重点不是复刻某个产品,而是把事件、记忆、后台整理和 Runtime 接入的边界划清楚。

1. 长期记忆和 RAG、Compaction 不是一回事

传统 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 只管理当前会话

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

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

1.3 真正的难题是记忆噪声和事实漂移

开源项目的功能列表不能说明记忆在长期使用中是否真的有帮助。Hacker News 和 Reddit 的讨论里,反复出现的并不是“向量检索还不够准”,而是自动记忆写入了太多短期细节、旧事实没有及时失效,以及错误内容被后续 Session 当成背景继续使用。

一项社区个人实验用 MeME eval 的 100 个 episodes 比较了 Claude Auto-Memory、Hermes、OpenClaw 和无记忆基线。作者报告,在其测试配置下,给 Claude Auto-Memory 增加重新读取原始 Transcript 的 Dreaming 后,得分从 42.5% 提高到 66.0%;OpenClaw 启用 Dreaming 后从 26.4% 提高到 54.2%。这说明回看原始经历可能弥补增量写入和摘要压缩的信息损失,但它不是受控的行业基准:数据集、模型、Prompt 和功能配置都会影响结果,不能据此给产品排座次。

另一类反馈恰好相反:有用户关闭自动记忆,清理 Claude Code 自动生成的内容,因为不少条目只对刚完成的任务有用,带到后续 Session 反而造成干扰。还有实践者提到,数据库 Schema、ETL 或业务定义改变后,自由文本记忆没有同步更新,Agent 仍按旧信息生成 SQL。这些都是长期运行中的真实成本,不能靠提高 Recall@K 解决。

这些反馈对架构有直接影响:原始事件可以完整留存,但不是每条事件都应该晋升为长期记忆;记忆必须保留来源和有效时间;Dreaming 的修改要能检查和回滚;评估要看任务结果、错误注入和上下文开销,而不只是召回率或记忆条数。相关讨论和实验链接放在文末,数据应理解为社区案例,不是独立验证过的普遍结论。

2. 现有开源实现各自解决了哪一段

没有哪个开源项目能直接覆盖所有 Runtime 和完整记忆生命周期。下面按它们实际提供的能力来看,不按功能数量排名。

2.1 OpenClaw:从事件到分阶段整理

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 生态:最适合做本地试验

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 负责接入,Core 负责记忆

这里把系统分成三块:Runtime Adapter 处理框架生命周期;Memory Core 管事件、记忆记录和检索;Dream Worker 在后台提取、校验和整理记忆。Agent 的主循环不负责维护长期知识。

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

图源:HTML 版本。

图中是职责边界,不是部署拓扑。个人环境可以先用一个 Node.js 进程和 SQLite;出现并发、故障隔离或独立扩缩容需求后,再拆 API、Worker 和存储。

3.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 直接操作同一套表结构。

3.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,不要混进长期记忆。

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

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

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

4. Dream Worker:候选记忆怎样变成长期知识

Dream Worker 不是定时摘要任务。它要能追踪候选项从哪里来、经过哪些校验、是否晋升,以及后续是否被替代。

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

图源:HTML 版本。

4.1 捕获与提取分开

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

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

4.2 校验来源和时间冲突

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

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

例如,今天记录的一条历史信息可能描述的是两年前的架构状态。若只根据记录时间排序,Agent 就会把旧事实误认为当前事实。对数据库 Schema、API、部署命令等会随代码或上游系统变化的内容,还应尽量保存可重新验证的源位置或版本,而不是只保留一段自然语言结论。

对于冲突候选,优先生成 Supersedes 或时间版本关系,而不是直接覆写。相似度高不代表两条事实互相矛盾,时间较新也不代表新值已经验证。 找不到足够证据时,保留冲突状态或拒绝晋升。记忆越重要,来源和时间约束越不能省略。

4.3 晋升策略:不必逐条审批,也不能见候选就保存

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

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

人工确认应留给会影响权限、风险或业务责任的事项,不要用逐条审批来弥补候选筛选能力不足。记忆准入和操作授权是两回事:一条知识可以被保存,但它不能因此获得执行权限。

也不要把自动晋升设成默认目标。Hacker News 上有用户反馈,自动记忆会把只对当前任务有用的细节带进后续 Session;有人因此关闭自动记忆,改为定期整理。更合适的策略是:所有事件先按规则捕获,提取结果先成为候选;证据充分、风险较低的候选可以自动晋升,其余保留待验证状态。 用户还应能查看、纠正、禁用某类记忆,并撤销后台整理结果。

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

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

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

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

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

建议至少测量四个指标:

评估时至少做三组对照:无记忆、只有增量写入、增量写入加 Dreaming。保持 Answer Model、任务集和 Token Budget 一致,并按任务类别拆分结果;报告模型版本、Prompt、Memory 配置和数据集,避免把某个系统默认关闭的功能拿来做不公平比较。对重要任务还要做删除与冲突测试,检查旧事实是否真的不再被使用。

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

5. 治理边界:隔离、来源、删除和故障

记忆会长期影响 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 和重试处理恢复。对于检索失败,要能区分“没有记忆”和“记忆服务不可用”,不能让两者表现为同一个空结果。

6. 接入三个 Runtime

三个 Runtime 的接入差异主要在生命周期:事件从哪里捕获、记忆何时注入、后台整理由谁触发。Adapter 负责处理这些差异,Core 不应因此分裂成三套数据模型。

6.1 GitHub Copilot SDK

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。

6.2 DeepAgents 与 LangGraph Store

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 外置。

6.3 Pi 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。

7. 落地顺序与 Runtime 选型

先不要同时建设三个 Runtime Adapter、向量检索和知识图谱。先用一个 Runtime 跑通事件捕获、候选提取、记忆更新、检索、删除和效果评估,再决定哪些能力值得抽成共享服务。

三个 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 的执行和后台管理框架本地试点和快速验证

按下面的顺序验证,每一步都应有真实任务和验收结果。

第一步:用 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 成本则在有基线后再优化。

第二步:提取共享的 Memory Contract

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

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

第三步:接入 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 层。

第四步:接入 DeepAgents

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

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

第五步:再考虑图谱和共享服务

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

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

参考资料

Anthropic

GitHub Copilot SDK

LangChain

Pi / pi-mono

Hacker News 与 Reddit 讨论

Memory Engine 与知识图谱