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_loop21 行这是整个 Agent 的灵魂def agent_loop(messages: list): while True: response client.messages.create( modelMODEL, systemSYSTEM, messagesmessages, toolsTOOLS, max_tokens8000, ) 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, shellTrue, cwdos.getcwd(), capture_outputTrue, textTrue, timeout120) 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 fError: {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 [...] 硬编码黑名单约束行为四层安全纵深成本线性增长多任务无法共享messagesmessages 全量发送约束成本Fork 缓存编排者模式注意messagesmessages代表每次调用 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 和 L4Skill 加载在 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_URLhttps://taotoken.net/api export ANTHROPIC_API_KEYsk-你的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/apiAPI 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_urlos.environ.get(ANTHROPIC_BASE_URL), api_keyos.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, shellTrue, cwdos.getcwd(), capture_outputTrue, textTrue, timeout120) 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 fError: {e} def agent_loop(messages: list): while True: response client.messages.create( modelMODEL, systemSYSTEM, messagesmessages, toolsTOOLS, max_tokens8000, ) 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.py120 行、utils.py45 行、test_agent.py88 行总计 253 行。这个过程展示了 Agent 循环的完整生命周期第一轮 API 调用返回stop_reasontool_use模型决定执行ls *.pyAgent 执行命令并把结果回填到 messages第二轮 API 调用模型看到结果后决定执行wc -l *.py第三轮模型拿到行数信息后判断任务完成返回stop_reasonend_turn循环退出。你可以通过打印每轮的response.stop_reason和response.usage来观察 token 消耗模式。第一轮可能只用了 200 input tokens第二轮变成 400第三轮变成 600——这就是全量历史发送带来的线性增长。如果你把 messages 打印出来会看到每一轮都在前面累积了完整的对话记录。现在你可以做一个对比实验把messagesmessages改成只发送最近 3 轮的消息观察 Agent 的行为变化。你会发现对于简单任务影响不大但对于需要多步推理的任务Agent 可能会「忘记」前面已经执行过的命令导致重复操作。这就是上下文管理要解决的核心矛盾——既要控制 token 成本又要保持任务连续性。如果你想验证 Prompt Cache 的效果可以在请求中加入cache_control参数。Anthropic 的 Prompt Cache 机制是当两个 API 请求的前缀部分系统提示词 工具定义 前面的消息历史字节级完全相同时第二个请求的前缀不需要重新处理直接从缓存读取输入 token 的计费降低到标准价格的 10%。response client.messages.create( modelMODEL, system[{ type: text, text: SYSTEM, cache_control: {type: ephemeral} }], messagesmessages, toolsTOOLS, max_tokens8000, )加上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 格式的接口或者反过来就会报这个错。检查你用的 SDKClaude Code 和 Anthropic SDK 用client.messages.create返回的是response.contentOpenAI SDK 用client.chat.completions.create返回的是response.choices[0].message.content。TaoToken 同时支持两种协议但你要根据自己用的 SDK 选择对应的 Base URL 路径。Anthropic 格式用https://taotoken.net/apiOpenAI 格式也用同一个地址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 系统。