☰
Claude Code源码拆解:工业级Agent系统的核心架构与设计
2026/10/26 13:59:40 网站建设 项目流程

前阵子Claude Code的部分源码被公开流了出来,圈子里讨论声不小。我把能翻到的头尾资料过滤了一遍,耐着性子啃完之后,最大的感受是:这不是一个普通的“AI小插件”,而是一整套把语言模型包装成“可靠执行者”的工业级Agent系统。题目里说的“学工业级Agent系统设计”,重点不在抄代码,而在看它怎么组织循环、怎么管工具、怎么控上下文、怎么防失控。

如果你是写过几个AI脚本、或正在从“单轮调用”往“自主Agent”方向走的开发者,这篇文章值得认真看。它会帮你在自己设计Agent的时候,直接绕开那些“能跑但一遇到真实任务就散架”的坑。

1. 先看全局:Claude Code到底是个什么结构

1.1 从命令行入口说起

Claude Code本质上是一个跑在终端里的交互式Agent进程,你给它一个任务描述,它会自己规划步骤、调用文件读写和命令执行等工具、观察结果、调整思路,最后给你交付结果。这套流程听起来简单,但落到工程实现,和那种“一问一答式”的AI接口调用完全是两个世界。

源码泄露出来的结构里,最外层是一个命令行Client,负责参数解析、启动交互Shell、渲染流式输出。再往里是核心的会话管理层,它维持着一次任务的完整状态:当前工作目录、历史消息、工具定义、权限策略、上下文预算。最核心的是那个Agent Loop,也就是“模型决策-工具执行-结果回填-再次决策”的闭环。

很多人拿到的所谓“源码”其实是经过构建混淆的JS产物,不要指望像读开源项目一样逐行注释。但混淆归混淆,模块划分和调用关系还是能通过报错栈、日志、API请求体反推出来。这个“源码级拆解”的重点,是搞懂每个模块的职责边界,而不是还原每一行语句。

1.2 为什么叫“工业级”而不是“demo级”

我见过不少自己写的Agent原型:一个while循环里调模型API,把工具结果硬塞进messages里,跑两个来回就Adaptive卡死,token满得飞快,还无法中断。这类demo最大的问题是没有控制面——你无法在任务中途干预行为,无法限制危险操作,无法控制上下文膨胀,更没有任何审计记录。

Claude Code几乎把这些问题都做了专门处理。它在循环里塞入了权限裁决、错误分类、上下文压缩、流式中断等机制。这些机制单独拎出来都很简单,难点在于把它们组合在一起之后仍然能保持低延迟、可预期、可调试。工业级和demo级的差距,就在这些“看不见的胶水”上。

1.3 适合谁来读这篇拆解

如果你符合下面任意一条,这篇文章会比较对口:

  • 正在用Claude等模型API开发Agent应用,想学习更成熟的架构范式
  • 想搞懂Claude Code为什么比普通提示词追问式脚本稳定得多
  • 打算维护或二次开发类似工具,需要理解它的运行机制和限制
  • 纯粹好奇“头部团队是怎么设计AI Agent系统”的工程读者

技术内容会尽量保持在不依赖具体模型版本的程度,讲解的是通用设计思想。

2. 核心架构:Agent系统的四个关键模块

2.1 会话状态是Agent的地基

Claude Code把一次任务的生命周期定义得非常清楚。启动时,它会加载配置文件,初始化一个Session对象。Session里装着完整的状态树:对话消息序列、当前目录、环境变量、Hook配置、权限白名单、上下文剩余预算。

这个设计看起来常规,但有一个容易被忽略的细节:所有工具执行结果都必须回流到Session状态里,而不是简单拼在Prompt里就完事。比如Read工具读了文件,返回内容同时也会记录“已读文件的路径”。后续模型如果引用这个路径,系统能更快定位上下文来源,也让权限审计变得清晰。

我在自研Agent的时候犯过类似的错误:全局变量满天飞,工具调用结果散落在各个函数返回值里,一旦流程变长就根本没法追踪“模型到底基于什么做出的这个判断”。所以学这套源码,第一个该学的就是——State First,先定义好完整状态树,再写业务逻辑。

2.2 Agent Loop:规划、执行、观察的闭环

把循环拆开看,每一轮实际发生的事是固定的:

  1. 组装请求:把系统提示词、工具定义、历史消息、当前环境信息打包成API请求
  2. 模型响应:模型决定下一步是继续对话、还是发起工具调用tool_use
  3. 工具执行:如果是工具调用,系统进入工具分派器,执行真实操作
  4. 结果回填:把工具返回的内容作为新的消息附加到对话上下文
  5. 循环判断:检查是否满足停止条件,不满足就回到第1步

