🔑 核心观点:构建 Agent 不是调 API——模型负责推理,harness 提供环境。而记忆系统,是 harness 中最容易被低估的那一层。

很多人以为构建 AI Coding Agent = 给大模型配上工具调用 API,写几个 system prompt,挂个文件系统就完事了。但真正用过 Claude Code 的人会发现:它不只是一个「调模型的壳」,而是一套完整的工程系统。

在这套系统里,有一个模块经常被忽略,却决定了 Agent 能否「持续工作」而不是「每次从零开始」——那就是记忆系统(Memory System)。

本文基于 shareAI-lab/learn-claude-code 教学仓库和 ChinaSiro/claude-code-sourcemap 源码还原项目,深入拆解 Claude Code 记忆系统的设计理念、数据结构、源码实现,以及它与其他 18 个模块的耦合关系。

📌 不是调 API:Agent 架构的本质

在讨论记忆系统之前,需要先建立一个核心认知:

The model does the reasoning. The harness gives the model a working environment.

Claude Code 的架构远比「模型 + 工具调用」复杂得多。根据 learn-claude-code 仓库的梳理,它包含 19 个相互协作的模块:

  • Agent Loop —— 最小工作循环,驱动整个推理-执行循环
  • Tool Use —— 稳定的工具分发层,让模型能操作真实环境
  • Todo/Planning —— 可见的会话计划,让复杂任务有章可循
  • Subagent —— 每个子任务独立上下文,避免主上下文爆炸
  • Skills —— 按需加载专业知识,而非全量塞入 prompt
  • Context Compact —— 上下文压缩,保持窗口精简
  • Permission System —— 执行前安全门
  • Hook System —— 循环周围的扩展点
  • Memory System —— 跨会话持久知识(本文主角)
  • System Prompt —— 分区提示词组装
  • Error Recovery —— 续行和重试分支
  • Task System —— 持久任务图
  • Background Tasks —— 非阻塞执行
  • Cron Scheduler —— 时间触发
  • Agent Teams —— 持久队友
  • Team Protocols —— 共享协调规则
  • Autonomous Agents —— 自领自恢复
  • Worktree Isolation —— 隔离执行通道
  • MCP & Plugin —— 外部能力路由

模型越来越强,但 harness 的好坏决定了 Agent 能否真正「工作」。就像一个天才但没有手、没有记忆、没有约束——再聪明也没用。

🧠 记忆系统:不是什么都记

核心原则:选择性遗忘

Claude Code 记忆系统的核心设计哲学可以概括为一句话:

不是所有信息都该进入 memory。只有跨会话仍然有价值、且不能轻易从当前仓库状态直接推出来的信息,才适合进入 memory。

这个原则看似简单,却蕴含着深刻的工程智慧。Agent 的上下文窗口是有限资源,每一条记忆都会占用宝贵的 token。存多了,上下文臃肿,推理变慢变差;存少了,Agent 每次从零开始,反复犯同样的错。

四种记忆类型

Claude Code 将记忆分为四种类型,每种有明确的职责边界:

  • user — 用户偏好。代码风格(如「用 tabs 不用 spaces」)、回答风格(简洁 vs 详细)、工具链偏好(用 pnpm 不用 npm)等。这类信息在每个会话中都会用到,但每次都要用户重新说一遍就很烦。
  • feedback — 用户明确纠正过的错误或确认正确的做法。比如用户说过「不要用 any 类型」「这个目录不要动」,或者确认过「这样处理是对的」。这类记忆帮助 Agent 从错误中学习,也帮助它记住用户认可的好做法。
  • project — 不容易从代码直接看出来的项目约定或背景。比如「这个模块有合规要求,改动需要审批」「某目录是历史遗留,尽量不要碰」。这些信息藏在代码之外,但对正确理解项目至关重要。
  • reference — 外部资源指针。看板 URL、监控面板地址、技术文档链接、团队 wiki 等。这些资源不在代码仓库里,但经常在工作中被引用。

不该存的:五类禁区

知道「该存什么」还不够,知道「不该存什么」同样重要。以下五类信息被明确排除在记忆系统之外:

数据结构

Claude Code 的记忆系统采用了极简的数据结构设计:

  • 每条 memory 一个 .md 文件,带 frontmatter(name, description, type)
  • 索引文件 MEMORY.md,快速知道有哪些 memory 可用
  • 会话开始时重新加载,确保每次对话都有最新的记忆

memory、task、plan、CLAUDE.md 的边界

这是一个经常被混淆的问题。Claude Code 用非常清晰的边界来区分:

  • 只对这次任务有用 → task/plan(临时性)
  • 以后很多会话可能都有用 → memory(跨会话知识)
  • 长期系统级固定说明 → CLAUDE.md(项目宪法)

🔧 从源码看记忆系统的实现

源码是最好的教科书。通过 ChinaSiro/claude-code-sourcemap 还原的 Claude Code v2.1.88 源码,我们可以看到记忆系统在工程层面是如何落地的。

memdir/ —— 记忆目录管理

这是记忆系统的核心模块。从源码结构看,memdir/ 目录负责管理记忆文件的存储、读取、索引和生命周期。它将抽象的「记忆」概念映射到具体的文件系统操作上。

这种设计的精妙之处在于:记忆文件本身就是人类可读的 Markdown 文件。这意味着开发者可以直接阅读、编辑、审查 Agent 的记忆内容——没有黑盒,没有专有格式。

hooks/ —— 自动触发记忆保存

