· 6 分钟 · 工程

Claude Code 的持久记忆:跨会话上下文究竟如何运作

Claude Code 的持久记忆:跨会话上下文究竟如何运作

周五,你合上笔记本电脑,结束了一场持续三小时的会话。在这期间,Claude Code 重构了你的身份验证模块,了解到你的团队使用自定义的 Result<T> 包装器,而不是抛出异常,并终于理解了为什么 UserService 不能从以下模块导入: billing/。周一,你打开一个新会话,Claude 自信地用 try/catch 包裹了一个新函数,还导入了那个禁止使用的模块。它学到的一切都消失了。

这是智能体编程工具最典型的使用体验问题:模型没有情景记忆。每次会话都从零开始,只能加上你手动记录的内容。让我们用工程方法解决这个问题,并如实说明,包括那些行不通的部分。

核心问题:会话在设计上是无状态的

Claude Code 会话是一段对话。会话结束时,对话记录会被归档,而工作上下文——通过数十次工具调用逐步积累的理解——则会消失。下一次会话会从以下三个来源重建上下文:

  • 你的 CLAUDE.md files (project and user scope)
  • Claude 在会话期间读取的文件
  • 你输入的内容

这些来源都无法记录 推导出的 知识:你已经探索过的死胡同、某个奇怪的变通方案存在的原因,以及你曾尝试方案 A 却导致 CI 失败的事实。每次会话重新推导这些知识,都会消耗 token 和实际时间。

直觉上的做法是构建一个记忆层:记录发生过的事情,将其压缩,并在启动时重新注入相关部分。开源 claude-mem 项目正是这一模式的简洁参考实现,无论你是否采用它,都值得研究。

Claude Code 持久记忆背后的架构模式

所有严肃的 Claude Code 持久记忆方案都遵循同一个三阶段循环。分别理解各个阶段,能让你决定自行构建、购买现成方案,还是跳过其中某个阶段。

阶段 1:捕获

你需要观察会话中发生了什么。Claude Code 提供了生命周期 钩子 ——即在事件发生时触发的 shell 命令。相关钩子如下:

钩子触发时机用途
SessionStart会话开始时注入记忆
SessionEnd会话终止时触发捕获
PostToolUse每次工具调用之后流式记录细粒度活动
Stop智能体完成响应时创建检查点

在以下位置配置的最简捕获钩子: .claude/settings.json:

{
  "hooks": {
    "SessionEnd": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/mem/capture.sh"
          }
        ]
      }
    ]
  }
}

钩子通过标准输入接收描述事件的 JSON。对于 SessionEnd ,你会获得会话 ID 和会话记录路径,因此 capture.sh 可以读取完整的对话日志:

#!/usr/bin/env bash
INPUT=$(cat)
TRANSCRIPT=$(echo "$INPUT" | jq -r '.transcript_path')
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id')

# Hand the raw transcript to the compression stage
python3 "$CLAUDE_PROJECT_DIR/.claude/mem/compress.py" \
  --transcript "$TRANSCRIPT" \
  --session "$SESSION_ID"

一个常见陷阱是:钩子有执行超时限制,而将大型会话记录同步传给 LLM 会超过这一限制。实际使用中,捕获操作应当触发后就返回——将会话记录路径写入队列,再由后台工作进程处理,而不是直接在钩子内处理。

阶段 2:压缩

原始会话记录无法直接用作记忆——它们体积庞大,充斥着噪声(文件内容转储、冗余工具输出、你的拼写错误)。压缩阶段使用 LLM 将会话提炼为可长期保留的事实。这里应该调用 Haiku 4.5,而不是 Opus——摘要正是 Haiku 的定价所适合的高调用量、低风险任务。

import anthropic

client = anthropic.Anthropic()

PROMPT = """You are a memory compressor. From this coding session
transcript, extract ONLY durable facts worth remembering next session:
- Architectural decisions and their rationale
- Conventions discovered (naming, error handling, imports)
- Dead ends already explored (so we don't repeat them)
- Unresolved TODOs

Output JSON: a list of {fact, category, files[], confidence}.
Omit anything transient. If nothing durable happened, output []."""

resp = client.messages.create(
    model="claude-haiku-4-5",
    max_tokens=1500,
    system=PROMPT,
    messages=[{"role": "user", "content": transcript_text}],
)
memories = resp.content[0].text

请注意 confidence 字段,以及明确要求输出 []的指令。这两者都是为了减轻后文将讨论的失效问题。将输出存储在可查询的位置——例如带有嵌入向量列的 SQLite,或以项目和文件路径为键的向量存储。

阶段 3:注入

会话开始时,你检索相关记忆并将其注入。 SessionStart 钩子可以向标准输出打印内容,这些输出会被加入 Claude 的上下文:

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "*",
        "hooks": [
          { "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/mem/inject.sh" }
        ]
      }
    ]
  }
}

关键的工程决策是 相关性过滤。注入所有记忆是一种幼稚且错误的做法——它会让上下文膨胀,淹没有用的事实。更好的做法是根据会话可能涉及的内容进行检索。如果暂时无法判断,就只注入高置信度、适用于整个项目的事实,并通过一个 MCP 服务器 提供其余记忆,让 Claude 可以按需查询:

# A tiny MCP tool the agent calls when it wants history
@mcp.tool()
def recall(query: str, files: list[str] = []) -> list[dict]:
    """Retrieve relevant memories about this codebase."""
    return memory_store.search(query, files=files, min_confidence=0.6)

在实践中,这种拉取模式优于推送模式。你无需在启动时猜测哪些内容相关,而是让智能体在确实需要时询问:“我们以前处理过这段计费代码吗?”钩子提供一份始终保留在上下文中的简短摘要;MCP 服务器负责定向检索记忆。

哪些地方会出问题——而且确实会出问题

长期使用记忆层的经历让我对它们持怀疑态度。这些失效问题真实存在,而且会相互叠加。

AI 压缩的记忆会产生幻觉。 压缩器本身也是语言模型。它偶尔会把一个实际已被放弃的想法记录为“决策”,或者把某个选择的理由颠倒过来。这样一来,会话启动时就会注入听起来很权威的错误记忆,而智能体对它的信任超过了应有的程度。这比没有记忆更糟,因为它错得信心十足。 confidence 字段和对记忆存储的人工审核,是仅有的真正缓解措施。

过时的上下文是无声的杀手。 你在周二重构了 UserService 。周一的记忆却仍然写着“认证逻辑位于 UserService”。没有任何机制会让这条记忆失效。代码一旦变化,记忆的准确性就开始下降,而又没有明确的信号来判定它们何时过期。将记忆与文件哈希或提交 SHA 关联起来,并在底层文件发生移动时,将相关记忆视为存疑。

每次会话都会增加 token 成本。 注入的记忆属于上下文,无论是否用到,你都要在每一轮为它付费。如果记忆层在启动时注入 4K 个 token,那么整个会话的输入成本都会增加相应的用量。基于拉取的 MCP 模式有所帮助,但捕获和压缩本身也会产生持续费用。

下面是实事求是的比较:

方式配置成本持续 token 用量过时风险最适合
CLAUDE.md 仅使用数分钟低且可确定由你掌控大多数项目
限定范围的 MCP 工具数小时仅按需消耗低实时系统状态
完整记忆层数天较高,每次会话都会消耗高长期探索性工作

令人不太舒服的事实:你可能并不需要它

在构建这些机制之前,先写好一份 CLAUDE.md。一份人工整理的文件,只要写明“我们使用 Result<T>,绝不抛出异常; UserService 不得导入 billing/;认证逻辑已迁移至 auth/ ,迁移时间为第二季度”,就能解决 80% 的记忆问题,而且没有幻觉风险,成本也可确定。它由人工编写,因此从源头上就值得信赖。

记忆层真正物有所值的适用范围,比宣传中所说的更窄:在长期探索性工作中, 推导出的 知识(走不通的路、尚未完成的调查)确实无法预先写好,因为你还不知道这些内容。对于常规功能开发, CLAUDE.md 加上几个限定范围的 MCP 服务器,比自动化记忆流水线能覆盖更多需求,而且绝不会骗你。

如果你确实要构建这条流水线,压缩阶段最需要关注成本——频繁使用 Haiku 生成摘要的费用并不高,但前提是你的每 token 定价本来就合理。这也是团队将此类高调用量后台工作交给 AI Prime Tech 等服务提供商的原因之一。

实用要点

  • 会话是无状态的。 真正丢失的是推导出的知识——走不通的路、决策理由、约定——而重新推导这些知识的代价很高。
  • 基本模式是捕获 → 压缩 → 注入。 通过 SessionEnd/SessionStart 钩子连接捕获与注入流程;异步执行捕获,避免钩子超时。
  • 优先采用拉取,而非推送。 启动时提供一份极简摘要,然后开放一个 recall MCP 工具,让智能体仅在相关时获取记忆。
  • 使用 Haiku 4.5 进行压缩,为每条记忆标注置信度和源文件哈希,并在文件变化时将相关记忆视为存疑。
  • 自动化记忆可能说谎。 产生幻觉或已经过时的上下文比没有上下文更糟,因为它显得很权威。让人工持续参与记忆存储的审核。
  • 先试试 CLAUDE.md 。 对于大多数项目,手写上下文文件加上限定范围的工具,在成本、可信度和维护方面都优于完整记忆层——只有当你的工作确实长期且具有探索性时,才考虑持久化。
A

本博客由 AI Prime Tech 发布。文章借助 AI 辅助起草;在据此采取行动前,请对照官方文档核实模型 ID、价格和限额等特定版本的细节。

以更低价格接入 Claude API

一个 API 密钥即可使用 Claude Opus 5.5、Sonnet 5、Haiku 4.5 和 Fable 5.1,以及 GPT-6 个模型。按量付费,无需订阅。

获取 API 密钥 →
AI Prime Tech 是独立的第三方 API 网关。Claude™ 和 Anthropic® 是 Anthropic, PBC 的商标。此处不暗示任何关联或背书。