首页
/ Mem0 插件 /mem0:forget 技能详解:基于确认机制的记忆删除、撤销与会话统计源码解析

Mem0 插件 /mem0:forget 技能详解:基于确认机制的记忆删除、撤销与会话统计源码解析

2026-09-04 17:13:36作者:滕妙奇

本篇围绕 mem0 插件中的 /mem0:forget 技能(SKILL.md)展开:它定义了 AI 编码代理(Claude Code、Codex、Cursor 等)删除持久记忆时“按查询或 ID 检索、逐条确认、删除后汇报”的完整执行协议,以及如何通过会话统计脚本撤销最近写入的记忆。读完本文,你将掌握该技能的五步执行流程与“撤销最近写入”的底层数据流,并能结合 session_stats.py 源码理解 recent_ids 的写入、上限与读取机制,以及对应的测试依据。

/mem0:forget 的定位:mem0 插件记忆生命周期中的删除入口

mem0 插件为 AI 代理提供跨会话的持久语义记忆,通过 Mem0 Platform 的 MCP 服务器暴露 add_memorysearch_memoriesget_memoryupdate_memorydelete_memory 等工具(见 README.md 的 MCP Tools 表格)。插件共提供 17 个 /mem0: 前缀的斜杠命令技能,其中 /mem0:forget 的定位是:通过搜索查询或记忆 ID 删除记忆,且删除前必须经过用户确认

SKILL.md 的 frontmatter 可以看到该技能的官方描述:

  • name: forget
  • description: “Deletes memories by search query or memory ID with confirmation before removal. Use when removing outdated decisions, incorrect memories, sensitive data, or cleaning up after experiments. Also handles undo of recent additions.”

也就是说,该技能覆盖两类典型场景:

  1. 主动清理:删除过时的决策记录、错误的记忆、敏感数据,或实验结束后的残留记忆;
  2. 撤销写入:用户说 “undo last N memories” / “undo last write” 时,回滚本会话刚刚写入的记忆。

技能还明确了两条硬性约束:没有确认绝不删除(This is destructive);所有检索都限定在当前用户与项目作用域内(通过 user_id + app_id 过滤器)。

使用前提

运行该技能的前提是插件已完成安装并连通 Mem0 Platform:

  • 已设置 MEM0_API_KEY(以 m0- 开头的平台密钥);
  • MCP 服务器已注册,工具调用形如 mcp__mem0__delete_memoryhooks.json 中钩子匹配器即使用 mcp__mem0__.*|mcp__plugin_mem0_mem0__.* 这类正则识别 mem0 工具调用);
  • 会话中已确定 active_user_idproject_id(分别对应记忆作用域的 user_idapp_id)。

技能文档中的 SCRIPT_DIR 解析也体现了多宿主兼容:

SCRIPT_DIR="${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-}}}/scripts"

即依次回退 Claude、Codex、Cursor 三类宿主注入的插件根目录变量。

五步执行流程:从输入解析到删除汇报

下面完整继承 SKILL.md 的 Execution 部分,并结合 MCP 工具语义逐项说明。

Step 1:解析输入

用户可能提供两种形式的参数:

  • 搜索查询/mem0:forget auth module decisions
  • 记忆 ID/mem0:forget <memory_id>(形如 UUID 或十六进制字符串)

如果未提供参数,代理必须反问:“What should I forget? Provide a search query or memory ID.”——不允许在缺参时做任何猜测性删除。

Step 2:定位记忆

分支 A:提供了记忆 ID

  1. get_memory 工具按 ID 拉取该记忆,验证其确实存在;
  2. 向用户展示摘要:Found: "<memory content first 120 chars>" (created <date>)——只展示内容前 120 个字符,避免长文本刷屏。

