Appearance
原文:第 6 章 说明:忠实翻译原网页内容,并补入与经典文献、业界系统的对照。术语首次出现给出英文锚点。
第 6 章 Claude Code 内部机制:agent 运行时的重建与解读
本章在体系中的位置
这是全书唯一一章讲「AI 基础设施」的章节。原站课程(16 周)把它放在第六周(ma-infra),夹在 CUDA 与编译器之间——前面几章的主角是硅片、访存层级和线程调度,这一章的主角突然变成了一个大语言模型。看上去很跳,但读下去你会发现:前五章那套「软件-硬件协同」的思路,原封不动地发生在「模型-运行时」这一层。硬件系统的教训——不要指望硬件自己做好调度,要用软件显式组织并发、局部性与数据搬移——在 agent 系统里变成了:不要指望模型自己守好纪律,要用运行时显式组织它的动作、上下文与安全边界。学完这一章,你会带走一个看待所有「AI 智能体」产品的新镜头:模型是头脑,运行时才是机体。
生产级 AI 编码 agent 不是「一个会写代码的强模型」。它是一个被装进执行环境里的模型。这个环境给了模型眼睛、手、记忆和边界:它让模型能检查仓库、搜索源码、读改代码、跑终端命令、请求权限、从工具失败中恢复、压缩自己的工作记忆、把子任务委派给下级 worker。同样重要的是,它阻止模型任意行动:它告诉模型存在哪些动作、哪些动作需要批准、哪些动作不安全、观察结果该如何回流进推理循环。
这一章要展开的正是这个转变。如果你认为产品是「模型 + 聊天界面」,你的注意力会过度落在提示词上;如果你认出产品是「被运行时包裹的模型」,最重要的问题就变成架构问题:控制回路怎么组织?上下文怎么装配?长会话怎么保持一致?危险动作怎么被调解?并行怎么被约束?怎么让一个会犯错的概率性规划器不毁掉用户的机器?又怎么让这一切在终端里可观察、可打断?
Claude Code 是极好的教学案例,因为它在相对具体的层面上暴露了严肃 agent 工程的形态。一旦你不再只问「模型有多聪明」,而是开始问「什么样的软件架构,能让一个聪明但会犯错的模型表现得像个有纪律的软件工作者」,魔法就不再神秘。
本章按这条路走。先重新框定 Claude Code 是一个 agent 运行时而不是聊天界面;再审视组织整个系统的设计原则、把行动与特权分离的分层架构、执行核心的双层循环结构、定义 agent 动作词汇表的工具契约、调解 shell 访问与文件改写的安全机制、让系统能在长时间线上工作的记忆与上下文机制、防止单个上下文过载的多 agent 分解策略、以及把运行时内部状态变成用户可监督对象的终端 UI。走到最后,「魔法」应该被还原成硬核系统工程。
一处诚实声明
原文标题是「重建与解读」(reconstructed and interpreted):本章描述基于 Claude Code 的公开行为、官方文档与社区分析,其中关于内部实现的细节带有推断成分。具体的产品行为会随版本演进,本章教的是架构逻辑,不是截图般的现状。
1. 为什么 Claude Code 不只是「终端里的 Claude」
理解 Claude Code 最偷懒的方式,是把它当成一个大语言模型的命令行包装:你敲一句话,模型回一段话。这个描述不算全错——系统里确实有一个语言模型,你也确实在终端里跟它打交道。但它把产品最决定性的部分藏起来了:那个让模型在真实工作环境里「用得起来」的运行时(runtime)。
为了看清差别,并排摆两个系统。
第一个系统:你把一句话发给模型,模型返回一段文本,交互结束。
第二个系统:你让它「修复这个仓库里挂掉的测试」。它搜索代码库,打开可疑文件,读失败的堆栈,提出修改方案,请求打补丁的权限,跑测试套件,看到新的报错,修订计划,再试,最后带着一份 diff 和解释回来。
两个系统可能调用同一个模型。但只有第二个配得上 agent 运行时(agent runtime) 这个名字。
差别不是「第二个系统有工具」。差别是第二个系统围绕与外部世界的闭环交互组织自己:它行动、观察、适应。这意味着它的架构必须解决一类根本不同的工程问题:
- 模型被允许怎样行动?
- 合法动作用什么数据结构表示?
- 状态如何跨轮保存?
- 昂贵的上下文窗口如何管理?
- 工具失败时怎么办?
- shell 与文件系统的访问如何调解?
- 人类介入的口子开在哪里?
- 整个进行中的过程如何透过一个可用界面暴露出来?
停下来想想
把「产品 = 模型」换成「产品 = 运行时 + 模型」,你问的问题就从「这个模型够不够聪明」变成了「什么不变量让整套系统在真实世界里可靠」。第二种问法才是生产系统里的那个问题。这一章通篇都在回答它。
所以,比「高级聊天机器人」更准确的说法是:Claude Code 是一个为模型驱动的软件工作服务的小型操作系统。它像操作系统一样管理资源、隔离危险操作、调解特权、调度工作、提供控制面、给底层能力做抽象。模型仍然关键,但它不是整台机器。是运行时把模型智能变成了可操作的东西。
2. 概念转移:从聊天机器人到 agent 运行时
普通聊天机器人是一种很浅的交互模式:用户说一句,模型答一句,循环结束。就算这来回答得很聪明,它本质上仍是一次「文本输入 → 文本输出」的单发映射。
这不是软件工程的方式。
真实的软件工作是迭代的、有状态的、扎根于外部世界的。开发者很少一遍就把问题解决。他们查看仓库、读错误日志、搜符号、检查接口、测试假设、改一个文件、重跑程序、把新输出和旧输出并排比较——然后常常发现最初的假设是错的。整个过程是一个「观察 → 行动 → 修正」的循环。关键的地方在于:很多关键信息一开始并不在开发者脑子里,它们是在与环境互动的过程中被发现的。
所以编码 agent 必须比「会说话」做更多事,它必须参与这个循环。这需要四类机制:
- 选择性感知的机制,让它只检查该看的文件和日志;
- 行动的机制,让它能编辑代码或执行命令;
- 反馈的机制,让行动结果重新进入推理;
- 控制的机制,让循环不会滑向不安全或不连贯的行为。
这就是从聊天机器人到 agent 运行时的概念跳跃。系统不再围绕一次响应设计,而是围绕内部认知与外部状态之间的反复转换设计。一次工具调用不只是输出,它是对世界的干预;一个工具结果不只是文本,它是重塑模型内部假设的观察。运行时的职责,是把这套往返结构化,让系统保持有用而不是陷入混乱。
Claude Code 正是这次转移的具象化。语言模型仍然是规划器(planner)和解释器,但它被一圈运行时包围:这圈运行时决定存在哪些工具、适用什么权限、什么内容该进上下文、命令输出保留多久、能同时跑多少个子代理、部分输出如何流向终端、系统何时该停下。换句话说,Claude Code 不只是模型的对话接口,它是模型与代码库之间受控互动的架构。
所以正确的心智模型不是「更强的聊天工具」,而是——一台以迭代软件工作为目标的运行时,它的规划引擎碰巧是个大语言模型。
学术坐标
「把语言模型当规划器、外面包一圈工具与环境」的范式,在文献里早有谱系:ReAct(Yao et al., 2022)让推理与行动在同一个循环里交替,模型既「想」也「做」;Toolformer(Schick et al., 2023)让模型学会自己调用 API 来增强能力。Claude Code 的价值不在发明这个范式,而在于把范式推到生产级:它把「模型会调工具」变成了「运行时设计一台可调工具的机器」。
3. 组织整个系统的五条设计原则
一旦把 Claude Code 看成运行时而非聊天包装,架构就开始收敛成一小组连贯的原则。这些原则不是随手选的实现偏好,它们是让产品成立的结构性承诺。
3.1 工具定义能力边界
模型不被允许即兴发明新的动作通道。想读文件,必须用读文件工具;想编辑文件,必须用编辑工具;想搜索,必须用显式的搜索原语;想执行 shell 命令,必须走 Bash 工具。
这是整个系统里最重要的想法之一,因为它把「什么叫行动」的定义权从模型手里放到了运行时手里。模型可以提议,但它不能发明特权。 这个设计与 OpenAI 的函数调用(function calling)规范一脉相承:模型输出的永远只是「一个工具名 + 一组结构化参数」,而不是可以随意解释的自由文本。动作词汇表由运行时给出,模型只能从里面选。
3.2 失败关闭的默认安全
当一个系统能改文件、跑命令、连外部服务,宽松的默认值就成了负债。在默认放行的系统里,忘配一个旗标会让风险悄悄扩大;在失败关闭(fail-closed)的系统里,忘配旗标的结果是被挡住。这是正确的非对称:工具默认被视为不安全,直到在某个具体维度——比如「只读」或「并发安全」——被明确声明为安全。
这条原则纸面上很小,实践后果巨大。它决定了整个产品在规格不完整时如何表现:是倾向于贸然继续,还是倾向于停下来确认。
3.3 上下文工程优先于提示工程
学生的第一直觉常常是:编码 agent 的力量藏在一条精心炮制的系统提示里。生产系统教会的是反例。应用真正的智能在于上下文工程(context engineering)——上下文如何被动态组装:哪些稳定规则留在前缀里,哪些项目特定事实被注入,工具输出如何裁剪,历史什么时候压缩,缓存如何保留,哪些细节被保护起来不让摘要吃掉。
提示不是一句咒语。它是一个被精心打理的工作环境。
3.4 可组合性
这种规模的系统不能永远靠特例逻辑存活。如果子代理、团队、外部工具、权限规则、用户自定义扩展各走一套互不相通的机制,结果会变得脆弱。强系统复用抽象:同一个查询循环既驱动主会话也驱动下级 worker;同一套权限逻辑管理内部工具与外部工具;同一套流式架构处理父代理、子代理和 UI 渲染。
这种复用不只是优雅。它是大型系统能保持可维护性的原因。
3.5 编译期消除而非运行时门控
被禁用的功能,最安全的状态是在运行时产物里根本不存在。一个只是藏在运行时分支后面的功能仍然存在,仍然能和其余代码交互;一个在构建期就被剥掉的功能,不可能被意外触发、不可能被部分启用、不可能在线上产品里形成一片未测试的面。在有显著安全压力的系统里,这个区别比普通应用代码重要得多。
五条原则合起来是一句完整的论题:Claude Code 建立在「不要信任模型能在内部独自扛起全部纪律」的假设之上。 纪律应该活在被模型包围的确定性基质里——工具契约、状态机、安全检查、上下文编译器、UI 循环、权限边界。这是现代 agent 工程里最深的一课之一。
4. 分层架构:行动为什么必须分离关注点
Claude Code 的架构最好被看成一个分层系统。这不是因为分层图时髦,而是因为一旦让认知、执行、状态、呈现塌缩成一大团过程化代码,agent 行为就会产生危险的耦合。
分层大致是这样:
text
┌─────────────────────────────┐
│ 表现层 presentation │ 终端 UI、流式文本、diff、权限提示
├─────────────────────────────┤
│ 应用层 application │ 会话编排、主循环、事件流、生命周期
├─────────────────────────────┤
│ 领域层 domain │ 工具、消息、权限、记忆工件的类型化定义
├─────────────────────────────┤
│ 基础设施层 infrastructure │ 模型 API、文件系统、子进程、Git、认证
└─────────────────────────────┘最上层是表现层(presentation layer)。 在 Claude Code 里就是终端 UI:对话记录、流式文本、权限提示、diff、进度指示、正在运行的工具的视觉状态、键盘交互模型。把它轻视为「只是前端」是个错误。在异步编码 agent 里,表现层是用户观察运行时的唯一窗口。如果它不清晰、有延迟、或与执行不同步,整个系统都难以监督。
其下是应用层(application layer)。 运行时在这里编排会话主循环:追踪对话状态、协调对模型的调用、决定工具请求如何执行、管理长生命周期会话、处理连接认知/执行/UI 的事件流。表现层是系统的脸,应用层是它的编排脊柱。
再下是领域层(domain layer)。 产品在这里定义「什么样的对象存在」。一个「工具」不只是一个函数,它是一份带类型输入、类型输出、元数据、权限语义和执行行为的结构化能力。一条「消息」不只是一串字符串,它属于一个带角色、载荷和状态转移的类型化历史模型。权限、会话模式、记忆工件、任务协调抽象都活在这一层。系统在这一层决定「什么在概念上是真实的」。
最底层是基础设施层(infrastructure layer)。 副作用在这里发生:调用外部模型 API、读写文件、派生子进程、检查 Git 状态、存持久记忆、接入外部工具、处理 OAuth 等认证流程、与操作系统交互。危险也在这里——因为副作用,正是「坏的模型提议变成真实的机器动作」的那个地方。
分层的理由不是软件洁癖,而是以调解为安全。大语言模型强大但会犯错,它们能提出看似合理实则错误的行动。如果解释模型输出的同一段逻辑,可以立刻自由地改动机器,那么推理与特权就以最糟糕的方式纠缠在一起。分层阻止了这一点:模型表达意图 → 领域层把意图编码成合法形式 → 应用层调解执行路径 → 基础设施层在所有检查通过后才执行 → 表现层把正在发生的事展示给用户、让用户介入。
一旦系统允许 LLM 行动,架构就变成了安全。 Claude Code 的分层应该在这个意义上被理解。
5. 启动即世界构建
大部分程序员把初始化代码当成不起眼的脚手架。在 agent 运行时里,启动值得更严肃的解读:它是认知流水线的一部分。
一个「醒来时失明」的 agent 不只是信息不足,它是在一个有缺陷的世界模型上运行。设想运行时在下面这些问题确定之前就把模型放出来了:当前工作目录是什么?仓库是否受版本控制?存在哪些项目本地规则或记忆文件?哪些工具可用?要恢复什么先前的会话状态?
那模型就会在一个它并不真正理解的环境上开始推理。它可能臆想出假设了错误工具链的命令;可能漏掉项目特定指令;可能因为不知道运行时已经知道什么,而重复完成过的活儿。
所以 Claude Code 的启动应该被理解成世界构建(world construction)。在模型进入活动循环之前,运行时先构建一个局部世界表示:检查仓库、加载持久记忆、备好工具、确定权限状态、校验关键依赖、把当前会话绑定到模型将要推理的那个环境上。只有这个世界存在之后,运行时才把控制权交给交互循环。
停下来想想
在任何智能系统里——人类的、生物的、人工的——推理质量都强烈依赖供给推理器的世界描述质量。坏的世界模型产生坏的行动,哪怕推理器本身能力在线。这个规律在模型对齐、机器人、以及 agent 运行时里是同一句话,只是载体不同。
这比它看起来更深。Claude Code 的启动路径反映的正是这个现实:运行时不只是「把模型拉起来」,它先准备好一个可推理的局部世界。启动不是无聊的样板代码,它是认知支持的第一幕。
6. 双层 agent 循环:以及为什么异步生成器重要
如果说有哪个子系统最能抓住 Claude Code 的本质,那就是查询循环。更准确地说,是这样一个事实:Claude Code 似乎不只有一个查询循环,而是双层循环架构。
外层循环把会话当作一个持久对象管理:跟踪多轮状态、存储转录、适配协议边界、记录用量信息、协调对话跨轮的连续性。这是系统里「知道有一场会话存在」的部分。
内层循环管理单轮执行:组装上下文、调用模型、接收部分输出、识别工具调用、把工具调用路由过恰当的调解路径、吞入工具结果、决定循环是继续还是终止。这是「推理、行动、观察」相遇的本地控制回路。
为什么拆成两层?因为这两层解决根本不同的问题。会话管理是长时间尺度的事:关心持久化、历史、资源核算、生命周期。单轮执行是短时间尺度的事:关心流式输出、取消、工具排序、错误恢复、模型与环境之间即时的反馈循环。如果两者融合进一个巨大的循环,代码必然变得难以推理,更难以安全地打断。
异步生成器(async generator) 的选用在这里特别有信息量。传统函数模型是「调用一次,返回一次」。但一轮 agent 执行不像这样:一轮执行可能先吐出部分文本,触发多次工具调用,产生进度信号,请求权限,在用户回应后恢复,最终以最终答案收场。这种行为的本质是流(stream),而不是函数。
异步生成器因此不是风格偏好,它是正确抽象。它让下游消费者在数据就绪时才取值,从而提供背压(backpressure);它让嵌套 agent 自然组合——子 agent 本身可以成为父循环里的另一个流式源;它让取消变得更干净——用户中断时,.return() 式的终止可以向下传播进嵌套操作,包括工具执行和子代理,这比在同步调用栈上硬贴临时的中断处理安全得多。
一帧单轮执行
内层循环里的一轮,实际长这样:组装上下文 → 调模型 → 模型输出流里出现工具调用标记 → 解析出(工具名, 参数)→ 过权限检查 → 执行工具 → 把工具结果包装回消息流 → 再次调模型 → 如此反复,直到模型产出终止信号。任何一步都可能停下来等用户确认。这是一条有状态的事件流,而不是一次函数返回——这也是为什么它需要异步生成器这种「长寿命、可中断、增量产出」的抽象。
这里有一条更深的架构教训:好的 agent 工程,往往可以归结为选对控制抽象。异步生成器恰好贴合 agent 轮次的真实行为:长生命周期、可中断、增量产出、可嵌套、充满事件。Claude Code 似乎认出了这一点,并按此搭建了执行循环。
7. 工具契约与类型化状态:为什么运行时必须支配动作词汇表
生产级编码 agent 的核心抽象不是提示,是工具契约(tool contract)。
模型可以用自然语言表达意图,但自然语言太软、太含糊,撑不起可靠的动作。模型说「找一下 auth 的 bug,打开相关文件,改掉它」,运行时仍然需要结构化的动作:调用哪个工具?什么参数?受什么约束?什么执行语义?结果如何表示回循环?——这就是工具契约的角色。
一份工具契约不只是描述一个可调用函数,它定义了概率性认知与确定性执行之间的一条合法交互边界。它声明:这个动作存在;这些参数必填;这些输出被期望;这些权限语义适用;这些副作用可能发生;这条执行路径有效。运行时就是这样把模糊的模型意图变成它可以安全调解的东西。
看一个极简的工具契约长什么样(JSON schema 风格,与 OpenAI 函数调用规范同构):
json
{
"name": "EditFile",
"parameters": {
"type": "object",
"required": ["path", "old_string", "new_string"],
"properties": {
"path": { "type": "string", "description": "要编辑的文件路径" },
"old_string": { "type": "string", "description": "必须唯一匹配的原文锚点" },
"new_string": { "type": "string", "description": "替换后的文本" }
}
}
}模型输出的是这样一份「名称 + 参数」的结构化请求;运行时负责验证参数、检查权限、执行并回传结果。模型从头到尾接触不到「直接写文件」这个动作本身——它只能触达契约允许的那条路径。这正是「运行时支配动作词汇表」的工程含义。
这也是类型化状态如此重要的原因。消息、工具调用、工具结果、权限决策、记忆工件、会话元数据不能永远是松散的文本。系统越有行动能力,原始字符串就越危险。运行时需要 schema 和内部结构,才能纪律严明地验证、检查、路由、缓存、展示、压缩。
这套架构最深刻的后果是模型与运行时之间权威的倒置。在朴素的系统里,模型隐式地定义「什么动作被尝试、怎么尝试」。在 Claude Code 里,运行时定义动作词汇表,模型必须把自己嵌进这套词汇表。这种倒置不可或缺:它阻止模型通过自由文本走私任意语义,也让权限与安全规则变得可处理——因为每个有意义的动作都以运行时能理解的结构化形式存在。
所以工具层不是边缘零件。它是语言模型认知变成操作功率的地方,也是运行时确定性特征最显眼的地方。
8. 受控行动的三个案例:Grep、FileEdit 与 Bash
理解 Claude Code 运行时纪律的最好办法,是比较三个表达力递增的工具:一个搜索原语、一个结构化文件编辑工具、以及 shell 本身。这三个案例展示出:运行时的调解负担如何随能力的力量与风险一起增长。
8.1 Grep:选择性感知,而不是盲目读全文
搜索是软件工作里最基础的认知原语之一。人类开发者不会先把整个仓库装进脑子再开始干活——他们搜符号、栈迹片段、报错信息、接口、重复出现的模式。编码 agent 需要同样的原语。
一个 Grep 式工具的意义在于:它给了模型选择性获取信息的路径。与其盲目读文件、白白烧掉上下文,agent 可以先搜可能的位置、缩小不确定性的范围,再细读相关文件。这不仅是省钱,它在认知上忠实于真实的调试行为。搜索是运行时让模型把一个大仓库变成「可增量查询的环境」的方式。
架构上的意义是:感知变得结构化。环境不是被整袋倒进提示里,而是通过工具被查询。这是一个可扩展得多的模式——把「读一切」换成「按需查」,上下文开销从仓库规模降到查询规模。
8.2 FileEdit:受约束的修改与反幻觉纪律
编辑在质上比读取危险。一旦 agent 能改代码库,它就能造成伤害;更微妙的是,它能以「局部听起来很合理」的理由做错事。所以文件编辑工具需要更强的不变量。
有两个约束特别有启发性。
第一,编辑目标必须可唯一定位。 如果模型说「替换这一块」,而存在多个匹配区域,运行时不应该猜。要求唯一锚点,防止意外改错位置——这也正是上面那段 JSON schema 里 old_string 必须「唯一匹配」的含义。
第二,不允许编辑一个没先读过的文件。 这是个伪装成小规则的强大不变量。大语言模型极擅长从部分记忆里生成「听起来很合理」的局部状态——在仓库里,这意味着它们很容易「记住」并不完全存在的代码。如果运行时允许纯粹基于模型内部信念的写操作,agent 就会频繁对着过期或想象出的上下文打补丁。要求先读后写,是从机制上压制这个失效模式。
注意这里的模式:不是请求模型「小心一点」,而是把小心编码成行动的前置条件。
8.3 Bash:表达力变成安全负担
shell 访问是另一量级的能力。一条 shell 命令不只是「一个动作」,它是一门微型编程语言:支持顺序、重定向、替换、环境变量、命令组合,以及任意与文件系统和网络的交互。这种灵活性是 shell 对开发者强大的原因,也是它成为 agent 里主要风险面的原因。
一旦模型能调用 Bash,运行时继承了一个安全问题。它必须理解语法而不只是字符串;必须检测可疑模式而不只是命令名;必须评估旗标——同一命令族内部,安全与不安全的行为可能只有一旗之差;必须约束执行环境;必须在危险路径上保住用户控制权。而且所有这些必须系统化地做,因为 shell 表达力太强,简单启发式中介不了。
三个工具揭示出 agent 架构的一条普遍定律:
表达力-结构守恒
工具越有表达力,运行时必须在它周围施加越多结构。 搜索需要轻度中介。编辑需要更强的不变量。shell 执行需要一整套安全架构。这个梯度不是偶然,它是对增长的能力的正确回应。
9. 权限、钩子与机械安全不变量
一旦模型被允许行动,权限系统就变成运行时的免疫系统。
agent 设计有两个糟糕的极端。一个极端是每个琐碎动作前都问用户——机器安全了,产品没法用了。另一个极端是什么都不问——agent 快了,但鲁莽了。严肃的权限系统必须活在这两个极端之间:区分无害感知、受限修改与真正危险的动作;允许策略随模式变化,同时保住不可谈判的保护。
Claude Code 里的权限模式可以理解为一台分级自治机制(文档里的模式从严格到宽松大致是 plan → default → acceptEdits → bypassPermissions):
plan模式下,agent 基本只读,只做分析不做改动;default交互模式下,工具使用需要明确确认;acceptEdits更宽松,文件编辑可以自动执行,shell 动作仍停下等审批;bypassPermissions最高自治,多数常规动作直接放行,但危险路径仍受硬边界保护。
即便在最宽松的模式里,某些动作也始终受硬边界保护。这正是「默认失败关闭」那条原则在权限层的落地。
停下来想想
为什么权限要用「模式」而不是「一条总开关」?因为风险不是均匀分布的:读文件、改文件、跑任意 shell 命令、访问网络,是四档完全不同的风险。一条总开关要么太松要么太紧;分档才能既让高频安全动作不打断人,又让低频危险动作被拦住。
这一点是关键。好 agent 系统里最强的安全机制不是建议性规范,而是机械不变量(mechanical invariants):
- 让模型「避免危险 shell 命令」是弱的;要求每条 shell 命令在执行前都经过语法解析、策略检查、注入检测与环境约束,是强的。
- 让模型「编辑文件要小心」是弱的;拒绝让它编辑一个没先读过的文件,是强的。
钩子(hooks)自然地嵌进这个故事。无论它们是用户定义的生命周期拦截点还是内部运行时检查点,架构角色都一样:在有意义的边界处,给系统一个检查、修改、增补或阻断行为的位置。钩子重要的原因在于:它让安全与控制逻辑能被注入运行时的动作路径,而不必要求模型自己记住并自我执行每一条策略。在 Claude Code 里,你可以挂 PreToolUse 钩子:某个工具被调用前,一段用户脚本有机会先看参数、再决定放行还是拦截——机械、确定、可审计,完全绕开模型的「自觉」。
这里最深的教训很简单:
好 agent 系统主要不靠模型守规矩。它们让不安全的行为在结构上很难甚至不可能发生。
这条原则比任何「请谨慎」的提示级恳求都可靠得多。
10. 上下文工程与记忆压缩:长时间线的一致性
构建长期运行的编码 agent,最难的部分不是生成代码,而是在时间上维持连贯性。
一次编码会话会积累海量状态:用户指令、仓库事实、开放假设、栈迹、工具输出、diff、测试结果、命令日志、先前的失败、成功的干预、临时观察、持久的项目规则。如果这一切永远保持完整细节,上下文窗口膨胀、延迟上升、模型失去清晰度。如果系统总结得太激进,它又会忘记当初为什么要做这件事。
所以 Claude Code 里的上下文工程,最好被理解为不确定性下的 token 预算管理。运行时必须反复决定——这就是记忆压缩(memory compaction):什么要原样高保真保留,什么要压缩,什么要丢弃。
一条粗粒度但实用的分层:
- 稳定指导——项目规则、持久记忆、显式用户约束——常常在语义上承重,应该被仔细保护;
- 近期工作证据——刚刚发生的操作与结果——支配接下来几步的决策,需要以更多细节保持可访问;
- 大型工具输出——新鲜时有用,之后大多是冗余的——应该被截断或折叠;
- 更早的历史——通常可以被总结,但前提是摘要保住了正确的不变量。
压缩不是免费的
工程在这里变得微妙:总结会丢信息,而且会丢错的信息。一段工具日志可能安全地塌缩成「命令成功」;但一条「不要引入 Redux」的用户指令,丢了就是灾难。所以运行时既需要压缩策略,也需要一套语义重要性判断:不是所有信息等价,也不是所有信息都适合等强度压缩。
这也就是为什么像 Claude Code 这样的系统里,「记忆」不是「存几条笔记」,而是一个分层架构:
- 有些记忆是静态的,可跨轮复用;
- 有些是动态的,会话特有;
- 有些属于持久工作流规则;
- 有些属于活动工作集;
- 有些可以从原始转录衰变为结构化摘要;
- 有些是根本该消失的噪声。
把这一思想显式理论化的是 MemGPT(Packer et al., 2023):把大语言模型当作操作系统,上下文窗口是「内存」,外部记忆是「磁盘」,由运行时负责调页与换页。Claude Code 不引用这套比喻,工程上却殊途同归——长会话要能跑得下去,上下文就必须被当成存储层级来管理,而不是被当成一个越塞越满的袋子。
从这个角度看,上下文工程和模型本身一样重要:强模型配上糟糕的上下文策展,推理会很差;弱模型配上优秀的上下文策展,常常能超出天真预期。 具体到工程现实:以 Claude 系列为例,上下文窗口已从 20 万 token 一路扩展到百万级,提示缓存(prompt caching)把重复前缀 token 的命中成本降到近零——这些都在改变「什么值得放进上下文」的经济学。Claude Code 对上下文分段、缓存与压缩的投入,反映的正是这个现实。
11. 技能、子代理与团队:认知的分解
单个庞大的上下文,常常不是软件工程里正确的工作单元。
假设一个 agent 必须什么都干:搜仓库、读文件、规划修复、改代码、跑测试、检查结果、判定对错。两个问题很快浮现。第一,上下文变脏:取证、规划、执行、验证全部挤进一条流。第二,模型变得容易自我确认:生成修复的那个过程,也倾向于相信修复成功了。
Claude Code 对技能(skill)、子代理(subagent)、团队(team)的支持,应该被理解为对这两个问题的回应。
一个技能是可复用的过程化包。它把反复出现的工作模式固化下来,让运行时不必每次从零重建整条推理路径,就能把专门行为带到问题上。技能把重复的实践变成可复用的结构——比如「按项目约定跑测试」「为某类 bug 走一遍标准排查步骤」,都可以做成技能,一次定义、处处调用。
一个子代理隔离性更强。它可以有自己的提示、自己的局部上下文、更窄的动作范围。这让运行时能为聚焦的子任务——搜索、探索、验证、收集上下文——派出专用 worker,而不必让主会话被每个中间细节污染。子代理的产出在完成后作为一份结构化结果回到主循环,而不是把它的全部推理过程灌进来。
团队或 swarm 式结构更进一步:多个 worker 在更高级别规划器的协调下分工,每人处理问题更窄的一片。有人收集信息,有人尝试编辑,有人验证。协调者的工作不只是派活,而是在委派前精炼理解——把子问题阐述得足够精确,免得 worker 在一个规格不明的世界里醒来,把预算花在从头重建整条推理链上。
关键在于认知卫生
分解不只是关于并行,它关乎认知卫生(epistemic hygiene)。搜索 worker 不需要和打补丁的 worker 同样的上下文;验证 worker 最好别继承它要评判的那条实现路径的每个假设。上下文更窄、角色更清晰,验证就更少被生成偏差污染。这条路线在学术上有两个对得上的坐标:AlphaCodium(Ridnik et al., 2024)主张「从提示工程转向流程工程」,把生成与验证拆开、用迭代的测试反馈收窄答案;SWE-bench(Jimenez et al., 2024)则把「在真实 GitHub issue 上端到端跑完整 agent 循环」变成可度量的基准——它测的不是模型会不会写代码,而是整套运行时在真实仓库上能不能走通。
这也是为什么多 agent 支持是「Claude Code 是平台而不是单个助手」的强信号。它已经越过「一个带工具的模型」的门槛,进入「一个能分解认知本身的编排运行时」。
12. 终端 UI:可观测性与控制面
Claude Code 的终端界面太容易被低估。许多人把它当成真正智能外面的一层薄壳。实际上,UI 是运行时控制架构的一部分。
异步编码 agent 不像普通命令行程序那样行事。它流式输出部分文本;它启动的工具其结果随时间到达;它可能需要停下来请求权限;它可能在打补丁前展示 diff;它可能跑持续几秒乃至更久的命令;用户可能中途打断;它可能派出下级工作,而下级工作的状态仍应可见。「往终端打印几行」的做法很快就撑不住了。
这就是响应式终端 UI 重要的原因。用户不只是读一份对话记录,他们在监督一个进行中的过程。UI 必须让运行时的内部状态可读:
- agent 现在在做什么;
- 哪个工具在跑、跑到了哪一步;
- 什么输出已经到了;
- 系统是否在等批准;
- 是否有子任务还活着;
- agent 是不是卡住了;
- 代码里到底发生了什么变化。
在这个意义上,终端更像一个实时状态机的仪表盘,而不是被动的文本控制台。
这里还有一个深刻的安全含义。可观测性是安全自治的一部分。 如果用户看不到系统在做什么、看不懂它处于哪个阶段、无法在对的时刻介入,那么「人在回路中」就只是名义上的——用户在功能上被排除在控制之外。好 UI 通过保持运行时状态可理解,来保住人类的主体性。
所以终端层不是装饰。它是人类还能监督、批准、打断、信任系统的原因。
13. MCP:不塌缩架构的扩展性
扩展性是现代 agent 系统的一大强项,也是最快破坏架构纪律的方式之一。
核心问题很简单。一个运行时的内部可以结构优美:严格的工具 schema、权限规则、状态模型、执行路径。可一旦外部能力以临时方式被硬接进来,那些不变量就会被削弱:schema 对不上、权限被绕过、认证变得不一致、可观测性退化。
所以 Claude Code 里的扩展性不应该被理解成「它能接更多工具」,而应该被理解成「它试图把外部工具吸收进自己已有的纪律」。一个外部能力应该对运行时「可读」——以结构化工具的形式;它的参数应该被验证;它的权限应该路由过同一套策略机制;它的认证应该以与原生交互同样的、用户可见的方式浮出;它的结果应该以运行时能推理、能展示的形式回到循环里。
这才是对的架构:新能力通过遵守运行时的法则进入系统,而不是绕过它们。 当扩展保持了运行时的核心不变量,系统在变强大的同时不会变混乱。
模型上下文协议(Model Context Protocol,MCP) 是这条思想的标准化落地。它是一份由 Anthropic 发起的开放协议,定义了一套与厂商无关的接口,让外部数据源和工具以统一方式进入 agent 运行时:工具描述、参数 schema、资源读写、提示模板都由协议规定,运行时照单验证,客户端与服务器之间用 JSON-RPC 消息通信。对 Claude Code 这类系统来说,MCP 的价值恰恰在于:接入一个新 MCP 服务器,等于声明「又有了一批遵守我已有纪律的工具」,而不是「给系统开一个例外口子」。
这也是「Claude Code 是平台」的又一个信号。平台不只是「能做很多事的系统」,它是能在不丢失内部逻辑的前提下成长的系统。
14. 这套架构做对了什么,下一步该往哪走
Claude Code 做对的事情相当多。
它明白工具是能力边界而不是便利插件。它把上下文工程当作一等公民问题而不是事后补丁。它似乎在运行时不变量层面认真对待安全,而不只是停留在高层政策语言。它认识到验证常常应该与生成分离。它分解工作来管理上下文与偏差。它把 UI 当作可观测性层而不是装饰壳。而且它似乎意识到,一旦提示缓存与重复推理主导系统的经济学,成本与延迟就变成了架构关切。
同时,这套架构也指向自然的下一个改进方向。
一个很可能出现的压力点是全局状态蔓延(global state sprawl)。代码库长大时,共享运行时状态常常累积到「搞不清什么依赖什么」。更显式的状态分区,或更强的依赖注入,可以在时间推移中改善这一点。
另一个机会是更声明式的安全与权限策略层。过程化的策略逻辑链能用,但随着系统长大越来越难审计、难扩展。一个用于表达权限、路径保护、执行规则的声明式引擎,能让架构更清晰、更安全、更容易演化。
第三个、也许更重要的机会是结构化记忆与工具结果存储。如果工具结果主要以原始文本保存,后续压缩就只能从语言中推断什么重要;如果结果在可能时保存为类型化工件,运行时就能以强得多的保证去总结它们——那会让长时间线的一致性更加可靠。
这些不是「架构失败了」意义上的批评。它们只是当系统不再是一个 demo、而成为一个承载模型驱动工作的真实运行环境时,自然出现的下一组问题。严肃系统总会积累压力。成熟的标志不是压力消失,而是架构在成长时仍然保持有原则。
15. 这对学生与系统构建者意味着什么
把视角从产品拉回你自己的书桌。
Claude Code 教给你的最重要一课,不是「前沿模型无所不能」,而是:模型一旦能行动,模型周围的确定性脚手架就变得决定成败。 一个生产级编码 agent 主要不是一个提示词,它是一个分层的执行环境,让概率性的规划器在真实世界里可用、可检查、可打断、可存活。
对学生来说,这句话应该让你放心,而不是劝退。你不需要造出一个前沿模型才能做出有意义的贡献。模型智能周围有大量可以改进的层:更好的工具契约、更安全的权限系统、更强的上下文工程、更清晰的可编排性、更干净的 UI 可观测性、更可靠的记忆压缩。在很多真实系统里,这些层决定产品到底成不成立。换句话说,agent 工程是一片「不用发明模型也能创造巨大价值」的土壤——你打磨的是让模型不闯祸、不忘事、不跑偏的那层机器。
如果你想把这章变成自己能摸到的东西,别去复刻 Claude Code 本身。去搭一个保留核心思想的教学型微型运行时:
- 一个能在模型调用与工具执行之间交替的循环;
- 类型化的工具契约;
- 一个搜索原语;
- 一条「先读后写」的文件编辑规则;
- 一条简单的权限边界;
- 一条针对长输出的压缩规则。
有条件的话,再加一个独立验证的子代理,让学生亲眼看到为什么生成与评估有时必须分离。
这样的小系统会经常失败:它会臆想参数、过度用工具、卡进死循环、误读局部状态、请求该被拒绝的命令。但这些失败恰恰是教学资产——它们精确地暴露了生产 agent 为什么需要前面讨论的整套架构机制。失败的形态,就是架构的论据。
把整台系统压成一条循环,它长这样:
text
用户意图
↓
会话层
↓
agent 循环
↓
工具契约层
↓
权限 / 安全层
↓
执行层
↓
观察层
↓
上下文管理层
↓
UI 渲染层
↓
回到 agent 循环这才是生产级编码 agent 的真实形状。
模型是头脑。
运行时是机体。
而在 Claude Code 这样的系统里,绝大多数工程——以及绝大多数决定产品质量的东西——都活在那个机体里。
延伸阅读
- 推理-行动交替的经典论文:Shunyu Yao 等, ReAct: Synergizing Reasoning and Acting in Language Models, 2022(ICLR 2023)。
- 工具使用自学习:Timo Schick 等, Toolformer: Language Models Can Teach Themselves to Use Tools, NeurIPS 2023。
- 记忆分层与上下文工程的理论化:Charles Packer 等, MemGPT: Towards LLMs as Operating Systems, 2023。
- 真实仓库任务的编码 agent 基准:Carlos E. Jimenez 等, SWE-bench: Can Language Models Resolve Real-World GitHub Issues?, ICLR 2024。
- 生成与验证分离 / 流程工程:Tal Ridnik 等, Code Generation with AlphaCodium: From Prompt Engineering to Flow Engineering, 2024。
- 结构化工具调用的规范出处:OpenAI, Function Calling 官方文档(工具与结构化参数格式的行业惯例)。
- 标准化的工具接入协议:Anthropic, Model Context Protocol(MCP)官方文档与规范。
- 本章主角的官方资料:Anthropic, Claude Code Documentation(产品文档、权限模式、hooks、子代理的说明)。
- 进阶综述(可选):Xi Zhiheng 等, The Rise and Potential of Large Language Model Based Agents: A Survey, 2023。