☰
Claude Code源码解析学习:从Agent循环到Prompt Cache的Harness Engineering实践
2026/10/9 19:14:24 网站建设 项目流程

1. 从 s01_agent_loop.py 看懂 Claude Code 源码解析的 Agent 主循环

很多人第一次翻 Claude Code 的源码会懵:五十多万行 TypeScript,从哪下手?我建议你别急着啃生产代码,先找一个把核心逻辑抽出来的简化实现,把 Agent 循环这件事彻底搞明白,再回头看真实源码,你会发现那 98% 的代码其实都在解决同一批问题。

Claude Code 的本质就是一个 AI Agent。Agent 的核心是一个「感知—决策—行动」的自主循环:你给它一个目标,比如「帮我修复这个 bug」,它会自己决定先读哪个文件、再跑什么命令、然后改哪行代码,整个过程可能循环几十轮,直到任务完成。关键点在于——大模型自己决定下一步做什么。

这里我用一个开源教学项目 learn-claude-code 的简化版代码来拆解,它把 Claude Code 的核心架构用 Python 拆成了 12 个渐进式阶段。第一阶段s01_agent_loop.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"], }, }]

这个工具的作用在于它告诉 AI:「你现在拥有了一个执行 Bash 命令的能力。如果你在解决问题时需要运行代码或操作系统,你可以调用这个工具。」Claude Code 生产版有 40 多个工具——文件读写、搜索、Git 操作等,都是基于上述这个结构的扩展。

第二部分是核心循环agent_loop,21 行,这是整个 Agent 的灵魂:

def agent_loop(messages: list): while True: response = client.messages.create( model=MODEL, system=SYSTEM, messages=messages, tools=TOOLS, max_tokens=8000, ) messages.append({"role": "assistant", "content": response.content}) if response.stop_reason != "tool_use": return 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 模式的智能体循环:它通过一个 while 循环让 AI 处于「思考—行动—观察」的迭代状态,当 AI 发现无法直接解决问题时,会主动调用工具来执行系统命令,并将命令的返回结果作为新的上下文反馈给 AI,直到任务完成且 AI 停止调用工具为止。

整个模式可以用一句话总结:Agent = while 循环 + 工具调用 + stop_reason 检查。LLM 说「我需要执行一个命令」,Agent 就执行;执行完把结果喂回去,LLM 看了结果决定是继续调工具还是回复用户。这个循环一直转,直到 LLM 说「我说完了」。

第三部分是安全检查和命令执行。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}"

安全检查的全部代码就这两行,一个硬编码的字符串列表,非常鸡肋。任何一个有经验的开发者看到都会说「这不够」。

在真实 Claude Code 的 51.2 万行 TypeScript 中,这个核心循环藏在src/query.ts里,1729 行的文件,核心结构是一个 AsyncGenerator 驱动的状态机。听起来复杂?其实核心逻辑和我们简化版代码的 while 循环完全对应。与 LLM API 直接交互的代码只有约 8000 行,占 1.6%,核心循环与这 21 行 Python 的逻辑完全一致。剩下的 98.4% 就是 Harness Engineering 要解决的问题。

这 30 行代码就是一个能工作的 Agent,但有四个致命缺陷:

致命问题根本原因(代码层面)对应的约束
上下文越来越大,质量下降history.append() 只增不减约束工作台(上下文管理)
关掉就失忆,无法跨会话延续history=[] 每次从零开始约束记忆(三层记忆+AutoDream)
安全裸奔,5 行字符串匹配dangerous = [...] 硬编码黑名单约束行为(四层安全纵深)
成本线性增长,多任务无法共享messages=messages 全量发送约束成本(Fork 缓存+编排者模式)

注意messages=messages代表每次调用 API 都会把完整的 history 发过去。这意味着第 1 轮 API 调用发送 100 token,第 2 轮发送 200 token,第 N 轮发送 N×平均单轮 token。API 按 token 计费,你为早期的对话内容反复付费——第 1 轮的内容在第 100 轮还在付费。而且如果你同时跑多个任务,比如让一个 Agent 改前端、另一个改后端,它们各自独立积累 history,没有任何共享。更糟糕的是,这种成本增长和上下文膨胀是同一枚硬币的两面:上下文越大,既消耗注意力(质量下降),又消耗 token(成本上升)。

