mem0-dream 记忆整合(Memory Consolidation)技能指南:让 AI Agent 的持久记忆保持精简、一致与新鲜
本文围绕 mem0 仓库中 integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0-dream/SKILL.md 这一技能定义展开,系统讲解 Mem0 为 OpenCode(以及 Claude/Codex/Cursor 等宿主)Agent 提供的记忆整合能力:当 Agent 跨会话累积了大量记忆后,/mem0-dream 会抓取项目全部记忆,识别近似重复、判定相互矛盾的陈述、按保留策略清理过期条目,并以「先展示 diff、再人工确认」的安全方式执行写入。读完本文,你将掌握该技能的六步执行管线、保留策略配置、自动(--auto)运行模式,以及它与插件级 auto-dream 门控机制(源码见 dream.ts)之间的配合关系,能够直接用于维护生产级 Agent 的记忆质量。
为什么 AI Agent 需要「记忆整合」
Mem0 定位为 AI Agent 的记忆层(参见 项目 README),它的理念是:让 Agent 的决策、偏好与学习成果以语义记忆的形式跨会话保留。但记忆是持续累积的,随着项目推进,真实世界里会自然出现三类「记忆熵」:
- 近似重复:同一事实被不同措辞重复记录,例如"认证用 PostgreSQL"与"认证库是 PostgreSQL";
- 矛盾冲突:关于同一主题先后记录了相反的决定,例如"部署到 ECS"与"部署到 Vercel";
- 过期无用:
session_state、compact_summary这类高频生成的临时性记忆超过时效后不再有价值,或者置信度过低且不含项目独有信息。
当记忆库膨胀后,search_memories 的检索结果会变得嘈杂、重复,Agent 甚至在多个会话里给出相互矛盾的回答。mem0-dream 技能正是针对这一场景设计的:它像一次"夜间整理",把记忆库收敛回原子、新鲜、无冲突的状态。该技能是 OpenCode 插件(@mem0/opencode-plugin)内置的 9 个技能之一,与 /mem0-remember、/mem0-search、/mem0-status、/mem0-forget 等共同构成完整的记忆生命周期管理工具链(见插件 README 中 技能清单)。
技能总览:一次整合遍历的六步骨架
技能文档开篇即强调了一个关键约束:
严格按照 1 → 2 → 3 → 4 → 5 → 6 的顺序执行,每步依赖前一步的结果,禁止并行执行或跳步。
完整流程为:
| 步骤 | 名称 | 做什么 | 是否写数据 |
|---|---|---|---|
| 1 | 加载保留策略 | 读取项目根目录 .mem0.json / .mem0.md 中的 retention 配置,或使用内置默认值 |
否 |
| 2 | 抓取全部记忆 | 用 get_memories 按当前项目过滤拉取全量记忆(分页直到拉完) |
否 |
| 3 | 分析问题 | 在内存中按 metadata.type 分组,找出近重复对、矛盾对、可清理项 |
否 |
| 4 | 打印 diff 报告 | 以结构化纯文本输出改动方案,等待确认 | 否 |
| 5 | 等待输入并应用 | 用户裁决矛盾(A/B/skip),最终确认后按 合并→矛盾→清理 顺序写库 | 是 |
| 6 | 打印汇总 | 输出合并/清理/解决/跳过的数量 | 是 |
第 1~4 步完全只读,第 5 步才是唯一的写库环节——这种"先报告、后动手"的设计保证了整合过程可审计、可取消。
Step 1:加载保留策略(Retention Policies)
配置文件的发现顺序
技能在项目根目录(当前工作目录)按以下优先级查找配置:
.mem0.json(优先):若存在,以 JSON 解析并读取其中的retention字段,其形态为category → days | null的字典。days为具体天数表示该类型记忆超过多少天即可清理,null表示该类型不做时效清理。.mem0.md(备选):若不存在.mem0.json,扫描该文件中的retention:段或 YAML front matter,解析其中的保留设置。- 都找不到:跳过配置加载,直接使用内置默认值。
一个符合预期的 .mem0.json 配置形如:
{
"retention": {
"session_state": 90,
"compact_summary": 90,
"task_learning": 365,
"preference": null
}
}
值得说明的是,OpenCode 版本(即本文聚焦的 mem0-dream/SKILL.md)由 Agent 直接解析上述配置文件;而同仓库面向其他宿主(Claude/Codex/Cursor)的姊妹版本 skills/dream/SKILL.md 则是调用 scripts/parse_mem0_config.py 解析脚本读取同一份配置,两者解析目标一致,只是实现载体不同。
内置默认保留策略
当没有配置或配置不含 retention 时,技能回退到如下内置默认值:
metadata.type |
默认保留期 |
|---|---|
session_state |
90 天 |
compact_summary |
90 天 |
| 其他所有类型 | 不做时效清理 |
可以看出,默认策略只对两种"会话/摘要型"临时记忆施加 90 天时效,其余类型一律不动——这体现了保守原则:不给业务性记忆(偏好、决策、学习)预设误删风险。解析出的策略会被保存,供 Step 3 判定清理候选时使用。
Step 2:抓取当前项目的全部记忆
整合的对象是"当前激活项目"下的全部记忆,因此先要确定活动身份。插件在会话启动时通过 shell.env 钩子把 MEM0_USER_ID、MEM0_APP_ID、MEM0_BRANCH、MEM0_SESSION_ID 等注入环境(见插件 README Memory scope),技能中的 <active_user_id> 与 <active_project_id> 即取自这些值。
技能调用 get_memories 拉取数据:
get_memories(
filters={"AND": [{"user_id": "<active_user_id>"}, {"app_id": "<active_project_id>"}]},
page_size=200,
)
要点:
filters使用 AND 组合,同时限定user_id与app_id,确保只处理当前项目、不越界到全局记忆;page_size=200为单页容量;若响应表明还有更多页,必须继续翻页直至拉全——漏掉任何一条都可能让重复项残留;- 拉全后再进入分析阶段。
如果项目内一条记忆都没有,技能打印以下内容并直接结束:
No memories found for project <project_id>. Nothing to consolidate.
Step 3:分析——找出记忆库的问题
这一步严格在内存中完成,不修改任何数据。所有记忆先按 metadata.type 分组(字段缺失的归入 "unknown"),再在每个组内执行三类检查。
3a. 近重复对(合并候选)
两条记忆构成"近重复"的判定需要同时满足以下全部启发式条件:
- 相似度阈值:估算余弦相似度 > 0.9。技能给出了无需嵌入模型的代理算法——若两条记忆中 60% 以上的显著名词重叠,即可按 > 0.9 相似度处理;
- 类型相同:
metadata.type一致; - 均未被固定:
metadata.pinned != true(被固定的记忆不参与自动合并)。
典型例子是 "Use PostgreSQL for auth" 与 "Auth DB is PostgreSQL"——事实相同、措辞不同。对每个合格配对,技能会起草一个比两条原文都更完整、更具体的合并版本。示例记忆来自技能文档,语义一致性的判断最终由执行整合的模型基于该启发式完成。
3b. 矛盾冲突(需要人工裁决)
两条记忆在同一主题上陈述了相反事实(如 "Deploy to ECS" vs "Deploy to Vercel")即为矛盾。技能会先给出倾向性结论:更近的时间戳、更高的置信度者更可能是"赢家"。但删除哪条不由技能决定,而是把双方的 ID 与内容都保留下来,交给用户在 Step 5 裁决。
3c. 清理候选
一条记忆只要命中以下任一条件即可进入清理候选:
- 其
metadata.type配置了保留策略,且created_at距今超过配置天数; - 置信度低于 0.3,且不包含任何本项目独有信息(没有文件路径、标识符或领域专有名词)——也就是说它只是泛泛的噪音,删掉不影响任何项目上下文。
保护性铁律:无论年龄多老、置信度多低,只要 metadata.pinned == true 就一律跳过。这与 /mem0-pin 技能形成呼应——用户主动固定的重要记忆永远不受 dream 影响。
Step 4:输出结构化 diff 报告(不改任何数据)
在动手前,技能必须以精确的纯文本格式向终端打印改动方案,格式如下:
## dream — consolidation report
Merges (<N>):
[mem0:<id1>] + [mem0:<id2>] → "<merged content, 100 chars>"
Conflicts (<N>):
[mem0:<idA>] vs [mem0:<idB>] — "<topic>" [A/B/skip]
Prune (<N>):
[mem0:<id>] — <type>, <age>d old
Proposed: <N> merges, <N> prunes, <N> conflicts. Apply? [Y/n]
报告规则的细节值得注意:
- 某类别的数量为 0 时,整个类别段落都省略;
- 若合并、清理、冲突三个类别全部为空,则打印下面这行并结束,不做任何写操作:
Dream complete. No duplicate, contradictory, or stale memories found.
这一设计让每次 dream 运行都可视、可审:Agent 对记忆库的一切变更意向都在这里透明呈现,用户可以据此判断合并文案是否忠实、冲突双方是否正确配对。
Step 5:等待输入并应用变更
5a. 裁决矛盾对
报告展示后,技能逐个等待用户对每条冲突输入 A、B 或 skip(大小写不敏感);直接回车(空输入)按 skip 处理。所有裁决收集完毕、记录下每对的赢家之后,才进入最终确认——保证用户的裁决不会被后续确认打断而丢失。
5b. 最终确认
技能打印:
Apply? [Y/n]
- 输入
n或no(大小写不敏感):打印Cancelled. No changes made.并停止,不写任何数据; - 输入
Y、yes或直接回车:按「合并 → 矛盾 → 清理」的固定顺序应用全部变更。
各类别的写库方式
合并(Merge)——每条被批准的合并对依次执行三步:
delete_memory(<id1>)delete_memory(<id2>)add_memory写入合并结果,参数约定非常明确:text="<merged content>":更完整的合并文案;user_id=<active_user_id>、app_id=<active_project_id>:app_id必须放在顶层参数,而不是塞进metadata;metadata={"type": "<original type>", "branch": "<active_branch>", "confidence": <higher of the two original scores>, "source": "mem0-dream"}:合并记忆继承原类型与当前分支,置信度取两者中的较高值,并打上source: mem0-dream溯源标记;infer=False:关闭实体抽取与自动标签推理,避免对人工整理过的内容做二次加工。
矛盾(Conflict,已裁决):用户选择 A 或 B 的配对,删除落败方(delete_memory(memory_id=<loser_id>));选择 skip 的配对原样保留。
清理(Prune):对每个清理候选执行 delete_memory(<memory_id>)。
这里引用的 delete_memory / add_memory 是插件注册到宿主 Agent 的原生记忆工具。以 Python 侧 SDK 为参照,其底层能力对应 mem0/client/main.py 中 Memory 客户端的 add、get_all、search、delete 等方法(见 main.py),OpenCode 插件则以 TypeScript 形态、经 mem0ai SDK 直接注册同名工具(见插件 README Memory Tools 清单)。
Step 6:打印汇总
所有变更应用完毕后,打印:
Dream complete — merged: <N>, pruned: <N>, conflicts resolved: <N>, skipped: <N>
四条计数分别对应本次运行实际完成的合并数、清理数、解决矛盾数以及被跳过的冲突数,方便用户在会话日志里快速核对 dream 是否如报告所示执行。
Auto 模式:无人值守的自动化整合
当以 /mem0-dream --auto 调用时,技能进入非交互模式,行为差异如下:
- 合并:自动应用(两个候选兼容、无矛盾,无需人审);
- 清理:自动应用(基于时效/置信度,判定无歧义);
- 矛盾:一律跳过——矛盾裁决依赖人类判断,交给交互式
/mem0-dream处理。
并发守卫(Concurrency guard)
自动模式开始任何工作前,先检查锁文件 /tmp/mem0_dream_auto.lock:
- 若锁文件存在且年龄小于 10 分钟,打印
[mem0-dream --auto] Another run in progress — skipping.并停止,避免多个会话同时整合造成写冲突; - 否则创建锁文件并写入当前时间戳;无论走哪条退出路径,结束时都必须删除锁文件。
执行与去重提醒
自动模式执行流程为:正常完成 Step 1~3(加载策略、抓取记忆、分析),然后静默应用合并与清理(不打印 diff、不弹确认),最后打印紧凑汇总:
[mem0-dream --auto] project=<id> merged=<N> pruned=<N> conflicts_skipped=<N>
自动模式的一个贴心设计是"矛盾提醒只存一条":若检测到矛盾但被跳过,技能会先反查是否已存在待处理提醒,避免每次运行都堆积重复提醒——
search_memories(
query="mem0-dream contradictions manual review",
filters={"AND": [
{"user_id": "<active_user_id>"},
{"app_id": "<active_project_id>"},
{"metadata": {"source": "mem0-dream-auto"}}
]},
top_k=1,
)
若已有相似度 > 0.9 的结果则不再写入;否则存一条带 source: mem0-dream-auto、type: task_learning 的提醒记忆:
add_memory(
text="mem0-dream detected <N> contradiction(s) requiring manual review. Run /mem0-dream to resolve them interactively.",
user_id="<active_user_id>",
app_id="<active_project_id>",
metadata={"type": "task_learning", "source": "mem0-dream-auto", "branch": "<active_branch>"},
infer=False,
)
这样,自动整合负责"确定性"的清理合并,而把"需要判断力"的矛盾留到用户下一次交互式运行时解决——两者边界清晰。
插件级 auto-dream:比技能更深一层的自动触发
除了用户手动执行 /mem0-dream,OpenCode 插件还内置了自动触发的记忆整合机制,实现在 dream.ts。auto-dream 的核心不是分析算法,而是门控(gating):当三类条件全部满足时才把一段 DREAM_PROTOCOL 注入 Agent 上下文,让其在回应前先完成一次整合(见 dream.ts)。
门控阈值定义在 DREAM_DEFAULTS(见 dream.ts):
| 配置项 | 默认值 | 含义 |
|---|---|---|
enabled |
true |
总开关 |
auto |
true |
是否允许自动运行 |
minHours |
24 | 距上次整合至少经过的小时数 |
minSessions |
5 | 距上次整合至少经过的会话数 |
minMemories |
20 | 当前项目记忆数下限 |
对应实现为两组检查函数:不做 API 调用的"廉价门" checkCheapGates(时间 + 会话数,见 dream.ts)与使用会话初始化时已抓取的记忆数的"数量门" checkMemoryGate(见 dream.ts)。配置来源与技能有所不同:
- 优先级最高的环境变量
MEM0_DREAM=false|0|no|off可强制关闭; - 其次是
~/.mem0/settings.json中的dream块,可逐项覆盖上述阈值(加载逻辑见 loadDreamConfig)。
auto-dream 的状态机与锁都存放在 ~/.mem0/ 下:状态记录在 mem0-dream-state.json(lastConsolidatedAt、sessionsSince、lastSessionId),文件锁为 mem0-dream.lock,超过 1 小时的陈旧锁会被回收(见 acquireDreamLock),每次成功后调用 recordDreamCompletion 重置门控。也就是说:技能文档 /tmp/mem0_dream_auto.lock(10 分钟)守卫的是手动 --auto 调用,而 dream.ts 的 mem0-dream.lock(1 小时)守卫的是插件自动整合,两者属于不同层级,配合使用防止并发。
判断当前是否具备自动整合条件,可直接运行 /mem0-status:其 Check 6 会读取同一份状态与阈值文件,指出哪道门还在阻塞(格式如 sessions 2/5, memories 3/20),并提示可用 /mem0-dream 立即整合或通过 ~/.mem0/settings.json 的 dream 块调低阈值(见 mem0-status/SKILL.md)。
与相邻技能的分工与配合
mem0-dream 不是孤立的清理工具,它与记忆生命周期里的其他技能各司其职:
| 技能 | 定位 | 与 dream 的关系 |
|---|---|---|
/mem0-remember |
主动写入当前会话学习到的事实 | dream 的上游——负责"产生"待整理记忆 |
/mem0-search |
语义检索记忆 | 收益方——去重后检索噪音显著下降 |
/mem0-status --deep |
只读体检,不应用任何改动 | dream 的"侦察兵":--deep 会报告重复对、过期项、矛盾对、孤儿记忆,最后提示 Run /mem0-dream to fix(见 mem0-status/SKILL.md) |
/mem0-forget |
按搜索词或 ID 定向删除特定记忆 | 定点手术 vs dream 的全量体检;/mem0-forget 支持按 MEM0_SESSION_ID 撤销本会话近期写入(见 mem0-forget/SKILL.md) |
/mem0-pin |
固定重要记忆 | 为 dream 提供 pinned 保护标记 |
推荐的运维节奏是:平时由插件 auto-dream 在门控满足时自动做确定性整理;当 /mem0-status --deep 提示质量问题、或用户感觉检索结果重复嘈杂时,手动运行 /mem0-dream 交互式处理矛盾;对需要精确删除的个别记忆,用 /mem0-forget 定向处理。
技能本身的工程约束与最佳实践
最后,该技能定义还包含两条值得注意的工程约定:
纯文本输出约束。 技能文档末尾明确要求:不要在输出中使用 Markdown。OpenCode TUI 会原样渲染文本,**bold**、## 标题、| 表格 | 都会以原始字符形式出现。正确做法是使用带缩进的纯文本、用 - 表示列表、用空格对齐列。这也是为什么本文展示的所有报告样例都是无 Markdown 的纯文本——它们就是技能要求 Agent 逐字打印的格式。
文档自身的可移植性。 这份 SKILL.md 的 front matter(name: mem0-dream 与 description)为宿主 Agent 提供了技能发现与触发依据——description 明确写出适用场景:"当记忆数量很大、搜索结果嘈杂或重复、需要周期性清理以维持记忆质量时使用"。OpenCode 插件通过 skills.paths 指向插件自带的 opencode-skills/ 目录实现就地发现,无需复制到用户级 skills 目录(见插件 README Config hook 说明)。
小结
mem0-dream 是一套把"记忆卫生"工程化的完整规范:以 .mem0.json/.mem0.md 承载保留策略、以固定纯文本 diff 承载审计、以严格的 Y/n 确认与 pinned 保护承载安全、以 --auto 模式与并发锁承载无人值守、再通过插件级门控把自动整合嵌入日常会话。对任何希望让 AI Agent 长期稳定工作的团队而言,掌握这套记忆整合机制,就等于掌握了让 Agent"越用越准、且不互相矛盾"的底层保障。
如果你想进一步动手,可从本文引用的三份关键文件入手:技能协议本身 opencode-skills/mem0-dream/SKILL.md、门控与锁的参考实现 dream.ts、以及技能清单与安装方式 .opencode-plugin/README.md。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00