第 18 章:尾声——我们学到了什么

五个架构押注

Claude Code 不是唯一的智能体系统,也不是第一个。但它做出了五个架构押注,使它在众多智能体框架中显得与众不同。走过近两千个文件和十七章之后,这些押注值得仔细审视。

押注 1:用生成器循环取代回调

大多数智能体框架会给你一条管线:定义工具、注册处理器、让框架来编排。开发者编写回调,框架决定何时调用它们。

Claude Code 反其道而行之。query() 函数是一个异步生成器——循环由开发者掌握。模型流式生成响应,生成器产出工具调用,调用方执行这些工具调用、追加结果,然后生成器继续循环。这里只有一个函数、一条数据流、一个所有交互都必须经过的地方。生成器返回类型中的 10 个终止状态和 7 个继续状态编码了每一种可能的结果。循环就是系统。

这个押注认为:单个生成器函数,即便增长到 1,700 行,也会比分布式回调图更容易理解。研究源码之后可以说,这个押注成功了。当你想理解一个会话为什么结束时,只需要看一个函数。当你想添加新的终止状态时,只需要给一个判别联合添加一个变体。类型系统会强制进行穷尽处理。回调式架构会把这套逻辑分散到几十个文件中,回调之间的交互也会隐含在各处,而不是显式呈现在控制流里。

押注 2:用基于文件的记忆取代数据库

第 11 章已经详细论证过这一点,但它的架构意义并不止于记忆。选择使用普通 Markdown 文件,而不是 SQLite、向量数据库或云服务,是在透明度和能力之间押注透明度。数据库可以支持更丰富的查询、更快的查找和事务保证。文件没有这些能力。文件提供的是信任。

当用户在 vim 中打开 ~/.claude/projects/myapp/memory/MEMORY.md,并确切看到智能体记住了关于自己的哪些内容时,他们与系统的关系,和那些必须询问智能体“你记得什么?”并希望答案完整的用户有根本不同。基于文件的设计让智能体的知识状态可以被外部观察,而不仅仅依赖自我报告。这比查询性能更重要。由 LLM 驱动的召回系统用检索智能弥补了存储的简洁性——一次 Sonnet 侧查询从清单中选出五条相关记忆,比嵌入相似度更精确,并且不需要任何基础设施。

押注 3:自描述工具优于中央编排器

智能体框架通常会提供一个工具注册表:你在中央配置中描述工具,然后框架把它们呈现给模型。Claude Code 的工具会描述自己。每个 Tool 对象都携带自己的名称、描述、输入 schema、提示贡献、并发安全标志和执行逻辑。工具系统的职责不是替工具向模型描述工具——而是让工具描述自己。

这个押注带来了可扩展性收益。MCP 工具(第 15 章)通过实现同一个接口成为一等公民。来自 MCP server 的工具和内置工具对模型来说没有区别。系统不需要单独的“MCP tool adapter”层——包装过程会生成一个标准的 Tool 对象,从那一刻起,现有工具管线就会处理它:权限检查、并发执行、结果预算、钩子拦截。

押注 4:用分叉智能体共享缓存

第 9 章介绍了 fork 机制:子智能体启动时,上下文窗口中带有父级的完整对话,并共享父级的提示缓存。这不是一个便利性优化——而是一项架构押注:提示缓存共享模型值得为 fork 生命周期管理付出复杂度。

另一种选择——用对话摘要启动一个全新智能体——更简单,但成本高。每个全新智能体都必须从头承担处理上下文的完整成本。分叉智能体可以免费获得父级已缓存的前缀(输入 token 享受 90% 折扣),这让为小任务派生智能体变得经济可行:记忆抽取、代码审查、验证轮次。后台记忆抽取智能体(第 11 章)会在每个查询循环轮次之后运行,而它的成本之所以只是边际成本,正是因为它共享父级缓存。没有基于 fork 的缓存共享,这个智能体的成本会高到难以承受。

押注 5:用钩子取代插件

大多数可扩展性系统使用插件——注册能力并在宿主进程内运行的代码。Claude Code 使用钩子——在生命周期点运行、并通过退出码和 stdin/stdout 上的 JSON 通信的外部进程。

这个押注认为:进程隔离值得付出开销。插件可能让宿主崩溃。钩子只会让自己的进程崩溃。插件可能把内存泄漏到宿主堆中。钩子的内存会随进程一起消亡。插件需要一套必须版本化并持续维护的 API 表面。钩子只需要 stdin、stdout 和退出码——这是自 1971 年以来一直稳定的协议。

开销是真实存在的:每次调用钩子都要生成一个进程,会消耗进程内回调不需要的毫秒级时间。内部回调的 -70% 快速路径(第 12 章)说明系统知道这项成本很重要。但对于外部钩子——用户脚本、团队 linter、企业策略服务器——隔离保证让系统扩展起来更安全。企业可以部署基于钩子的策略执行,而不必担心格式错误的钩子脚本会让开发者的会话崩溃。


什么可以迁移,什么不能