理解了这四个缺陷,你就理解了 Claude Code 那 98.4% 代码存在的理由。接下来我们看 Harness Engineering 是怎么系统性地约束这个不完美的 AI 的。

2. Harness Engineering 四支柱与 Prompt Cache 经济学

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 架构差异化的核心。

先说上下文管理。大模型其实有点像金鱼,它没有真正的「记忆」。每次你提问,它都得把自己「重置」一下,然后重新读一遍小抄。这个小抄里装着系统预设的指令、你俩之前聊过的所有历史、还有你刚问的这个问题。问题是这小抄不能无限长,容纳的极限就叫「上下文窗口」,用 token 衡量。

那既然有窗口限制,加大窗口不就可以解决了吗?其实不然。加大窗口会带来三个致命问题:上下文越长,单次推理的 token 消耗就越大,金钱开销越大;上下文越长,模型生成第一个 token 的延迟就越高;第三个硬伤最致命,叫 Lost in the Middle(中间迷失)。当上下文非常长时,大模型对开头和结尾的信息记得比较清楚,但对中间那一大段,记忆就很模糊了。你以为你把历史全塞进窗口里它就能全看到?不一定。中间那一大段,模型可能只是「瞟一眼」就过去了。这是注意力机制的固有特性,跟窗口开多大没关系。

业界常见的上下文管理方案无非几类。滑动窗口最简单,设个阈值,超过就从最老的消息开始砍。问题在于 Agent 的关键信息往往在最开始——比如用户开局说「禁止做 B」,你砍掉了,后面 Agent 就开始疯狂干 B。而且工具调用有依赖,砍掉前面的 tool_result,后面引用它的地方就成了无头苍蝇。每 N 轮做摘要比滑动窗口好,至少信息没全丢,但触发时机太死板,摘要粒度也粗,细微的状态变化、错误修复过程、中途改的需求,全压成几句话就没了。向量召回历史在 RAG 里很好用,但放到 Agent 场景直接翻车:向量召回不管时序,可能把 B 召回来、A 丢了,执行顺序全乱。

那 Claude Code 怎么干?它用了四层压缩防御机制。

Snip 是最粗暴但也最高效的一层,直接把对话开头的一批老消息移除掉,然后插入一个边界标记告诉模型「这之前的内容已经被清理了」。它不做任何摘要,直接砍掉。对于那些确实已经完全过时的消息来说,这是代价最低的做法,因为它不需要额外调用大模型来生成摘要,零 API 开销。

Micro-Compact 处理的是经过截断后剩下的「不太老但也不太新」的消息。这些消息不能直接砍掉,但里面大量的工具输出其实已经过时了,于是采用的核心思想是时间衰减:越老的工具结果越不重要,可以被裁剪。但不是所有工具的结果都能裁剪。可以被裁剪的都是「可重新获取」的工具——Read 的结果可以再读一次,Bash 的输出可以再执行一次,搜索结果可以再搜一次。但 AgentTool(子 Agent 的输出)、TaskTool(任务状态)这类工具的结果永远不会被裁剪,因为子 Agent 的推理过程是不可重复的,砍掉就真的丢了。

Context Collapse 不修改原始消息,它只在调用 API 的那一刻,动态计算一个「压缩视图」给模型看。90% 上下文窗口时主动开始分段压缩旧消息。这个设计最精妙的地方是它和 Auto-Compact 的配合:Context Collapse 运行在 Auto-Compact 之前。如果 Context Collapse 已经通过「读时投影」把上下文压到了阈值以下,Auto-Compact 就完全不需要触发了。这样模型保留了更多的细节上下文,而不是被一段粗糙的全量摘要替代。

Auto-Compact 是最重的兜底。它会生成摘要,然后替换掉旧的消息,接着是整个流程中最关键的一步——压缩完不是就完了,还要主动恢复最重要的上下文:系统会从文件状态缓存中找出最近访问过的文件,按最后访问时间排序,挑选最多 5 个、总共不超过 50K Token 的文件内容重新注入。同时恢复活跃的 Skill,如果有进行中的 Plan 也会恢复 Plan 文件。