分支 B:提供了搜索查询

  1. 调用 search_memories,参数约定为:
    • query=<用户查询>
    • filters={"AND": [{"user_id": "<active_user_id>"}, {"app_id": "<project_id>"}]}——AND 语义保证只检索“当前用户 + 当前项目”作用域内的记忆,防止误删其他项目/用户的记忆;
    • top_k=10——一次最多召回 10 条候选。
  2. 以编号列表展示结果,固定格式:
Found <N> memories matching "<query>":
1. <content, 120 chars> (type: <type>, created: <date>) [ID: <short_id>]
2. ...

每行同时给出内容摘要、类型(type)、创建时间和短 ID,让用户可以按编号或 ID 精确指定删除对象。

Step 3:确认(技能的强制安全门)

  • 对搜索列表,询问:“Delete which memories? Enter numbers (e.g., 1,3,5), 'all', or 'cancel'.”——支持多选编号、全选 all 或取消 cancel
  • 对单一记忆 ID,询问:“Delete this memory? [y/N]”——默认否定(N 大写),需要用户显式输入 y;
  • 技能文档以加粗强调:Never delete without confirmation. This is destructive.

这一步是破坏性操作与用户意图之间的唯一闸门,任何自动化流程(包括代理的“自主模式”)都不应跳过。

Step 4:执行删除

对每一条被确认的记忆,调用 delete_memory 工具并传入该记忆 ID。注意是逐条调用,而不是批量清空——批量清空属于另一个独立工具 delete_all_memories(见 README.md 的工具表),不在本技能的职责范围内。

Step 5:汇报结果

  • 成功时输出:Deleted <N> memories.
  • 若部分删除失败,必须逐条报告哪条失败、失败原因,不允许静默吞掉错误。

从仓库钩子代码还可以看到删除操作的旁路观测:on_post_tool_use.shcase 分支中,*__delete_memory 命中后只调用 telemetry.py tool_use --tool=delete_memory 上报事件,不写入本地统计(__add_memory__search_memories|__get_memories 才会调用 session_stats.py add/search)。这说明删除是低频、高后果操作,被单独纳入遥测而不计入会话读写计数。

撤销最近写入:基于会话统计的 undo 机制

SKILL.md 的 “Undo recent writes” 章节定义了另一条入口:当用户说 “undo last N memories” 或 “undo last write” 时:

  1. 读取本会话最近写入的记忆 ID——执行只读探查命令:
SCRIPT_DIR="${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-}}}/scripts"
python3 "$SCRIPT_DIR/session_stats.py" peek
  1. 解析 JSON 输出中的 recent_ids 数组,每个元素含三个字段:id(记忆 ID)、category(分类)、ts(写入时间戳);
  2. 展示最近 N 条(默认 N=1)并请求确认;
  3. 对确认的条目逐条调用 delete_memory 删除。

如果 recent_ids 为空,则提示用户:“No recent memory IDs tracked this session. Try /mem0:tour to browse recent memories, or /mem0:forget <search query> to find specific ones.”——把用户引导到浏览或搜索路径,而不是直接报错。

源码解析:recent_ids 从哪来

上述撤销机制的数据源头是 session_stats.py。该脚本按用户维护一个临时统计文件:

STATS_FILE = f"/tmp/mem0_session_stats_{os.environ.get('USER', 'default')}.json"   # L21
MAX_RECENT_IDS = 50                                                                    # L45

关键函数行为(以 session_stats.py 源码为准):

  • init():新会话开始时重置全部字段,recent_ids 清空;
  • record_add(category, memory_id)adds 计数 +1、累加 category_counts,并在提供了 memory_id 时recent_ids 追加 {"id", "category", "ts"},超出 MAX_RECENT_IDS(50)时只保留最新的 50 条(滑动窗口裁剪);
  • peek():直接 json.dumps 返回当前统计,不删除统计文件(与 report() 清理文件的语义区分开)——这正是 forget 技能第一步 peek 命令能安全读取的原因。

写入链路:生命周期钩子如何填充统计

recent_ids 的数据由插件的生命周期钩子(hooks.json)写入,链路如下:

钩子事件 脚本 行为
SessionStart on_session_start.sh 调用 session_stats.py init,每用户每会话一个统计文件
PostToolUse on_post_tool_use.sh 从 stdin JSON 解析 tool_name,命中 *__add_memory 时提取 metadata.type/category 后执行 session_stats.py add "$CATEGORY";命中 *__search_memories|*__get_memories 时执行 session_stats.py search
PreToolUse(mem0 工具) enforce_metadata_defaults.sh 在强制 user_id/app_id 元数据默认值的同时,旁路追加 session_stats.py add / search 记录

需要注意一个实现细节:从源码结构看on_post_tool_use.sh 当前只向 session_stats.py add 传入分类一个参数,CLI 解析中 memory_id 取自第三个位置参数(session_stats.py),因此经由该钩子路径写入的 record_add 不会携带记忆 ID,recent_ids 保持为空;auto_capture.py 中的 session_stats.record_add("auto_capture") 同样未传 ID。这解释了 SKILL.md 为何专门设计了 recent_ids 为空时的兜底话术——撤销流程在“本会话未记录到记忆 ID”的场景下会优雅降级到 /mem0:tour(浏览)或 /mem0:forget <query>(搜索)两条路径,而不是失败。若后续某条写入路径开始向 record_add 传入真实记忆 ID,undo 流程即可直接按 ID 删除,无需搜索。

测试证据

test_session_stats.py 对 undo 机制依赖的每个行为都有针对性断言:

  • test_recent_ids_tracked:连续两次带 ID 的 record_add 后,recent_ids 按序保存 idcategory
  • test_recent_ids_capped:写入 60 条后 recent_ids 恰好保留 50 条,且最早保留项为第 10 条(id-{10}),验证了滑动窗口裁剪;
  • test_recent_ids_empty_without_memory_id:不带 memory_id 的 record_add 不会向 recent_ids 追加任何条目;
  • test_peek_returns_json_without_clearingtest_cli_peekpeek 以 JSON 形式返回完整统计且不删除文件,CLI 子进程调用同样可用;
  • test_cli_init / test_cli_report_no_data:验证 initreport 两个子命令的退出码与无数据兜底输出。

这些用例与 SKILL.md 的 undo 步骤一一吻合,可以作为该技能行为契约的回归依据。

与相邻技能的协作关系

/mem0:forget 不是孤立存在的,它在 17 个技能中承担“删除”这一环,与检索、保护类技能形成闭环(命令表见 README.md 的 Available Skills 一节):

场景 推荐命令 说明
找不到要删的记忆 /mem0:tour 按分类浏览全部记忆
快速定位候选 /mem0:peek 单行摘要式快速搜索
删除明确目标 /mem0:forget <query 或 ID> 本文主题,删除前强制确认
防止关键记忆被清理 /mem0:pin 为重要记忆加保护
记忆冗余/矛盾治理 /mem0:dream/mem0:memory-reviewer 合并重复、审计过期记忆,是删除前的“体检”环节

实践上较稳妥的顺序是:先用 /mem0:memory-reviewer/mem0:dream 找出重复、矛盾、过时的记忆,再用 /mem0:forget 精确删除,最后用 /mem0:stats 核对作用域内的记忆数量变化。

小结

  • /mem0:forget 以“检索(get_memory / search_memories + AND 过滤器)→ 编号/ID 确认 → 逐条 delete_memory → 结果汇报”的五步协议,把破坏性删除约束在“当前用户 + 当前项目”作用域内,并以确认门杜绝误删;
  • 其 undo 能力依赖 session_stats.pyrecent_ids(上限 50 条、滑动裁剪),由 SessionStart/PostToolUse 等钩子链路写入,peek 子命令提供无副作用读取;
  • tests/test_session_stats.py 对 ID 记录、50 条上限、空 ID 不落盘、peek 不清文件等行为均有断言,可作为理解该机制的权威参照。
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

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