Claude Code源码解析学习
Agent 的核心骨架(如所示)极其简单,可以概括为一个公式:Agent =while循环 + 工具调用 +状态检查,历史记录只增不减,导致模型注意力稀释,质量下降。会话结束即失忆,无法跨任务或跨时间延续。仅靠简单的字符串黑名单(如rm -rf)拦截风险,极易被绕过。每一轮都发送全量历史,Token 消耗呈线性甚至指数级增长为了解决上述问题,Claude Code 引入了Harness Engin
前端时间,claude code源码泄漏。不过需要注意的是,泄漏的是 Claude Code 客户端的源码,并不是 Claude Opus 大模型的源码。那既然是客户端而不是大模型的源码,我们学它有什么用呢?主要在于,claude code源码模型简直就是这段时间AI圈爆火的Harness Engineering的最佳教科书。即给大模型套上缰绳Harness。用系统去约束它的行为,让大模型不至于肆意洒脱,而是稳稳当当的完成任务。
一.agent的实现及四大隐患
首先在接下来我们用的简化版代码来自开源项目learn-claude-code(shareAI Lab出品,MIT 许可,GitHub46.8K星),它把Claude Code的核心架构用Python拆解成了12个渐进式阶段。后续我们统一称之为"简化版代码"。今天先看最基础的第一阶段-------s01_agent_loop.py
那么首先来看一眼完整文件的结构。s01_agent_1oop.py总共120行,但去掉注释、导入和辅助代码后,真正的Agent逻辑只有大约30行有效代码,分成三个部分:
第一部分:工具定义(9行)
Agent需要"手"来操作世界。s01只给了它一只手一一一个bash工具:
TOOLS = [{
"name": "bash",
"description": "Run a shell command.",
"input_schema": {
"type": "object",
"properties": {"command": {"type": "string"}},
"required": ["command"],
},
}]
我们知道当代agent在运行的时候,其实他的这个整体的运行逻辑或者核心骨架其实非常非常简单啊,就是能调用工具的大模型加上CMD命令行工具,基本上就构成了所有我们现在能看到的。
这个工具的作用在于它告诉 AI:“嘿,你现在拥有了一个执行 Bash 命令的能力。如果你在解决问题时需要运行代码或操作系统,你可以调用这个工具。”Claude code生产版有40多个工具---文件读写,搜索,Git操作等都是基于上述这个结构的扩展。
第二部分:核心循环agent_loop(21行)
这是整个Agent的灵魂,也是我们今天最重要的代码。仔细看这21行:
# -- The core pattern: a while loop that calls tools until the model stops --
def agent_loop(messages: list):
while True:
response = client.messages.create(
model=MODEL, system=SYSTEM, messages=messages,
tools=TOOLS, max_tokens=8000,
)
# Append assistant turn
messages.append({"role": "assistant", "content": response.content})
# If the model didn't call a tool, we're done
if response.stop_reason != "tool_use":
return
# Execute each tool call, collect results
results = []
for block in response.content:
if block.type == "tool_use":
print(f"\033[33m$ {block.input['command']}\033[0m")
output = run_bash(block.input["command"])
print(output[:200])
results.append({"type": "tool_result", "tool_use_id": block.id,
"content": output})
messages.append({"role": "user", "content": results})
这段代码实现了一个 基于 ReAct 模式的智能体循环(Agent Loop):它通过一个 while 循环让 AI 处于“思考—行动—观察”的迭代状态,当 AI 发现无法直接解决问题时,会主动调用工具来执行系统命令,并将命令的返回结果作为新的上下文反馈给 AI,直到任务完成且 AI 停止调用工具为止。
这就是Agent的全部秘密。整个模式可以用一句话总结:
Agent=while循环+工具调用+ stop_reason 检查
LLM说”我需要执行一个命令",Agent就执行;执行完把结果喂回去,LLM看了结果决定是继续调工具还是回复用户。这个循环一直转,直到LLM说”"我说完了"(response.stop_reason != "tool_use":)
注:Claude Code的51.2万行TypeScript中,与LLM API直接交互的代码只有约8,000行(1.6%),核心循环与这21行Python的逻辑完全一致。剩下的98.4%一一后面会揭晓它们在干什么。
真实Claude Code源码中的核心循环
在Claude Code 的51.2万行 TypeScript 中,这个核心循环藏在 src/query.ts 里1,729行的文件,核心结构是一个AsyncGenerator驱动的状态机。听起来复杂?其实核心逻辑和我们简化版代码的while循环完全对应:

但是我们应该知道,就这个命令行和loop组成的大模型会有各种各样的问题,那么claude code是如何解决他的呢?
首先第一个问题就是上下文会不断地增加。首先第一点就是上下文窗口的问题,只能保留一部分记忆;第二点就是上下文越长,重要性会被稀释。第二个问题就是关掉就失忆,重开全不知。第三个就是安全方面的问题。s01对其的安全措施是怎么样的,可以看一下run_bash函数:
def run_bash(command: str) -> str:
dangerous = ["rm -rf /", "sudo", "shutdown", "reboot", "> /dev/"]
if any(d in command for d in dangerous):
return "Error: Dangerous command blocked"
try:
r = subprocess.run(command, shell=True, cwd=os.getcwd(),
capture_output=True, text=True, timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
return "Error: Timeout (120s)"
except (FileNotFoundError, OSError) as e:
return f"Error: {e}"
安全检查的全部代码就这两行,一个硬编码的字符串列表,非常鸡肋:
if any(d in command for d in dangerous):
return "Error: Dangerous command blocked"
第四个问题就在于每轮都要发全量历史,导致线性成本增长,那么我们回头来看agent_loop函数的第83行:
while True:
response = client.messages.create(
model=MODEL, system=SYSTEM, messages=messages,
tools=TOOLS, max_tokens=8000,
)
注意 messages=messages代表我每次调用API都会把完整的history发过去。这意味着:
- 第1轮API调用发送100 token
- 第2轮发送200token(第1轮+第2轮)
- 第3轮发送300token(第1、2、3轮全部)
- 第N轮发送N*平均单轮token
API按token计费,这意味着你为早期的对话内容反复付费。第1轮的内容在第10轮还在付费在第100轮还在付费。而且如果你同时跑多个任务一一比如让一个Agent改前端、另一个改后端它们各自独立积累history,没有任何共享。
更糟糕的是,这种成本增长和前面的"上下文膨胀"问题是同一枚硬币的两面:上下文越大,既消耗注意力(质量下降),又消耗token(成本上升)
| 致命问题 | 根本原因(代码层面) | 对应的约束 |
|---|---|---|
| 上下文越来越大,质量下降 | history.append() 只增不减 |
约束工作台(上下文管理) |
| 关掉就失忆,无法跨会话延续 | history=[] 每次从零开始 |
约束记忆(三层记忆+AutoDream) |
| 安全裸奔,5行字符串匹配 | dangerous = [...] 硬编码黑名单 |
约束行为(四层安全纵深) |
| 成本线性增长,多任务无法共享 | messages=messages 全量发送 |
约束成本(Fork缓存+编排者模式) |
这30行代码就是一个Agent一一能工作,但有四个致命缺陷。而Claude Code的51.2万行代码,就是为了约束这个不完美的AI。
二.Harness Engineering架构

98.4%的代码是Harness一一这个词不是我们发明的。
2025年12 月,Anthropic 发表了 "Building Effective Agents" 系列文档利后续的 Harness Engineering 理论框架,正式定义了AI Agent系统中 "模型之外那部分" 的工程方法论。Claude Code的51.2万行代码,就是这个理论蓝图的工业级实现。
理论四支柱
Harness Engineering 将Agent 系统中围绕模型构建的工程基础设施划分为四大支柱:

可以先看一下claude code的五层架构:

- 约束工作台(上下文管理)一一主要在L3引擎层,QueryEngine的上下文协调器和Compact模块
- 约束记忆(三层记忆+Auto Dream)一一跨L3和L4,Skill加载在L4,记忆索引管理在L3
- 约束行为(安全纵深防御)一一主要在L4工具层,每个工具的执行前后都有安全检查管线
- 约束成本(Prompt Cache经济学)一主要在L3引擎层,Fork模式和缓存编排
L1入口层和L5基础设施层今天不深入一一它们重要,但不是Agent架构差异化的核心。
上下文管理
首先我们来看上下文是如何进行管理的?
那么在我们的看的learn-claude-code中的代码示例中,他提供了三层的压缩策略,三层压缩各有分工------从"几乎无损"到"有损且可控",按紧急程度分层触发

- Layer1一一微压缩: 运行很多轮之后,将之前的工具调用的结果给他删了;因为我们平时在工具调用的时候,工具返回的信息其实是非常非常多的,当前的对话肯定是需要运行的工具返回的内容来提供给用户的回答,但是随着对话的增多,后续的对话可能就不用包含这些消息了(一般在3到5论对话)。所以你如果需要对话中使用skill的时候,每轮都得告诉它使用skill,不然大模型容易忘记。
注意两个Claude Code设计细节:
- KEEP_RECENT = 3 :保留最近3个工具结果不压缩。Agent经常需要回顾刚刚做的操作,压掉太近的结果会导致它"短期失忆”。
- PRESERVE_RESULT_TooLs={"read_file"}:读文件的结果永远不压缩。因为文件内容是参考资料,一旦压掉,Agent就必须重新读一遍文件,反而浪费更多token。
微压缩在每轮LLM调用前自动执行,用户完全感知不到。一个bash 命令的5000字节输出,过三轮后变成一句[Previous:used bash],即从5000字节缩减到20字节,压缩率99.6%。
- Layer 2一一自动压缩: 超阈值触发LLM摘要。但是在cc中,阈值结算远比简化版代码的硬编码5000精细。真实实现中,触发阈值是动态计算的:先选取当前模型的上下文窗口,减去为输出预留的token数20000,再减去13000作为缓冲区。这意味着对于200K窗口的模型,自动压
缩大约在167Ktoken时触发一一而不是简化版代码里的固定50K。源码中还有一个熔断机制:如果连续三次压缩都失败,就停止尝试,避免陷入无限制重试的死循环。 - Layer3一一手动压缩:Agent主动调用.第三层是Agent自己判断"该压缩了"时主动调用compact工具
而Claude Code有四层压缩防御,从优雅降级到紧急丢弃:


与简化版代码的三层对比,关键差异在于两个地方:
第一,API 原生的MicroCompact。简化版代码的微压缩是纯本地操作,在发送API请求之前自己处理。Claude Code多了一层:Anthropic API本身会返回一个信号,告诉客户端”某些旧的工具结果可以安全压缩了”。这意味着压缩决策部分由服务端协助做出,比纯客户端估算更精准。(本地运行的工具是客户端,背后提供 AI 大脑能力的云端接口是服务端。)
第二,Reactive Compact(被动压缩)。当AutoCompact 因为某种原因没有及时触发一一比如个超大的工具输出一次性把上下文推过了限制一一API会直接返回错误。Reactive Compact 在捕获到这个错误后紧急执行压缩,然后自动重试请求。这是一个兜底机制:即使主动防御失效,系统也不会直接崩溃。
最底层的Snip则是终极保险一一当所有压缩策略都无法把上下文缩减到限制以内时,直接裁剪掉最大的内容块。信息损失最大,但保证系统不会因为token超限而停止工作。
四层压缩防御之外,ClaudeCode在每一轮对话开始前还有一条五步预处理流水线一一它在消息发送给API之前就完成了大量优化工作:

安全检查
Agent 安全的本质是什么? 当我们给一个 AI 系统"执行 Bash 命令"的能力时,我们实际上把系统级权限交给了它——删文件、改配置、发网络请求、安装软件、甚至横向移动到其他机器。这不是"能不能跑通"的问题,而是风险管理的问题。
s01 用 5 个字符串做安全检查,任何一个有经验的开发者看到都会说"这不够"。但"不够"之后该怎么做?直觉是"加更多规则"——但加多少规则才算够?规则之间会不会冲突?规则覆盖不到的地方怎么办?那么claude内部有五层相关设计:

安全系统还有一个容易被忽视的设计——断路器(Circuit Breaker)。当出现 3 次连续拒绝或 20 次累计拒绝时,系统自动降级,不再继续尝试。这个设计借鉴了微服务架构中的断路器模式:与其让一个不断被拒绝的 Agent 反复撞墙(每次拒绝都消耗一次 API 调用),不如果断停下来。这是安全性和成本控制的双重务实——既防止了 Agent 的"执念式重试",也避免了无意义的 token 消耗。
Claude Code 的安全架构中有一个出人意料的维度——它不只防用户的误操作,还防竞品的数据窃取。泄露的源码揭示了一种叫"假工具注入"的反蒸馏机制:Claude Code 会在 API 请求中夹带一些并不存在的虚假工具定义。如果竞品公司通过 API 请求来逆向工程 Claude Code 的行为——比如训练自己的模型来模仿 Claude Code 的工具调用模式——这些假工具就会成为训练数据中的"毒丸",导致竞品模型产生错误的工具调用行为。

还有一个策略是双AI对抗, 用一个 AI 监督另一个 AI,两者上下文完全隔离。安全分类器是最直接的双 AI 对抗案例——一个 Claude(任务模型)负责干活,另一个 Claude Sonnet(分类器)负责判断干活的那个是否越界。两者的上下文完全隔离——分类器看不到任务模型的推理过程,任务模型也无法影响分类器的判断。
这个模式之所以有效,是因为它解决了 AI 系统中的一个根本性问题:如何信任一个不完全可靠的系统? 答案不是"让它变得完全可靠"(做不到),而是"用另一个独立系统来检查它"。两个系统同时犯同一个错误的概率,远低于单个系统犯错的概率。
对抗知识熵增
回到第四个致命问题。我们看到 s01 的 agent_loop 每轮 API 调用都把完整的 messages历史发送一遍——第 N 轮要发送前面所有 N-1 轮的内容。
这已经够贵了。但真正让成本爆炸的场景是多Agent 并行。多Agent 并行的成本挑战是什么
当一个任务足够复杂——比如"重构一个前后端分离的项目"——你可能想让一个Agent 改前端,另一个改后端,第三个写测试。每个子Agent都有自己的独立消息历史( messages=[] ),每个都在各自的上下文窗又里累积 token。三个 Agent 各跑 20 轮,成本就是单Agent 的三倍。
那么我们先看s04的实现:

# -- Parent tools: base tools + task dispatcher --
PARENT_TOOLS = CHILD_TOOLS + [
{"name": "task", "description": "Spawn a subagent with fresh context. It shares the filesystem but not conversation history.",
"input_schema": {"type": "object", "properties": {"prompt": {"type": "string"}, "description": {"type": "string", "description": "Short description of the task"}}, "required": ["prompt"]}},
]
父Agent通过一个叫 task 的工具来启动子Agent。
注意工具描述中的那句话:"It shares the filesystem but not conversation history"——共享文件系统,不共享对话历史。这一行代码注释精准地定义了子 Agent 的隔离边界:文件系统层面互通(子 Agent 可以读写父 Agent 创建的文件),上下文层面完全隔离。
父Agent调用task工具后,子 Agent在自己的上下文中独立工作——可能调用了多次 bash、read_file 等工具。完成后只返回一段精简的文本摘要,父 Agent 收到摘要继续工作。
s04 的子Agent创建方式是sub_messages = [] ——一个全新的空上下文。Claude Code 的做法完全不同:子Agent字节级继承父Agent的完整上下文。
"字节级继承"是什么意思?当Claude Code通过AgentTool启动一个子Agent时,它不是创建一个空白的新会话,而是复制一份父Agent当前的完整消息历史——包括系统提示词、工具定义、项目上下文、之前的对话记录。子Agent从父Agent停下的地方继续,就像Unix的fork() 系统调用一样。
这意味着子Agent天然拥有父Agent积累的所有上下文信息,不需要重新"介绍"项目背景、编码规范、当前任务状态。
Fork 的字节级继承不只是功能上的便利,它还带来了一个巨大的成本红利——Prompt Cache 复用。
Anthropic API 的 Prompt Cache 机制是这样工作的:当两个 API 请求的前缀部分(系统提示词 + 工具定义 + 前面的消息历史)字节级完全相同时,第二个请求的前缀不需要重新处理,直接从缓存读取——输入 token 的计费降低到标准价格的 10%。
Claude Code 的系统提示词被精心分割为两个区域:
- 稳定区(前半部分):基础行为指令、工具定义、全局规则——这些在同一个会话中不会变化,可以被所有子Agent共享缓存
- 动态区(后半部分):会话特定上下文、环境状态、用户指令——这些在每次调用时可能不同
Fork 的子 Agent 继承了父Agent的完整上下文,意味着它们的请求前缀天然与父Agent相同——自动命中Prompt Cache。

三.总结
Agent 的核心骨架(如 s01_agent_loop.py 所示)极其简单,可以概括为一个公式: Agent = while 循环 + 工具调用 + stop_reason 状态检查,虽然这 30 行代码能完成任务,但存在四个“致命伤”:
-
上下文膨胀: 历史记录只增不减,导致模型注意力稀释,质量下降。
-
记忆缺失: 会话结束即失忆,无法跨任务或跨时间延续。
-
安全裸奔: 仅靠简单的字符串黑名单(如
rm -rf)拦截风险,极易被绕过。 -
成本爆炸: 每一轮都发送全量历史,Token 消耗呈线性甚至指数级增长
为了解决上述问题,Claude Code 引入了 Harness Engineering(装甲工程) 体系,将 98.4% 的代码用于约束和优化模型行为,主要分为四大支柱,那么我们主要来看以下几点:
A. 约束工作台(上下文管理)
通过四层压缩防御机制,确保上下文窗口始终高效:
-
微压缩 (MicroCompact): 自动清理旧的工具执行结果(保留最近 3 轮及关键结果)。
-
自动压缩 (AutoCompact): 动态计算阈值(如 200K 窗口留 33K 缓冲区),触发 LLM 摘要。
-
响应压缩 (Reactive Compact): API 报错(超长)后的紧急补救重试。
-
终极裁剪 (Snip): 物理截断超长块,作为系统不崩溃的最后底线。
B. 约束行为(五层安全纵深)
安全不再是简单的黑名单,而是一套防御体系:
-
双 AI 对抗: 使用独立的分类器模型(如 Sonnet)监控任务模型的行为。
-
断路器模式: 连续失败或拒绝后自动停机,防止 Agent 陷入“无效重试”浪费 Token。
-
假工具注入: 针对竞品窃取数据的“毒丸”防御。
C. 约束成本(Fork 模式与缓存经济学)
Claude Code 通过模仿 Unix 的 fork() 机制,解决了多 Agent 协同的成本问题:
-
字节级继承: 子 Agent 直接复制父 Agent 的上下文,无需重复发送背景信息。
-
Prompt Cache 复用: 利用 Anthropic API 的缓存机制,使重复前缀的费用降低 90%。
-
任务编排: 通过父子 Agent 模式,将复杂任务拆解,完成后仅回传摘要,保持父上下文清爽
更多推荐


所有评论(0)