Auto-Compact 的触发线是这么算的:拿到模型的有效上下文窗口(比如某个模型是 200k),减去一个固定值 13k,得到的就是触发阈值。token 数一旦超过这条线,就开始压。这里有个容易混淆的点:/compact命令和自动压缩到底有什么区别?如果了解过 Claude Code 的源码,你会发现这里面其实藏着两套「入口」,但底层并不是两套系统。

/compact属于手动模式,它允许额外传入一段 customInstructions。这个参数可以理解成「摘要偏好」——你可以提前告诉压缩器,这次保留哪些上下文最重要。比如正在排查某个 bug,就能明确要求它重点保留与该问题相关的调用链、日志或者推理过程。而自动压缩不会接收用户额外指令,但会默认开启一个内部选项:suppressFollowUpQuestions。这个开关的作用很直接:禁止摘要过程中生成「后续待确认问题」。原因也不难理解——自动压缩通常发生在 Agent 已经进入连续工作状态的时候,如果压缩完突然冒出一句「请确认一下 XXX」,整个执行流就会被打断。所以本质上,两种模式的区别并不在「怎么压缩」,而在「压缩时优先考虑什么」。手动模式强调「按用户意图保留重点」,自动模式则更关注「不中断任务流程」。

Claude Code 压缩的摘要 prompt 包含 9 个固定项:Primary Request and Intent、Key Technical Concepts、Files and Code Sections、Errors and fixes、Problem Solving、All user messages、Pending Tasks、Current Work、Optional Next Step。

第 6 项其实非常关键,它要求的不是「总结用户说了什么」,而是完整保留所有用户输入。很多人第一反应会觉得,这不就是把聊天记录再抄一遍吗?其实不是。这里真正强调的是:所有非 tool result 的用户消息,都必须进入摘要。原因很简单,Agent 的任务方向并不是固定不变的,而是在长对话里不断被用户修正。用户可能在几十轮之后新增限制条件,也可能突然推翻之前的方案,甚至直接改变目标。如果摘要遗漏了其中某一句,后续 Agent 就可能沿着错误方向继续执行。所以这一项本质上是在保证「任务演化历史」不丢失。

第 8 项则解决的是另一个问题:压缩之后,Agent 如何无缝继续工作。这里要求摘要必须用「最细粒度」描述当前进度。不是简单一句「正在调试登录问题」,而是要精确到:正在排查 token 刷新逻辑;已经定位到 cookie 过期判断异常;下一步准备修改 auth.ts 中的 refreshToken()。为什么要这么细?因为压缩完成后,Agent 接下来首先面对的问题其实是「我刚刚做到哪里了?」如果当前状态描述太粗,Agent 很容易进入一种「知道自己在干什么,但不知道具体做到哪一步」的状态,然后重新扫描上下文、重新验证推理、重新排查问题,最终结果就是重复消耗 token 和时间。

所以这 9 个摘要项的设计,并不是单纯为了「信息完整」,而是在尽可能保留 Agent 的工作记忆:用户意图不能丢,技术决策不能丢,已踩过的坑不能丢,文件修改状态不能丢,当前执行位置更不能丢。Claude 的摘要机制,本质上并不是在「缩短聊天记录」,而是在尽量压缩 token 的同时,维持 Agent 的连续工作状态。

3. 可复制配置:用 TaoToken 跑通 Agent 循环验证

理解了 Agent 循环和 Harness 的上下文管理机制后,下一步是本地跑通一个最小 Agent 循环来验证你的理解。这里我用 TaoToken 作为 API 接入层来演示,因为它兼容 Anthropic 的 Messages API 格式,配置方式和官方 SDK 一致,方便你直接对照源码里的client.messages.create调用。

TaoToken 的 API 地址是https://taotoken.net/api,你需要在控制台创建一个 API Key。拿到 Key 之后,先配置环境变量:

export ANTHROPIC_BASE_URL="https://taotoken.net/api" export ANTHROPIC_API_KEY="sk-你的key"

