☰
Claude Code源码拆解:Agent循环、工具调用与上下文管理
2026/10/25 4:49:16 网站建设 项目流程

有些代码泄漏,吃瓜群众看的是热闹,开发者看的才是门道。前段时间网上流传出的Claude Code源码,在技术圈里炸了一圈。很多人第一反应是去翻代码找漏洞、找彩蛋,但真正值得花时间的,是把这个工业级AI Coding Agent从里到外拆一遍——看清楚它凭什么能在真实项目里自己读代码、改文件、跑命令、修错误,而不是像大多数Demo那样只会聊天。

这篇文章我会用这套源码作为引子,带你把一个Agent的核心循环、工具系统、上下文管理、权限控制这几个关键模块逐个过一遍,最后给一个可以自己抄作业的Mini版编码Agent骨架。适合三类人:正在开发Agent产品的工程师、好奇Claude Code为什么"好用"的同学、以及想在自己的项目里接入编码Agent能力的架构师。

1. 这次"源码泄漏"到底泄了什么:一个不含大模型参数的Agent壳子

1.1 事件背景与"壳"的含义

先说清楚一件事:网上流传的这套Claude Code源码,里面并没有大模型权重,也没有神秘的推理算法。它本质上是一层跑在Claude API外面的"调度壳"。Claude Code本身不是一个模型,而是一个用Node.js/TypeScript写成的命令行程序,它负责把用户的自然语言请求转换成一系列具体的行动——比如读文件、写文件、执行shell命令、调用外部API——然后通过Anthropic的API把上下文和工具定义发给Claude模型,拿到模型的决策后再继续执行。

打个比方。你问一辆超跑为什么跑得快,你以为会看到发动机里的燃油配方和活塞工艺,结果打开机舱盖,发现有人把方向盘、行车电脑、油门踏板的标定逻辑全给你摊在桌子上了——发动机本体依然是黑盒。Claude Code的源码就是那套方向盘和行车电脑,而发动机是Claude模型本身。

这个区分非常重要。很多人一听"Claude Code源码泄漏",以为拿到了什么核心模型权重的碎片,实际上拿到的是Anthropic团队在产品工程层面的大量心血:Agent Loop的编排方式、系统提示词的设计、工具调用Schema的写法、权限交互逻辑、上下文压缩策略。这些才是从0到1复刻一个编码Agent时最容易被低估、也最难做对的部分。

1.2 为什么工业级实现值得认真读

市面上的AI编程助手已经很多了,但大多数是"IDE里多了一个补全框"——你写一行,它猜下一行,本质上是tab补全的升级版。Claude Code和它们的区别在于:它是一个真正的Agent,被放到终端里,拿到了执行命令和修改文件的权限。它能自己初始化一个项目、跑测试、看报错、再改代码,直到测试通过。这个"端到端闭环"能力,才是它在众多工具里显得特殊的原因。

从热闹的搜索词也能看出来,很多人已经在用Claude Code了——搜"claude code安装教程""claude code fde""claude code 报错"的人不在少数。但会用和懂原理是两码事。你安装好工具,敲一句"帮我修一下这个bug",它背后到底怎么定位问题、怎么决定改哪个文件、怎么判断改完了没有——这些逻辑就是藏在源码和系统提示词里的"工业级秘密"。

读这套源码,不是为了复刻一个一模一样的竞品,而是为了理解一个成熟产品在真实工程约束下做设计的方式。举个例子,你写一个工具调用循环,可能只需要几十行代码;但要让这个循环在跑一整天、处理上千次工具调用时不崩、不跑偏、不泄露上下文、不被恶意命令坑,就需要很多在Demo里根本不会出现的工程细节。

2. Agent Loop:让整个工具"转起来"的主循环与系统提示词设计

2.1 ReAct循环的工程化实现:思考、行动、观察

要说Claude Code这类工具的核心,绕不开ReAct(Reasoning + Acting)模式。简单讲,就是把"推理"和"行动"交替进行,让模型每一步都基于新获得的信息继续做出判断,而不是一次输出结论就完事。

