首页
/ Mem0 Plugin Pin 技能解析:用 [PINNED] 标记保护关键记忆不被 Dream 整理裁剪

Mem0 Plugin Pin 技能解析:用 [PINNED] 标记保护关键记忆不被 Dream 整理裁剪

2026-09-04 20:45:48作者:柏廷章Berta

Mem0 插件(mem0-plugin)通过 /mem0:pin 命令将一条记忆标记为高优先级,使其在 /mem0:dream 记忆整理(consolidation)时永远不会被合并或裁剪。本文基于仓库中 pin 技能定义文件 完整拆解其执行流程、MCP 工具调用细节与"文本标记 + 元数据"的双重保护机制,并结合 dream 技能 与插件配置说明该机制如何真正落地生效。读完后你将能够:手动执行 pin/unpin 全流程、理解为什么 pin 走的是 text 前缀而不是 metadata 参数、以及确认哪些记忆值得被 pin。

1. 背景:Mem0 插件的记忆体系与 pin 的定位

Mem0 插件是为 Claude Code、Claude Cowork、Cursor、Codex、OpenCode 和 Antigravity 等编码 Agent 提供的持久记忆插件,插件清单见 plugin.json(Apache-2.0 许可,16+ 斜杠命令 + 生命周期钩子)。它连接 Mem0 远程 MCP 服务(连接配置见 mcp_config.json,认证使用 MEM0_API_KEY 环境变量),提供一套记忆操作工具。与 pin 直接相关的工具有(工具清单来自 插件 README):

工具 说明
add_memory 为用户/Agent 保存文本或对话历史
search_memories 带过滤条件的语义搜索
get_memory 按 ID 获取单条记忆
update_memory 按 ID 覆盖记忆文本
delete_memory 按 ID 删除单条记忆

插件自带 17 个可通过 /mem0: 调用的技能,其中 /mem0:pin 的职责在 README 中概括为一句:"Protect critical memories from pruning"(保护关键记忆免于裁剪)。pin 技能文件头部的描述给出了明确的适用场景:架构决策(architecture decisions)、安全约束(security constraints)、不可变的团队约定(immutable team conventions) 这类"绝不能被删掉"的记忆。

为什么需要"绝不能被删"?因为 dream 技能 会在记忆数量变多时执行整理:合并近重复项、裁决矛盾、按保留策略裁剪过期条目。如果没有保护机制,一条"本系统禁止在测试库写入真实数据"的安全约束,完全可能因为置信度低或过期而被自动清理掉。pin 就是为此设计的显式豁免开关。

2. 完整执行流程:从查找记忆到固定

pin 技能的执行流程分四步,全部基于 MCP 工具完成,无任何本地存储副作用。

2.1 第一步:定位目标记忆

用户输入有两种形态,处理路径不同:

  • 提供记忆 ID:直接调用 get_memory(memory_id=<ID>)
  • 提供搜索词:调用 search_memories,参数固定为:
search_memories(
    query=<用户的搜索词>,
    filters={"AND": [{"user_id": "<active_user_id>"}, {"app_id": "<active_project_id>"}]},
    top_k=5,
)

注意两个约束:filters 必须同时限定 user_idapp_id(即"当前用户 + 当前项目"作用域,app_id 承载项目维度),top_k=5 限制候选数量。随后向用户展示带编号的内容预览列表,并询问 "Which memory to pin? Enter a number."。

这个"作用域过滤 + 候选列表 + 人工确认"的模式与 forget 技能 保持一致——pin 虽然是写操作,但同样要求用户对具体哪条记忆负责。

2.2 第二步:读取当前内容

确定目标 ID 后,再次调用 get_memory 读取其完整内容,并在内存中保存两个值:

  • original_text — 记忆的文本内容;
  • original_metadata — 现有的 metadata 字典。

这一步看似冗余(第一步可能已读到内容),但它是幂等性检查的前提:pin 操作必须基于最新原文判断标记是否已存在,避免重复操作时破坏元数据或产生多余前缀。

2.3 第三步:执行 pin——文本标记机制

这是整个技能最关键、也最容易误解的部分。原文档明确指出:

MCP 的 update_memory 工具只接受 memory_idtextsource 三个参数——它不接受 metadata 参数

因此 pin 不能通过 update_memorymetadata.pinned=true,而是退而求其次,在文本层面追加标记:

pinned_text = (
    "[PINNED] " + original_text
    if not original_text.startswith("[PINNED]")
    else original_text
)
update_memory(memory_id=<selected_id>, text=pinned_text)

两个实现细节值得注意:

  1. 幂等性startswith("[PINNED]") 检查确保重复 pin 同一条记忆时不会变成 [PINNED] [PINNED] ...
  2. 标记格式:固定为 [PINNED] (含一个空格)前缀,这决定了后续 unpin 和 dream 识别都依赖这一定长前缀。

对于尚未入库的新文本(用户想把一段话直接固化为已 pin 的记忆),流程改为 add_memory 一步到位:

add_memory(
    text="[PINNED] <用户的文本>",
    user_id=<active_user_id>,
    app_id=<active_project_id>,
    metadata={"pinned": True, "type": "decision", "confidence": 1.0},
    infer=False,
)

这里有两个参数约定:

  • metadata 中写入 pinned: truetype: "decision"confidence: 1.0——新记忆在元数据层面也被标记,与文本前缀形成双重标记(详见第 3 节);
  • infer=False 关闭 Mem0 的自动抽取/推断,保证文本被逐字存储,[PINNED] 前缀不会被记忆抽取管线改写。

add_memory 的响应中只返回 event_id 而非记忆 ID,因此技能要求再调用一次 get_event_status(event_id=<event_id>) 换取真正的 memory ID,然后向用户确认。