如果你用的是 Claude Code CLI,可以在 settings.json 里配置。这个文件通常位于~/.claude/settings.json:

{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_API_KEY": "sk-你的key", "ANTHROPIC_MODEL": "claude-sonnet-4-20250514" } }

如果你用的是 Cline 或 Roo Code 这类 VS Code 插件,配置项在插件的设置面板里,需要填三个东西:Base URL 填https://taotoken.net/api,API Key 填你创建的那个,Model ID 填claude-sonnet-4-20250514。这三件套缺一不可,很多人只填了 Key 忘了改 Base URL,结果请求发到官方地址去了,报 401 还以为是 Key 的问题。

对于 Codex 用户,配置文件在~/.codex/auth.json,格式如下:

{ "OPENAI_API_KEY": "sk-你的key", "OPENAI_BASE_URL": "https://taotoken.net/api" }

注意 Codex 用的是 OpenAI 兼容格式,而 Claude Code 用的是 Anthropic 格式,两者不要混用。TaoToken 同时支持两种协议,你根据自己用的工具选择对应的 Base URL 和 Key 即可。

配置好之后,我们来写一个最小验证脚本,直接复现 s01 的 Agent 循环逻辑:

import os import subprocess from anthropic import Anthropic client = Anthropic( base_url=os.environ.get("ANTHROPIC_BASE_URL"), api_key=os.environ.get("ANTHROPIC_API_KEY"), ) MODEL = "claude-sonnet-4-20250514" SYSTEM = "You are a helpful coding assistant. Use bash when needed." TOOLS = [{ "name": "bash", "description": "Run a shell command.", "input_schema": { "type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"], }, }] 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}" def agent_loop(messages: list): while True: response = client.messages.create( model=MODEL, system=SYSTEM, messages=messages, tools=TOOLS, max_tokens=8000, ) messages.append({"role": "assistant", "content": response.content}) if response.stop_reason != "tool_use": return response 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}) if __name__ == "__main__": msgs = [{"role": "user", "content": "列出当前目录下的 Python 文件,并统计行数"}] result = agent_loop(msgs) print("\n--- Final Response ---") for block in result.content: if hasattr(block, "text"): print(block.text)

这个脚本完整复现了 s01 的核心逻辑:工具定义、while 循环、stop_reason 检查、工具结果回填。你可以直接运行它,观察 Agent 如何自主决定调用 bash、执行命令、拿到结果、继续推理直到给出最终回答。

如果你想让 Agent 循环更接近 Claude Code 的生产行为,可以在 system prompt 里加入更明确的工具使用规范,比如「优先使用 bash 探索文件结构,再决定修改哪些文件」。这其实就是 Harness Engineering 里「约束行为」的雏形——通过系统提示词和工具定义来引导模型的行为边界。

4. 验证请求与成功结果:观察 Agent 循环的实际行为

配置好之后,运行上面的脚本,你会看到类似这样的输出:

$ ls *.py main.py utils.py test_agent.py $ wc -l *.py 120 main.py 45 utils.py 88 test_agent.py 253 total --- Final Response --- 当前目录下有 3 个 Python 文件:main.py(120 行)、utils.py(45 行)、test_agent.py(88 行),总计 253 行。

这个过程展示了 Agent 循环的完整生命周期:第一轮 API 调用返回stop_reason="tool_use",模型决定执行ls *.py;Agent 执行命令并把结果回填到 messages;第二轮 API 调用模型看到结果后决定执行wc -l *.py;第三轮模型拿到行数信息后判断任务完成,返回stop_reason="end_turn",循环退出。

你可以通过打印每轮的response.stop_reason和response.usage来观察 token 消耗模式。第一轮可能只用了 200 input tokens,第二轮变成 400,第三轮变成 600——这就是全量历史发送带来的线性增长。如果你把 messages 打印出来,会看到每一轮都在前面累积了完整的对话记录。

现在你可以做一个对比实验:把messages=messages改成只发送最近 3 轮的消息,观察 Agent 的行为变化。你会发现对于简单任务影响不大,但对于需要多步推理的任务,Agent 可能会「忘记」前面已经执行过的命令,导致重复操作。这就是上下文管理要解决的核心矛盾——既要控制 token 成本,又要保持任务连续性。

