首页
/ Mem0 插件 stats 技能:会话与项目级记忆统计、API 延迟测量与每周摘要的实现剖析

Mem0 插件 stats 技能:会话与项目级记忆统计、API 延迟测量与每周摘要的实现剖析

2026-09-04 13:45:24作者:伍希望

/mem0:stats 是 Mem0 官方插件(integrations/mem0-plugin/)内置的核心技能之一,用于在 Claude Code、Cursor、Codex 等客户端中以一条斜杠命令输出「当前会话 + 当前项目」两个维度的记忆统计仪表盘:会话内写了多少条记忆、检索了多少次、记忆按类别如何分布、记忆账龄(age distribution)、平台 API 往返延迟,以及可选的每周活动摘要(weekly digest)。读完本文,你将掌握该技能的完整执行流程(本地统计脚本 + MCP 工具调用 + 展示规则),理解其统计数据的采集链路(SessionStart/PostToolUse 钩子驱动 session_stats.py 落盘),并能依据 SKILL.md 的原始定义复现或定制一套记忆审计面板。

技能定位:一条 /mem0:stats 命令背后是什么

插件元信息 plugin.json 将 mem0 描述为「为 Agent 提供跨会话持久语义记忆的插件,包含 16+ 斜杠命令与生命周期钩子」;README 的技能列表中,/mem0:stats 的说明是「Session and project memory statistics」。技能的完整行为规范定义在 stats/SKILL.md 中,分为两大部分:

  • 标准统计仪表盘:三步走 —— 采集本地会话统计(Step 1)→ 从 Mem0 Platform API 拉取项目级终身统计并测量 API 延迟(Step 2)→ 按固定规则渲染紧凑表格(Step 3);
  • 每周摘要模式:以 --weekly 参数(如 /mem0:stats --weekly)触发,在标准仪表盘之后追加「本周新增记忆、最活跃类别、最活跃日期、亮点摘要」,并落盘摘要文件。

它的前置条件是整个 mem0 插件已完成安装与 /mem0:onboard 引导:MEM0_API_KEY 已导出、MCP 服务器(https://mcp.mem0.ai/mcp/,见 mcp_config.json)可连通、身份三元组(user_id / project_id / branch)可解析。这些前提决定了 stats 技能中 <active_user_id><active_project_id> 等占位符的取值来源。

Step 1:本地会话统计——session_stats.py peek

技能的第一步命令定义在 SKILL.md

SCRIPT_DIR="${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CURSOR_PLUGIN_ROOT:-}}}/scripts"
python3 "$SCRIPT_DIR/session_stats.py" peek 2>/dev/null || echo "{}"

三个要点:

  1. SCRIPT_DIR 的多客户端兼容写法:依次回退 CLAUDE_PLUGIN_ROOTCODEX_PLUGIN_ROOTCURSOR_PLUGIN_ROOT,使同一份技能文档在 Claude Code、Codex、Cursor 三种宿主环境下都能定位到插件的 scripts/ 目录;
  2. 为什么用 peek 而不用 report:文档明确说明 peek 返回 JSON 且不删除统计文件,而 report 会做清理(「unlike report」)。这一点在源码中可印证——peek() 只读不写,注释直接写明 “Return current stats as JSON without clearing the file”;
  3. 失败兜底2>/dev/null || echo "{}" 保证脚本缺失或报错时命令不中断,技能继续执行并在输出中标注 “No session data available”。

统计文件的数据结构与生命周期

session_stats.py 是一个 138 行的独立脚本,其设计可以从源码直接读出:

  • 单一文件、按用户隔离STATS_FILE/tmp/mem0_session_stats_${USER}.jsonUSER 缺省为 default)。每个用户在 /tmp 下只有一个统计文件,会话启动时重置;
  • 初始结构init()):adds(写入计数)、searches(检索计数)、categories(去重类别列表)、category_counts(类别计数映射)、recent_ids(近期写入的记忆 ID,上限 MAX_RECENT_IDS = 50)、started(ISO 时间戳);
  • record_add(category, memory_id):写入一次记忆时自增 adds,按类别累加 category_counts,并在提供了 memory_id 时把 {id, category, ts} 追加进 recent_ids,超出 50 条时裁剪最旧记录;
  • record_search():仅自增 searches
  • CLI 入口init | add <category> | search | peek | report 五个子命令(main()),report 在无数据时返回空字符串,CLI 层会打印 “Session: no memory operations.”。

测试套件 test_session_stats.py 对上述行为逐条断言:peek 不清空文件(test_peek_returns_json_without_clearing)、recent_ids 上限裁剪(test_recent_ids_capped,写入 60 条后断言长度恰为 50 且最旧 10 条被丢弃)、空类别不入 category_countstest_category_counts_empty_category_not_tracked)等,且通过 autouse fixture 将 STATS_FILE monkeypatch 到临时目录,避免污染真实 /tmp 状态。