这里有一个非常微妙的工程点:模型可能在一条回复里同时生成多段内容,甚至并行请求多个不相关的工具。Claude Code的做法是支持内容块级别的解析,把文本块和工具使用块拆开处理,逐个执行后按顺序回填。如果你做Agent只会“一问一答”,这个细节将来一定会遇到——真实任务里,模型经常想同时读三个文件、查两处文档。

2.3 停止条件不能只看“模型说完了”

demo级Agent最常见的停止条件是“模型回复里没有工具调用就结束”。但工业场景下还有更复杂的退出场景:

  • 用户主动中断(Ctrl+C或按Esc)
  • 连续多轮工具执行失败,系统判定任务不可继续
  • 上下文预算耗尽,进入强制压缩模式
  • 达到自定义的最大迭代轮数
  • 权限被拒绝后,任务无法推进

源码里这些条件都被当作“控制信号”统一处理,而不是散落在一堆if里。这一点很值得学:Agent的终止不是简单返回结果,而是一个状态机迁移的过程。每个终止场景都应当触发对应的收尾动作,比如保存日志、清理临时文件、输出失败摘要。

3. 工具层设计:怎么让模型“手脚干净且受控”

3.1 工具Schema的自动化和版本控制

从泄漏源码中能明显看出,工具定义不是手写在Prompt里的,而是结构化对象。每个工具包含名称、描述、输入Schema、权限级别、超时时间、是否允许后台运行等属性。系统在请求时会把这些Schema自动序列化注入模型上下文。

这一点对自研Agent极为重要。我最初是手写JSON Schema给模型,后来工具一多,描述要么过时要么互相冲突,模型开始频繁调用“不存在的参数”。学这套设计的正确姿势是:定义工具注册表,所有工具都通过装饰器或注册函数声明,统一生成Schema,并给Schema写单元测试。最少要让工具描述能够通过一个“互不冲突”的静态检查。

3.2 工具调用的完整生命周期

源码里工具调用不是一个函数,而是一条流水线:

  • 解析:从模型响应中提取tool_use块,校验参数格式
  • 授权:根据工具类别和参数内容,匹配权限规则,决定放行、询问、或拒绝
  • 执行:调用真实实现,设置超时与输出上限
  • 裁剪:对超大输出做截断或摘要处理
  • 回填:把结果包装成tool_result,追加到消息序列

这条流水线里,最容易出问题的其实是授权环节。比如Bash工具,模型可能执行rm -rf也可能执行ls,你不能只看工具名,还得看参数内容。工业级Agent的权限判断,一定是“工具名+参数内容”两条腿走路。

3.3 错误处理:工具失败不能变成黑盒

很多Agent实现里,工具一报错就把异常信息原样丢给模型,模型读不懂、开始胡编。Claude Code的源码里,工具错误被分成了几类:

  • 命令不存在、文件不存在这类确定性错误,直接返回可读信息
  • 超时、资源耗尽这类资源性错误,提示模型稍后重试或换方案
  • 权限拦截这类控制性错误,返回明确原因,不让模型硬刚

这种分类的目的,是让模型在下一轮决策时有足够信息调整策略。你完全可以把这个分类逻辑搬到自己项目里:规定每一种错误返回的结构里必须包含error_type、message、suggestion三个字段,模型拿到手的就不是一堆堆栈,而是一个“带建议的路标”。

4. 上下文工程:Agent能不能打得持久,就看这里

4.1 上下文不是“聊天记录”,是预算内资源

Claude Code把上下文视为一种有限资源来管理。它的请求消息结构里,除了系统提示词和历史对话,还包含工具返回值、文件内容、环境快照。这些内容的体积很容易超出模型窗口,所以它必须有预算控制机制。

从源码里反推出来的策略很直接:监控每一轮请求的token消耗,设定软阈值和硬阈值。软阈值之下一切照常;超过软阈值就启动“压缩候选人选择”——找最老、最不重要的历史轮次做摘要;超过硬阈值则强制压缩,甚至暂停接受新任务。

我自己的实践是给上下文管理增加了一个“观光车视角”:定期记录Messages数组的整体体积和预估token,打印到日志里。别小看这一行日志,它能暴露出“某一次工具返回了50万字符导致后文全部失效”的诡异问题。

4.2 压缩不是简单截断,而是“摘要化重写”

源码里最值得学习的一处是压缩算法。它不是简单地把最早的对话删掉,而是:

  1. 提取前面多轮对话中的关键状态:完成的任务、失败原因、当前目标
  2. 用一次独立的模型调用,生成结构化摘要
  3. 用摘要替换掉旧对话,保留最近几轮的完整记录
  4. 把摘要和剩余上下文重新拼接成新的请求

这样做的好处是,Agent在很长任务中依然能记住“事情的前因后果”。我实测过,一个2000行的代码检索任务,经过四五次压缩后,模型依然能准确说出“我们已经确认了模块A的问题,现在转向模块B”,这就是摘要化的价值。

4.3 文件内容的“按需注入”代替“全量塞入”