如果你想验证 Prompt Cache 的效果,可以在请求中加入cache_control参数。Anthropic 的 Prompt Cache 机制是:当两个 API 请求的前缀部分(系统提示词 + 工具定义 + 前面的消息历史)字节级完全相同时,第二个请求的前缀不需要重新处理,直接从缓存读取,输入 token 的计费降低到标准价格的 10%。

response = client.messages.create( model=MODEL, system=[{ "type": "text", "text": SYSTEM, "cache_control": {"type": "ephemeral"} }], messages=messages, tools=TOOLS, max_tokens=8000, )

加上cache_control后,第一次请求会创建缓存,后续请求如果前缀相同就会命中缓存。你可以在response.usage里看到cache_creation_input_tokens和cache_read_input_tokens两个字段,前者是创建缓存消耗的 token,后者是从缓存读取的 token。命中缓存的部分,成本只有标准价格的 10%。

这就是 Claude Code Fork 模式的经济学基础。当 Claude Code 通过 AgentTool 启动一个子 Agent 时,它不是创建一个空白的新会话,而是复制一份父 Agent 当前的完整消息历史——包括系统提示词、工具定义、项目上下文、之前的对话记录。子 Agent 从父 Agent 停下的地方继续,就像 Unix 的 fork() 系统调用一样。这意味着子 Agent 天然拥有父 Agent 积累的所有上下文信息,不需要重新「介绍」项目背景、编码规范、当前任务状态。更重要的是,Fork 的子 Agent 继承了父 Agent 的完整上下文,意味着它们的请求前缀天然与父 Agent 相同——自动命中 Prompt Cache。

Claude Code 的系统提示词被精心分割为两个区域:稳定区(前半部分)包含基础行为指令、工具定义、全局规则,这些在同一个会话中不会变化,可以被所有子 Agent 共享缓存;动态区(后半部分)包含会话特定上下文、环境状态、用户指令,这些在每次调用时可能不同。这种分割方式最大化了缓存命中率,同时保持了上下文的灵活性。

你可以自己做一个实验:连续发两次相同的请求,第一次不带 cache_control,第二次带上,对比两次的usage字段。你会看到第二次的cache_read_input_tokens大于 0,而input_tokens显著减少。这就是 Prompt Cache 在实际计费中的体现。

5. 本篇常见错排查:401、local proxy failed 与 reading choices

在配置和运行过程中,你可能会遇到几类典型报错。我把它们整理出来,方便你对照排查。

401 Authentication Error。这是最常见的错误,通常有三个原因:Key 没填对、Base URL 没改、或者 Key 已经过期。先检查你的环境变量是否正确导出:

echo $ANTHROPIC_BASE_URL echo $ANTHROPIC_API_KEY

如果 Base URL 还是官方的https://api.anthropic.com,说明你没改成 TaoToken 的地址。如果你用的是 Claude Code CLI,检查~/.claude/settings.json里的env字段是否生效。有时候你在 shell 里 export 了变量,但 CLI 读取的是配置文件,两者不一致就会导致 401。

local proxy failed / connection refused。这个报错通常出现在你本地跑了代理工具的情况下。如果你之前配置过系统代理,SDK 可能会尝试走代理连接,但代理没有运行或者配置不对。检查你的HTTP_PROXY和HTTPS_PROXY环境变量:

env | grep -i proxy

如果有输出,尝试取消这些变量:

unset HTTP_PROXY unset HTTPS_PROXY

然后重新运行脚本。如果你确实需要代理才能访问外网,确保代理工具正在运行,并且 TaoToken 的地址在代理规则里是直连或者正确转发的。

reading 'choices' of undefined。这个报错说明你用的 SDK 和 API 协议不匹配。choices是 OpenAI 格式的响应字段,而 Anthropic 格式的响应字段是content。如果你用 OpenAI SDK 去调 Anthropic 格式的接口,或者反过来,就会报这个错。检查你用的 SDK:Claude Code 和 Anthropic SDK 用client.messages.create,返回的是response.content;OpenAI SDK 用client.chat.completions.create,返回的是response.choices[0].message.content。TaoToken 同时支持两种协议,但你要根据自己用的 SDK 选择对应的 Base URL 路径。Anthropic 格式用https://taotoken.net/api,OpenAI 格式也用同一个地址,SDK 会自动处理路径差异。

