01

AI 为什么每天早上都"失忆"?

你花了两小时向 Claude 解释你的项目结构。明天它什么都不记得了。

那个让你崩溃的瞬间

你已经打开了 Claude Code,跟它讲清楚了你的 monorepo 结构、认证逻辑和数据库设计。终于,它完全理解了你的项目。

第二天,你打开新会话,输入"继续昨天的工作"——它回复:"我没有之前会话的上下文。"

😤
这不是 Claude 的问题,是架构设计

所有大型语言模型都是"无状态"的:每次对话都是一张白纸。这是设计决定,不是 bug。claude-mem 的任务就是在这个白纸上偷偷预先写好记忆。

重复解释的代价

这不只是烦人的问题,它实际影响你的工作质量。

⏱️

时间浪费

每次新会话都要花 10-30 分钟重新解释架构、约定和上下文。

🔁

重复踩坑

AI 重新犯你已经解决过的错误,因为它不记得上次的教训。

📉

决策质量降级

没有项目历史,AI 的建议可能与你已有的技术决策相矛盾。

如果 AI 有一本随身笔记本……

想象你有一位顾问,每次拜访前都会翻一翻他们的工作笔记本,上面记着你们上次讨论的决策、你提到的项目偏好、以及他们自己记录的观察。

claude-mem 就是那本笔记本。它自动运行在幕后,无声地记录每次 Claude 做的事情,然后在下次会话开始时,把相关记忆偷偷注射进去。

💡
核心洞察:上下文是专业知识的载体

Claude 之所以能帮你写出高质量代码,靠的是你在对话中传授的上下文。claude-mem 让这份上下文不再随着会话消失。

理解检验

你安装了 claude-mem 后,打开明天的新会话。最可能发生的变化是?

为什么 Claude 默认不记得昨天的对话?

02

认识五大角色

claude-mem 不是一个脚本——它是一个分工明确的小团队,每个成员有自己的专属职责。

backstage 的五个演员

就像一台电影的幕后团队——导演、摄影师、剪辑师各司其职,claude-mem 的五个组件各自负责记忆流水线的一个环节。

🪝
生命周期钩子 (Hooks)

Claude Code 的"事件监听器"——在会话开始、每次工具调用后、会话结束时被触发

⚙️
Worker Service(工作进程)

后台常驻的 HTTP 服务,监听 37777 端口,接收 Hook 发来的事件并处理

🤖
SDK Agent(AI 观察者)

一个独立的 Claude 子进程,专门负责把原始工具调用数据压缩成结构化"观察"

🗄️
SQLite + Chroma(记忆仓库)

持久化存储所有观察和摘要;Chroma 提供语义向量搜索能力

💉
Context Generator(注入器)

会话启动时从数据库检索相关记忆,格式化成 XML 注入到 Claude 的上下文窗口

点击探索每个组件

Claude Code 进程(主角)

💻
Claude Code
🪝
Hooks 系统

后台服务(常驻运行)

⚙️
Worker :37777
🤖
SDK Agent

存储层(持久化记忆)

📦
SQLite DB
🔍
Chroma 向量库
点击任意组件查看它的职责详解

在代码里找到它们

了解文件结构,你就知道当 claude-mem 出问题时该去哪里找原因。

plugin/ 插件入口——Claude Code 读取的配置
hooks/hooks.json定义所有 6 个 Hook 的触发时机和命令
scripts/worker-service.cjs打包后的 Worker Service 入口
scripts/bun-runner.js用 Bun 启动 Worker 的启动器
src/services/ 核心业务逻辑(TypeScript 源码)
worker-service.tsWorker 主编排器,协调所有子模块
worker/SDKAgent.ts启动观察者 Claude 子进程
context/ContextBuilder.ts生成要注入的记忆内容
sqlite/SessionStore.tsSQLite 数据库读写操作

理解检验

claude-mem 里,谁负责把"Claude 读了 main.ts 并返回了 500 行输出"这个原始事件,压缩成一条结构化的观察记录?

你的团队使用了一个公司内部的 SDK(外部没有文档)。你在一次会话里花时间向 Claude 解释了这个 SDK 的用法。claude-mem 对下次会话最大的帮助是?

03

捕获:Hook 拦截机制

claude-mem 怎么在 Claude Code 完全不知情的情况下,监视它的每一次行动?

Hook 是什么:快递包裹的追踪系统

想象快递公司的包裹追踪:每次包裹状态变化(揽件、分拣、派送、签收),系统自动在日志里记录一条。你不需要手动打电话问——系统自动帮你记。

Claude Code 的 生命周期钩子也是这样:在特定事件发生时,自动触发 claude-mem 注册的脚本。

