编程 claude-mem 深度拆解:治好 Claude Code 的「金鱼记忆」,捕获-压缩-检索流水线如何把工具输出炼成长期记忆

2026-07-29 11:43:34 +0800 CST views 7

claude-mem 深度拆解:治好 Claude Code 的「金鱼记忆」,捕获-压缩-检索流水线如何把工具输出炼成长期记忆

写在前面:AI 编码助手的「失忆症」是个真问题

用过 Claude Code、Codex CLI 这类终端编码 Agent 的人,大概都经历过这样的场景:

昨天下午你和 Claude Code 一起排查了一个诡异的支付回调 Bug,翻了十几个文件,最后定位到是时区处理和幂等键生成的组合问题,还顺手重构了一小块代码。今天早上你打开新会话,问它「昨天那个回调的问题后来怎么处理的」——它一脸茫然,仿佛你们从未见过面。

于是你只能重新粘贴上下文、重新解释项目结构、重新描述昨天的决策。每个新会话都是一次冷启动,每次冷启动都在烧你的时间和 Token。

这不是模型不够聪明,而是架构使然:Claude Code 的会话默认是无状态的,上下文窗口再大,也只覆盖当前会话。会话一关,一切归零。业内管这个叫 AI 的「金鱼记忆」。

claude-mem(GitHub: thedotmack/claude-mem)就是冲着这个问题来的。它是一个 Claude Code 插件形态的持久化记忆压缩系统,上线后热度一路狂飙——从开源初期的 3 万 Star 涨到如今的数万级别,长期霸榜 GitHub Trending,单周新增 Star 曾冲到 8000+。这个数字背后是一个非常朴素的共识:记忆系统正在成为 AI 编码工具链的必备基础设施,而不是锦上添花的玩具。

这篇文章我会从工程视角把 claude-mem 拆开揉碎讲清楚:它的钩子捕获机制、压缩流水线、混合存储与检索架构,以及上手实战、成本分析和它做不到的事。读完你应该能回答三个问题:它是怎么工作的?值不值得装?以及更重要的——它的设计思想能不能搬到你自己的 Agent 系统里?

一、核心思路:不是「存聊天记录」,而是「炼记忆」

很多人第一反应是:这不就是把聊天记录存下来,下次再喂回去吗?

不是。直接存原始记录是最差的方案。 一次编码会话里,Claude Code 会产生大量工具调用输出——读文件、跑命令、grep 搜索,单次输出动辄 1000~10000 个 Token。把这些原始流水账全部存下来再回填,等于把上一个会话的上下文爆炸问题原封不动搬到下一个会话,Token 成本翻倍,信噪比极低。

claude-mem 的做法可以概括为一条流水线:

原始工具输出 (1k~10k tokens)
    ↓ 钩子捕获
观察记录 (Observations)
    ↓ Claude Agent SDK 智能压缩
语义化摘要 (~500 tokens,带类型和标签)
    ↓ SQLite + FTS5 持久化
可检索的长期记忆
    ↓ 新会话启动时按相关性注入
Claude「想起」项目历史

关键在中间那步压缩:它借助 Claude Agent SDK,把 1000~10000 Token 的原始输出压缩成约 500 Token 的结构化观察记录,压缩比最高可达 20:1。而且这些记录不是无差别的文本块,而是被分类打标的:

  • 决策(Decision):为什么选了方案 A 而不是方案 B
  • Bug 修复(Bugfix):问题现象、根因、修复方式
  • 功能(Feature):新增了什么能力
  • 重构(Refactor):改了哪里、为什么改
  • 发现(Discovery):探索过程中弄清楚的项目事实
  • 变更(Change):文件级别的修改记录

每条记录还会打上相关概念标签和文件引用。这套分类不是装饰——它直接决定了后续检索的精度。当你问「上次那个鉴权 Bug」时,系统可以优先在 Bugfix 类型里做匹配,而不是在全部历史里大海捞针。

这就是「炼记忆」和「存日志」的本质区别:前者是有损但高价值密度的语义提炼,后者是无损但低信噪比的数据堆积。 人脑也是这么干的——你不会记得三年前某天每分钟做了什么,但你记得那天做了个重要决定。