统计数据是谁写入的:钩子采集链路

stats 技能只是「读者」,数据生产者插件的生命周期钩子。从 hooks.json 的钩子注册与脚本实现可以看到完整链路:

  • SessionStart → initon_session_start.shsource == "startup"(新会话)时执行 session_stats.py init 重置计数——注意 resume/compact 场景不重置,保证会话统计跨恢复保持累计;
  • PostToolUse → add/search:钩子 mem0-post-tool 匹配 mcp__mem0__.*|mcp__plugin_mem0_mem0__.*hooks.json),由 on_post_tool_use.sh 解析 tool_name:命中 *__add_memory 时从 tool_input.metadata.typemetadata.category 提取类别并调用 session_stats.py add "$CATEGORY";命中 *__search_memories|*__get_memories 时调用 session_stats.py search。两条记录均为非阻塞(2>/dev/null || true);
  • 自动捕获旁路auto_capture.py 在自动捕获记忆路径中也以 session_stats.record_add("auto_capture") 记账,这解释了统计面板中 auto_capture 类别来源。

因此技能文档强调:本地统计文件「accurately tracks adds and searches for the current session」——它的准确性不依赖平台 API 的过滤能力,而依赖钩子事件流的完整性。

Step 2:项目级终身统计与 API 延迟测量

会话统计回答「这次会话做了什么」,终身统计回答「这个项目积累了什么」。SKILL.md 的 Step 2(原文)规定了三件事:

1. 用 get_memories 拉取全量记忆并分组

调用 MCP 工具 get_memories,参数为:

  • filters={"AND": [{"user_id": "<active_user_id>"}, {"app_id": "<active_project_id>"}]}
  • page_size=100

分组优先级:

  1. categories[0](平台自动分配的类别)——主分组;
  2. metadata.type(Agent 写入时标注的类型)——次分组,仅在记忆没有平台类别时使用;
  3. created_at 日期——用于账龄(age)分析。

其中 user_id + app_id 的双重过滤是 mem0 插件的项目级作用域约定(on_session_start.sh 的启动横幅同样要求每次 search_memories/add_memory 都携带这两个字段)。类别来源则是 README 所述的「coding-tuned categories」机制:插件在会话启动时后台运行 setup_coding_categories.py 为项目安装 17 个面向开发场景的类别(architecture_decisionsbug_fixescoding_conventions 等),这就是 stats 面板中类别列的取值集合。

类别归一化规则auto_captureuncategorized 必须合并为一行 uncategorized——它们代表平台未能分配有意义内容类别的记忆,文档特别强调“不要在表格中把 auto_capture 单独列行”。

2. 会话统计只信本地文件,禁止用 API 过滤会话

这是 SKILL.md 中一条关键的设计约束(原文):不要用 run_idmetadata.session_id 过滤去 API 查会话数据,原因是记忆存储时不携带 run_id,且 session_id 的 metadata 过滤结果不一致(“unreliable results”)。从 on_post_tool_use.sh 的记录方式看,会话维度的数据本来就只写在本地统计文件里,API 侧并无可靠的会话键——这条规则本质上是在规避「查询模型与写入模型不匹配」的问题,把会话统计的数据源锁定为钩子写入的本地文件。

3. 用一次最小检索测量 API 往返延迟

search_memories(
  query="project",
  filters={"AND": [{"user_id": "<active_user_id>"}, {"app_id": "<active_project_id>"}]},
  top_k=1
)

技能要求记录 MCP 调用前后的时间戳,二者之差即为面板上 API: 84ms 这类延迟指标,并明确禁止改用原始 HTTP 调用(必须走 MCP 工具通道,测得的才是 Agent 真实感受到的链路延迟)。这与 health 技能 的连通性检查使用同一套「top_k=1 最小查询」手法,形成插件内一致的诊断范式。

Step 3:仪表盘展示与渲染规则

SKILL.md 规定了固定的紧凑输出模板(原文),明确要求「No ASCII bar charts — use a clean table layout」:

## mem0 stats

**Session** (<session_id, first 12 chars>) — 3 written, 5 searches, categories: decision, convention

**Project: my-project** — 55 memories, API: 84ms

| Category             | Count |
|----------------------|-------|
| decision             |    24 |
| convention           |    15 |
| anti_pattern         |     6 |
| task_learning        |     5 |
| user_preference      |     3 |
| session_state        |     2 |

**Age** — oldest: 2026-02-15, newest: 2026-05-23
  < 7 days: 5 · 7–30d: 12 · 30–90d: 10 · > 90d: 8

**Identity** — user: kartik · project: my-project · branch: main

配套的五条展示规则(原文):