Claude Code的任务循环大致可以抽象成这样:用户输入一句请求 → 模型生成一段回复(中间夹带一个或多个工具调用)→ 工具执行并返回结果 → 结果追加到对话历史 → 模型基于新的历史继续推理,再生成下一步行为……直到模型认为任务已经完成,不再请求工具,而是输出最终答案。

我举个具体例子。你在终端里对Claude Code说:"帮我把项目里的fetch换成axios。"一个成熟的Agent不会直接回答"好的我改了",而是会先调用Grep找到哪些文件里用了fetch,再调用Read打开对应文件确认上下文,接着调用Edit把代码替换掉,最后用Bash跑一遍lint或测试确认没有破坏东西。每一步都是一个完整的"思考→行动→观察"循环,模型的推理不是一次性的,而是跟着工具结果不断调整。

这里最玄学但其实也最工程的,是终止条件。模型什么时候停止调用工具开始输出答案,不是靠一个计数器,而是靠模型自己在生成时判断"当前信息足够了"。源码里做了一件事:持续解析模型输出中是否包含工具调用请求。如果包含,就执行工具并把结果追加回去;如果不包含,就认为链路结束了,把最后一段非工具调用文本作为最终答案呈现给用户。

2.2 系统提示词:整个产品团队经验的浓缩

网传源码里最值得逐字读的,我认为不是算法,而是那几万字的系统提示词。它基本就是整个Anthropic产品团队踩了无数坑之后总结出来的"操作手册"。系统提示词不包含任何模型权重,但它决定了模型在具体场景下的行为边界和工作习惯。

从泄漏出的内容能看出这几个核心指令方向:

第一,要求Agent"hands-on"。收到问题后不要去问用户"你想怎么做",而是主动去查代码、找证据、自己形成判断。这和我见过的大多数AI助手产品形成了鲜明对比——很多工具收到请求后的第一反应是给建议,而Claude Code被训练成"先动手查,再给结论"。

第二,要求"一次只做一个变更"。完成一个目标之后再做下一个,不要在一个工具调用里夹带一堆不相关的改动。这个设计很克制,但它保证了一个长任务里每一步都是可审查、可回滚的。

第三,强调"不能用工具验证的事,要如实说明"。系统提示词里有一类要求是禁止模型假装自己执行过某个命令、禁止编造工具输出结果。这条重要性怎么强调都不为过——Agent一旦开始"表演",整个闭环就失去了可靠性。工业级和玩具级之间,往往就差这一点诚实。

2.3 流式输出:一边干活一边写"工作日志"

使用过Claude Code的人都知道,它的体验是边思考边打字,你能实时看到它在读什么文件、执行什么命令、得到了什么结果。这看起来像是个体验优化,背后其实是一套流式事件机制。

源码层面,Claude Code维护了一个内部的事件流。模型每次生成一段文本或者请求一个工具,都会作为一个事件被推给前端界面渲染。这样做的工程价值有两点:一是用户能感知到Agent在工作,而不是对着空白屏幕干等;二是事件流同时也构成了后续上下文的一部分——中间过程被完整记录下来,模型在生成最终答案时能回溯自己做过什么。

这一点还顺带解释了为什么Agent的token消耗比普通对话高得多。每一次工具调用、每一条工具结果、每一段中间推理都会累积到上下文里,所以你在终端里用Claude Code跑一个复杂任务,后台的token消耗往往很惊人。理解了这一点,再看后文的上下文压缩机制,就能明白为什么那是必备功能,而不是加分功能。

3. 工具调用:从源码细看一个工业级Agent的手脚如何分工

3.1 内置工具矩阵:每个工具干一件事,边界清楚

Agent的行动能力,全部来自它注册给模型的那批工具。Claude Code的工具集没有追求数量多,而是每个工具的职责边界非常清楚。我从源码里梳理出来的核心工具大概是这样一张矩阵:

工具功能核心参数典型使用场景
Read读取文件内容file_path, offset, limit查看某个文件的具体实现
Write整文件写入file_path, content新建文件或整体覆盖
Edit按行精确替换file_path, old_string, new_string局部修改代码
Bash执行shell命令command, timeout跑测试、装依赖、启动服务
Grep正则搜索文件内容pattern, path定位用例或函数定义
Glob按模式匹配路径pattern,path找文件、统计数据规模
WebFetch抓取网页内容url查阅文档、确认API用法