Setup Claude Code 首次启动时 — 检查依赖是否安装
SessionStart 新会话开始时 — 启动 Worker、注入历史记忆
UserPromptSubmit 你每次发消息时 — 确保会话已在数据库中初始化
PreToolUse 工具调用前 — 检查文件相关的上下文(目前仅 Read 工具)
PostToolUse 工具调用完成后 — 捕获这次工具使用的输入输出,送去分析
Stop Claude 停止响应时 — 触发 AI 对本次工作的总结

真实代码:钩子是怎么配置的

这是 plugin/hooks/hooks.json 里 PostToolUse 钩子的实际配置。

hooks.json
"PostToolUse": [{
  "matcher": "*",
  "hooks": [{
    "type": "command",
    "shell": "bash",
    "command": "node ... worker-service.cjs hook claude-code observation",
    "timeout": 120
  }]
}]
PLAIN ENGLISH

"PostToolUse" — 每次任何工具(Bash/Read/Write/Edit...)执行完毕后

"matcher: *" — 匹配所有工具,不区分类型

"type: command" — 运行一个 shell 命令

"shell: bash" — 用 Bash 执行(确保跨平台兼容)

"command: ... observation" — 通知 Worker Service 有新观察需要处理

"timeout: 120" — 最多等 120 秒(防止卡死)

🔍
为什么 matcher 是 "*"?

claude-mem 想捕获所有类型的工具使用——无论 Claude 是在读文件、执行命令还是搜索代码。每次行动都是潜在的有价值信息。

会话启动时:记忆是怎么注入的

SessionStart 钩子做三件事:安装检查、启动 Worker、然后调用记忆注入。

src/services/context/ContextBuilder.ts
function buildContextOutput(
  project: string,
  observations: Observation[],
  summaries: SessionSummary[],
  config: ContextConfig,
): string {
  const output: string[] = [];
  const economics = calculateTokenEconomics(observations);
  output.push(...renderHeader(project, economics, config));
  output.push(...renderTimeline(observations, summaries));
  output.push(...renderFooter());
  return output.join('\n');
}
PLAIN ENGLISH

这个函数负责把数据库里的记忆组装成文字

接收:项目名、观察记录列表、会话摘要列表、配置

先算出这些记忆要花多少 token(token 就像字数,AI 处理文字的计量单位)

添加头部(项目名、记忆统计)

添加时间线(按时间排列的观察 + 摘要)

添加尾部说明,然后拼成一整段文字返回

PostToolUse 钩子的完整触发流程

当 Claude 执行完一次 工具调用后,发生了什么?

💻
Claude Code
⚙️
Worker
🤖
SDK Agent
🗄️
SQLite
点击"下一步"开始

理解检验

你让 Claude 读了 10 个文件、运行了 5 个命令。claude-mem 是通过哪个钩子记录这 15 次操作的?

如果 Worker Service 因为某些原因没有响应,PostToolUse 钩子超过了 120 秒超时上限,会发生什么?

04

压缩与注入:记忆管道

原始工具输出是噪音,结构化观察是信号。这一模块讲清楚信号是怎么提炼出来的。

为什么不直接存储原始输出?

想象一位编辑看完一部 300 页的初稿,然后写了一段 3 句话的摘要。他不会一字不差地复述全书——他提炼了核心决策、关键转折点、重要发现。

claude-mem 的压缩机制做的正是同样的事:把可能长达数千行的工具输出,提炼成一条简洁的 观察记录。

📄

原始输出

npm test 的完整输出可能有 500 行:每个测试的详细日志、堆栈追踪、覆盖率报告……

💡

提炼后观察

类型: testing | 标题: 认证模块测试 | 结果: 47/48 通过 | 失败: token_refresh_race_test | 原因: 竞争条件

一条观察长什么样

SDK Agent 使用固定的 XML 格式输出观察,这让结构化存储和检索变得非常可靠。

SDK 输出格式(src/sdk/prompts.ts)
<observation>
  <type>bugfix</type>
  <title>JWT 刷新竞争条件修复</title>
  <subtitle>Redis GETSET 原子性问题</subtitle>
  <facts>
    <fact>并发刷新触发双写</fact>
    <fact>用 Lua 脚本解决原子性</fact>
  </facts>
  <files_modified>
    src/auth/token-refresh.ts
  </files_modified>
</observation>
PLAIN ENGLISH

一条观察记录的开始标签

类型:bugfix(还有 feature、analysis、investigation 等)

这次工作的简短标题(一眼看懂做了什么)

更具体的副标题(技术细节)

关键事实列表开始——这是记忆的核心内容

关键事实一:发现了什么问题

关键事实二:用什么方案解决的

修改了哪些文件——未来搜索时可以按文件过滤

记忆是怎么注入到新会话的

SessionStart 钩子触发时,Context Generator 从数据库检索与当前项目相关的记忆,然后把它们格式化成一段文字,悄悄插入到你给 Claude 的第一条消息之前。

