AI 为什么每天早上都"失忆"?
你花了两小时向 Claude 解释你的项目结构。明天它什么都不记得了。
那个让你崩溃的瞬间
你已经打开了 Claude Code,跟它讲清楚了你的 monorepo 结构、认证逻辑和数据库设计。终于,它完全理解了你的项目。
第二天,你打开新会话,输入"继续昨天的工作"——它回复:"我没有之前会话的上下文。"
所有大型语言模型都是"无状态"的:每次对话都是一张白纸。这是设计决定,不是 bug。claude-mem 的任务就是在这个白纸上偷偷预先写好记忆。
重复解释的代价
这不只是烦人的问题,它实际影响你的工作质量。
时间浪费
每次新会话都要花 10-30 分钟重新解释架构、约定和上下文。
重复踩坑
AI 重新犯你已经解决过的错误,因为它不记得上次的教训。
决策质量降级
没有项目历史,AI 的建议可能与你已有的技术决策相矛盾。
如果 AI 有一本随身笔记本……
想象你有一位顾问,每次拜访前都会翻一翻他们的工作笔记本,上面记着你们上次讨论的决策、你提到的项目偏好、以及他们自己记录的观察。
claude-mem 就是那本笔记本。它自动运行在幕后,无声地记录每次 Claude 做的事情,然后在下次会话开始时,把相关记忆偷偷注射进去。
Claude 之所以能帮你写出高质量代码,靠的是你在对话中传授的上下文。claude-mem 让这份上下文不再随着会话消失。
理解检验
你安装了 claude-mem 后,打开明天的新会话。最可能发生的变化是?
为什么 Claude 默认不记得昨天的对话?
认识五大角色
claude-mem 不是一个脚本——它是一个分工明确的小团队,每个成员有自己的专属职责。
backstage 的五个演员
就像一台电影的幕后团队——导演、摄影师、剪辑师各司其职,claude-mem 的五个组件各自负责记忆流水线的一个环节。
Claude Code 的"事件监听器"——在会话开始、每次工具调用后、会话结束时被触发
后台常驻的 HTTP 服务,监听 37777 端口,接收 Hook 发来的事件并处理
一个独立的 Claude 子进程,专门负责把原始工具调用数据压缩成结构化"观察"
持久化存储所有观察和摘要;Chroma 提供语义向量搜索能力
会话启动时从数据库检索相关记忆,格式化成 XML 注入到 Claude 的上下文窗口
点击探索每个组件
Claude Code 进程(主角)
后台服务(常驻运行)
存储层(持久化记忆)
在代码里找到它们
了解文件结构,你就知道当 claude-mem 出问题时该去哪里找原因。
理解检验
claude-mem 里,谁负责把"Claude 读了 main.ts 并返回了 500 行输出"这个原始事件,压缩成一条结构化的观察记录?
你的团队使用了一个公司内部的 SDK(外部没有文档)。你在一次会话里花时间向 Claude 解释了这个 SDK 的用法。claude-mem 对下次会话最大的帮助是?
捕获: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 钩子的实际配置。
"PostToolUse": [{
"matcher": "*",
"hooks": [{
"type": "command",
"shell": "bash",
"command": "node ... worker-service.cjs hook claude-code observation",
"timeout": 120
}]
}]
"PostToolUse" — 每次任何工具(Bash/Read/Write/Edit...)执行完毕后
"matcher: *" — 匹配所有工具,不区分类型
"type: command" — 运行一个 shell 命令
"shell: bash" — 用 Bash 执行(确保跨平台兼容)
"command: ... observation" — 通知 Worker Service 有新观察需要处理
"timeout: 120" — 最多等 120 秒(防止卡死)
claude-mem 想捕获所有类型的工具使用——无论 Claude 是在读文件、执行命令还是搜索代码。每次行动都是潜在的有价值信息。
会话启动时:记忆是怎么注入的
SessionStart 钩子做三件事:安装检查、启动 Worker、然后调用记忆注入。
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');
}
这个函数负责把数据库里的记忆组装成文字
接收:项目名、观察记录列表、会话摘要列表、配置
先算出这些记忆要花多少 token(token 就像字数,AI 处理文字的计量单位)
添加头部(项目名、记忆统计)
添加时间线(按时间排列的观察 + 摘要)
添加尾部说明,然后拼成一整段文字返回
PostToolUse 钩子的完整触发流程
当 Claude 执行完一次 工具调用后,发生了什么?
理解检验
你让 Claude 读了 10 个文件、运行了 5 个命令。claude-mem 是通过哪个钩子记录这 15 次操作的?
如果 Worker Service 因为某些原因没有响应,PostToolUse 钩子超过了 120 秒超时上限,会发生什么?
压缩与注入:记忆管道
原始工具输出是噪音,结构化观察是信号。这一模块讲清楚信号是怎么提炼出来的。
为什么不直接存储原始输出?
想象一位编辑看完一部 300 页的初稿,然后写了一段 3 句话的摘要。他不会一字不差地复述全书——他提炼了核心决策、关键转折点、重要发现。
claude-mem 的压缩机制做的正是同样的事:把可能长达数千行的工具输出,提炼成一条简洁的 观察记录。
原始输出
npm test 的完整输出可能有 500 行:每个测试的详细日志、堆栈追踪、覆盖率报告……
提炼后观察
类型: testing | 标题: 认证模块测试 | 结果: 47/48 通过 | 失败: token_refresh_race_test | 原因: 竞争条件
一条观察长什么样
SDK Agent 使用固定的 XML 格式输出观察,这让结构化存储和检索变得非常可靠。
<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>
一条观察记录的开始标签
类型: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 查询更多)
claude-mem 不会把所有记忆一股脑丢进上下文——那会浪费大量 token 预算。它只注入最相关的摘要,并告诉 Claude 可以用 mem-search 工具查询更多。这是一个智能的"按需展开"设计。
完整记忆管道:捕获 → 压缩 → 存储 → 注入
理解检验
你发现 Claude 知道昨天的工作(记忆注入正常),但今天做的事情没有被记录下来。你应该排查哪个组件?
你问 Claude 关于两个月前修复的一个 bug,它说它知道这个 bug 修复了(因为记忆摘要里提到了),但不知道详细步骤。这是为什么?
安装、配置与驾驭 claude-mem
从零到第一条记忆被注入,只需要一行命令。但要真正用好它,你还需要知道这几件事。
安装:30 秒上手
claude-mem 用 npx 一键安装,自动完成所有配置。
npx claude-mem install — 自动下载、安装依赖、注册 Hooks
重启后 Setup 钩子触发,检查 Bun/Python/Chroma 等依赖是否就绪
无需任何额外操作。claude-mem 在后台静默运行,你只需要看到成果
在浏览器访问 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 的情况下找到它?
从 AI 失忆的根本原因,到 Hook 拦截机制,到 AI 压缩流水线,到记忆注入——你现在掌握了足够的知识来有效驾驭这个工具,并在它出问题时知道从哪里入手排查。