2.4 第四步:确认输出

pin 完成后,输出格式被严格约束为:

Pinned: "<记忆内容前 80 字符>"
Memory ID: <id>

仅当内容超过 80 字符时才追加 ... 省略号。这种"固定截断长度 + 省略号规则"的约定保证了 Agent 输出的一致性,也方便用户核对 pin 的是不是自己选的那条。

2.5 解除 pin(unpin)

当用户说 "unpin" 时,流程是对称的三步:

  1. get_memory 读取当前内容;
  2. 移除文本标记:
unpinned_text = original_text.removeprefix("[PINNED] ")
update_memory(memory_id=<id>, text=unpinned_text)
  1. 打印 Unpinned: "<内容>..."

注意 unpin 只移除文本前缀。对于存量记忆(第三步 pin 的旧路径),update_memory 本来就不写 metadata,所以解除后文本与元数据都不带 pinned 痕迹;而对于"新记忆直接 pin"路径写入的 metadata.pinned=true,unpin 流程并未给出对应的清除步骤——从技能文本看,该路径的元数据标记会保留,这是一个可以推断出的实现边界,实际操作中如需彻底解除应自行通过支持 metadata 写入的通道(如 Mem0 SDK 或 REST API)处理。

3. 机制剖析:为什么文本标记能让 Dream 跳过这条记忆

pin 的保护效果不在 pin 技能内部实现,而由 dream 技能的整理规则 兑现。dream 是 Mem0 的记忆整理通道:拉取项目全部记忆(get_memoriespage_size=200 分页取完),在内存中分析出三类问题——近重复对(合并候选)、矛盾对(需人工裁决)、过期条目(裁剪候选)——打印 diff 报告并经用户确认后应用修改。

pin 在其中体现为两条硬性规则:

规则一:pin 记忆永不进入裁剪候选。 dream 的 Step 3c 规定,裁剪候选的判定条件是"类型有保留策略且超过保留天数"或"置信度低于 0.3 且不含项目独有信息",但紧接着有一条加粗的强制例外:

Always skip memories where metadata.pinned == true, regardless of age or confidence.(无论年龄或置信度,凡 metadata.pinned == true 的记忆一律跳过)

规则二:pin 记忆不参与近重复合并。 dream 的 Step 3a 中,两条记忆构成近重复对需要同时满足三个条件:词法相似度足够高(>60% 实词重合近似余弦 0.9)、metadata.type 相同、且双方均未 pin(metadata.pinned != true

把两节串起来看,pin 技能采用了"双通道标记"策略:

通道 载体 写入方式 dream 如何消费
文本前缀 [PINNED] text 字段 update_memory / add_memory(MCP 工具唯一可写文本) 人类与 Agent 可读,search_memories 结果中一眼可辨
元数据 pinned: true metadata 字段 add_memory 路径(新记忆) dream 裁剪/合并规则的程序化判定依据

之所以需要双通道,根源在于第 2.3 节提到的工具限制:MCP 的 update_memory 不能写 metadata,只能改 text。对存量记忆,pin 只能保证文本前缀这个"软标记",dream 规则中依赖的 metadata.pinned 判定在存量路径下依赖该记忆原本就有的元数据;对新记忆,则两条通道同时落位,保护是完备的。从源码结构看,这种不对称正是技能文档把"新记忆"单列一步、并在其中显式写 metadata={"pinned": true, ...} 的原因。

值得补充的是插件侧的配套机制:hooks.json 中注册了 mem0-enforce-metadata 钩子(PreToolUse 匹配 mcp__mem0__.* 工具调用,执行 scripts/enforce_metadata_defaults.sh),会对记忆写入做元数据补全与约束。也就是说,pin 标记并不是孤立约定,而是嵌在插件"元数据强制 + 分类标签"的整体纪律里——插件 README 说明插件会自动安装 17 类面向编码场景的记忆分类(architecture_decisionssecurity_constraintsteam_norms 等),这些恰好就是 pin 技能描述中点名的适用对象。

4. 使用与验证边界

  • 前提:插件已安装且 MEM0_API_KEY 已配置(README 要求安装前先完成 API key 配置),MCP 服务可达。
  • 作用域:所有搜索与写入都绑定 user_id + app_id(当前用户与当前项目),pin 不会跨项目生效。
  • 可验证点:pin 之后可运行 /mem0:tour/mem0:peek 浏览记忆,确认内容以 [PINNED] 开头;随后执行 /mem0:dream,在输出的 Prune/Merges 清单中确认该记忆未被列为候选,即可端到端验证保护链路。
  • 与其他命令的边界/mem0:forget 是定向删除(搜索 + 确认 + delete_memory),它不做 pinned 豁免判断——pin 保护的是 dream 的自动整理,不代表 forget 时不能删;/mem0:memory-reviewer 做质量审计(重复、矛盾、过期)但不自动修改,与 pin/dream 配合形成"审计 → 固定/整理"的工作流。

5. 小结

pin 技能展示了 Mem0 插件处理"MCP 工具能力受限"问题的一个典型手法:当 update_memory 不支持 metadata 写入时,用文本前缀 [PINNED] 承载用户可见的固定状态,用 add_memoryinfer=False + metadata.pinned 承载新记忆的完整标记,最终由 dream 的整理规则("pinned 一律跳过裁剪、不参与合并")兑现保护承诺。对使用者而言,关键结论是:把架构决策、安全约束、不可变的团队约定 pin 起来,是保证它们在长期 Agent 使用与记忆自动整理中不被侵蚀的标准做法;而 unpin 时只清除文本前缀这一实现细节,也提示我们在处理"新记忆直接 pin"路径写入的元数据时需要留意其残留。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
504
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384