首页
/ claude-mem 工作原理深度解析:工具调用如何沉淀为跨会话自动注入的记忆

claude-mem 工作原理深度解析:工具调用如何沉淀为跨会话自动注入的记忆

2026-09-06 18:24:51作者:鲍丁臣Ursa

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 可以看到这套闭环对应的四个步骤:

  1. 启动 Claude:最近若干次会话的上下文自动出现;
  2. 正常工作:每一次工具执行都被捕获;
  3. Claude 结束响应:Stop hook 自动生成并保存会话摘要;
  4. 下一次会话:之前的工作自动出现在上下文中。

也就是说,记忆不是用户手动“存档”出来的,而是完全自动地发生在会话的正常使用过程中。原文档提到的“无需重新解释代码库、无需重新发现决策”(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_NAMESRead(读取类工具);
  • WRITE_TOOL_NAMESEditMultiEditWriteNotebookEditwrite_file(修改类工具);
  • PATCH_TOOL_NAMESapply_patch(补丁类工具)。

随后通过 extractObservationFileEvidence 从这些工具的 tool_input 中解析出 files_readfiles_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 定义了 bugfixdiscoverydecisionrefactor 四类,其余归入 other——这也从侧面印证了记忆系统会刻意把“修过什么 bug、做过什么决策、做过什么重构、发现过什么”这类高价值信号从流水账中区分出来。

观察如何被压缩

“压缩”意味着不做原文抄录,而是用 AI 把大量工具调用归纳为结构化、token 高效的记忆单元。worker 侧为此对接了多种 provider,对应源码为 src/services/worker/ClaudeProvider.tssrc/services/worker/GeminiProvider.tssrc/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,其流程可以概括为:

  1. getProjectContext(cwd) 依据当前工作目录解析项目身份(多项目场景还会按 input.projects 扩展检索范围);
  2. queryObservationsMulti / querySummariesMulti 从 SQLite 中检索该项目最近的 observations 与 session summaries;
  3. 将观察按时间线(timeline)编排,叠加会话标记,渲染为结构化上下文;
  4. 若最近一条摘要在最后一条观察之后生成(即摘要代表最新状态),则展开显示完整摘要字段;否则隐藏,避免展示“过期摘要”误导后续会话。

关于第 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.tsresolveDataDir()

  1. 若设置了环境变量 CLAUDE_MEM_DATA_DIR,优先使用它;
  2. 否则默认取 join(homedir(), '.claude-mem')
  3. 若默认目录下存在 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 进一步阅读):

  1. 选用哪家 provider 做压缩,等于选择把“工作内容的提炼请求”交给谁,这在涉密项目上需要审慎评估;
  2. 由于一切数据都落在本机目录,备份记忆最简单的方式就是备份 ~/.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 的设计主线非常清楚:

  1. 做什么:Read/Edit/Bash 等工具活动 → 压缩 observation → 会话末汇总摘要 → 相关记忆自动注入下一次会话,做到“不用重新解释代码库、不用重新发现决策”;
  2. 何时生效:项目第一个会话播种,从第二个会话开始自动注入;/clear 不打断捕获;/learn-codebase 可按需一次性预载整个仓库;
  3. 数据与隐私: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 开销还是评估隐私影响时,你都能准确判断“此刻记忆到底发生在哪一环”。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388