claude-mem 工作原理深度解析:工具调用如何沉淀为跨会话自动注入的记忆
claude-mem 是一款为 Agent(Claude Code、Codex、Gemini、OpenClaw 等)提供跨会话持久上下文的记忆层:它把会话中每一次工具调用捕获并压缩为 observation,在会话结束时汇总结论,并在下一次会话启动时把相关记忆自动注入提示词。本文以 plugin/skills/how-it-works/onboarding-explainer.md 为骨架,结合本仓库的源码与测试,逐层拆解“捕获 → 压缩 → 摘要 → 注入”的完整链路、记忆注入的启动时机,以及数据本地存储与隐私边界,帮助你从原理上理解 claude-mem 在何时、以何种方式"想起"过去的工作。
一次“记忆闭环”的三个阶段
原文档(plugin/skills/how-it-works/onboarding-explainer.md)把核心行为概括为一句话:每一次 Claude 的 Read、Edit、Bash 调用都会变成一个被压缩的 observation;会话结束时这些 observation 被汇总成摘要;其中相关的摘要会在未来的提示词中自动注入——于是下一个会话从上一个会话的上下文开始,无需重新解释代码库、无需重新发现决策。
从 docs/public/usage/getting-started.mdx 可以看到这套闭环对应的四个步骤:
- 启动 Claude:最近若干次会话的上下文自动出现;
- 正常工作:每一次工具执行都被捕获;
- Claude 结束响应:Stop hook 自动生成并保存会话摘要;
- 下一次会话:之前的工作自动出现在上下文中。
也就是说,记忆不是用户手动“存档”出来的,而是完全自动地发生在会话的正常使用过程中。原文档提到的“无需重新解释代码库、无需重新发现决策”(no re-explaining the codebase, no re-discovering decisions),正是这套闭环带来的直接收益。
每次 Read / Edit / Bash 如何变成一条 observation
原文档强调“Every Read, Edit, and Bash that Claude makes turns into a compressed observation”。这并非抽象描述,而是有明确实现路径的。
底层调用链
在 src/services/worker/agents/ResponseProcessor.ts 中,worker 服务会对 Agent 产生的输出做结构化解析(parseAgentXml),并从中提取“文件证据”(file evidence)。源码把工具按读写语义分类:
READ_TOOL_NAMES:Read(读取类工具);WRITE_TOOL_NAMES:Edit、MultiEdit、Write、NotebookEdit、write_file(修改类工具);PATCH_TOOL_NAMES:apply_patch(补丁类工具)。
随后通过 extractObservationFileEvidence 从这些工具的 tool_input 中解析出 files_read 与 files_modified 集合,并调用 broadcastObservation 广播新观察。也就是说,一条观察最少要回答三个问题:用了什么工具、动了哪些文件、发生了什么。
一条 observation 长什么样
worker 对观察做进一步加工后会得到结构化字段。依据 docs/public/usage/getting-started.mdx,每条 observation 至少包含:
- Title / Subtitle:发生了什么事的简要描述;
- Narrative:更详细的叙述;
- Facts:以要点形式沉淀的关键结论;
- Concepts:相关标签与分类;
- Type:类型分类(decision 决策、bugfix 修复、feature 特性等);
- Files:读/写了哪些文件。
其中 type 分类在上下文注入侧有对应的统计口径:src/services/context/ContextBuilder.ts 中的 STAT_TYPE_BUCKETS 定义了 bugfix、discovery、decision、refactor 四类,其余归入 other——这也从侧面印证了记忆系统会刻意把“修过什么 bug、做过什么决策、做过什么重构、发现过什么”这类高价值信号从流水账中区分出来。
观察如何被压缩
“压缩”意味着不做原文抄录,而是用 AI 把大量工具调用归纳为结构化、token 高效的记忆单元。worker 侧为此对接了多种 provider,对应源码为 src/services/worker/ClaudeProvider.ts、src/services/worker/GeminiProvider.ts、src/services/worker/OpenRouterProvider.ts 以及 src/services/worker/OpenAICompatibleProvider.ts——这与原文档“压缩依赖你配置的 AI provider(Claude / OpenRouter / Gemini)”的说法相互印证,并且 OpenRouter / OpenAI 兼容路线让自建网关(如 LiteLLM)也能接入压缩环节。
会话结束时:observation 如何汇总成摘要
原文档指出:observations get summarized at session end。对应到项目行为(docs/public/usage/getting-started.mdx):当 Claude 结束响应触发 Stop hook 时,系统会自动生成会话摘要。
一份会话摘要(Session Summary)包含五个语义字段,正好对应上一轮工作交接中最需要记住的部分:
- Request:你请求了什么;
- Investigated:Claude 探索了什么;
- Learned:关键发现与洞察;
- Completed:完成了什么;
- Next Steps:接下来要做什么。
这条“请求 → 调查 → 学到 → 完成 → 下一步”的摘要模板被用于后续会话的注入,从工作流交接的角度看,它等价于每次会话结束时自动产出一份精炼的“handoff 文档”。摘要生成完成后同样通过 broadcastSummary 广播并落库。
记忆注入何时启动:第二个会话才开始
原文档给出了一个非常明确的时间点:Memory injection starts on your second session in a project(记忆注入从你在某个项目里的第二次会话开始)。
这一规则也被安装流程的落地文案引用:src/npx-cli/commands/install.ts 中安装成功提示即包含 Memory injection starts on your second session in a project.。其语义是:
- 第一次会话在全新项目中扮演“播种”角色:这一轮没有历史记忆可注入,它负责产生第一批 observation 与会话摘要;
- 从第二次会话起,每次会话启动都能从库里检索到该项目之前的记忆,并自动注入与过去工作相关的上下文。
首次会话“无记忆”的占位状态
在还没有任何记忆的新项目里,系统并不会注入报错或空内容,而是写入友好的占位文案。例如 src/services/integrations/McpIntegrations.ts 定义了:
# claude-mem: Cross-Session Memory
*No context yet. Complete your first session and context will appear here.*
src/services/integrations/CursorHooksInstaller.ts 也会为 Cursor 的 rules 文件创建同样的占位内容,并提示“will populate after first session”。这套“完成第一个会话后,上下文自然出现”的交互约定,与“注入从第二个会话开始”的规则完全自洽。
/clear 不会中断记忆
值得注意的一个边界场景:当你在会话中使用 /clear 时,底层 session 并不会结束。依据 docs/public/usage/getting-started.mdx,/clear 会触发 SessionStart hook(source: "clear")重新注入最近会话的上下文,同时当前会话仍在持续捕获 observation,并在 Claude 结束响应时照常生成摘要——因此清空对话上下文并不会清空记忆。
第二个会话启动时:注入什么、注入多少
当记忆注入开始生效后,SessionStart hook 会把上下文拼接进新会话。核心实现位于 src/services/context/ContextBuilder.ts,其流程可以概括为:
getProjectContext(cwd)依据当前工作目录解析项目身份(多项目场景还会按input.projects扩展检索范围);queryObservationsMulti/querySummariesMulti从 SQLite 中检索该项目最近的 observations 与 session summaries;- 将观察按时间线(timeline)编排,叠加会话标记,渲染为结构化上下文;
- 若最近一条摘要在最后一条观察之后生成(即摘要代表最新状态),则展开显示完整摘要字段;否则隐藏,避免展示“过期摘要”误导后续会话。
关于第 4 点,docs/public/usage/getting-started.mdx 给出了精确的判定示例:观察发生在 14:00、摘要生成于 14:05 → 显示摘要;摘要生成于 14:00、之后又有新观察 → 隐藏摘要。源码中对应 shouldShowSummary(config, mostRecentSummary, mostRecentObservation) 的判定逻辑。
此外,ContextBuilder 还会通过 calculateTokenEconomics 计算每次注入的 token 开销与“相对朴素全量重放所节省的 token”,并在渲染时暴露 ContextInjectStats(包含注入 token 数 tokens_injected、节省 token 数 tokens_saved_vs_naive、时间线深度 timeline_depth_days、观察类型分布等)。这些字段表明注入不是“把历史全倒出来”,而是受控、可度量、面向 token 经济的。
渐进式披露:索引轻量、细节按需
为了让自动注入保持轻量,系统采用“渐进式披露(progressive disclosure)”策略(详见 docs/public/usage/getting-started.mdx):
- 第一层 · 索引展示(会话启动):时间线仅展示观察标题、会话标记与 token 估算,占用约 50–200 token;
- 第二层 · 按需细节(MCP 工具):需要时以自然语言提问(如“我们之前修过哪些 bug?”),由 Claude 调用 mem-search 类 MCP 工具拉取完整细节,单条约 100–500 token;对应技能可参考 plugin/skills/mem-search/SKILL.md;
- 第三层 · 完美回忆(代码访问):真正需要时直接读取源码文件、原始 transcripts 与原始数据。
这套分层让“轻量自动注入 + 重量按需检索”得以兼顾上下文效率与历史完整性。
想一次性载入整个仓库?/learn-codebase
原文档为“希望在单次会话中把整个仓库一次性预载进记忆”的场景提供了一个可选命令:/learn-codebase,并注明耗时约 5 分钟。对应技能文件为 plugin/skills/learn-codebase/SKILL.md,其工作方式是系统性地、逐文件完整读取仓库中每一个源文件,对超大文件借助 Read 工具的 offset / limit 参数分页读取(例如 offset: 1, limit: 500,再 offset: 501, limit: 500),从而在项目生命周期早期就建立完整的代码库认知。该技能文档也特别提示:它虽然消耗 token,但属于“前端加载认知缓存(front-load a cognitive cache)”,长期来看能降低后续开发成本——这与“第一会话播种”的理念一致,只是把播种范围从“这一轮的交互”扩展到“整个仓库”。
数据存放位置:~/.claude-mem 目录解剖
原文档明确:Everything stays in ~/.claude-mem on this machine。在源码层面,数据目录的解析逻辑位于 src/shared/paths.ts 的 resolveDataDir():
- 若设置了环境变量
CLAUDE_MEM_DATA_DIR,优先使用它; - 否则默认取
join(homedir(), '.claude-mem'); - 若默认目录下存在
settings.json,还会读取其中env.CLAUDE_MEM_DATA_DIR(或顶层同名键)作为候选值,并做~展开。
因此“~/.claude-mem”是默认值,也可以通过环境变量或 settings.json 自定义(例如存放在系统盘之外的目录)。默认情况下,该目录内会包含以下实体(对应 src/shared/paths.ts 的 paths 定义与 docs/public/usage/getting-started.mdx 的 SQLite 说明):
| 路径 | 实体 | 作用 |
|---|---|---|
~/.claude-mem/claude-mem.db |
SQLite 数据库 | 存储 sessions、observations、session summaries 等结构化记忆(DB_PATH) |
~/.claude-mem/chroma/ |
向量索引 | 语义检索所用的向量索引目录(paths.chroma()) |
~/.claude-mem/logs/ |
日志 | 运行日志目录(LOGS_DIR) |
~/.claude-mem/settings.json |
设置 | 用户配置与默认配置解析入口(USER_SETTINGS_PATH) |
~/.claude-mem/.env |
环境配置 | 供 worker/server 读取的环境变量文件(paths.envFile()) |
~/.claude-mem/worker.pid 等 |
运行时状态 | worker / server 的 pid、port、runtime 信息 |
如果你想直接审视自己的记忆数据,可以对 SQLite 库做只读查询。参考 docs/public/usage/getting-started.mdx 中的示例:
# 打开数据库
sqlite3 ~/.claude-mem/claude-mem.db
# 查看最近会话
SELECT session_id, project, created_at, status
FROM sdk_sessions
ORDER BY created_at DESC
LIMIT 10;
# 查看会话摘要
SELECT session_id, request, completed, learned
FROM session_summaries
ORDER BY created_at DESC
LIMIT 5;
# 查看某会话的观察
SELECT tool_name, created_at
FROM observations
WHERE session_id = 'YOUR_SESSION_ID';
注意:从源码看,ContextBuilder 打开数据库时使用的是只读模式(new Database(DB_PATH, { readonly: true, create: false })),因此即使注入逻辑发生问题也不会反过来破坏记忆库数据。
隐私边界:什么留在本机,什么会离开
原文档给出的隐私承诺是清晰的两分法:
- 留在本机:SQLite 数据库、向量索引、日志与设置全部位于
~/.claude-mem; - 可能离开本机:只有发送给你所配置的 AI provider(Claude / OpenRouter / Gemini)的压缩调用——也就是把工具活动压缩成 observation / summary 的那部分请求。
换句话说,观察与摘要的“原文级”数据始终是本地资产,出网的仅是交给模型做归纳总结的压缩请求。基于此可以推导出两条实践建议(可结合 docs/security.md 进一步阅读):
- 选用哪家 provider 做压缩,等于选择把“工作内容的提炼请求”交给谁,这在涉密项目上需要审慎评估;
- 由于一切数据都落在本机目录,备份记忆最简单的方式就是备份
~/.claude-mem(或你通过CLAUDE_MEM_DATA_DIR配置的自定义目录)。
卸载时的清理
原文档最后一条承诺与清理相关:数据会在 npx claude-mem uninstall 时被干净移除。该命令在仓库中的实现位于 src/npx-cli/commands/uninstall.ts,属于 npx claude-mem 的 CLI 命令集(入口见 src/npx-cli/index.ts)。也就是说,卸载不仅移除 hook / MCP 集成,也会一并清理本地记忆数据,避免在机器上残留会话痕迹。
总结:一条贯穿始终的主线
把原文档的三个问题连起来看,claude-mem 的设计主线非常清楚:
- 做什么:Read/Edit/Bash 等工具活动 → 压缩 observation → 会话末汇总摘要 → 相关记忆自动注入下一次会话,做到“不用重新解释代码库、不用重新发现决策”;
- 何时生效:项目第一个会话播种,从第二个会话开始自动注入;
/clear不打断捕获;/learn-codebase可按需一次性预载整个仓库; - 数据与隐私:SQLite、向量索引、日志、设置全部落在
~/.claude-mem,只有压缩用的 provider 调用会出网,npx claude-mem uninstall可一次性干净清理。
如果希望进一步深入,可以顺藤摸瓜阅读 src/services/context/ContextBuilder.ts(注入渲染主流程)、src/shared/paths.ts(数据目录解析)、src/services/worker/agents/ResponseProcessor.ts(观察生成)以及 docs/public/usage/getting-started.mdx(面向使用者的完整生命周期说明)。理解这条链路之后,无论是在排障、调优 token 开销还是评估隐私影响时,你都能准确判断“此刻记忆到底发生在哪一环”。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00