Claude Code源码拆解:从Agent Harness架构到工程实践
最近群里在讨论 Claude Code问得最多的不是“这东西怎么装”而是“它的源码到底怎么读”。网上教程大多停留在怎么安装、怎么让它写代码一旦你想搞清楚它为什么能自主调用工具、为什么每执行一步都要向你确认、为什么上下文快满时会提示压缩你就绕不开一个概念Agent Harness。这篇文章不打算贴一堆“看完更懵的源码片段”而是把 Claude Code 当成一个典型的 Agent Harness 来拆。先讲清楚 agent 和 harness 的区别再沿着“入口 → 主循环 → 工具层 → 权限层 → 上下文管理 → 会话持久化”这条主线给出源码阅读路线最后补上安装部署、功能测试、批量调用和常见问题排查。读完你不仅能上手用也能把同一套分析视角迁移到 Codex、Cursor 这类终端编程智能体上。1. 核心能力速览Claude Code 是 Anthropic 推出的终端 AI 编程智能体也是目前最值得当样板研究的一个 Agent Harness 实现。它跑在终端里模型本身在云端推理本机只需要一个轻量 CLI。能力项说明项目定位终端 AI 编程智能体可自主读写文件、执行命令、运行测试开发方Anthropic核心功能文件读写编辑、Bash 命令执行、代码搜索、跨文件修改、子 Agent、MCP 工具接入运行平台macOS、Linux、Windows原生或 WSL硬件门槛常规模式不需要独立显卡本地部署兼容推理服务时才有显存需求启动方式CLI、VSCode 插件、桌面版交互能力交互式会话、非交互 Headless 模式、会话恢复接口能力claude -p打印模式、官方 Agent SDK、环境变量路由兼容端点批量任务可通过脚本循环调用 Headless 模式批量执行典型场景代码补全、Bug 修复、跨文件重构、测试补充、文档生成从这张表能看出两件事。第一Claude Code 的硬门槛非常低不依赖 GPU普通开发机都能跑第二它不是一个“聊天玩具”它天生设计成可以被脚本和接口驱动这正是 harness 该有的样子。后面所有内容都会围绕“Agent 循环”和“Harness 外壳”这两个关键词展开。2. Agent 与 Harness先把概念理清楚网上关于“harness 和 agent 区别”的讨论非常多但大部分解释都停留在比喻层面。这里直接给一个能落地的定义。Agent智能体指的是模型本身加上“能自主决策下一步做什么”的能力。它看到用户需求后不是一次性吐出答案而是反复思考我要不要先读文件要不要执行命令要不要改代码每次行动之后把观察结果拿回来继续推理直到任务完成。这个“思考 → 行动 → 观察 → 再思考”的循环是 agent 的灵魂。Harness框架 / 外壳指的是把模型变成“能干活”的那一层工程外壳。模型本质上只是一个神经网络它自己不能操作文件系统、不能执行终端命令、不能感知代码仓库。是 harness 给它提供了手脚一般至少包含五个部分工具注册表模型可以调用哪些工具每个工具的参数 schema 是什么。主循环一轮轮调用模型解析工具调用结果追加到上下文。权限闸门工具会修改文件、执行命令所以必须要有 allow / deny 策略。上下文管理把对话历史、文件内容、工具输出塞进有限的上下文窗口必要时压缩。会话持久化把整个任务的过程保存下来支持断点续跑和事后审计。OpenAI 在 Codex 的官方博客里有一段很有名的表述说 Agent Harness 就是“model 和 world 之间的那一层”。你可以把两个不同的模型塞进同一个 harness它们能完成差不多的任务同一个模型换掉 harness能力表现可能天差地别。Claude Code 就是 Anthropic 为 Claude 系列模型专门打造的那层 harness。把概念映射到实际操作上你会发现这些事情全都对应 Claude Code 的可见功能。工具调用对应它能在你的终端里执行命令权限闸门对应每次执行危险操作前的确认上下文管理对应它偶尔告诉你“上下文快满了”会话持久化对应/resume恢复上一次任务。读源码时顺着这些可见功能去找对应实现比逐行通读高效得多。3. Claude Code 的 Agent Harness 架构拆解3.1 主循环是核心一个 Agent Harness 最核心的骨架是主循环。Claude Code 交互式运行时的行为可以简化成下面这个伪代码。把这段代码看懂后面所有源码阅读都有方向了。def agent_loop(user_request: str): messages build_initial_messages(user_request) while True: # 1. 调用模型带上工具定义和完整对话历史 response model_complete(messages, toolsTOOL_SCHEMAS) # 2. 模型可能返回文本也可能返回一组工具调用 if response.has_tool_calls(): for call in response.tool_calls: result execute_tool(call.name, call.arguments) messages.append(tool_call_message(call)) messages.append(tool_result_message(result)) else: # 3. 没有工具调用说明任务完成输出最终文本 emit(response.text) break这个循环体现了三个关键点。第一模型每次输出可能是普通文本也可能是一组工具调用harness 负责解析并执行第二工具执行结果会被追加回消息列表成为模型下一轮推理的输入第三循环不会无限跑下去模型必须在某次输出中给出最终文本或者被用户中断、被步数上限打断。Claude Code 源码里真正复杂的部分都是在这个主循环之上扩展出来的权限、压缩、会话、错误恢复。3.2 工具层模型的手脚Claude Code 里最常见的工具包括读写文件、编辑文件、执行 Bash 命令、搜索文件、搜索代码、查看目录结构、维护待办列表以及创建子 Agent 来拆分任务。每一个工具在设计上都要包含三样东西name机器可读的工具名比如Read、Bash。description给模型看的自然语言说明什么时候该用、怎么用。input_schema参数结构比如要读哪个文件、要执行哪条命令。源码阅读时最容易踩的坑是盯着模型调用层不放却忽略了工具层。其实真正决定一个 agent 上限的往往是工具层。工具的 description 写得清不清楚、参数校验规不规范直接决定模型会不会误用工具。你发一条命令给 Claude Code发现它做了错误的工具调用很多时候不是模型不够聪明而是工具 schema 没有给出足够的约束。3.3 权限与安全层Claude Code 默认模式下读操作一般直接执行写文件、执行命令这类有副作用的操作会要求用户确认。它提供几种权限模式常用的包括默认模式每个危险操作都要确认。acceptEdits自动接受文件编辑类操作。bypassPermissions跳过所有确认适合无人值守的批处理。此外还能用配置文件预设 allow / deny 规则把某些命令直接放行或直接禁止。从源码视角看权限层就是夹在主循环和工具执行之间的一个拦截器def execute_tool(name, arguments, permission_policy): if not permission_policy.allow(name, arguments): decision ask_user(f允许执行 {name} 吗?) if decision ! allow: return TOOL_CALL_DENIED return run_tool(name, arguments)这一层在整个 harness 里价值很高。没有权限闸门的 agent 看起来跑得更快但它随时可能删掉你的文件、执行危险命令。Claude Code 在体验上“每一步都要确认”会让新手觉得麻烦这恰恰是它作为终端级 harness 的安全底线。3.4 上下文管理层模型有上下文窗口限制而一次稍微复杂的编程任务会产生大量对话历史、文件内容和工具输出。harness 需要做几件事把系统提示词、项目规则、用户需求、历史对话组装成一次请求当历史过长时做压缩把前面的对话概括成摘要后继续控制单次工具输出的长度避免一个超长命令结果把窗口塞爆。从用户视角能看到的现象是任务执行到一半Claude Code 提示“上下文即将用完是否压缩”然后继续干活。读源码时找到这个压缩点和它触发压缩的条件基本就理解了上下文管理的设计思路。这也是区分“普通聊天应用”和“正经 agent harness”的关键分界点。3.5 会话与状态层Claude Code 会把会话记录按 JSONL 形式保存到本地配置目录支持/resume列出历史会话并恢复。这对工程应用来说有两个直接价值批量任务的任务现场不会因为中断全部丢失外部脚本可以通过读取会话记录做审计和复盘。会话层虽然不起眼但它是把 agent 从一个“临时对话”变成“可持续运行的工具”的基础设施。4. 源码阅读路线从入口到主循环既然要“手撕源码”就得有一套阅读路线而不是打开仓库随机乱翻。Claude Code 的实际实现包含大量细节但你可以先定位四个问题入口、主循环、工具注册、权限判断。找到这四个问题的答案整个 harness 的地图就出来了。第一步找入口。CLI 工具一定会有一个 main 函数或启动脚本负责解析命令行参数、读取配置、初始化日志和会话。参数解析是最好读懂的起点能帮你快速看到支持哪些 flag、哪些环境变量。第二步找主循环。交互式 agent 一定有一个循环负责“调模型 → 取结果 → 执行工具 → 回填结果”。在代码里搜索类似agent_loop、run_loop、tool_call这样的命名一般几分钟就能定位。第三步找工具注册表。所有工具都要在一个地方登记包括工具名、描述、参数 schema、执行函数。搜索工具名比如Bash、Read、Edit可以反向找到注册表。第四步找权限判断。在工具执行前通常有一段权限检查代码。搜索permission、allow、deny关键字能在很短时间内定位到安全层的实现。读的时候不要按文件顺序通读而是按执行链路走用户输入 → 模型请求 → 工具调用 → 工具结果 → 下一次模型请求。这条链路每经过一个模块就停下来记录这个模块的职责。最后你得到的不是“代码抄写笔记”而是一张架构图。对于 Claude Code 这种大型项目建议先读官方文档确认三件事CLI 命令列表、环境变量列表、配置文件格式然后用运行时的可观察行为去反查源码位置。比如你在交互模式里点了“允许执行”接下来权限层必然会被触发直接在源码里打断点或加日志很快就能把整条链路串起来。5. 安装部署与启动方式5.1 安装Claude Code 常见的安装方式有两种npm 全局安装和官方原生安装脚本。# 方式一npm 全局安装 npm install -g anthropic-ai/claude-code # 方式二官方原生安装脚本 curl -fsSL https://claude.ai/install.sh | bash安装完成后验证版本claude --version5.2 登录与启动直接在终端执行claude首次启动会进入登录流程。也可以设置环境变量ANTHROPIC_API_KEY跳过交互式登录export ANTHROPIC_API_KEY你的 API Key claude启动后进入交互式终端输入需求即可开始对话。常用斜杠命令包括/resume恢复历史会话、/compact手动压缩上下文、/permissions查看权限状态具体以会话内/help输出为准。5.3 VSCode 集成如果希望直接在编辑器里用可以安装官方 Claude Code 插件安装后即可在 VSCode 中调用。遇到集成异常运行claude /doctor做诊断它会检查登录状态、配置和运行环境。5.4 非交互模式与命令行参数# 打印模式适合脚本调用执行完直接退出 claude -p 总结当前目录的 README.md # 指定输出格式为 JSON claude -p 把上面的功能整理成清单 --output-format json-p是 Claude Code 对外暴露的非交互接口也是它区别于“聊天玩具”的重要特性。后面的批量任务和接口式调用都依赖这个模式。5.5 本地部署与其他模型路由Claude Code 默认连 Anthropic 官方模型服务需要网络和 API Key。社区里常见做法是通过环境变量把请求路由到其他兼容端点例如export ANTHROPIC_BASE_URLhttp://127.0.0.1:8000 export ANTHROPIC_MODELyour-model-name claude需要明确的是这段是社区实践不是官方承诺的功能。兼容端点必须实现 Anthropic 消息格式否则会出现模型名不被识别、请求格式不兼容等一堆问题。同时本地部署如果接到自建推理服务硬件门槛就完全不一样了显存需求取决于你接的是哪套模型不要再套用“Claude Code 不吃显卡”的结论。6. 功能测试与效果验证装好之后建议按下面的顺序做一轮功能验证。每个测试都有明确的输入、操作、预期结果和判断标准。6.1 最小可用链路测试claude --version claude -p 用一句话介绍你自己预期结果版本号正常输出-p模式能在几秒内返回文本。如果这里的调用失败优先排查登录状态、API Key 和网络连接。6.2 文件读写与代码生成测试mkdir -p /tmp/claude-test cd /tmp/claude-test claude -p 在当前目录创建 calculator.py实现加减乘除四个函数并写一个简单的 main 演示。预期结果目录下出现calculator.py内容完整。判断成功的标准是文件存在、函数定义正确、没有明显语法错误。这一步能验证工具层的文件写入链路是否正常。6.3 命令执行与权限确认测试继续在同一个目录下执行claude -p 运行 python3 calculator.py检查输出是否正确。预期结果Claude Code 尝试执行 Bash 命令交互模式下会弹出权限确认。如果选择允许模型会拿到命令输出然后给出结论。这一步重点验证的是权限闸门是否生效以及工具结果是否能正确回填给模型。6.4 跨文件重构测试claude -p 把 calculator.py 的加减乘除函数拆到单独文件 operations.py并在 calculator.py 中 import 使用。预期结果新增operations.pycalculator.py被改写两个文件能协同运行。判断标准执行python3 calculator.py后功能不变。这一步能验证模型在多个工具调用之间的连贯性也是衡量 harness 稳定性的重要指标。6.5 会话恢复测试在一个交互式会话中让 Claude Code 执行一个较长任务中途用/resume退出再恢复观察它是否还记得之前的任务状态。预期结果是会话能恢复到中断位置继续完成剩余工作。如果恢复后上下文缺失需要检查本地会话文件的写入权限。测试项输入预期结果判断标准基础问答claude -p正常返回文本输出非空、无报错文件生成创建脚本文件生成文件存在且内容正确命令执行运行脚本输出结果命令执行成功、结果回填跨文件重构拆分模块多个文件协同功能不变会话恢复/resume上下文保留能继续完成任务7. 接口 API 与批量任务7.1 Headless 模式就是接口Claude Code 的-p模式可以理解为一个“一次任务一张请求”的接口。它没有常驻 HTTP 服务但完全可以通过脚本驱动把所有需要自动化处理的任务变成可编排的流水线。如果你的诉求是批量修改多个仓库、批量生成文档、批量跑代码检查这个模式足够用。7.2 Bash 批量调用示例for repo in repo-a repo-b repo-c; do cd /path/to/$repo echo 处理 $repo claude -p 检查当前项目的 Python 文件列出明显的代码质量问题。 --output-format json done这个例子能直接跑但只适合少量目录。真正的批处理建议用 Python 或 Node 脚本管理方便加日志、超时和重试。7.3 Python 批量调用示例import subprocess import time tasks [ 为 project.py 补充 docstring, 修复 project.py 中可能的空指针问题, 为 project.py 编写单元测试, ] for i, task in enumerate(tasks, 1): print(f[{i}/{len(tasks)}] 执行任务: {task}) try: result subprocess.run( [claude, -p, task, --output-format, json], capture_outputTrue, textTrue, timeout600, cwd/path/to/your/project, ) print(退出码:, result.returncode) print(输出摘要:, result.stdout[:500]) except subprocess.TimeoutExpired: print(任务超时跳过) time.sleep(2) # 控制请求频率降低限流概率这里有几个工程要点。第一timeout必须设置避免单个任务把整个队列卡死第二每个任务最好都能独立执行任务之间不要有隐藏的先后依赖第三输出要做截断和落盘方便事后审计。7.4 批量任务的工程建议批量跑 Claude Code 时最怕的不是模型答错而是三点限流、超时、上下文污染。限流大量并发请求容易触发 529 或 429 错误脚本里要加退避重试。超时长任务可能跑很久必须设置单任务超时超时后进入重试队列。上下文污染如果一个会话里连续塞入多个不相关任务模型容易混淆批量任务尽量一条-p对应一个独立任务。如果你的批量任务需要更精细的程序化控制可以参考 Anthropic 官方 Agent SDK它把 harness 封装成可编程接口支持更细粒度的工具定义和事件回调。具体 API 以官方文档为准这里不展开写死。8. 资源占用与性能观察Claude Code 的常规使用方式不加载本地模型所以本机 CPU 和内存占用都很低也没有显存压力。真正需要关注的是“Token 消耗”和“请求延迟”这是它区别于本地模型工具的地方。可以观察的几个维度进程状态用ps aux | grep claude或系统监视器看 CLI 进程的内存占用一般很小。网络请求观察 Claude Code 进程是否有持续的 HTTPS 请求这对应模型推理的远端调用。上下文消耗会话内可以通过/cost或类似命令查看 token 消耗长任务中上下文会增长快满时会触发压缩提示。任务耗时同样一个任务仓库越大、工具调用次数越多耗时越长。性能瓶颈通常不在本机而在模型推理延迟和工具执行往返次数。如果你把 Claude Code 路由到本地兼容推理服务情况就完全不同了。本地模型需要 GPU 推理显存占用取决于模型尺寸和并发数这时才需要像优化传统推理服务那样去观察显存、吞吐和排队延迟。所以别把“Claude Code 不吃显卡”这句话泛化到所有部署方式上。9. 常见问题与排查方法问题现象可能原因排查方式解决方案返回 529 错误服务端过载或限流观察错误码和请求频率退避重试、降低并发、换个时间段模型名不被识别兼容端点的模型名与工具不匹配检查ANTHROPIC_MODEL环境变量换成端点支持的模型名登录失败或 401API Key 失效或过期检查环境变量和登录状态重新登录或更新 API Keynpm 安装失败Node 版本过低或权限不足node -v、查看 npm 日志升级 Node、检查全局安装权限VSCode 插件连不上网络或插件配置问题运行claude /doctor按诊断结果修复环境权限确认过于频繁默认权限模式偏保守查看/permissions在配置中增加 allow 规则批量任务卡住单任务超时或被限流查看脚本日志拆小任务、加超时和重试上下文很快变满任务复杂、历史过长手动/compact提前分段任务、减少多余输出529 是很多新手第一次跑批量任务时遇到的头号问题。它的本质不是你的代码写错了而是请求太密或者服务端繁忙。处理方式就是退避重试指数退避比固定间隔更有效。10. 最佳实践与使用建议10.1 工程化建议用 CLAUDE.md 做项目记忆。在仓库里维护一份CLAUDE.md写清楚项目结构、构建命令、代码规范Claude Code 会在每次任务开始时自动读入这是提升稳定性的成本最低的手段。配置权限规则。在配置里预设 allow / deny 规则把高频安全命令直接放行把危险命令永远禁止减少不必要的确认打断。用 Git 分支托管 agent 改动。所有自动修改先提交到单独分支人工 review 后再合入主干永远不要把 agent 的输出直接推上生产环境。批量任务要落盘。每条任务的输入、输出、耗时、错误信息都写进日志方便失败后重放。敏感信息不要进入对话。API Key、数据库密码、未脱敏的用户数据都不应该出现在任务文本里避免进入模型请求和会话日志。10.2 合规与安全边界Claude Code 能直接操作文件系统和执行命令使用时要特别注意授权边界。处理他人代码、版权素材、私有业务数据之前先确认是否有合法授权在团队或公司环境中使用要遵守数据合规要求不要拿生产环境的敏感代码去测试不熟悉的模型端点或第三方兼容服务。它只是个工具工具没有边界意识使用的人必须有。10.3 总结与下一步回到标题的问题。手撕 Claude Code 源码最终目标不是背下它的实现细节而是理解 Agent Harness 的通用结构一个主循环、一组工具、一层权限闸门、一套上下文管理、一份会话持久化。这套结构在 Claude Code、Codex、Cursor 里反复出现你只要吃透过一个后面再接触新工具都会很快。建议的上手路径是先用claude -p跑通最小链路再做一个跨文件修改任务观察权限确认和工具调用过程然后读源码时沿着“入口 → 主循环 → 工具注册 → 权限判断”四个定位点去走把观察到的行为和代码位置一一对应。最容易踩的坑集中在两个地方批量任务限流导致的 529以及本地兼容端点时的模型名不匹配。先把这两个点想清楚