二、架构拆解:六个组件、一条单向数据流

claude-mem 的整体架构可以分为五层,形成一条清晰的单向数据流:

┌─────────────────────────────────────────────────┐
│  插件钩子层 (TypeScript + Bun)                     │
│  6 个生命周期钩子,监听 Claude Code 事件             │
└───────────────┬─────────────────────────────────┘
                │ stdin 传递事件数据
┌───────────────▼─────────────────────────────────┐
│  Worker 服务层 (Express.js)                       │
│  HTTP API + AI 处理调度,异步压缩                   │
└───────────────┬─────────────────────────────────┘
                │
┌───────────────▼─────────────────────────────────┐
│  存储层 (SQLite + FTS5)                           │
│  结构化观察记录 + 全文检索索引                        │
└───────────────┬─────────────────────────────────┘
                │
┌───────────────▼─────────────────────────────────┐
│  检索层 (HTTP API + MCP)                          │
│  渐进式搜索、语义检索、时间线查询                     │
└───────────────┬─────────────────────────────────┘
                │
┌───────────────▼─────────────────────────────────┐
│  注入层 / 可视化层 (React)                          │
│  SessionStart 上下文注入 + 记忆流 Web 界面           │
└─────────────────────────────────────────────────┘

2.1 钩子层:六个「隐形传感器」

claude-mem 不修改 Claude Code 本体,而是利用其官方 Hooks 机制,在生命周期的关键节点挂载了 6 个钩子:

钩子触发时机职责
context-hook.jsSessionStart(会话启动)拉起 Bun Worker 进程,从数据库取最近观察记录注入上下文
new-hook.jsUserPromptSubmit(用户提交提示)创建会话记录,保存用户意图
post-tool-hook.jsPostToolUse(工具调用完成)捕获文件读写、命令执行等工具输出
stop-hook.jsStop(任务暂停)保存中间状态
save-hook.jsSessionEnd(会话结束)触发总结、压缩与入库
user-message-hook.jsUserMessage开发调试用

钩子通过 stdin 接收 Claude Code 发来的 JSON 事件数据,处理后转发给 Worker 服务。这个设计有两个值得注意的点:

第一,对用户完全透明。 你不需要改变任何工作习惯,不需要手动喊「记住这个」。所有捕获都在后台静默发生——这是它和「手动维护 MEMORY.md」类方案最大的体验差异。

第二,钩子层刻意做薄。 钩子本身几乎不做重逻辑,100ms 内响应,把事件甩给 Worker 就返回,避免阻塞 Claude Code 的主流程。压缩这种耗时操作全部异步化。这是典型的「采集与处理解耦」——任何做过日志系统或埋点系统的人都会认出这个模式。

2.2 压缩层:用 AI 总结 AI

压缩层是整个系统的灵魂,也是最「贵」的部分。它用 Claude Agent SDK 起一个独立的 AI 处理流程,对原始观察数据做渐进式压缩:多轮迭代提取核心事实、技术决策、代码变更和概念理解,最终产出前面说的结构化记录。

用 AI 总结 AI 的工作过程,听起来有点套娃,但仔细想想这是目前唯一靠谱的路径:

  • 规则提取(正则、AST 分析)只能抓到表层事实(改了哪个文件),抓不到语义(为什么这么改)
  • 纯向量化存储保留了语义相似性,但丢失了结构,无法回答「上周做了哪些架构决策」这类聚合问题
  • LLM 压缩能同时产出结构化分类 + 自然语言摘要,代价是每次压缩要花推理成本

claude-mem 选了第三条路,并用异步化把延迟成本藏在会话间隙里——压缩发生在会话结束后,不占用你的交互时间。

2.3 存储层:SQLite + FTS5,务实到骨子里

存储方案上,claude-mem 没有上时髦的向量数据库全家桶,而是选了 SQLite + FTS5 全文检索为主、向量检索为辅的混合方案。

这个选择非常「本地工具」思维:

-- 简化后的核心表结构示意
CREATE TABLE observations (
  id INTEGER PRIMARY KEY,
  session_id TEXT NOT NULL,
  type TEXT CHECK(type IN ('decision','bugfix','feature',
                           'refactor','discovery','change')),
  summary TEXT NOT NULL,        -- ~500 token 的语义摘要
  concepts TEXT,                -- 概念标签,JSON 数组
  file_refs TEXT,               -- 关联文件路径
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- FTS5 虚拟表,毫秒级全文检索
CREATE VIRTUAL TABLE observations_fts USING fts5(
  summary, concepts, content=observations
);

为什么不直接全上向量库?因为编码场景的检索请求里,精确匹配的比重远比想象中高。你搜「payment_callback.go」「JWT 过期」「docker-compose 端口」,这些都是 FTS5 的主场——分词命中、毫秒返回、零额外依赖。向量检索留给「上次那个和缓存有关的坑」这类模糊语义查询做补充。

SQLite 单文件存储还带来一个隐性好处:你的记忆完全在本地,想备份就复制文件,想审计就直接 SQL 查询,想销毁就删文件。 没有云端同步,没有隐私出境问题。对于会接触公司代码库的开发工具来说,这一点在很多团队里是能不能用的分水岭。

2.4 检索与注入:时机比算法更重要

记忆系统最容易被忽视的一环是:什么时候、注入多少、注入什么。

claude-mem 的策略是分层的:

  1. SessionStart 兜底注入:新会话启动时,自动取最近约 50 条观察记录,按时间线组织后注入初始上下文。这保证了「基础连续性」——不管你问什么,Claude 至少知道这个项目最近发生了什么。
  2. 按需渐进检索:通过 MCP 暴露搜索工具,Claude 在会话中可以主动发起检索——先搜索概要,判断相关后再拉取详情。这种「渐进式披露」避免了一次性把大块历史灌进上下文。
  3. 类型与文件过滤:检索可以按记录类型(只看决策)、按文件引用(只看和 auth/ 相关的)收窄范围。

这里有个值得所有 Agent 开发者记住的经验:注入太少,记忆等于没有;注入太多,等于自己制造上下文污染。 claude-mem 用「少量兜底 + 按需检索」的组合拳来平衡,比一股脑塞满上下文窗口的做法高明得多。

三、上手实战:从安装到验证记忆生效

3.1 安装

claude-mem 以 Claude Code 插件形式分发,安装是一行命令的事:

# 在 Claude Code 中直接安装插件
/plugin install claude-mem

# 或通过 npm 全局安装
npm install -g claude-mem
claude-mem install

安装后它会自动注册钩子到 Claude Code 的配置中,并初始化本地 SQLite 数据库(默认在 ~/.claude-mem/ 下)。运行时依赖 Bun 作为 Worker 进程运行时——如果本机没有,安装脚本会处理。

验证安装:

claude-mem status
# 输出示例:
# ✓ Hooks registered (6/6)
# ✓ Worker service running (port 37777)
# ✓ Database: ~/.claude-mem/memory.db (128 observations)

3.2 验证记忆闭环

装完之后不需要任何配置,正常使用 Claude Code 即可。做个简单实验验证闭环:

第一个会话,让 Claude Code 做点有信息量的事:

> 帮我看看 internal/payment/callback.go 里的幂等处理逻辑,
  我怀疑重复通知会导致重复入账

Claude Code 读文件、分析、给出结论(假设发现幂等键没包含商户订单号)。会话结束后,claude-mem 的 save-hook 会触发压缩,产出一条类似这样的观察记录:

{
  "type": "bugfix",
  "summary": "排查 payment/callback.go 幂等逻辑:幂等键仅使用渠道流水号,
              未拼接商户订单号,同一渠道流水号在跨商户场景下可能碰撞导致重复入账。
              建议幂等键改为 channel_txn_id + merchant_order_no 复合键。",
  "concepts": ["幂等", "支付回调", "重复入账"],
  "file_refs": ["internal/payment/callback.go"]
}

第二天开新会话,直接问:

> 昨天支付回调那个幂等问题,最后结论是什么?

如果一切正常,Claude 会基于注入的记忆直接回答复合键方案,而不是反问你「哪个幂等问题?」。这一刻你会真切感受到:它不再是每天初次见面的临时工,而是一个有工作记忆的搭档。

3.3 主动检索与记忆管理

除了被动注入,你也可以显式操作记忆:

# 全文检索历史记忆
claude-mem search "JWT 过期"

# 查看某个文件相关的所有历史观察
claude-mem search --file internal/auth/jwt.go

# 只看架构决策类记录
claude-mem search --type decision "缓存"

# 打开 Web 可视化界面,浏览记忆流
claude-mem ui

Web 界面是 React 写的记忆流视图,支持无限滚动,可以按时间线回看整个项目的「AI 协作史」。实际用下来,这个界面有个意外的价值:它是一份自动生成的、语义化的工作日志。 周报没得写的时候翻一翻,比 git log 有人味多了。

3.4 在自己的 Agent 里复刻这套思路

如果你在构建自己的 Agent 系统,claude-mem 的架构完全可以低成本复刻。核心就三段代码的事:

捕获(伪代码,任何有钩子/中间件机制的 Agent 框架都适用):

agent.on('toolResult', async (event) => {
  // 不阻塞主流程,异步入队
  await queue.push({
    session: event.sessionId,
    tool: event.toolName,
    output: event.output,
    ts: Date.now(),
  });
});

压缩(会话结束后批量处理):

async function compress(rawEvents: ToolEvent[]) {
  const prompt = `以下是一次编码会话的工具调用记录。
提取其中的关键信息,输出 JSON 数组,每项包含:
- type: decision|bugfix|feature|refactor|discovery|change
- summary: 200字以内的中文摘要,保留具体文件名、函数名、根因
- concepts: 概念标签数组
- file_refs: 涉及的文件路径
只保留有长期价值的信息,丢弃临时性输出。

${serializeEvents(rawEvents)}`;

  const result = await llm.complete(prompt, { format: 'json' });
  await db.insertObservations(JSON.parse(result));
}

注入(新会话启动时):

async function buildStartupContext(projectId: string) {
  const recent = await db.query(
    `SELECT type, summary, created_at FROM observations
     WHERE project_id = ? ORDER BY created_at DESC LIMIT 50`,
    [projectId]
  );
  return `## 项目近期记忆(自动注入)\n` +
    recent.map(o => `- [${o.type}] ${o.summary}`).join('\n');
}

真正难的不是这三段代码,而是压缩 Prompt 的调优(什么该记、什么该扔)和注入量的把控。claude-mem 把这些经验值都开源了,直接抄作业就行。

四、性能与成本:冷静算一笔账

4.1 性能表现

从公开的架构信息和实际体验看:

  • 钩子响应:100ms 内完成,对交互延迟基本无感
  • 检索延迟:SQLite FTS5 在万条记录量级下毫秒级返回
  • Worker 并发:Express 服务支持 100+ 并发请求,个人使用远达不到瓶颈
  • 存储增长:每条观察约 500 Token(约 1~2KB),重度使用一个月大概几 MB,可忽略

4.2 Token 成本:这是唯一需要认真权衡的地方

claude-mem 不是免费午餐,成本在两头:

压缩成本:每次会话结束后的压缩要调用 Claude API。一次中等强度的会话(几十次工具调用)压缩一遍,输入可能有几万 Token。按天算,重度用户每天多花的推理费用是真金白银——虽然对 Max 订阅用户来说是包月额度内的事,但 API 按量付费用户要留意。

注入成本:每个新会话启动时注入约 50 条 × 500 Token 级别的记忆(实际注入的是摘要的摘要,量会更小),这部分会计入每次会话的输入 Token。

收益端:省掉的是你手动重述背景的 Token 和时间,以及 Claude 重新探索项目(重复读文件、重复 grep)的工具调用成本。对于长期维护的项目,这笔账大概率是正的;对于一次性脚本任务,装它纯属浪费。

一个粗略的判断标准:如果你每周在同一个代码库里和 Claude Code 协作超过 3 次,claude-mem 的收益就能覆盖成本;低于这个频率,意义不大。

五、边界与不足:它治不好所有失忆

吹完优点,说说它做不到的事——这些在选型时同样重要。

1. 记忆质量受制于压缩模型的判断。 「什么值得记」是 LLM 决定的,它偶尔会记住噪音、漏掉你认为重要的细节。目前缺少便捷的「人工修正记忆」工作流——你可以删记录,但很难高效地批量校正。

2. 跨项目记忆隔离偏弱。 如果你在多个语义相近的项目间切换(比如两个都用 Go + PostgreSQL 的微服务),检索时可能出现记忆串味。虽然有项目维度的组织,但边界不如预期严格。

3. 只对 Claude Code 有效。 它是深度绑定 Claude Code Hooks 机制的插件,你在 Cursor、Codex CLI 里的工作它一无所知。多工具混用的开发者会得到一份「偏科」的记忆。想要跨工具记忆,得看 MCP 化的通用记忆服务那条路线。

4. 记忆不等于理解。 注入的是历史摘要,不是项目的实时状态。如果记忆里写着「幂等键方案已修复」,但后来有人 revert 了那个提交,Claude 可能基于过期记忆给出错误判断。记忆系统缓解冷启动,但代替不了对当前代码的核实——这一点对使用者的心智要求反而更高了:你得意识到它引用的「记忆」可能是旧的。

5. 安全面扩大。 所有工具输出都会被捕获、压缩、落盘。如果你的会话中出现过密钥、内部 URL、敏感数据,它们可能以摘要形式留在本地数据库里。虽然不出本机,但「本地明文持久化」本身就是需要纳入威胁模型的一环。团队使用前建议明确:~/.claude-mem/ 目录的备份和权限策略。

六、更大的图景:记忆层正在成为 Agent 的标准组件

claude-mem 的爆火不是孤立事件。回看最近半年多的 GitHub Trending,会发现一条清晰的主线:Agent 基础设施化。模型能力趋同之后,竞争转向了外围系统——路由网关、技能框架、沙箱执行,以及记忆。

而记忆这一层,行业正在收敛出一套共识架构,claude-mem 恰好是这套共识的教科书式实现:

  1. 事件驱动捕获:靠钩子/中间件被动采集,不打扰主流程
  2. LLM 语义压缩:有损提炼,追求价值密度而非完整性
  3. 结构化 + 全文 + 向量的混合存储:按查询模式选索引,不迷信单一方案
  4. 少量兜底注入 + 按需渐进检索:把上下文预算当稀缺资源管理

这四条原则的适用范围远超编码助手。客服 Agent 的用户偏好记忆、运维 Agent 的故障处置经验库、个人助理的生活上下文——本质上都是同一条流水线换个数据源。

我个人的判断是:一两年内,「无记忆的 Agent」会像「无缓存的 Web 服务」一样,被视为架构不完整。 而记忆系统的胜负手不在存储技术(SQLite 够用了),在于压缩策略和注入时机——这两个恰恰是最依赖工程打磨、最难被论文指标衡量的部分。

七、总结

最后按惯例给个务实的行动建议:

  • 重度 Claude Code 用户、长期项目维护者:直接装,一行命令,收益立竿见影。记得把 ~/.claude-mem/ 纳入备份,并检查敏感信息策略。
  • API 按量付费用户:先评估压缩成本,可以观察一周的 Token 消耗变化再决定是否常开。
  • 多工具混用者:claude-mem 只覆盖 Claude Code,考虑等等 MCP 化的通用记忆方案,或者接受「偏科记忆」。
  • Agent 系统开发者:即使你不用 Claude Code,也强烈建议读一遍它的源码——钩子捕获、异步压缩、SQLite+FTS5 混合检索、渐进式注入,这套流水线是目前开源界最完整的记忆系统参考实现,抄作业成本极低。

工具会过时,但「把原始交互炼成结构化记忆」这个思路不会。金鱼记忆的时代正在结束,而结束它的不是更大的上下文窗口,是更聪明的遗忘与提炼。


参考资料:thedotmack/claude-mem 项目 README 与源码结构、Claude Code Hooks 官方文档、社区架构解析文章。文中性能数据来自项目公开资料,实际表现以本机环境为准。

推荐文章

20个超实用的CSS动画库
2024-11-18 07:23:12 +0800 CST
使用Python实现邮件自动化
2024-11-18 20:18:14 +0800 CST
一键配置本地yum源
2024-11-18 14:45:15 +0800 CST
程序员茄子在线接单