这篇文章围绕 Claude Code 的架构设计与核心实现 展开,从源码角度分析了 Anthropic 如何构建一个真正可用的 AI 编程 Agent。核心内容可以概括为以下几个方面:
一、Claude Code 本质是什么
Claude Code 并不是聊天机器人,而是一个 AI Agent。
区别在于:
- ChatBot:一问一答
- Copilot:代码补全
- Claude Code:自主完成任务
它能够:
- 阅读代码
- 修改文件
- 执行 Shell
- 管理 Git
- 多轮自主决策
整个运行模式遵循:
感知(Perception)→ 决策(Reasoning)→ 行动(Action)→ 再感知……
而不是固定流程。
二、四层架构设计
作者认为 Claude Code 最大的优点就是分层非常清晰。
① 引擎层(Brain)
引擎层 是 Agent 的「大脑」,负责思考和调度。它的关键设计原则是不包含任何业务逻辑 ,它不知道怎么读文件、怎么改代码、怎么搜索,这些全是工具层的事。
引擎层只负责:
- 调用 LLM
- 组织 Prompt
- 决定下一步
- 调度工具
不负责任何业务逻辑, 这种设计的好处是新增能力只需要新增一个工具,引擎层完全不用改 。
② Tool 层
真正提供能力是工具层 ,40 多个工具都在这一层。每个工具就是 Agent 的一个能力:执行 Shell 命令、读写文件、搜索代码、生成子 Agent……
例如:Read、Edit、Write、Bash、Git、Search、Sub Agent.....
这些工具不是随便写的,它们实现一个统一的接口,每个 Tool 都必须声明,
- 是否只读
- 是否危险
- 是否允许并发
安全能力直接进入类型系统。
③ Service 层
提供公共能力:
例如:
- 调大模型 API
- 上下文压缩
- MCP 协议
- Prompt Cache
属于所有模块共享基础设施。
④ Security 层(安全与治理层)
覆盖整个系统:
包括:
- 权限控制
- Hook
- Bash 安全分析
- Git 安全
相当于横切关注点(Cross Cutting Concern)。
三、Agent 工作方式
Claude Code 的 Agent Loop 实现了经典的 ReAct 模式
用户输入
↓
[调用 LLM,流式获取响应]
↓
响应中有 tool_use?
├─ 是 → 执行工具 → 将结果追加到对话 → 回到"调用 LLM"
└─ 否 → 结束,将最终文本响应展示给用户
每次"调用 LLM → 执行工具 → 返回结果"是一个 turn,Agent Loop 就是把多个 turn 串联起来的无限循环。
在 ReAct 基础上做了哪些改进
Extended Thinking
Claude Opus 可以在模型内部完成推理。
无需输出:
Thought:
Action:
Observation:
节省大量 Token。
四、Plan Mode
Claude Code 还有一种:Plan Mode
规划
↓
探索代码(只读)
↓
生成 Plan
↓
用户审批
↓
执行
Everything is Tool: 连复杂的 Plan Mode(规划-审批-执行)都被封装为 EnterPlanMode和 ExitPlanMode 两个工具,确保了引擎架构的纯粹。
五、System Prompt 设计
这是文章篇幅最大的部分。
作者认为 Claude Code 的 Prompt 十分值得学习。
主要包括:
身份定义
传统的 ChatBot 设定通常是 You are a helpful assistant(你是一个得力的助手)。这种设定会导致模型极其被动——一问一答,说得多、动得少,甚至充满客套话。
Claude Code 将其重塑为 Interactive Agent(交互式智能体):
- 主动推进: 明确告知模型,你的核心任务是“执行并解决问题”,而不是“解释如何解决问题”。强调主动执行。
行为规范
LLM 在面对编程任务时,有一个根深蒂固的坏习惯:喜欢盲猜代码结构,直接开始重写。 这在真实大型项目里是灾难性的。Claude Code 在 Prompt 中死死卡住了这个行为:
例如,修改任何文件前,必须先调用工具读取该文件或其上下文
不要:
- 自作主张重构
- 添加没要求的功能
- 一次失败立刻放弃
风险控制
Claude Code 在 Prompt 中构建了一个精妙的风险控制矩阵,核心围绕两个指标:可逆性(Reversibility) 和 影响度/爆炸半径(Blast Radius)。
- 自动执行区(低风险): 本地修改一个源文件、运行一次本地测试。这些操作是可逆的(通过 Git 可以轻松 Rollback),因此 Prompt 允许 Agent 自主决策,无需频繁弹窗打扰用户。
- 人工审批区(高风险): 涉及
Git Push、线上部署、删除核心配置等。这些操作一旦执行,爆炸半径会波及整个团队或线上环境,且极难逆转。Prompt 中有硬性死命令:必须停下来,输出解释,等待人类手动确认(Approval)。
Tool 使用规范
Prompt 严格禁止模型在 Bash 中使用原生的 cat、grep、find 等命令,而是必须调用平台提供的专属 Tool(如 Read Tool、Grep Tool)。
为什么要多此一举?
如果模型直接用
rm -rf或cat,对宿主系统来说就是一个黑盒的 Bash 字符串,安全层很难做精细化审计。而当 Prompt 强迫模型将意图转化为结构化的工具调用(Tool Call)时,Service 层就可以在底层轻松拦截:
- 检查
Read Tool是否越界访问了系统敏感文件(如/etc/passwd)。- 统计
Grep Tool的并发率,防止把 CPU 跑满。 原因:
Tool 可审计、可权限控制。
Git 安全
禁止:
- force push
- reset --hard
- checkout .
- clean -f
并特别强调,不要使用:
git commit --amend(修改上一次提交)
避免 hook 失败导致覆盖旧 Commit。
Prompt Cache
作者认为这是非常巧妙的一点,这是降本增效的核心。在调用大模型 API 时,如果整个 Prompt 每次都在变,就无法触发服务端的 Prompt Cache(提示词缓存),Token 成本会高上天。
Claude Code 将 System Prompt 做成了极其完美的动静分离结构:
- 固定部分(Static Base): 包含上述所有的身份定义、安全防线、工具规范、Git 规则。这部分内容长达数千甚至上万 Token,但全球所有用户、每一次对话都完全一模一样。
- 动态部分(Dynamic Context): 仅包含当前用户具体的提问、当前目录的树状结构、近几轮的对话历史。
因此:
Claude API 可以共享 Prompt Cache。
官方称:
缓存命中成本可下降约 90%。
六、记忆系统(Memory)
文章认为这是 Claude Code 最优秀的设计之一。
很多创业项目在做 AI Agent 时,只要一听到“记忆系统”,脑子里蹦出的第一个想法绝对是:“上向量数据库(Vector DB),搞 RAG(检索增强生成)!”
但 Claude Code 的工程团队非常务实,他们在这个设计上展现了极强的克制与极高明的工程手腕:彻底抛弃向量数据库,转向“文件系统 + 结构化分类记忆”的模式。
这种设计巧妙地解决了传统向量检索在源码场景下的痛点,我们可以从以下四个核心维度来深度拆解:
为什么坚决不用向量数据库?
在代码世界里,向量检索(Semantic Search)往往面临两个致命缺陷:
- 精度灾难(丢失精确匹配): 向量检索擅长处理模糊的“语义相似度”,但在编程中,一个配置项、一个特殊的函数名或者某个 Bug 的底层依赖,往往需要100% 的精确匹配。语义相似的东西,在代码里可能风马牛不相及。
- 缺乏因果与上下文: 向量数据库吐出来的通常是支离破碎的文本片段(Chunks),它无法告诉模型这行代码背后的历史背景、演进逻辑,以及当初为什么要这么写(Why)。
因此,Claude Code 直接把记忆做成了物理文件,存放在项目的特定目录中,利用大模型的结构化理解能力来做“精准人肉检索”。
四类分类记忆机制
为了防止记忆无限扩张、最后反过来撑爆上下文(Context Window),Claude Code 极其克制地只划定了四条狭窄的记忆赛道。
通过下面这张表,我们可以清晰地看出它们的分工和工程价值:
| 记忆分类 (Type) | 存储核心内容 |
|---|---|
| User Memory | 用户的个人偏好、编码习惯、高频业务线等。 |
| Feedback Memory | 全系统最核心。 记录历史踩坑、人类纠错、运行失败的复盘。 |
| Project Memory | 当前仓库的整体架构、技术栈选型、核心开发规范。 |
| Reference Memory | 复杂的第三方 API 契约、特殊的内部库说明、特定魔数含义。 |
核心亮点:Feedback 的“因果双轨制”
Claude Code 的 Feedback 记忆强制要求存入因果链(Why & How):
- Rule(规则): 不要 Mock 数据库。
- Why(原因): 曾经因为 Mock 导致线上真实环境发生级联翻车。
- How(如何落地): 所有集成测试,必须全部使用 Testcontainers 启动真实数据库进行连接。
有了这种包含“痛点和解法”的因果记忆,大模型在推理时就能瞬间判定:“哦!当前我正在写集成测试,结合历史教训,我绝不能图省事用 Mock,必须老老实实拉起真实容器。”
MEMORY.md
即便只有四类记忆,随着项目变大,记忆文件的体积也会滚雪球般增长。如果每次对话都把所有记忆灌给大模型,不仅 Token 费用吃不消,还会引发模型的“大海捞针”认知疲劳(Needle in a Haystack),导致遗忘。
Claude Code 的解法是引入了 MEMORY.md 索引路由机制:
[ 触发任务 ]
│
▼
┌────────────────────────┐
│ MEMORY.md │ ◄─── (只有这个索引文件默认加载)
│ (仅包含目录与核心摘要) │
└────────────────────────┘
│
├─ 匹配到 User Preferences? ────► [ 按需读取 user_custom.md ]
├─ 匹配到 Database Issue? ──────► [ 按需读取 db_feedback.md ]
└─ 匹配到 Project Stack? ───────► [ 保持关闭 ]
在初始状态下,Agent 的上下文中只会加载一个非常精简的 MEMORY.md。这个文件扮演着“目录”的角色。当且仅当大模型在索引中看到了相关的关键词或标签时,才会下达工具调用指令,去读取背后真正的记忆细节文件。不触发,就不加载,完美控容。
Sonnet 做秘书
Claude Code 不让 Opus 自己检索记忆。它发明了“秘书模式”(The Secretary Pattern):
- Sonnet 降维打击(小模型做检索): 让速度极快、成本更低的 Sonnet 扮演“秘书”。它负责拿着当前的问题,去浏览
MEMORY.md索引目录,利用快速泛化的语义能力,精准挑出最相关的 5条记忆碎片。 - Opus 定海神针(大模型做决策): Sonnet 把这 5 条精心挑选、毫无噪声的干货打包好,呈送给核心大脑 Opus。Opus 拿到手后心无旁骛,全身心投入到最核心的代码推理与逻辑决策中。
体现:
小模型做检索,大模型做决策。
七、上下文压缩(最精彩部分)
这可能是整个 Claude Code 里最复杂也最精妙的部分。
大模型有上下文窗口限制。即使是 200K Token 的窗口,一次复杂的编程任务(读了几十个文件、执行了几十条命令)很容易就塞满了。
业界常见的做法是「简单截断」,只保留最近的 N 条消息,旧的扔掉。但这对于编程 Agent 来说是灾难性的:你可能 20 轮前读过一个关键配置文件,现在要改代码时那个文件的信息已经被截掉了,Agent 就会犯低级错误。
另一种做法是「全量摘要」,把整段对话总结成一段摘要。但这很贵(摘要本身就是一次 API 调用),而且有信息损失。
Claude Code 的核心理念是:压缩一定有信息损失,所以能不压就不压,必须压的时候从最轻的手段开始 。它设计了五个从轻到重的压缩手段,就像医院的分诊制度一样:先试最温和的,不行再上猛药。在每次 API 调用前依次尝试:
第一层:工具输出转储
- 痛点: 运行一个测试脚本或
Grep搜索,经常会吐出上千行的日志或源码,瞬间塞满上下文。 - 做法: 引擎层一旦发现 Tool 输出的内容超过阈值,立刻将其写入本地磁盘的临时文件。在发送给大模型的 Context 中,只保留前几行、后几行以及一个结构化的语义摘要(例如:“成功搜索到 14 处匹配,已为您转储至临时文件…”)。
- 无损原理: 模型如果后面真的需要看详情,可以通过专门的读取工具去翻那个临时文件。
第二层:远古消息剪枝
Claude Code 怎么做? Snip 是最「粗暴」但也最高效的一层,直接把对话开头的一批老消息移除掉,然后插入一个边界标记告诉模型「这之前的内容已经被清理了」。
- 痛点: 对话轮次太多后,早期的“客套话”或“过时讨论”毫无价值。
- 做法: 采用极其轻量级的滑动窗口,直接物理删除最早期的几轮对话。
- 无损原理: 这属于成本最低的操作,不涉及复杂的语义理解,针对的是与当前代码开发完全无关的“远古垃圾信息”。
第三层:裁剪老的工具输出
问题是什么? 经过前两层之后,上下文里剩下的都是「不太老但也不太新」的消息。
这些消息不能直接砍掉(可能还有用),但里面大量的工具输出其实已经过时了,比如 30 分钟前读的一个文件,现在那个文件可能已经被改过了。
- 做法: 系统会自动扫描历史对话,主动删掉早期那些 Tool 的具体输出内容(比如 20 轮对话前,模型调用
Read Tool读取的某个文件内容)。 - 无损原理: 为什么敢直接删代码内容?因为代码资产存在于物理文件系统中,它是“可再生的”。只要这个 Tool 的调用历史(Intent)还在,模型如果后面发现数据缺失,它自己会再次调用
Read Tool去读一遍。
Claude Code 巧妙地利用了“外部物理世界(文件系统)”来当做自己的无限缓存,从而释放了昂贵的 Context。
被裁剪的工具结果会被替换成一个标记:
export const TIME_BASED_MC_CLEARED_MESSAGE =
'[Old tool result content cleared]'
这样模型看到这个标记就知道「这里原来有内容但被清理了」。
第四层:上下文折叠
- 做法: 在 Agent 内部的内存中,完整的历史聊天记录(Master History)是一直原封不动保留的。但是,在准备向 Anthropic API 发起 HTTP 请求的刹那间,系统会通过一个管道(Middleware),动态地把历史记录中的长文本折叠、压缩成一个“压缩版 View”。
- 无损原理: 这样做保证了 Agent 本地的 Log 和调试信息是绝对完整的(方便人类排查),而送给大模型的则是全新的、精简版的消息数组。
第五层:自动压缩
-
这是最后的“原子弹”手段,当上述四层手段用尽、Context 依然压不住时才会触发。
-
做法: 它会调用一个速度极快的辅助模型(如 Sonnet),对大段的历史交互弧线(Historical Arc)进行真正的 Summary(语义摘要)。
-
如何解决“摘要导致模型变蠢”的痛点?
通常的 AI Agent 压缩到这一步就废了,因为生成摘要后,模型会忘记自己刚刚正在修改哪个文件、刚才的 Bug 改到一半进行到哪了(丢失了 Working Memory)。
Claude Code 在这里做了一个神级操作——主动恢复(Post-Summary Hydration):
在生成的摘要文本后面,系统会强行补填三个硬性上下文卡片:
- Active Plan: 当前正在执行的 Plan 目标是什么。
- Current File Buffers: 最近频繁访问和修改的 3 个核心文件的最新内容。
- Skills: 当前沉淀下来的临时技能定义。
绝妙的比喻: 第五层压缩就像是把你脑子里的记忆打包压缩成了一段陈述历史,但为了防止你断片,在推入手术室前,医生强行在你手里塞了一张便签,上面写着:“你叫张三,你手里的活干到一半了,目前正在改 index.ts 文件的第 45 行,别搞错了!”。
八、文章核心思想
作者最后总结了 Claude Code 的几个设计理念:
- 信任大模型,把应用层做到最简单。
- 能力全部抽象成 Tool,而不是写死流程。
- Prompt 决定 Agent 行为边界。
- 记忆应该是结构化信息,而不是所有内容都向量化。
- 上下文压缩要分层处理,尽量减少信息损失。
- 大量性能优化(缓存、预取、按需加载)比单纯提升模型能力更重要。
一句话总结
每一件事单拿出来都算不上什么黑科技,但全串在一起,就是一套能把一匹野马驯成耕牛的缰绳系统。
这给我一个很大的启发:做 Agent,别老盯着模型发呆。模型是发动机,但一辆车能不能安全上路,靠的是刹车、方向盘、安全带。这些「不起眼」的东西,才是真正决定成败的。 这
篇文章的核心观点是:Claude Code 的真正优势并不只是模型本身,而是围绕模型构建的一整套工程体系——包括 Tool-Use Loop、分层架构、动态 System Prompt、结构化记忆系统、五级上下文压缩和严格的安全治理。这些工程设计共同把一个强大的大模型,打造成了一个稳定、高效、可控的 AI 编程 Agent。