规则 说明
类别表排序 按 Count 降序,省略 0 条记忆的空类别
账龄行 单行点号分隔的分桶(< 7 days / 7–30d / 30–90d / > 90d),由 created_at 计算
Session 行 本地统计文件不可得时整行跳过
小规模项目 总记忆数仅 1–2 条时跳过类别表,只展示总数
紧凑原则 不加装饰边框与填充内容

「Identity」行的 user / project / branch 三元组正是 Step 1 中 _identity.sh 解析出的 MEM0_RESOLVED_USER_IDMEM0_PROJECT_IDMEM0_BRANCH,与启动横幅展示的身份信息保持一致。

每周摘要模式(--weekly

/mem0:stats --weekly 触发时,技能在标准仪表盘之后追加一个五步流程(W1–W5):

  • W1 拉取近期记忆:并行发起三个带时间窗(created_at.gte = 7 天前,YYYY-MM-DD)的 search_memories 查询,top_k=20

    1. query="decisions made this week"
    2. query="bugs errors fixes"
    3. query="patterns conventions learnings"

    三者共用同一组 user_id + app_id 过滤。三路查询的语义切分(决策 / 缺陷 / 模式学习)与插件默认的 coding-tuned 类别体系对齐,用自然语言查询覆盖结构化类别难以穷举的本周动态。

  • W2 分析:按记忆 ID 合并去重,按 categories[0]metadata.type 归组到「New this week」;计算近 7 天新增数、最活跃类别、最活跃日期。

  • W3 展示:在标准统计之后追加固定格式区块:

### This week (May 16 – May 23)

+12 memories — most active: Wednesday (5)

| Category      | New |
|---------------|-----|
| decision      |   5 |
| task_learning |   4 |
| bug_fix       |   3 |

**Highlights**
- <2-3 sentence summary of most important decisions/learnings this week>
  • W4 落盘:摘要写入 ~/.mem0/weekly-digest.md(覆盖写),并向 ~/.mem0/digest-history.log 追加一行历史记录:
<YYYY-MM-DD> | <project_id> | +<new_count> memories | top: <top_category>
  • W5 空状态:7 天内无新增时仅输出一行 No new memories in the past week. Total: <N> memories in <project_id>.

digest-history.log 的「一行一摘要」设计使其天然可被后续技能做趋势对比(例如连续几周的项目活跃曲线),相当于把每次 weekly 运行压成一个可 grep 的事件点。

与 health 技能的配合:统计文件也是诊断信号

stats 技能的 Step 1 依赖本地统计文件存在,而该文件的存在性本身就是一个可诊断信号。health 技能 的 Check 5 正是验证这一点:

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

其诊断逻辑:文件由 SessionStart 钩子创建、PostToolUse 钩子持续更新;若 stats 技能看到空数据而 health 又报告 FAIL,大概率是钩子尚未触发(会话尚未真正启动过工具调用),应先发送一条消息再复查。这构成了「stats 读数据 → health 查数据源 → hooks.json 定数据来源」的排障闭环。

适用前提与限制

  • 运行环境:stats 技能依赖 mem0 插件完整安装(MCP 服务器 + 钩子)且已完成 /mem0:onboard 引导;MEM0_API_KEY 必须是 m0- 前缀的平台密钥。仅以「Direct MCP」方式接入(如 Codex 手动配置 config.toml)而没有钩子的场景下,本地统计文件不会生成,仪表盘会退化为只剩 Project 行;
  • 会话统计的语义边界adds/searches 统计的是「经过插件工具通道的 MCP 调用」(钩子只匹配 mcp__mem0__* 工具名),直接以 SDK 方式写入的记忆不计入会话统计;peek 返回的 recent_ids 同样受 50 条上限约束;
  • 终身统计的规模假设:Step 2 使用 page_size=100get_memories,SKILL.md 未定义翻页逻辑,可推断其面向「单项目记忆量级在百条上下」的典型开发场景,超大项目下类别计数以首页 100 条为样本;
  • 平台依赖:延迟指标与账龄数据均依赖 Mem0 Platform API 的可用性与 created_at 字段的准确性,OSS 本地部署形态不在此技能的适配范围内。

小结

/mem0:stats 的价值不在「查一下有多少条记忆」,而在于它把三个本不互通的信息源拼装成了一致的审计视图:钩子采集的本地会话行为session_stats.py 的 JSON 统计文件)、平台侧的终身记忆资产get_memories + 类别归一化 + 账龄分桶)、以及一次 top_k=1 检索测得的真实链路延迟。其工程细节——peekreport 的读写语义区分、拒绝用不可靠 API 过滤查会话、auto_capture/uncategorized 合并、weekly 摘要的双文件落盘——都直接写在 SKILL.md 中并有 测试用例钩子脚本 佐证,是研究「Agent 插件如何做运行时可观测性」的一个完整样本。

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

项目优选

收起
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
980
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384