Claude 看到的实际上是:

# 项目记忆(由 claude-mem 自动注入)
项目:my-saas-app | 记忆:23 条 | 最近:2 天前

[2025-05-01] bugfix: JWT 刷新竞争条件修复
  并发刷新触发双写;用 Lua 脚本解决原子性
  文件:src/auth/token-refresh.ts

[2025-04-30] feature: 用户档案页面
  实现头像上传 + 压缩;使用 sharp 库
  文件:src/pages/profile.tsx

... 还有 21 条记忆(使用 mem-search 查询更多)
💡
渐进式披露(Progressive Disclosure)

claude-mem 不会把所有记忆一股脑丢进上下文——那会浪费大量 token 预算。它只注入最相关的摘要,并告诉 Claude 可以用 mem-search 工具查询更多。这是一个智能的"按需展开"设计。

完整记忆管道:捕获 → 压缩 → 存储 → 注入

💻
Claude Code
⚙️
Worker
🤖
AI 压缩
🗄️
存储
📝
总结
点击"下一步"跟踪一条记忆从诞生到注入的完整旅程

理解检验

你发现 Claude 知道昨天的工作(记忆注入正常),但今天做的事情没有被记录下来。你应该排查哪个组件?

你问 Claude 关于两个月前修复的一个 bug,它说它知道这个 bug 修复了(因为记忆摘要里提到了),但不知道详细步骤。这是为什么?

05

安装、配置与驾驭 claude-mem

从零到第一条记忆被注入,只需要一行命令。但要真正用好它,你还需要知道这几件事。

安装:30 秒上手

claude-mem 用 npx 一键安装,自动完成所有配置。

1
运行安装命令

npx claude-mem install — 自动下载、安装依赖、注册 Hooks

2
重启 Claude Code

重启后 Setup 钩子触发,检查 Bun/Python/Chroma 等依赖是否就绪

3
正常使用 Claude Code

无需任何额外操作。claude-mem 在后台静默运行,你只需要看到成果

4
(可选)打开 Web 监控界面

在浏览器访问 http://localhost:37777 实时查看记忆流

ℹ️
系统要求

Node.js 18+,Claude Code 最新版,macOS/Linux/Windows(WSL)。Bun 和 uv 会自动安装——你不需要手动处理。

配置文件:调整 claude-mem 行为

所有设置都在 ~/.claude-mem/settings.json,自动生成,可直接编辑。

CLAUDE_MEM_MODE 工作模式:code(英文)/ code--zh(中文观察)/ code--ja(日文)
CLAUDE_MEM_PROVIDER AI 提供商:默认用 Claude SDK,也可以切换 Gemini 或 OpenRouter
WORKER_PORT Worker Service 端口,默认 37777。如果被占用可以改这里
CLAUDE_MEM_CHROMA_ENABLED 是否启用向量搜索。设为 false 可以用纯 SQLite 模式,省资源
CLAUDE_MEM_MAX_OBSERVATIONS 每次注入的最大观察数量。越多越全,但 token 消耗越大

主动搜索:mem-search 技能

自动注入是被动记忆,mem-search 让 Claude 可以主动检索任何历史记忆。

mem-search 实现了 3 层工作流:先用 search 获取 ID 索引,再用 timeline 看时间上下文,最后用 get_observations 按需获取完整内容。比一次性获取全部节省 10 倍 token。

隐私控制:不想被记录的内容怎么办

有时候你在对话里输入了 敏感信息——比如临时 API key 或者内部凭证。

claude-mem 支持用 <private>...</private> 标签包裹不想被记录的内容:

你: 帮我测试一下这个 API:
<private>
API_KEY=sk-prod-3x7mN2kL9pQ...
</private>
用上面的 key 调用用户 API

→ <private> 标签内的内容不会被 claude-mem 记录
⚠️
记忆存储在本机

所有记忆存储在你本地的 SQLite 文件里(~/.claude-mem/data/),不会上传到任何服务器。但仍然建议用 <private> 标签保护真正敏感的内容。

最终综合检验

你今天用 Claude 实现了一个功能,明天想让新会话知道这个功能已经完成了。哪个说法描述了这个信息是怎么从今天流向明天的?

你在一台配置较低的开发机上运行 claude-mem,发现 Chroma 向量数据库消耗了太多内存。你应该怎么处理?

你问 Claude 关于 6 个月前某个 bug 修复的详细信息,这条记忆的原始内容很长。mem-search 怎么在不消耗大量 token 的情况下找到它?

🎉
你已经理解了 claude-mem 的完整架构

从 AI 失忆的根本原因,到 Hook 拦截机制,到 AI 压缩流水线,到记忆注入——你现在掌握了足够的知识来有效驾驭这个工具,并在它出问题时知道从哪里入手排查。