Hook System 是 Agent Loop 周围的扩展点。在记忆系统的语境下,hooks 会在特定时机自动触发记忆的保存和加载:

  • 会话结束时,自动将本次对话中的有效信息沉淀为记忆
  • 会话开始时,自动加载相关记忆进入上下文
  • 用户明确纠正时,实时触发 feedback 类型记忆的写入

context.ts —— 记忆如何进入下一轮输入

记忆写入文件只是第一步,更重要的是:这些记忆如何在下一轮对话中被模型「看到」。context.ts 负责这个关键的桥接工作——它将记忆文件的内容注入到模型的输入上下文中,让记忆真正发挥作用。

QueryEngine.ts —— 查询引擎与记忆关联

QueryEngine 是 Claude Code 的查询引擎,它不仅处理代码搜索,还参与记忆的关联匹配。当用户提出一个请求时,QueryEngine 会同时在代码库和记忆库中搜索相关信息,为模型提供更完整的上下文。

🏗️ 记忆不是孤立的:它和 18 个模块的耦合

记忆系统之所以重要,是因为它不是一个可以独立工作的模块,而是深度嵌入到整个 Agent 架构中的基础设施。

与 System Prompt 的关系

System Prompt 是 Agent 的「宪法」,规定了基本行为准则。但记忆系统让 Agent 的行为可以随时间演化——System Prompt 说「你应该这样做」,记忆说「在这个项目里,你应该那样做」。记忆通过 prompt assembly 机制重新进入模型输入,与 System Prompt 协同工作。

与 task/plan 的边界

记忆 = 长期知识(跨会话),task = 当前工作(单会话)。这个边界非常重要:如果把任务进度存进记忆,下次会话开始时 Agent 会以为这个任务还在进行中,但实际上用户可能早就在另一个会话中完成了它。

与 CLAUDE.md 的关系

CLAUDE.md = 固定规则(项目宪法),memory = 动态知识(经验积累)。CLAUDE.md 是项目级别的、不变的约定;memory 是个人级别的、会演化的知识。两者互补但不替代。

与 Skills 的关系

技能加载时会参考记忆中的用户偏好。比如,如果记忆中记录了用户偏好 TypeScript strict mode,那么加载 TypeScript 相关技能时会自动应用这个偏好。技能提供「怎么做」的知识,记忆提供「按谁的习惯做」的知识。

⚡ 实际踩坑:记忆系统设计的 6 个进阶边界

learn-claude-code 仓库总结了 6 条进阶边界,每一条都是实际踩坑后提炼出的工程智慧:

  1. 作用域分层(private vs team)。个人偏好不应污染团队共享记忆,团队约定不应被个人覆盖。需要明确的隔离机制。
  2. 不只纠错,也记正反馈。很多记忆系统只记录「做错了什么」,但 Claude Code 也记录「确认有效的做法」。这避免了 Agent 反复试错,能直接复用成功经验。
  3. 太容易过时的信息不该存。即使用户明确要求存,也要判断这条信息的有效期。如果下周就变了,不如不存。
  4. memory 会漂移,要核对当前状态。一条记忆可能在创建时是对的,但随着项目演进已经过时。回答前要验证记忆的时效性。
  5. 用户说「忽略 memory」时当它是空的。这是对用户自主权的尊重——有时候用户就是想从头开始,不想被历史记忆干扰。
  6. 推荐具体路径/资源前要再验证。记忆中记录的路径可能已经被移动或删除,推荐前要先检查是否存在。

🎯 引发思考:Agent 记忆的未来

Claude Code 当前的记忆系统采用的是「文件 + frontmatter」的极简方案——每条记忆一个 Markdown 文件,用 MEMORY.md 做索引。这个方案够用,但远未到达终点。

向量数据库 + 语义检索

当前方案依赖文件名和 frontmatter 进行记忆检索,本质上是「关键词匹配」。下一代记忆系统很可能引入向量数据库,实现语义级别的记忆关联——当用户提到「那个性能问题」时,Agent 能自动关联到三个月前记录的一条关于数据库索引优化的记忆。

记忆的隐私问题

哪些记忆该本地存储、哪些可以同步到云端?用户偏好可能包含敏感信息(如「我习惯在凌晨写代码」),项目记忆可能涉及商业机密。记忆系统需要一套细粒度的隐私策略。

记忆 vs 长上下文

一个常见的误解是:随着上下文窗口变大(100K+ tokens),记忆系统就不再需要了。但实际上:

长上下文 ≠ 长期记忆。上下文是「当前看到的」,记忆是「曾经学到的」。再大的窗口也装不下几个月的经验积累,再强的模型也需要持久化的知识来避免重复犯错。

开源启示

learn-claude-code 仓库的意义不仅在于教学——它让我们可以「从零构建」一个有记忆的 Agent。理解了记忆系统的设计哲学,我们可以在自己的 Agent 项目中借鉴这些理念,而不是盲目地给模型「加个向量数据库」就以为解决了问题。


📖 相关阅读

  • shareAI-lab/learn-claude-code — GitHub,Claude Code 源码教学仓库,19 章完整架构拆解
  • ChinaSiro/claude-code-sourcemap — GitHub,通过 npm 包还原的 Claude Code 源码结构
  • affaan-m/everything-claude-code — GitHub,Agent harness 优化系统
  • Claude Code: An agentic coding tool — Anthropic 官方文档
  • Memory in LLM Applications — Harrison Chase,LangChain 博客

逍遥云初 | 2026.04.11