首页
/ mem0-plugin Health 技能详解:五项连通性体检与记忆质量深度审计

mem0-plugin Health 技能详解:五项连通性体检与记忆质量深度审计

2026-09-04 15:08:29作者:伍希望

mem0-plugin 是 Mem0 为 Claude Code、Cursor、Codex 等编码智能体提供的插件(见 integrations/mem0-plugin/README.md),它通过 MCP 服务器与生命周期 Hook 为智能体提供跨会话的持久记忆。当记忆操作失败、搜索返回空结果、MCP 连接中断时,插件内置的 health 技能(integrations/mem0-plugin/skills/health/SKILL.md)提供了一套结构化的诊断流程:五项连通性检查逐项排查 API 密钥、身份解析、MCP 读写能力与会话统计状态,--deep 扩展模式还能对存量记忆做重复、过期、矛盾、孤儿四类质量扫描。读完本文,你可以掌握这套体检流程的每一步操作、判定标准,以及它背后的身份解析链、Hook 机制与统计文件实现。

技能定位与执行总原则

health 技能的元数据声明了它的触发场景(SKILL.md 的 frontmatter):

name: health
description: Diagnoses mem0 connectivity, API key validity, and memory read/write functionality.
  Use when memory operations fail, searches return empty, add_memory errors occur,
  MCP connection drops, or to verify the plugin is working correctly.

即:当记忆操作失败、搜索返回空、add_memory 报错、MCP 连接掉线,或只是想验证插件是否正常工作时应调用它。文档给出了一条关键执行原则:运行全部检查项后再展示单一汇总,不要在第一处失败时就停下("Run ALL checks, then display a single summary. Do not stop on the first failure.")。这样做的好处是用户可以一次拿到完整的故障面,而不是逐项排雷。

整个诊断体系由四条链路构成,下文按检查顺序逐一展开,并结合插件源码说明每项检查实际验证的底层机制。

检查一:API 密钥是否配置

第一步用最轻量的方式确认密钥存在,且只打印前 6 位以避免在会话日志中泄露完整密钥:

_KEY="${MEM0_API_KEY:-${CLAUDE_PLUGIN_OPTION_MEM0_API_KEY:-}}"
[ -n "$_KEY" ] && echo "${_KEY:0:6}..." || echo "NOT_SET"

判定标准很直接:

  • 输出 NOT_SETFAIL — "No API key configured";
  • 已设置:PASS,命令本身只回显前 6 个字符(如 m0-dVe...)。

从源码看密钥解析的完整优先级链

检查一验证的是最终生效的 MEM0_API_KEY,而插件实际的密钥解析逻辑在 _identity.sh 中,优先级依次为:

  1. MEM0_API_KEY 环境变量(显式设置 / shell profile);
  2. CLAUDE_PLUGIN_OPTION_API_KEY(由 Claude Code 的 userConfig 注入,对应安装时提示输入的密钥);
  3. CLAUDE_PLUGIN_OPTION_MEM0_API_KEY(旧版 userConfig 变量名);
  4. 从 shell profile 文件(~/.zshrc~/.bashrc~/.zprofile~/.bash_profile~/.profile)中用 grep 提取 export MEM0_API_KEY=... 字面量。

第 4 条 fallback 值得注意:桌面应用(如 Claude Cowork)不会从 shell profile 继承环境变量,只读 PATH,因此 _identity.sh 特意做了基于 grep 的提取——只接受字面量值,跳过 ${OTHER_VAR} 这类变量引用,避免 source 整个 profile 产生副作用。这也解释了 health 检查只查 MEM0_API_KEYCLAUDE_PLUGIN_OPTION_MEM0_API_KEY 两个变量的原因:其余来源最终都会归一到 MEM0_API_KEY

MCP 连接本身的认证方式由 mcp_config.json 定义:服务器地址为 https://mcp.mem0.ai/mcp/,请求头 Authorization: Token ${MEM0_API_KEY}。所以检查一失败意味着后面所有 MCP 调用都会失败,它是整条诊断链的地基。

检查二:身份解析(user_id / project_id / branch)

mem0 的记忆是"按身份隔离"的,所有读写都要携带 user_idapp_id(项目 ID)。health 检查要求用插件自己的解析脚本来解析身份,确保诊断结果与 Hook 实际使用的值一致:

