claude-mem 深度拆解:AI 编码助手的「海马体」是怎么造出来的——钩子采集、语义压缩与渐进式披露全链路解析
一、背景:AI 编码助手的「金鱼记忆」问题
用过 Claude Code、Codex 这类终端编码 Agent 的人,大概率都经历过这样的循环:
- 昨天花了两个小时,跟 AI 一起把项目的鉴权模块重构完,中间做了七八个关键决策;
- 今天新开一个会话,AI 一脸茫然:「请问这个项目是做什么的?」
- 你只能把 README、架构说明、昨天的决策再喂一遍,烧掉几万 token,才把它「教」回昨天的状态。
这就是所有会话式 AI 工具的原罪:会话是无状态的。上下文窗口再大,也只是「工作记忆」,会话一关,一切归零。而人类工程师的价值,恰恰有很大一部分建立在「项目记忆」上——知道为什么当初选了 PostgreSQL 而不是 MySQL,知道那个诡异的 bug 是因为时区处理,知道哪个模块碰不得。
claude-mem(GitHub: thedotmack/claude-mem)就是冲着这个问题来的。它是一个 Claude Code 插件,给 AI 编码助手装上一个「海马体」:自动捕获每次会话中发生的事,用 AI 压缩成结构化记忆,存进本地数据库,并在未来的会话里按需注入相关上下文。
这个项目在 2026 年初开源后迅速蹿红,几个月内 Star 数从 3 万一路涨到 7 万+,长期霸榜 GitHub Trending。它火的原因不难理解:这是第一个把「Agent 记忆」做成开箱即用的工程产品而非论文 demo 的项目——一条 npx claude-mem install 就能跑起来。
但比「怎么用」更值得拆的,是它的架构。因为 claude-mem 本质上回答了一个所有 Agent 开发者都绕不开的问题:
记忆不是把聊天记录存下来,而是一条「采集 → 压缩 → 索引 → 检索 → 注入」的流水线。
这篇文章我们把这条流水线一段一段拆开看。
二、核心概念:记忆 ≠ 聊天记录
在看架构之前,先纠正一个最常见的误区:很多人以为给 AI 加记忆,就是把历史对话存起来,下次全部塞回上下文。这条路走不通,原因有三:
1. Token 经济学不允许。 一次中等强度的编码会话,工具调用输出轻松上万 token。存十次会话就是十几万 token,别说塞不进上下文窗口,就算塞得进,每次会话的前置成本也会贵到离谱。
2. 信噪比太低。 会话记录里 90% 是噪音——文件读取的原始输出、失败的尝试、中间状态。真正值得记住的,是「决策」和「结论」,不是「过程流水账」。
3. 检索粒度不对。 你需要的是「上次数据库连接池是怎么配置的」这种问题级检索,而不是把某天下午的完整聊天记录翻出来人肉查找。
claude-mem 的设计哲学可以概括成一句话:把高概率会被再次需要的信息,自动转化为结构化、可检索、可逐层展开的「观察记录」(observations)。
它对记忆做了明确的类型划分,每条观察记录会被归类为以下几种之一:
- 决策(decision):「选用 JWT 而非 session,因为要支持多端」
- Bug 修复(bugfix):「订单重复提交是因为前端防抖失效 + 后端无幂等键」
- 功能(feature):「新增了 webhook 重试机制,指数退避,最多 5 次」
- 重构(refactor):「把支付逻辑从 controller 抽到了 service 层」
- 发现(discovery):「这个项目的配置加载顺序是 env > yaml > 默认值」
- 变更(change):普通的代码修改记录
每条记录还会打上概念标签(如 authentication、database)和文件引用(如 src/auth/jwt.ts),这两个维度后面在检索时会起大作用。
原始工具输出通常在 1000~10000 token 之间,经过 Claude Agent SDK 压缩后,一条观察记录约 500 token——压缩比大致在 2:1 到 20:1。这是整个系统在 token 经济学上成立的前提。
三、架构分析:一条五段式流水线
claude-mem 的整体架构,从数据流角度看是这样的:
┌─────────────┐ ┌──────────────┐ ┌──────────────┐ ┌─────────────┐ ┌──────────────┐
│ 钩子采集 │ → │ Worker 服务 │ → │ AI 语义压缩 │ → │ 持久化存储 │ → │ 检索与注入 │
│ (lifecycle │ │ (HTTP API + │ │ (Agent SDK) │ │ (SQLite FTS5│ │ (渐进式披露) │
│ hooks) │ │ Web UI) │ │ │ │ + 向量库) │ │ │
└─────────────┘ └──────────────┘ └──────────────┘ └─────────────┘ └──────────────┘
3.1 钩子系统:观察者模式的教科书应用
claude-mem 不修改 Claude Code 本身,而是利用其插件生命周期钩子(hooks),以观察者模式旁路挂载。核心钩子包括:
| 钩子 | 触发时机 | 职责 |
|---|---|---|
context-hook | 会话启动(SessionStart) | 拉起 Worker 服务,把最近记忆注入初始上下文 |
new-hook | 用户发送新消息 | 创建会话记录,保存提示词 |
save-hook | 工具执行完成后 | 捕获文件读写、命令执行等操作输出 |
summary-hook | 会话结束 | 生成 AI 摘要并持久化 |
cleanup-hook | 收到停止指令 | 清理临时数据 |
这个设计有两个值得学习的点:
第一,采集与处理彻底解耦。 钩子只做一件事:把事件甩给本地 Worker 服务,立刻返回。压缩、入库这些重活全部异步处理,主会话的响应速度不受任何影响。这是所有「旁路增强型」工具的黄金法则——你可以增强宿主,但绝不能拖慢宿主。
第二,捕获点选在「工具调用」而非「对话消息」。 这是一个很聪明的取舍。对话消息里废话多,而工具调用(读了哪个文件、跑了什么命令、改了什么代码)是行为的 ground truth。从行为反推意图,比从聊天里挖信息靠谱得多。
3.2 Worker 服务:为什么需要一个常驻进程
钩子是短命的脚本进程,而压缩需要调用 LLM(有延迟)、入库需要维护数据库连接、检索需要加载索引——这些都需要一个常驻服务来承载。claude-mem 用 Bun/Node 起了一个本地 Worker,通过 PM2 做进程管理,对外暴露 HTTP API,顺便还提供了一个 Web UI 用来浏览和搜索记忆。
这个「本地钩子 + 常驻 Worker」的架构模式非常通用,几乎所有需要给 CLI 工具加持久化能力的场景都适用:
短命进程(hook/CLI) --HTTP--> 常驻 Worker --维护--> 数据库/索引/LLM 连接
好处是状态集中、连接复用、崩溃隔离;代价是多一个进程要管理(所以引入了 PM2 做守护和开机自启)。
3.3 存储层:SQLite FTS5 + 向量库的混合检索
claude-mem 的存储选型是「双引擎」:
- SQLite + FTS5:全文检索,负责关键词精确匹配。查
connection pool、查文件名、查报错信息,FTS5 又快又准,而且零部署成本——就是一个本地文件。 - Chroma 向量库:语义检索,负责「意思相近但用词不同」的场景。你问「登录怎么做的」,它能召回标记为
authentication的记忆,哪怕原文里没有「登录」两个字。
两路召回结果做混合排序(hybrid search),这是当前 RAG 领域公认的最佳实践:BM25 类精确匹配负责「查得准」,向量检索负责「想得开」,二者互补。
值得注意的是全部数据都在本地。这对企业用户是个关键卖点——代码相关的记忆是高度敏感的资产,claude-mem 不需要把你的项目决策上传到任何第三方服务。
3.4 渐进式披露:记忆注入的分层策略
这是 claude-mem 最精妙、也最容易被忽视的设计。
新会话启动时,它不会把所有相关记忆一股脑塞进上下文,而是分层展示(progressive disclosure):
- Level 1:只注入最近几次会话的高度浓缩摘要(每条几十 token),让 AI 知道「最近在干什么」;
- Level 2:AI 觉得某条摘要相关时,可以通过工具调用展开这条记忆的完整观察记录;
- Level 3:必要时进一步下钻到原始细节。
这本质上是把「记忆调用」变成了 AI 的主动行为而非被动灌输。类比人类:你不会在每天早上把过去一年的日记从头读一遍,你只保留一个模糊的索引,需要时才去翻具体那一页。
从工程角度算一笔账:假设有 200 条历史记忆,每条 500 token。全量注入 = 10 万 token,直接爆炸;渐进式披露的初始成本只有「摘要索引」的 2000~3000 token,之后按需展开,单次会话的记忆开销通常控制在几千 token 以内。这是整个系统能长期运行而不被 token 成本压垮的第二根支柱(第一根是压缩)。
四、代码实战:从安装到自己写一个迷你记忆层
4.1 五分钟上手
安装只需要一条命令:
npx claude-mem install
它会自动完成插件钩子注册、Worker 服务部署、PM2 配置。如果你习惯 Claude Code 的插件市场,也可以:
/plugin marketplace add thedotmack/claude-mem
/plugin install claude-mem
装完之后不需要任何操作——正常使用 Claude Code,记忆会自动积累。下次开新会话时,你会发现 AI 的开场白从「这个项目是做什么的」变成了「上次我们重构了鉴权模块,现在继续处理 token 刷新的问题吗」。
搜索历史记忆用自然语言即可:
> 我们之前是怎么处理数据库连接池超时的?
AI 会通过 MCP 工具查询本地记忆库,召回相关观察记录再回答。
4.2 动手写一个迷你版:150 行理解核心原理
看懂一个系统最好的方式是实现它的最小版本。下面用 Python + SQLite FTS5 实现一个可运行的迷你记忆层,覆盖 claude-mem 的三个核心环节:观察记录、压缩存储、混合检索。
import sqlite3
import json
import time
class MiniMemory:
"""迷你版 claude-mem:观察记录 + FTS5 全文检索"""
SCHEMA = """
CREATE TABLE IF NOT EXISTS observations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL,
kind TEXT NOT NULL, -- decision/bugfix/feature/refactor/discovery/change
summary TEXT NOT NULL, -- AI 压缩后的语义摘要
concepts TEXT DEFAULT '[]', -- 概念标签 JSON 数组
files TEXT DEFAULT '[]', -- 关联文件 JSON 数组
created_at REAL NOT NULL
);
CREATE VIRTUAL TABLE IF NOT EXISTS obs_fts USING fts5(
summary, concepts, files,
content='observations', content_rowid='id'
);
CREATE TRIGGER IF NOT EXISTS obs_ai AFTER INSERT ON observations BEGIN
INSERT INTO obs_fts(rowid, summary, concepts, files)
VALUES (new.id, new.summary, new.concepts, new.files);
END;
"""
def __init__(self, db_path="memory.db"):
self.db = sqlite3.connect(db_path)
self.db.executescript(self.SCHEMA)
def observe(self, session_id: str, kind: str, summary: str,
concepts: list = None, files: list = None):
"""记录一条观察(实际系统中 summary 由 LLM 压缩生成)"""
self.db.execute(
"INSERT INTO observations(session_id, kind, summary, concepts, files, created_at) "
"VALUES (?, ?, ?, ?, ?, ?)",
(session_id, kind, summary,
json.dumps(concepts or [], ensure_ascii=False),
json.dumps(files or [], ensure_ascii=False),
time.time()))
self.db.commit()
def search(self, query: str, limit: int = 5) -> list:
"""FTS5 全文检索,BM25 排序"""
rows = self.db.execute(
"SELECT o.kind, o.summary, o.files, bm25(obs_fts) AS score "
"FROM obs_fts JOIN observations o ON o.id = obs_fts.rowid "
"WHERE obs_fts MATCH ? ORDER BY score LIMIT ?",
(query, limit)).fetchall()
return [{"kind": k, "summary": s, "files": json.loads(f), "score": sc}
for k, s, f, sc in rows]
def recent_digest(self, n: int = 3) -> str:
"""渐进式披露 Level 1:最近 n 条记忆的索引摘要"""
rows = self.db.execute(
"SELECT kind, substr(summary, 1, 60) FROM observations "
"ORDER BY created_at DESC LIMIT ?", (n,)).fetchall()
return "\n".join(f"- [{k}] {s}…" for k, s in rows)
# ===== 模拟一次完整的「采集 → 检索 → 注入」流程 =====
mem = MiniMemory(":memory:")
# 会话 1:save-hook 捕获到的观察(已被"压缩")
mem.observe("s1", "decision",
"选用 JWT 无状态鉴权替代 session,理由:需支持移动端与 Web 多端登录,"
"refresh token 存 Redis,TTL 7 天",
concepts=["authentication", "jwt", "redis"],
files=["src/auth/jwt.ts", "src/auth/refresh.ts"])
mem.observe("s1", "bugfix",
"修复订单重复提交:根因是前端防抖失效叠加后端无幂等控制,"
"方案为订单号作幂等键写入 Redis SETNX,TTL 30 秒",
concepts=["idempotency", "orders", "redis"],
files=["src/order/create.ts"])
# 会话 2 启动:context-hook 注入 Level 1 摘要
print("=== 新会话注入的记忆索引 ===")
print(mem.recent_digest())
# 会话 2 中途:AI 主动检索(Level 2 展开)
print("\n=== 检索: idempotency ===")
for hit in mem.search("idempotency"):
print(f"[{hit['kind']}] {hit['summary']}\n 文件: {hit['files']}")
运行输出:
=== 新会话注入的记忆索引 ===
- [bugfix] 修复订单重复提交:根因是前端防抖失效叠加后端无幂等控制,方案为订单号作幂等键…
- [decision] 选用 JWT 无状态鉴权替代 session,理由:需支持移动端与 Web 多端登录,refre…
=== 检索: idempotency ===
[bugfix] 修复订单重复提交:根因是前端防抖失效叠加后端无幂等控制,方案为订单号作幂等键写入 Redis SETNX,TTL 30 秒
文件: ['src/order/create.ts']
这 100 来行代码就是 claude-mem 的骨架。真实系统在此之上补了四块:LLM 压缩(把原始工具输出变成上面那种高密度 summary)、向量检索(语义召回)、钩子自动化(无感采集)、Worker 服务化(异步与状态管理)。骨架不复杂,工程完成度才是护城河。
4.3 压缩环节的提示词设计
压缩是整条流水线的质量瓶颈。claude-mem 用 Claude Agent SDK 做压缩,其核心逻辑等价于这样一个结构化提取任务:
COMPRESS_PROMPT = """分析以下工具调用记录,提取值得长期记忆的信息。
要求:
1. 分类:decision / bugfix / feature / refactor / discovery / change
2. 摘要必须包含「结论 + 理由」,不要过程流水账
3. 提取概念标签(英文,如 authentication、caching)
4. 提取涉及的文件路径
5. 无长期价值的内容(如单纯的文件浏览)返回 SKIP
工具调用记录:
{tool_output}
以 JSON 输出:{{"kind": ..., "summary": ..., "concepts": [...], "files": [...]}}
"""
注意第 5 条:敢于丢弃。不是所有行为都值得记住,「读了一下 README」这种事记下来只会污染检索结果。好的记忆系统和好的笔记习惯一样,删的能力和记的能力同样重要。
五、性能与成本:记忆系统的三笔账
5.1 Token 账
以一个日均 5 次会话、每次 20 个工具调用的重度用户估算:
- 无记忆系统:每次新会话人工喂背景约 3000~8000 token,且信息不全;
- 全量记录方案:记忆膨胀速度约 10 万 token/天,一周后不可用;
- claude-mem 方案:每条观察约 500 token,且大量低价值调用被 SKIP,实际入库约 30~50 条/天;会话启动注入约 2000 token,按需展开每次几百 token。
结论:压缩 + 渐进式披露把记忆的边际成本从「线性增长」压成了「近似常数」,这是它能长期运行的根本原因。
5.2 延迟账
- 钩子采集:异步甩给 Worker,主会话感知为 0;
- 压缩:后台调用 LLM,1~3 秒,但不阻塞任何交互;
- 检索:SQLite FTS5 在万条记录规模下毫秒级,向量检索本地 Chroma 同样在几十毫秒内。
唯一的实际开销是会话启动时 context-hook 注入摘要,多几百毫秒——换来的是省掉几分钟的人工背景交代,划算。
5.3 隐患账(冷静部分)
claude-mem 不是银弹,有几个边界必须说清楚:
1. 记忆污染。 压缩由 LLM 完成,LLM 会犯错。一条错误的「决策记忆」(比如把临时方案记成了最终决策)会在未来的会话里持续误导 AI。系统提供了 Web UI 让你人工审查和删除记忆,但这引入了维护成本。记忆系统需要「遗忘机制」和「修正机制」,这是当前所有 Agent 记忆方案的共同短板。
2. AGPL-3.0 协议。 claude-mem 采用 AGPL 协议,个人使用没问题,但如果你想把它集成进商业 SaaS 对外提供服务,需要仔细评估协议传染性。
3. 后台 LLM 调用成本。 压缩本身要消耗 API 额度。虽然单次不多,但重度使用下这是一笔持续开销,相当于用「压缩时的小钱」换「注入时的大钱」——总体划算,但不是免费。
4. 与宿主强耦合。 它深度依赖 Claude Code 的钩子机制,换到其他编码 Agent(Codex CLI 等)不能直接复用。不过其架构模式是通用的,社区已经出现了针对其他工具的仿制品。
六、更大的图景:记忆层正在成为 Agent 的标准基建
把镜头拉远一点看,claude-mem 的爆火不是孤立事件。2026 年的 Agent 生态有一个清晰的趋势:竞争焦点正在从「模型有多聪明」转向「Agent 基础设施有多完善」——工具协议(MCP)、沙箱执行、可观测性,以及记忆层。
记忆层的技术路线目前大致三条:
- 上下文压缩派(claude-mem 属于此类):把历史压缩成摘要,按需注入。工程简单,效果立竿见影,但依赖压缩质量;
- 外部知识库派:把记忆当 RAG 文档处理,全部走向量检索。检索能力强,但缺少「时序」和「因果」结构;
- 结构化记忆图派:把记忆建成实体关系图谱(如一些研究型项目的做法)。表达能力最强,工程成本也最高。
claude-mem 的聪明之处在于选了第一条路线并做到了极致的产品化:不追求学术上的完美记忆,而是抓住「程序员最痛的是重复喂背景」这个具体场景,用最朴素的技术栈(钩子 + SQLite + 本地服务)解决 80% 的问题。
对我们普通开发者,有三条可以直接带走的工程启示:
- 旁路增强模式:给现有工具加能力,用钩子 + 常驻 Worker,别动宿主本体;
- 压缩优先于存储:任何日志型数据,先想清楚「什么值得记」,再想「怎么存」;
- 渐进式披露:面对大规模上下文,索引先行、按需展开,永远比全量加载可持续。
七、总结与展望
claude-mem 用一条「钩子采集 → AI 压缩 → 混合检索 → 渐进注入」的流水线,把 AI 编码助手的记忆问题从「玄学」变成了「工程」。它的价值不只是一个好用的插件,更是一份可复用的架构参考答案——未来无论你给什么 Agent 加记忆,大概率都会走过它趟出来的这条路。
接下来值得关注的演进方向:一是遗忘与置信度机制,让错误记忆能自动衰减;二是团队共享记忆,把个人记忆库升级为团队知识资产(这也是最有商业想象力的方向);三是跨工具标准化,如果记忆层能像 MCP 一样形成协议标准,一份项目记忆就可以在不同的编码 Agent 之间自由迁移。
AI 的智力每几个月上一个台阶,但「记得住你的项目」这件事,目前还得靠工程手段一砖一瓦地搭。claude-mem 证明了这堵墙并不难砌——难的是想清楚哪些砖该留,哪些砖该扔。