这张工具清单看起来简单,但设计上有很深的考量。比如Edit工具不是"整文件重写",而是精确到old_string和new_string的局部替换。这样做的直接好处是省token——你改一行代码不需要把整个500行的文件重新发给模型;另一个隐藏好处是更安全——局部diff比整体覆盖更容易被用户审查,误改动的影响范围也被限制住了。

再比如Read工具支持offset和limit参数,也就是说你可以分段读取一个超大文件。这在处理动辄几千行的配置文件或自动生成代码时非常关键。工具参数的设计,本质上是产品对"模型行为"的塑造——工具能怎么用,模型就会怎么用。

3.2 权限与安全:危险操作前的一道道闸门

工具能力越强,风险越高。Bash工具能执行任意shell命令,这既是Claude Code强悍的原因,也是最容易出安全事故的点。源码里对工具的权限管理做了分类处理,总体上可以总结成三个等级:自动允许、需要确认、始终禁止。

自动允许的一般是只读操作,比如Read、Grep、Glob这类不会改变系统状态的工具。需要确认的,则是Edit、Write这样会修改文件的操作,以及Bash里的非只读命令。用户可以在交互界面里按y同意、按n拒绝,也可以配置allow规则,让某些频繁使用的安全命令自动放行。

这套权限模型我用一个词来形容:人在回路。它不追求完全自主,而是在Agent能力和用户控制之间找一个平衡点。对任何做Agent产品的人来说,这是个非常重要的设计参考——你给Agent的权限越大,它的故障半径就越大,而绝大多数用户在第一次看到Agent擅自执行rm命令时,心里多少会有点发毛。

我还注意到一个经验性的细节:Bash工具的timeout参数是可以配置的。给长时间运行的命令设置超时,可以避免Agent被一个永远不会结束的命令卡死。这类细节在Demo里没人提,但在生产环境里,处理不当会直接导致整个会话不可用。

3.3 工具结果如何被消化

工具调用返回的结果,不是给用户看的,而是作为新的对话历史追加给模型的。这个机制是Agent闭环里最基础的一环,也是最容易被忽略的一环。

模型生成一个工具调用后,这个带工具调用的assistant消息会被放进历史消息列表;然后每种工具的执行结果,会以tool角色的消息追加到同一个列表里。模型的下一步推理会同时看到"自己刚才调用了什么工具"和"工具返回了什么"这两部分信息,从而做出更准确的判断。

这里面有个非常现实的工程问题:工具输出可能很长。一次Grep如果匹配了200个文件,返回的文本可能直接占掉几千token。源码里会对大输出做截断或摘要处理,避免无意义的token浪费。对工具结果的"消化"程度,直接影响Agent的上下文利用率和响应速度。

另一个值得学习的细节是失败处理。源码里反复出现的一个原则是:工具调用失败后,不要盲目重试同样的事。合理的Agent行为是分析失败原因、换一个方案、尝试新的路径。如果连续失败两次,就要停下来,把情况告诉用户,而不是无限循环。这个"止损"逻辑,在很多自己搭Agent的项目里往往被遗漏。

4. 上下文管理:无限对话窗口背后的压缩、检索与状态维护

4.1 不把项目塞进窗口:按需检索策略

新手设计AI编程助手时最容易犯的错误,是想"把整个项目都塞进上下文里,让模型全知全能"。这个思路在项目只有几十个文件时勉强能用,一旦项目扩大到上千个文件,光是一次完整读取的token量就能撑爆上下文窗口。

Claude Code的做法是典型的"按需检索":面对一个大型代码库,它不尝试一次性加载全部源码,而是先通过Glob了解项目文件结构,再用Grep定位关键符号,最后用Read精确读取涉及的文件。这套组合拳保证了上下文里装的永远是和当前任务直接相关的信息,而不是一堆无关代码。