SCRIPT_DIR="${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-}}}/scripts"
source "$SCRIPT_DIR/_identity.sh" 2>/dev/null
echo "user_id=${MEM0_RESOLVED_USER_ID:-}"
echo "project_id=${MEM0_PROJECT_ID:-}"
echo "branch=${MEM0_BRANCH:-}"

注意这里用 CLAUDE_PLUGIN_ROOT / CODEX_PLUGIN_ROOT / CURSOR_PLUGIN_ROOT 三级变量逐级回退,以适配不同平台注入的插件根目录。当 CLAUDE_PLUGIN_ROOT 不可用时的退化路径为:

  • user_id:取自 MEM0_USER_ID$USER
  • project_id:取自 MEM0_PROJECT_ID,或查 ~/.mem0/project_map.json$PWD 的映射;
  • branchgit branch --show-current

判定标准:三项全部非空为 PASS;任何一项退化为默认值则为 WARN

源码级解析细节

_identity.shuser_id 的处理极简(L48-L57):有 MEM0_USER_ID 就用它,否则用 $USER,再否则为 default。若 MEM0_USER_ID 与系统用户不同,还会导出 _MEM0_IDENTITY_ANNOTATION(形如 " (override; default: xxx)"),最终出现在会话启动横幅的状态行里,提醒当前用户身份是显式覆盖值。

project_idbranch 的解析在 _project.sh 中,项目 ID 的完整优先级为:

  1. MEM0_PROJECT_ID 环境变量(显式覆盖);
  2. ~/.mem0/project_map.json$PWD 查找(需要 jq);
  3. Git remote slug:去掉协议前缀与 .git 后缀,保留最后两级路径(owner/repo),其余 /: 替换为 -,例如 git@github.com:mem0ai/mem0.gitmem0ai-mem0;解析成功后还会把该 PWD → slug 映射持久化回 project_map.json(见 L56-L61),下次同目录启动直接命中第 2 级;
  4. 兜底:$PWD 的 basename。

branchgit branch --show-current,失败则记为 unknown

之所以要求"用插件自己的脚本解析"而非手工推导,是因为 on_session_start.sh 在会话启动时会把解析结果(MEM0_SESSION_IDMEM0_RESOLVED_USER_IDMEM0_PROJECT_IDMEM0_BRANCHMEM0_API_KEY)写入 CLAUDE_ENV_FILE,供后续 Bash 工具调用和 MCP 配置继承——health 检查直接 source 同一脚本,保证"诊断身份 = 运行时身份",不会出现"检查通过但 Hook 用错身份"的偏差。

另外,_identity.sh 还会从 ~/.mem0/settings.json 加载一组用户可改的配置并导出为环境变量,这些默认值与后文的保留策略直接相关:

配置项 环境变量 默认值 用途
auto_save MEM0_AUTO_SAVE true 是否自动捕获记忆
auto_search MEM0_AUTO_SEARCH true 是否自动检索记忆
search_limit MEM0_SEARCH_LIMIT 10 检索返回条数上限
retention_session_days MEM0_RETENTION_SESSION_DAYS 90 session 类记忆保留天数
confidence_threshold MEM0_CONFIDENCE_THRESHOLD 0.3 置信度阈值
global_search MEM0_GLOBAL_SEARCH false 是否全局搜索(跨用户/项目)
debug MEM0_DEBUG false 是否写 ~/.mem0/hooks.log 调试日志

检查三:MCP 服务器连通性(读路径)

通过 MCP 调用 search_memories 发一次最小化的真实查询:

  • query="health check"
  • filters={"AND": [{"user_id": "<active_user_id>"}, {"app_id": "<active_project_id>"}]}
  • top_k=1

