Maker Memory 实测审计:写多读少、发现开销近 40%、召回近乎为零(14 天日志分析与改进建议)
- Dominant language
- TypeScript
- Stars
- 2.7k
- Forks
- 401
- Avg merge
- 21h 48m
- Merged PRs (30d)
- 776
Description
## 背景
日常使用中体感 memory 调用偏频繁,于是对 Maker Memory 做了一次全量日志审计,评估它到底在产生价值还是在消耗时间。本 issue 汇报数据结论,并提出产品侧改进建议。
数据来源(近 14 天,2026-07-09 ~ 2026-07-23):
- Cindy 宿主 Codex 会话 rollout 日志 299 个(`codex-home/sessions/`)
- Cindy 宿主 / 独立 Claude Code 会话 transcript 113 个(`~/.claude/projects/`)
- maker-memory 存储目录(`maker-memory//`)与 `packages/maker-core/src/memory/` 源码交叉核对
- 统计口径覆盖 `cindy_memory` 与改名前的 `lizi_memory` 两个 MCP namespace
## 核心数据
14 天内真正发生记忆操作的会话只有 12 个(Codex 6 + Claude Code 6),但集中产生了约 150 次记忆相关调用:
| 调用类型 | 次数 | 说明 |
| --- | --- | --- |
| memory_write | 55 | 含大量同分片重复写(见下) |
| memory_read | 25 | 90%+ 是 update 前置读,不是开工回忆 |
| memory_search | 1 | FTS 检索近乎未被使用 |
| memory_consolidate | 9 | 维护性 churn |
| memory_list / delete | 4 | — |
| list_tools(渐进发现) | 44 | 每个会话重复 4~5 次 |
| tool_search(找 memory 入口) | 14 | — |
关键比例:**纯发现开销(list_tools + tool_search)占全部记忆调用的近 40%**;写入类调用是消费类(读+搜)的 ~2.5 倍,若剔除 update 前置读,真正的"回忆式消费"接近 0。
召回侧证据:
- 299 个 Codex 会话的 reasoning / 回复中,**0 次**按名引用记忆分片。
- 索引注入本身是生效的(实测某会话未调 memory_list 就按文件名直接读到了 32 分钟前另一会话新建的分片),但没有观测到它改变行为的正向案例;反例倒有:同一条 UI 反馈(自动任务 Timer 图标)用户在同日 18:19 和 18:51 两个会话里重复口述了两遍,记忆没有省掉这次重复沟通。
写入侧浪费的具体形态:
- 同分片单会话连写 3~5 次:`automation-task-timer-icon-contract` 一次 create + 三次 update;`feishu-meeting-minutes-lookup` 连续 update 5 次;`cindy_site_repo_workflow` create + append + append + update。
- 一次典型写入的完整链路:tool_search → list_tools ×4~5 → memory_read → memory_write → memory_consolidate,全部堆在任务收尾,单会话尾部多花 1~3 分钟。最重的一个会话累计 45 次记忆调用。
- 命名事故的产物至今留在索引里:`feedback_feedback_record-task-skill-scope.md`、`reference_reference_record-operations-generate-skill-issue.md`(LLM 把 type 前缀写进了 slug,存储层没有拦截);另有 `project_cindy-pricing-policy` 撞名重建 + delete 的完整试错链。
其它发现:
- 07-18 ~ 07-21 期间 Codex 原生 auto-memory 与 Maker Memory 双开并行注入(原生 User Profile 摘要约 13KB/会话,命中 122 个会话)。07-21 起 `setMemory(false)` 联动已生效,原生注入停止,但 `codex-home/memories/` 里仍留有 98KB MEMORY.md + 225KB raw_memories 死数据。
- 被动注入成本本身可接受:MAKER_MEMORY_RULES 3.5KB + 索引约 4KB ≈ 每会话 ~2k tokens。主要浪费在主动调用链路,不在注入。
## 改进建议
按性价比排序:
### 1. 消灭渐进发现开销
memory 的 read / write / search 是每次记忆操作的必经入口,却要走 `list_tools` 渐进发现,且发现结果不跨会话保留。建议二选一:
- 把 `memory_write` / `memory_read` / `memory_search` 直接顶层注册(维持 maintain 类工具走渐进发现);或
- 在 MAKER_MEMORY_RULES 里内联这三个工具的完整参数 schema,rules 反正每会话都注入。
预期直接砍掉约 40% 的记忆调用。
### 2. 给 memory_write 加 upsert 语义,压缩写入仪式
当前 create 撞名返回 `ALREADY_EXISTS` → LLM 改 update → update 又要求先 read,一条更新动辄 3~4 个来回。建议:
- 增加 `mode: "upsert"`(create-or-update),由存储层处理合并;
- rules 中明确"每个分片每会话最多写一次,任务收尾一次性写",禁止 append → append → update 连环。
### 3. 补上召回触发条件
`system-prompt.md` 详细教了"什么时候写",但没有一条"什么时候必须读"。索引注入了却没人消费,写入就是纯沉没成本。建议在 rules 增加明确触发:开工前若索引条目与当前任务相关,先 `memory_read` 对应分片再动手;并考虑在 flush-controller 之外,把"本会话读过哪些分片"纳入 usage 计量,让 read/write 比可观测。
### 4. 收紧写入触发信号
当前"用户任何纠正都存"过宽,一个 UI 微调会话能产生 4 次写入。建议 rules 收紧为:仅当内容跨会话可复用、且不属于仓库 docs 规则体系覆盖范围时才写;规则性质的内容应引导落到项目 docs,而非记忆。
### 5. 存储层修复与清理
- slug 校验拒绝(或自动剥离)与 type 相同的前缀,杜绝 `feedback_feedback_*` 类双前缀分片;
- Maker Memory 启用并关闭原生 auto-memory 时,提示或自动清理 `codex-home/memories/` 遗留数据(当前 320KB 死文件);
- 对存量库提供一次性去重/改名迁移(现有双前缀分片、语义重叠分片)。
## 验证方式
统计脚本为一次性分析(扫描 rollout jsonl 的 `function_call` payload 按 `namespace` 过滤 + Claude transcript 的 `tool_use` block),口径和原始数字都可复现;需要的话可以把脚本整理进 `scripts/` 供后续回归对比。
Contributor guide
Assessment
This issue has not been assessed yet.