我举一个实际工作流的例子。当Claude Code需要修改一个API接口的返回值结构时,它大概率会先用Grep搜索"return {"这类模式,锁定候选代码位置,再Read打开相关文件确认数据结构,而不是从项目入口文件开始一字一字读。这个策略和我们人类工程师的工作方式高度一致——先定位,再精读,而不是从头到尾通读。

有人在搜索词里问"claude code 免费使用""claude code安装教程",其实这类问题背后暴露的是同一个痛点:大家真的把Claude Code当成生产力工具来用了,但很少有人关心它为什么能在大项目里稳定工作。答案就在这个按需检索策略里——它的上下文效率,是通过"克制"换来的,而不是靠更大的窗口。

4.2 Compact机制:对话太长了怎么办

无论怎么按需检索,一个任务持续久了,历史消息还是会越积越长。Claude Code提供了一套压缩机制,官方命令叫/compact。当对话历史超过设定的长度阈值时,它会自动触发——把之前的对话历史浓缩成一个摘要,替换掉原有的大段历史,释放上下文空间。

值得细看的是源码里对"摘要格式"的要求。摘要不是简单的一句话说"用户改了一个bug",而是要结构化成类似这样:已完成哪些任务、当前在做什么、项目的关键路径是什么、待办事项有哪些。这个结构化摘要的质量,直接决定压缩之后的Agent是否还能高效工作。我自己对比过:摘要写得具体、可操作,压缩后Agent基本不"失忆";摘要写得笼统,压缩后同一个任务常常要重新摸索一遍。

这个机制对长程任务的意义不言而喻。你在终端里让Claude Code开发一个新功能,中间可能经历十几轮文件修改和测试执行,如果每轮的历史都原封不动保留,几轮下来上下文就会臃肿到影响模型判断。或者说,上下文压缩不是为了省成本,而是为了保证Agent在中后期仍然能聚焦在正确的目标上。

4.3 状态维护比聊天记录更重要

在Agent领域,有一个普遍被低估的坑:模型会记住聊过什么,但不太会主动记住"自己做到哪一步了"。Claude Code在源码和系统提示词层面都做了一件事——要求Agent在任务过程中维护一个显式的进度状态,比如待办清单、当前步骤、已完成步骤。

这个设计和我们的直觉相反。我们总觉得"上下文越长,模型越不容易失忆",但真实情况是,如果不把关键状态从聊天记录里"提取"出来,放到一个显眼的位置,几千行历史消息反而会让模型迷失重点。显式状态维护相当于给Agent准备了便利贴,把最重要的信息贴在桌面上,而不是埋在那堆历史文件里。

这一点我做自己的Agent时深有体会。早期版本里,我只靠历史消息来维持任务进度,经常出现的情况是:模型在执行到第5步时,忘了第1步定义的目标,开始往错误方向发散。后来我在提示词里强制要求模型维护一个"进度条",每个回复开头都输出当前任务的完成状态,发散问题明显减少。这不只是Claude Code的技巧,这是任何长任务型Agent都应该具备的基本素养。

5. 源码之外的可复用方案:给自己造一个Mini Coding Agent

5.1 直接可落地的骨架:Python实现一个极简版

读源码的最终目的,是为了形成自己的判断。我不建议你去复刻一个Claude Code,但我强烈建议你亲手搭一个Mini版——几百行代码就能跑起来的那种,把核心循环吃透。下面这个Python骨架,就是我建议的起点:

import json from your_llm_sdk import chat_with_tools # 替换为你用的模型SDK MAX_STEPS = 10 def run_agent(user_request): messages = [{"role": "user", "content": user_request}] for step in range(MAX_STEPS): resp = chat_with_tools(messages) msg = resp.choices[0].message # 没有工具调用,说明任务完成,输出最终答案 if not msg.tool_calls: return msg.content # 把带工具调用的assistant消息放进历史 messages.append(msg) # 依次执行每个工具调用 for call in msg.tool_calls: result = execute_tool(call.function.name, call.function.arguments) messages.append({ "role": "tool", "tool_call_id": call.id, "content": truncate(result, max_length=3000) }) return "达到最大步数,任务提前终止"

