编程 claude-mem 深度拆解:AI 编码助手的「海马体」是怎么造出来的——钩子采集、语义压缩与渐进式披露全链路解析

2026-07-30 03:43:40 +0800 CST views 3

claude-mem 深度拆解:AI 编码助手的「海马体」是怎么造出来的——钩子采集、语义压缩与渐进式披露全链路解析

一、背景:AI 编码助手的「金鱼记忆」问题

用过 Claude Code、Codex 这类终端编码 Agent 的人,大概率都经历过这样的循环:

  1. 昨天花了两个小时,跟 AI 一起把项目的鉴权模块重构完,中间做了七八个关键决策;
  2. 今天新开一个会话,AI 一脸茫然:「请问这个项目是做什么的?」
  3. 你只能把 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):普通的代码修改记录

每条记录还会打上概念标签(如 authenticationdatabase)和文件引用(如 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)、沙箱执行、可观测性,以及记忆层。

记忆层的技术路线目前大致三条:

  1. 上下文压缩派(claude-mem 属于此类):把历史压缩成摘要,按需注入。工程简单,效果立竿见影,但依赖压缩质量;
  2. 外部知识库派:把记忆当 RAG 文档处理,全部走向量检索。检索能力强,但缺少「时序」和「因果」结构;
  3. 结构化记忆图派:把记忆建成实体关系图谱(如一些研究型项目的做法)。表达能力最强,工程成本也最高。

claude-mem 的聪明之处在于选了第一条路线并做到了极致的产品化:不追求学术上的完美记忆,而是抓住「程序员最痛的是重复喂背景」这个具体场景,用最朴素的技术栈(钩子 + SQLite + 本地服务)解决 80% 的问题。

对我们普通开发者,有三条可以直接带走的工程启示:

  • 旁路增强模式:给现有工具加能力,用钩子 + 常驻 Worker,别动宿主本体;
  • 压缩优先于存储:任何日志型数据,先想清楚「什么值得记」,再想「怎么存」;
  • 渐进式披露:面对大规模上下文,索引先行、按需展开,永远比全量加载可持续。

七、总结与展望

claude-mem 用一条「钩子采集 → AI 压缩 → 混合检索 → 渐进注入」的流水线,把 AI 编码助手的记忆问题从「玄学」变成了「工程」。它的价值不只是一个好用的插件,更是一份可复用的架构参考答案——未来无论你给什么 Agent 加记忆,大概率都会走过它趟出来的这条路。

接下来值得关注的演进方向:一是遗忘与置信度机制,让错误记忆能自动衰减;二是团队共享记忆,把个人记忆库升级为团队知识资产(这也是最有商业想象力的方向);三是跨工具标准化,如果记忆层能像 MCP 一样形成协议标准,一份项目记忆就可以在不同的编码 Agent 之间自由迁移。

AI 的智力每几个月上一个台阶,但「记得住你的项目」这件事,目前还得靠工程手段一砖一瓦地搭。claude-mem 证明了这堵墙并不难砌——难的是想清楚哪些砖该留,哪些砖该扔。

推荐文章

记录一次服务器的优化对比
2024-11-19 09:18:23 +0800 CST
前端如何给页面添加水印
2024-11-19 07:12:56 +0800 CST
程序员茄子在线接单