Claude Code x agentmemory 系统工作原理图解
把 agentmemory 的整体设计压缩成一篇可顺读、可检索、可复用的机制说明:从三层架构、12 个 Hook、写入与读取流水线,到四层记忆巩固与一次端到端时序。
三层全景Three-Layer Topology
整套系统由三个独立的进程组成。它们之间没有任何 SDK 级耦合——通信的唯一介质是 HTTP 请求和子进程的 stdin/stdout。
~/.claude/settings.json 里注册的 Hook 脚本完成。Hook 是 Claude Code 在每个生命周期点会自动 spawn 的子进程,这些脚本读取 stdin 的 JSON、调用 agentmemory 的 REST API、再退出。这种"零侵入"设计意味着:换一个 Memory 后端,只需改 Hook 脚本里的 URL,前端无感知。
12 个 Hook 触发点Lifecycle Hook Reference
Hook 是整个系统的"采集神经"。Claude Code 把生命周期切成 12 个时刻点,agentmemory 在每个点上挂一个动作——要么写入,要么召回,要么巩固。
| Hook 名 | 触发时机 | agentmemory 干什么 |
|---|---|---|
SessionStart | 用户打开新会话 | 召回:拉项目画像 + 三流检索 + 注入上下文 |
UserPromptSubmit | 用户回车送出 prompt | 捕获:记录用户提示(隐私过滤) |
PreToolUse | Agent 决定调用工具前 | 召回 + 捕获:注入文件历史 + 记录意图 |
PostToolUse | 工具执行完毕 | 捕获:核心写入点,记录 tool/input/output |
PostToolUseFailure | 工具调用失败 | 捕获:错误上下文,用于学习避坑 |
PreCompact | 上下文窗口即将压缩 | 召回:把记忆重新注入避免遗忘 |
SubagentStart | 子 Agent 启动 | 捕获:sub-agent 生命周期标记 |
SubagentStop | 子 Agent 结束 | 捕获:sub-agent 结果汇总 |
Stop | Agent 完成本轮回复 | 巩固:会话总结 + 知识图谱抽取 |
SessionEnd | 会话结束 | 巩固:Working → Episodic 转化 |
写入流水线 · 6 步Write Pipeline (6 Stages)
每次 PostToolUse 触发,数据要走完 6 道工序才能成为可被检索的"记忆"。这是 agentmemory 把"原始观察"变成"结构化记忆"的核心通道。
捕获 · Capture
Hook 子进程读取 stdin 的 JSON:{tool_name, tool_input, tool_output, session_id, cwd},HTTP POST 到 localhost:3111/agentmemory/observe。这一步必须在 AbortSignal.timeout() 保护下进行,避免拖累 Agent 主流程。
SHA-256 去重 · Dedup
对 observation 内容做指纹,5 分钟窗口内的重复内容直接丢弃。这一步专治"循环调用工具产生的噪声"——比如 Agent 反复 Read 同一个文件。
隐私过滤 · Privacy
正则扫描,剥离 API Key、密码、Token、<private> 标签包裹的内容。这一步在落库前完成——这是一道硬约束,确保敏感信息永远不进入持久化层。
原始入库 · Persist
落到 SQLite 的 Working 层(./data/state_store.db),打时间戳、session_id、project 标签。Raw observation 永久保留作为 ground truth,是后续所有 citation provenance 的源头。
LLM 压缩 · Compress(可选)
仅当 AGENTMEMORY_AUTO_COMPRESS=true 时启用。调用 LLM 把 raw observation 压成结构化的 {facts, concepts, narrative}。这一步是 token 大头——所以默认关闭,让用户按需开启。
双索引 · Dual Index
同时构建 BM25 倒排索引(词干 + 同义词扩展)和 Vector embedding(默认本地 all-MiniLM-L6-v2,免费)。这两个索引是后续"三流融合检索"中的两条主流,决定召回精度。
读取流水线 · 4 步Read Pipeline (4 Stages)
每次 SessionStart 触发,agentmemory 要在毫秒级内决定"这次会话该让 Agent 知道哪些历史"。这就是读取流水线的工作。
三流的分工逻辑
| 流 | 算法 | 何时启用 | 擅长什么 |
|---|---|---|---|
BM25 | 词干匹配 + 同义词扩展 | 始终 | 精确关键词、类名、变量名 |
Vector | 余弦相似度(dense embedding) | 配置了 embedding provider | 语义相似、概念召回 |
Graph | 知识图谱 BFS 遍历 | 查询中检测到实体 | 关系类查询(A 用了什么) |
4 层记忆巩固Four-Tier Consolidation
这是 agentmemory 最有"哲学味"的设计——它仿照人脑睡眠记忆巩固机制,把记忆按"短期 → 长期 → 知识 → 习惯"四级流转。
衰减规则
Ebbinghaus 曲线,常访问的记忆强化(importance score 上升),陈旧的自动驱逐。检测到矛盾时新事实覆盖旧事实,但旧事实保留在 audit trail 可追溯——这是 citation provenance 的根基。
端到端时序 · 一次"加 Auth"任务End-to-End Sequence
把所有零件串起来。下面是真实场景的完整时序:用户让 Claude Code 给 API 加 JWT 认证,agentmemory 在背景里默默地完成了从召回到捕获到巩固的全过程。
test/auth.test.ts、你选了 jose 而非 jsonwebtoken(有 Edge 兼容理由)。零重新解释。这就是整个系统存在的意义。
关键设计取舍Architectural Trade-offs
读完机制只是入门,理解为什么这么设计才是真正的洞察。下面是四个核心问题。
① 为什么用 Hook 而不是 MCP 主动调?
Hook 由 Claude Code 强制触发,模型无权决定要不要记——这是"零人工成本"的根基。MCP 工具需要模型主动调用,会受 prompt 工程影响。Trae、Cursor 这些还没有 Hook 体系的客户端,体验就明显打折扣。
② 为什么先 raw 入库再 LLM 压缩?
LLM 压缩会丢细节。raw observation 永久保留作为 ground truth,压缩版用于检索召回。两者通过 citation provenance 链接,任何检索结果都可回溯到原始观察。这一双层设计是审计与信任的关键。
③ 为什么三流融合而不是单纯 vector?
Vector 召回会漏精确关键词(如类名、变量名)。BM25 精确但不懂语义。Graph 处理关系类查询。RRF 让三者互补,LongMemEval-S 上 R@5 达到 95.2%,比 BM25-only 提升 9 个百分点。
④ 为什么默认本地 embedding?
all-MiniLM-L6-v2 离线、免费、+8pp recall over BM25-only。年成本 $0,对比 OpenAI text-embedding-3-small 的 $0.02/1M token,对长期使用极友好。"开箱即用且免费"是社区采纳率的护城河。
一句话总结TL;DR
Hook 是手,让 Claude Code 在 12 个时机自动喂数据;流水线是胃,把 raw observation 消化成结构化记忆;三流融合是嘴,把对的记忆在对的时机吐回去。三者构成一个不需要用户操心的"长期记忆器官"。