这个骨架的核心就一个while循环。你只要补上三样东西:一个工具注册表、一个执行函数、一个上下文截断工具,一个最简Agent就成型了。

工具注册表长这样:

def execute_tool(name, args_json): args = json.loads(args_json) if name == "read_file": with open(args["path"]) as f: return f.read() if name == "grep": import subprocess return subprocess.run( ["grep", "-rn", args["pattern"], args.get("path", ".")], capture_output=True, text=True ).stdout if name == "bash": import subprocess return subprocess.run( args["command"], shell=True, capture_output=True, text=True ).stdout return f"未知工具: {name}"

你只需要给模型提供对应的工具Schema,它就会自动决定什么时候调用哪个工具。剩下的所有智能,都是模型和工具之间通过对话循环"碰"出来的。

5.2 必须处理的三个坑

骨架搭起来很快,但要让它真正稳定工作,有三个坑绕不过去。

第一个坑是工具结果不加控制地塞回上下文。我在前面提过,Grep输出可能非常长,如果不截断,几轮下来上下文就满了。所以一定要给工具结果设置一个最大长度,比如3000字符,超出部分截断或者做摘要。宁可信息不完整,也不能让上下文失控。

第二个坑是Agent死循环。模型在某些场景下会反复调用同一个工具,或者调用一个永远执行不完的命令。我的处理办法是设置最大迭代次数,比如10轮,超了就主动终止并把当前状态告诉用户。另外,每个Bash命令都要设timeout,防止命令阻塞整个会话。

第三个坑是权限不分级。如果你把Bash工具的定义暴露给模型,而不做任何限制,那它在某些时候可能会执行危险命令。我的建议是:只读操作自动放行,写操作需要确认,关键环境变量和系统目录直接列入黑名单。对个人项目可能无所谓,但一旦你想把这个骨架接进公司代码库,这条就是生死线。

这几个坑看起来都是工程细节,但正是它们把"能跑"和"能用在生产环境"区分开。Claude Code的源码里,这类细节占了相当大的比重——这大概是工业级实现和学术Demo之间最直观的差别。

5.3 关于"换个底座模型"的社区玩法

顺着源码话题延伸一下,很多人都在讨论"Claude Code接入DeepSeek"这类玩法,搜索词里也能看到"claude code接入deepseek v4"这样的高频提问。原理上并不神秘:Claude Code本质上是一个API Client,它通过环境变量读取API地址和模型名,把base_url和模型标识改掉,就能把后端从Claude换成其他兼容模型。

这个玩法的价值在于验证想法。你可以用一个更平价的模型,把Claude Code做Agent的循环体验完整跑一遍,看看自己是不是喜欢这种工作流。但你说要直接当生产力用,我的态度是比较保守的。

原因也很简单:Claude Code那套系统提示词是专门为Claude模型设计的,里面有很多针对Claude输出风格的隐含假设;换成另一个模型后,工具调用的格式兼容性、指令遵循程度、对长上下文的利用效率,都会打折扣。不是所有模型都能稳定输出符合工具Schema的JSON,也不是所有模型都愿意严格遵循"一次只做一个变更"之类的约束。

所以我的实践结论是:做实验、学原理,用开源模型完全可以;接生产环境,本质是拿稳定性换成本,这笔账要自己算清楚。

尝试在这个骨架基础上做一次真实的任务,比如让Agent自己初始化一个Python项目并跑通一个单测,你会发现很多理想化的假设会当场碰壁。但恰恰是这些碰撞,能让你真正理解Claude Code这类工业级产品背后那些"看不见的工程"有多值钱。

我自己在仿照这套架构做了一个内部小工具之后,最大的收获不是"我会写Agent循环了",而是深刻理解了工具设计对Agent体验的决定性作用。给模型越大的自由,不等于越好的结果;相反,把动作收敛到几个精心设计的高质量工具上,配合清晰的权限边界和克制的上下文管理,Agent的表现反而稳定得多。下一个真正好用的Coding Agent,大概不会赢在模型参数比谁更大,而是赢在这套循环设计、工具边界和上下文治理的综合功力上。

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

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

立即咨询