OAuth token expired / invalid_grant。如果你用的是 Claude Code 的 OAuth 登录方式而不是 API Key,可能会遇到这个报错。OAuth token 有有效期,过期后需要重新登录。但更常见的情况是你在 settings.json 里同时配置了 OAuth 和 API Key,导致认证方式冲突。建议统一用 API Key 方式,在 settings.json 里只保留ANTHROPIC_API_KEY字段,删掉 OAuth 相关的配置。

Model not found / invalid model ID。检查你填的 Model ID 是否正确。Claude 的模型 ID 格式是claude-sonnet-4-20250514这种带日期后缀的,不是claude-sonnet-4这种简写。如果你不确定当前可用的模型 ID,可以在 TaoToken 的模型对话页面测试一下,确认模型名称后再填到配置里。

Context length exceeded。当你跑多轮 Agent 循环后,messages 会越来越长,最终可能超过模型的上下文窗口。这时候你需要实现自己的上下文管理逻辑——最简单的做法是保留最近 N 轮消息,或者对早期消息做摘要。这正是 Claude Code 的 Snip、Micro-Compact、Context Collapse、Auto-Compact 四层机制要解决的问题。你可以先从最简单的滑动窗口开始,逐步加入摘要逻辑,观察 Agent 行为的变化。

排查问题的通用思路是:先确认 Base URL 和 Key 是否正确,再确认 SDK 和 API 协议是否匹配,最后检查网络和代理配置。大部分问题都出在前两步。

6. 从源码到工程落地:建立 Harness Engineering 的完整认知

回到最开始的问题:学 Claude Code 源码到底学什么?不是学那 51.2 万行 TypeScript 的每一行,而是学它如何用 98.4% 的代码去约束那 1.6% 的核心循环。

Agent 的核心骨架极其简单:Agent = while 循环 + 工具调用 + stop_reason 状态检查。这 30 行代码能完成任务,但存在四个致命伤——上下文膨胀、记忆缺失、安全裸奔、成本爆炸。Harness Engineering 的四大支柱就是针对这四个问题的系统性解决方案。

约束工作台通过四层压缩防御机制(Snip、Micro-Compact、Context Collapse、Auto-Compact)确保上下文窗口始终高效。约束行为通过五层安全纵深(包括双 AI 对抗、断路器模式、假工具注入)让 Agent 的行为可控。约束成本通过 Fork 模式和 Prompt Cache 复用,让多 Agent 协同的成本从线性增长变成接近常数。约束记忆通过三层记忆和 AutoDream 机制,让 Agent 能够跨会话延续任务。

如果你想继续深入,我建议的源码阅读路径是:先从src/query.ts的 AsyncGenerator 状态机入手,理解核心循环的生产级实现;然后看src/compact/目录下的压缩逻辑,对照本文讲的四层机制;接着看src/tools/目录下的工具定义和安全检查管线;最后看src/agent/目录下的 Fork 和子 Agent 编排逻辑。每看一个模块,都回到 s01 的 30 行代码去对照——你会发现所有复杂逻辑都是在解决那四个致命伤。

本地验证方面,你可以基于本文的脚本逐步扩展:加入多轮对话的上下文管理、加入工具结果的缓存复用、加入子 Agent 的 Fork 逻辑。每加一个功能,观察 token 消耗和行为变化。这种「从最小实现出发,逐步加入 Harness」的学习方式,比直接啃生产代码高效得多。

最后提醒一点:Claude Code 的源码泄漏的是客户端代码,不是 Claude Opus 大模型的源码。我们学的是 Harness Engineering——如何给大模型套上缰绳,用系统去约束它的行为,让大模型不至于肆意洒脱,而是稳稳当当地完成任务。这个思路不仅适用于 Claude Code,也适用于任何你正在构建的 Agent 系统。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询