判定标准:只要能成功返回(哪怕结果为空)即为 PASS——空结果只说明没有匹配记忆,不代表链路有问题;调用报错才是 FAIL,并需展示错误信息。输出格式中还包含实测延迟(如 PASS MCP Connection 142ms),这是唯一能真实验证 MCP 服务器(https://mcp.mem0.ai/mcp/)可达性、且同时验证密钥被服务端接受的一步:检查一只证明"本地有密钥",本检查才证明"密钥有效 + 网络通畅 + 服务在线"。

从源码结构看,这一步也顺带验证了 user_id + app_id 过滤语法是否可用——enforce_metadata_defaults.sh 这个 PreToolUse Hook 会在智能体遗漏身份参数时自动注入 filters.AND[](见 L78-L135),health 检查显式带上 filters,等价于验证了 Hook 注入后的最终形态。

检查四:记忆写入能力(写路径)

读通之后,再验证写链路。调用 add_memory 写入一条探针记忆:

  • text="Health check probe — safe to delete."
  • user_id=<active_user_id>
  • app_id=<active_project_id>
  • metadata={"type": "health_check", "probe": true}
  • infer=False

关键细节是 infer=False:探针写入不经过 LLM 抽取,直接落库,保证写入的是确定性数据而不是模型改写后的内容。

mem0 v3 API 的写入是异步的:add_memory 返回 event_id 而非记忆 ID,需要再调 get_event_status(event_id=<event_id>) 轮询处理状态。判定规则分三档:

事件状态 判定 后续动作
SUCCEEDED PASS 从事件结果中提取记忆 ID,调用 delete_memory 清理探针,不留垃圾数据
PENDING(5 秒后仍未完成) PASS 视为"写入已受理、处理延迟",服务端队列拥堵不等于链路故障
报错 FAIL 展示错误信息

"SUCCEEDED 后立即删除"构成一次完整的写→确认→读→删闭环:既然能从事件中取回记忆 ID 并成功删除,说明 delete_memory 这条路径同样可用,这正是汇总行 PASS Write/Read write + delete OK 的由来。

这里还有一个容易被忽略的机制:type: "health_check" 这个 metadata 字段是探针的"自报家门",与插件的元数据强制机制一脉相承。enforce_metadata_defaults.sh 在处理 add_memory 调用时,若 metadata 缺少字段会自动补齐默认值:confidence=0.7files=["*"]source="auto_capture"type="task_learning",并注入 session_id(来自 /tmp/mem0_session_id_$USER)用于会话内追踪。探针显式带了 type,就不会被改写成 task_learning,从而在后续质量扫描(检查 metadata.type)中可被清晰识别为探针残留。

检查五:会话统计追踪器

最后确认本地统计文件是否正常工作:

STATS_FILE="/tmp/mem0_session_stats_${USER}.json"
if [ -f "$STATS_FILE" ] && python3 -c "import json; json.load(open('$STATS_FILE'))" 2>/dev/null; then
  echo "OK"
else
  echo "FAIL"
fi

该文件由 session_stats.py 维护(STATS_FILE = /tmp/mem0_session_stats_{USER}.json,每个用户单文件、会话初始化时重置)。它记录 adds(写入次数)、searches(检索次数)、categories / category_counts(按类别统计)、recent_ids(最近 50 条记忆 ID,见 L45)和 started 时间戳,供 /mem0:stats 类技能汇总会话内的记忆活动。

写入时机在 on_session_start.sh 中可以确认:SessionStart Hook 在 source = startup 时执行 session_stats.py init 重置统计,并在 CLAUDE_ENV_FILE 中持久化身份变量。之后 PreToolUse 阶段的 enforce_metadata_defaults.sh 在每次 add_memory / search_memories / get_memories 工具调用时异步记录 add / search 计数(源码注释说明:插件 MCP 工具不触发 PostToolUse Hook,故统计放在 PreToolUse 侧)。

判定要点:文件不存在不一定是故障——文档明确提示,它由 SessionStart Hook 创建、由后续 Hook 更新,如果它缺失,很可能是会话 Hook 还没触发过,应"先发一条消息,再重新检查"。只有"发过消息后仍缺失或 JSON 无法解析"才真正说明 Hook 链断裂。debug 配置开启后所有 Hook 的 stderr 会追加到 ~/.mem0/hooks.log(见 on_session_start.sh),可作为进一步排查入口。

诊断结果展示格式

五项检查全部跑完后,按如下格式输出单一汇总:

## mem0 health

PASS  API Key          m0-dVe...
PASS  Identity         user=kartik, project=mem0, branch=main
PASS  MCP Connection   142ms
PASS  Write/Read       write + delete OK
PASS  Session Tracker  stats file active

All checks passed.

每行对应一项检查,值中携带最小化的证据(密钥前缀、身份三元组、延迟、读写结果、统计文件状态)。若任何一项失败,必须在汇总后追加 ## Troubleshooting 小节,逐项给出对应的具体修复步骤,而不是笼统建议"重新配置"。结合前文的机制,各失败项的典型修复方向是:API Key 失败 → 按 README 的 Step 1 重新配置(shell profile 或桌面应用本地环境变量编辑器);身份 WARN → 检查 MEM0_USER_ID / MEM0_PROJECT_ID 或清理 ~/.mem0/project_map.json;MCP 失败 → 核对网络与密钥有效性;会话追踪失败 → 触发一次对话再复查,并开 debug~/.mem0/hooks.log

扩展模式:--deep 记忆质量审计

/mem0:health --deep 方式调用时,在标准五项检查之外追加一轮记忆质量扫描。数据源统一为:get_memoriesfilters={"AND": [{"user_id": "<active_user_id>"}, {"app_id": "<active_project_id>"}]}page_size=200,拉取全量项目记忆后本地分析。

质量检查 1:重复记忆(Duplicates)

在同一 metadata.type 分组内两两比较文本重叠度(共享名词/关键词 > 60% 判定为疑似重复),输出:

Potential duplicates: <N> pairs
  [mem0:<id1>] ≈ [mem0:<id2>] — both about "<shared topic>"

按类型分组比较是有意为之:decisionbug_fix 两条记忆即使措辞相近也可能记录不同事实,组内比较才能避免误报。

质量检查 2:过期记忆(Stale memories)

标记满足以下任一条件的记忆:

  • metadata.typesession_statecompact_summary 且超过 90 天
  • metadata.confidence < 0.3 且超过 30 天
Stale candidates: <N>
  [mem0:<id>] — session_state, 142d old

90 天阈值与配置默认值呼应:retention_session_days 默认即 90(_identity.sh),而 0.3 的置信度下限对应 confidence_threshold 默认值(L72)。这两个数字同时出现在 dream 技能的内置保留策略表中(session_state / compact_summary 默认保留 90 天,见 dream/SKILL.md),说明 health 的"过期"判定与 dream 的"修剪"策略使用同一套标尺——health 负责发现,dream 负责处置。

质量检查 2b:低置信度记忆(Low-confidence)

独立于过期判断,凡 metadata.confidence < 0.5(不论年龄)单独列出:

Low-confidence memories: <N>
  [mem0:<id>] — confidence=0.3, "<content preview>"

与 0.3 的"过期门槛"不同,0.5 是"值得人工审视"的提示线;单独成组报告可避免低置信度但仍在有效期内的记忆被误判为过期。

质量检查 3:矛盾记忆(Contradictions)

在同一 metadata.type 组内,标记就同一主题断言相反事实的配对。文档强调使用语义判断——寻找否定模式、冲突的工具/框架选型、被推翻的决策,而不是字面匹配:

Possible contradictions: <N>
  [mem0:<idA>] vs [mem0:<idB>] — conflicting on "<topic>"

质量检查 4:孤儿记忆(Orphans)

未设置 metadata.type,或 metadata.type 不属于"17 个已知编码类别"的记忆,视为未经正确打标的孤儿数据:

Untagged/orphan memories: <N>

类别体系的自动安装由 auto_setup_categories.py 在会话启动后台完成(on_session_start.sh),正常情况下新写入的记忆都会带合法 type;出现孤儿通常意味着某次写入绕过了元数据强制 Hook(例如 global_search 模式或其他工具直写平台)。

质量汇总与处置

## Memory Quality

Duplicates: <N> · Stale: <N> · Contradictions: <N> · Orphans: <N>
  • 全部为 0:输出 Memory quality: clean.
  • 任一非零:追加一句 Run /mem0:dream to fix.

dream 技能(integrations/mem0-plugin/skills/dream/SKILL.md)是配套的自动整合流程:拉取全项目记忆 → 按保留策略(parse_mem0_config.py 解析项目 mem0.md,失败则用内置默认)识别近重复、矛盾与过期项 → 以 diff 形式展示全部拟议变更 → 经用户批准后才执行合并、修剪与冲突消解。health 与 dream 由此构成"体检—治疗"的分工闭环:health 的 --deep 输出量化问题面,dream 在用户确认下修复。

小结

health 技能把 mem0-plugin 的运行时链路拆成五个可独立判定的观测点,并刻意选择"与生产路径同源"的验证方式:检查二直接 source 生产 Hook 使用的 _identity.sh / _project.sh,检查三/四走与 Hook 注入后一致的 filters.AND[] 过滤语法,检查五验证 session_stats.py 所维护的统计文件。任何"诊断通过但实际不工作"的缝隙都源于诊断路径与生产路径不一致,该设计正是为消除这类缝隙。排查时建议按顺序执行:先标准五项定位断点(密钥 → 身份 → 读 → 写 → Hook 链),再以 --deep 评估存量记忆质量,发现问题后交由 dream 在用户确认下完成整合。

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