很多自研Agent一上来就把整个项目塞进上下文,几乎立刻爆炸。Claude Code的做法是配合Read、Grep、Glob这类精准工具,让模型按需读取文件,而不是一开始就全量倾倒。系统提示词里甚至会引导模型:“如果需要了解多个文件,优先使用搜索工具定位,再读取具体片段。”

这套“工具替代上下文”的思路,对设计通用Agent来说是战略级的。你的Agent应当对模型说“你可以去查”,而不是替它把整个知识库都读一遍。省下来的是上下文预算,换来的是更大规模任务的可执行性。

5. 权限、安全与审计:Agent不能无法无天

5.1 分级权限策略

从源码中可以看到,Claude Code的权限系统支持一个递进模型。对大部分安全操作自动放行;对可能有副作用的操作(如写文件、执行命令)询问用户;对高危操作(如删除文件、安装依赖)在交互模式下强制二次确认;同时可以通过参数或配置文件预设自动允许和自动拒绝规则。

这套设计最精髓的地方是:权限判定发生在每轮循环内、工具实际执行前。也就是说,模型规划了危险操作,不会等到执行了发现不行,而是提前就被系统拦下。Agent设计里,权限裁剪越靠前,系统越安全。

5.2 让权限白名单“持久化”

用户允许过的操作会被记录到本地配置中,下次相同操作不再询问。这功能看着简单,但工程上有讲究:它需要把用户意图从“一次性对话”转移到“长期策略配置”中。源码采用了一种类似规则引擎的方式,把“允许npm install”这类规则以文本形式存储,命中规则就走放行。

我在自研工具里也复刻了这个逻辑。核心不过是把(tool, argument_pattern, permission)三元组存成一个可落盘的列表。但这一个功能就让我的Agent从“每步都弹确认框烦死人”变成“大部分时候安静干活,危险动作才会打断我”。

5.3 会话记录与审计追踪

工业级Agent一定会有审计。Claude Code在本地保留了完整会话记录和操作日志,包括所有工具调用的输入、输出、耗时、权限决策结果。这个不是给用户看的,是给问题定位和回溯用的。

我自己接第三方便道时遇到过一次模型“撒谎”事件——它声称改了文件,但实际上没有。有了审计日志,一查就知道它根本没发出对应的工具调用,而是编了个中文文本假装完成。所以设计Agent时,日志一定要把模型说的和系统做的分开记录,说和做之间必须有独立审计证据,不然Agent就把你带沟里了。

6. 复现这套设计时,我踩过的坑和给你的一条路线

6.1 三个最容易踩的坑

第一,循环写成递归。如果你用递归实现Agent Loop,消息一长就会爆栈或内存泄漏。正确做法是显式的while循环,加上轮数上限。Claude Code因为是用异步运行时,循环结构做得非常显眼,绝对是刻意设计而非偶然。

第二,工具结果完全信任。模型会根据工具结果做下一步决策,如果你的工具返回值本身是错的或被截断污染,模型会一本正经地跑偏。务必在回填前校验结果完整性:至少检查是否超长、是否超时、是否为空。

第三,忽视中断信号。真实使用中用户一定会中断Agent。设计时要在每个工具执行点检查中断标记,而不是等一个超长工具执行完才响应。我自己早期做的一个Agent在中断后还会继续调用API三次,用户体验极其糟糕,被骂了一次才改好。

6.2 一条可落地的参考路线

如果你想自己实现一个简化版工业级Agent,建议按这个顺序迭代:

  1. 先写出最小的“模型-工具-循环”,用固定工具列表,跑通一个真实任务
  2. 加入工具注册表和Schema自动化生成,去掉硬编码提示词里的工具说明
  3. 加入权限模块,先做主目录限制和危险命令拦截
  4. 加入上下文预算监控,超过阈值自动压缩
  5. 最后补审计日志和中断处理

每一步之间都要有真实场景验证。我建议你找一个多文件代码库重构任务来测试,因为这种任务几乎会把所有模块的弱点都暴露出来:它需要文件读写、命令执行、多次搜索、长对话累积,正好检验Agent的持久作战能力。

6.3 最后说点个人的实在话

啃这份泄漏源码的过程,与其说是“拿答案”,不如说是“对答案”。它验证了一个我做Agent以来的感觉:模型能力再强,也只是循环里的一个决策组件,真正决定系统上限的,是工程上对状态的控制、对工具的约束、对上下文的预算管理。这套体系本身并不神秘,但把每个细节都做到不出岔子,才是工业级和玩具级的分水岭。

希望这篇拆解对你有用。下一篇我打算围绕“流式输出与用户交互中断”再往深挖——那部分在公开资料里讨论得最少,但在真实体验里最能决定一个Agent工具是“好用”还是“难用”。你有想听的方向,也欢迎留言告诉我。

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

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

立即咨询