Claude Code 中并非每一种模式都能泛化。有些模式是规模、资源或特定约束的结果,而其他智能体构建者未必面对这些条件。

可以迁移到任何智能体的模式

生成器循环模式。 任何需要流式生成响应、处理工具调用并管理多个终止状态的智能体,都能从显式化循环中获益,而不是把循环隐藏在回调之后。判别联合返回类型——精确编码循环停止原因——是一种能消除整类“智能体为什么停下来了?”调试会话的模式。

基于文件的记忆与 LLM 召回。 具体实现细节属于 Claude Code,但原则——简单存储结合智能检索——适用于任何需要跨会话持久化知识的智能体。四类型分类法(user、feedback、project、reference)以及可推导性测试(“这能否从当前项目状态重新推导出来?”)都是可复用的设计启发式。

用于远程执行的非对称读/写通道。 当读取是高频流而写入是低频 RPC 时,无论具体传输协议是什么,把二者分离都是正确选择。

用于搜索的位图预过滤器。 任何需要搜索大型文件索引的智能体,都能从作为预过滤器的 26-bit 字母位图中受益。每个条目四个字节,每个候选项一次整数比较——成本收益比非常出色。

把提示缓存稳定性视为架构关注点。 如果你的智能体使用带提示缓存的 API,那么把稳定内容放在提示前部、把易变内容放在后部并不是优化——而是决定成本结构的架构决策。

Claude Code 规模下特有的模式

分叉的终端渲染器。 Claude Code fork 了 Ink,并用紧凑 typed array、基于池的驻留化以及单元格级 diff 重新实现渲染管线,因为它需要在终端中实现 60fps 流式输出。大多数智能体渲染到 Web 界面或简单日志输出。只有当终端渲染是你的主要 UI,并且你在进行高频流式输出时,这种工程投入才有意义。

50+ 个启动分析检查点。 当你拥有数十万用户,并且 0.5% 采样能产生统计显著数据时,这才有意义。对于更小的智能体,一个更简单的计时系统就足够了。

八种 MCP 传输类型。 Claude Code 支持 stdio、SSE、HTTP、WebSocket、SDK、两种 IDE 变体以及 Claude.ai 代理,因为它必须集成每一种部署拓扑。大多数智能体只需要 stdio 和 HTTP。

钩子快照安全模型。 在启动时冻结钩子配置,并且之后绝不隐式重新读取,是为了防御一个特定威胁:恶意仓库代码在用户接受信任对话框之后修改钩子。当你的智能体运行在带有不受信任 .claude/ 配置的任意仓库中时,这一点很重要。只运行在可信环境中的智能体可以使用更简单的钩子管理。


复杂度的代价

近两千个文件。它买到了什么,又付出了什么?

文件数量作为复杂度指标具有误导性。很大一部分文件是测试基础设施、类型定义、配置 schema,以及 fork 出来的 Ink 渲染器。真正的行为复杂度集中在少数高密度文件中:query.ts(1,700 行,智能体循环)、hooks.ts(4,900 行,生命周期拦截系统)、REPL.tsx(5,000 行,交互式编排器),以及记忆系统的提示构建函数。

复杂度来自三个来源,每一种都有不同的性质:

协议多样性。 支持五种终端键盘协议、八种 MCP 传输类型、四种远程执行拓扑和七种配置作用域,本身就很复杂。每新增一种协议,都是对代码库的线性增加,而不是指数增加——但总量依然很大。这种复杂度是 Brooks 意义上的偶然复杂度:它来自环境(终端碎片化、MCP 传输演进、远程部署拓扑),而不是来自正在解决的问题本身。

性能优化。 基于池的渲染、位图搜索预过滤器、粘性缓存锁存器和推测工具执行,每一项都用复杂度换取可测量的性能收益。这种复杂度由测量结果支撑——每一次优化之前,都有性能分析数据识别出瓶颈。风险在于,优化会不断累积并相互作用,使热路径更难修改。

行为调优。 记忆系统的提示指令、陈旧性警告、验证协议、“ignore memory”反模式指令——这些不是代码复杂度。它们是提示复杂度,并且带来不同的维护负担。当模型行为在不同版本之间发生变化时,那些通过 eval 精心调优过的提示指令可能需要重新调优。eval 基础设施(在整个代码库中以 case 编号和 eval 分数被引用)是防止回归的防线,但它需要持续投入。

这个系统的维护负担相当可观。新工程师阅读代码库时,不仅要理解代码路径,还要理解驱动特定提示措辞的 eval 结果、驱动特定安全检查的生产事故,以及驱动特定优化的性能剖析。代码注释很详尽——许多注释包含 eval case 编号和前后对比测量——但近两千个文件中的详尽注释,本身也是阅读负担。


智能体系统正在走向何处

从 Claude Code 的模式中可以看到四个趋势,它们指向这个领域的发展方向。

MCP 作为通用协议

第 15 章把 Claude Code 描述为最完整的 MCP client 之一。其意义不在于 Claude Code 的实现,而在于 MCP 本身的存在。用于工具发现和调用的标准化协议意味着,为一个智能体构建的工具可以与任何智能体配合使用。生态效应很明显:一个为 Postgres 构建的 MCP server,一旦建成,就可以服务每一个会说 MCP 的智能体。开发者在工具集成上的投入是可迁移的。

这对智能体构建者的启示是:如果你正在定义自定义工具协议,那你很可能正在犯错。MCP 已经足够好,而且还在变得更好;标准协议的生态优势会随时间复利增长。构建一个 MCP client,参与规范贡献,让协议通过社区反馈持续演进。

多智能体协调

Claude Code 的子智能体系统(第 8 章)、任务协调(第 10 章)和 fork 机制(第 9 章)是多智能体模式的早期实现。它们解决了具体问题——缓存共享、并行探索、结构化验证——但也揭示了根本挑战:协调开销。

智能体之间的每条消息都会消耗 token。每个 fork 都共享缓存,但也会增加一条对话分支,而父级最终必须协调合并这条分支。Task 系统的状态机(queued、running、completed、failed、cancelled)是一套协调机器,它增加复杂度,但不增加能力。随着智能体变得更强,压力会从“我们如何协调多个智能体?”转向“我们如何让一个智能体足够强,以至于不需要协调?”

当前证据表明,两种方法都会共存。简单任务会使用单智能体。复杂任务会使用协调式多智能体系统。工程挑战在于把协调开销降到足够低,使得交叉点真正有利于并行工作中的多智能体,而不仅仅有利于那些复杂的任务。

持久记忆

Claude Code 的记忆系统是持久智能体记忆的第 1 版。基于文件的设计、四类型分类法、LLM 驱动的召回、陈旧性系统,以及面向长时间运行会话的 KAIROS mode,都是针对一个将显著演化的问题所给出的第一代解法。

未来的记忆系统很可能会加入结构化检索(当前系统检索整个文件;未来系统可能检索具体事实)、跨项目迁移学习(适用于所有地方的用户偏好,以及不适用于所有地方的项目约定),以及协作记忆(第 11 章的团队记忆是第一步,但同步、冲突解决和访问控制都还很简陋)。

悬而未决的问题是,基于文件的方法能否扩展。每个项目 200 条记忆时,它可以工作。每个项目 2,000 条记忆时,Sonnet 侧查询的清单会变得过大,整合会变得过于昂贵,索引也会超过上限。随着使用量增长,文件优于数据库的架构押注将面临最严峻的考验。

自主运行

KAIROS mode、后台记忆抽取智能体、auto-dream 整合、推测工具执行——这些都是走向自主运行的步骤。智能体会在没有被请求时做有用的工作:它会记住那些你忘记要求它记住的内容,会在你睡觉时整合自己的知识,会在当前响应还没完成时就开始执行下一个工具。

趋势很清楚。未来的智能体会更少被动反应,更多主动行动。它们会注意到用户尚未描述的模式,建议用户尚未请求的修正,并在没有显式 /remember 命令的情况下维护自己的知识。Claude Code 的记忆系统,凭借其后台抽取安全网和通过提示工程塑造的“应该保存什么”启发式,是这个未来的原型。

约束是信任。自主运行要求用户相信,智能体在无人看管时会做正确的事。基于文件的记忆、可观察的钩子系统、陈旧性警告、权限对话框——这些存在的原因都是:信任必须被赢得,而不是被假定。通往更自主智能体的道路,要经过更透明的智能体。


结语

十七章。六个核心抽象。生成器循环位于中心,工具向外延展,记忆沿时间回溯,钩子守护边界,渲染引擎把这一切翻译成屏幕上的字符,而 MCP 将它连接到代码库之外的世界。

Claude Code 中最深层的模式,不是任何单一技术。它是一个反复出现的决策:把复杂度推向边界。渲染系统把复杂度推向池和 diff——在管线内部,一切都是整数比较。输入系统把复杂度推向 tokenizer 和快捷键解析器——在处理器内部,一切都是类型化动作。记忆系统把复杂度推向写入协议和召回选择器——在对话内部,一切都是上下文。智能体循环把复杂度推向终止状态和工具系统——在循环内部,它只是:流式生成、收集、执行、追加、重复。

每个边界都吸收混沌并导出秩序。原始字节变成 ParsedKey。Markdown 文件变成被召回的记忆。MCP JSON-RPC 变成 Tool 对象。钩子退出码变成权限决策。在每个边界的一侧,世界是混乱的——五种键盘协议、脆弱的 OAuth server、陈旧记忆、不受信任的仓库钩子。在另一侧,世界是类型化的、有边界的,并且被穷尽处理。

如果你正在构建智能体系统,这就是可以迁移的教训。不是那些具体技术——你可能并不需要基于池的渲染、KAIROS mode 或八种 MCP 传输。但原则是:定义你的边界,在那里吸收复杂度,并保持边界之间的一切整洁。边界是工程困难之处。内部是工程愉悦之处。为愉悦的内部而设计,把你的复杂度预算投入到边缘。

源码是开放的。螃蟹的钳子里夹着地图。去读它吧。