第 18 章:我们学到了什么
五个架构赌注
Claude Code 并非唯一的 Agentic 系统,也不是第一个。但它做出了五个架构赌注,使其在众多 Agent 框架中脱颖而出。在经历了近两千个文件和十七章的探讨之后,这些赌注值得审视。
赌注 1:生成器循环优于回调机制
大多数 Agent 框架提供的是流水线模式:定义工具、注册处理器,然后由框架进行编排。开发者编写回调函数,而框架决定何时调用它们。
Claude Code 反其道而行之。query() 函数是一个异步生成器(async generator)——循环的控制权掌握在开发者手中。模型流式输出响应,生成器 yield 出工具调用,调用方执行这些调用并将结果追加回去,然后生成器继续循环。只有一个函数、一条数据流、一个所有交互必经的节点。生成器返回类型中的 10 种终止状态和 7 种延续状态编码了所有可能的结果。循环即系统。
这个赌注的核心假设是:即使单个生成器函数膨胀到 1,700 行,它也比分散的回调图更易于理解。研读源码后证明这一赌注是正确的。当你想了解会话为何结束时,只需查看一个函数。当你想添加新的终止状态时,只需在一个可辨识联合类型(discriminated union)中增加一个变体。类型系统强制要求穷尽处理。如果采用回调架构,这些逻辑将散落在数十个文件中,回调之间的交互将是隐式的,而非显式体现在控制流中。
赌注 2:基于文件的记忆优于数据库
第 11 章已详细论述了这一观点,但其架构意义远超记忆本身。选择使用纯 Markdown 文件而非 SQLite、向量数据库或云服务,本质上是在用能力换取透明度。数据库能支持更丰富的查询、更快的检索以及事务性保证,而文件无法提供这些。但文件提供了信任。
当用户在 vim 中打开 ~/.claude/projects/myapp/memory/MEMORY.md,并能确切看到 Agent 记住了关于他们的哪些内容时,他们与系统的关系就与那些必须询问 Agent“你记得什么?”并祈祷回答完整的用户有着本质不同。基于文件的设计使 Agent 的知识状态变得外部可观测,而不仅仅是自我报告。这比查询性能更重要。LLM 驱动的召回系统通过检索智能弥补了存储的简单性——Sonnet 侧查询从清单中筛选五条相关记忆,比嵌入相似度更精准,且无需任何基础设施。
赌注 3:自描述工具优于中央编排器
Agent 框架通常提供一个工具注册表:你在中央配置中描述工具,然后由框架将其呈现给模型。而在 Claude Code 中,工具是自描述的。每个 Tool 对象都携带自己的名称、描述、输入 Schema、Prompt 贡献、并发安全标志和执行逻辑。工具系统的职责不是向模型描述工具——而是让工具自我描述。
这一赌注在可扩展性上获得了回报。MCP 工具(第 15 章)通过实现相同的接口成为一等公民。对于模型而言,来自 MCP Server 的工具与内置工具没有区别。系统不需要单独的“MCP 工具适配器”层——封装过程会生成标准的 Tool 对象,此后现有的工具流水线即可对其进行处理:权限检查、并发执行、结果预算控制、Hook 拦截。
赌注 4:Fork Agent 以实现缓存共享
第 9 章介绍了 Fork 机制:子 Agent 启动时在其上下文窗口中包含父 Agent 的完整对话,从而共享父 Agent 的 Prompt Cache。这不是单纯的便利性优化——而是一个架构赌注,即缓存共享模型的价值足以抵消 Fork 生命周期管理的复杂性。
另一种方案——生成一个仅包含对话摘要的全新 Agent——虽然更简单但成本高昂。每个新 Agent 都要从零开始处理其上下文,付出全额成本。而 Forked Agent 可以免费获得父 Agent 的缓存前缀(Input Token 享受 90% 折扣),这使得为小型任务生成 Agent 变得经济可行:记忆提取、代码审查、验证流程。后台记忆提取 Agent(第 11 章)在每次查询循环轮次后运行,其成本之所以微乎其微,正是因为它共享了父 Agent 的缓存。如果没有基于 Fork 的缓存共享,该 Agent 的成本将高得令人望而却步。
赌注 5:Hooks 优于插件
大多数扩展系统使用插件——即在宿主进程中注册并运行的代码。Claude Code 则使用 Hooks——在生命周期节点运行的外部进程,通过退出码以及 stdin/stdout 上的 JSON 进行通信。
这一赌注认为进程隔离的价值高于其开销。插件可能导致宿主崩溃,而 Hook 只会导致自身进程崩溃。插件可能向宿主堆内存泄漏,而 Hook 的内存随其进程消亡。插件需要一套必须版本化和维护的 API 表面。而 Hook 只需要 stdin、stdout 和退出码——这套协议自 1971 年以来一直保持稳定。
开销是真实存在的:每次 Hook 调用都产生一个进程,这会消耗毫秒级的时间,而进程内回调则不会。内部回调的 -70% 快速路径(第 12 章)表明系统清楚这一成本的重要性。但对于外部 Hooks——用户脚本、团队 Linter、企业策略服务器——隔离保证使系统扩展更安全。企业可以部署基于 Hook 的策略执行机制,而无需担心格式错误的 Hook 脚本会导致开发者的会话崩溃。
哪些可迁移,哪些不可迁移
并非 Claude Code 中的所有模式都具有普适性。有些是规模、资源或特定约束的产物,其他 Agent 构建者未必面临相同情况。
可迁移至任何 Agent 的模式
生成器循环模式。 任何需要流式响应、处理工具调用和管理多种终止状态的 Agent,都能从显式循环中受益,而不是将其隐藏在回调之后。可辨识联合返回类型——精确编码循环停止的原因——是一种能够消除整类“为什么 Agent 停了?”调试问题的模式。
基于文件的记忆配合 LLM 召回。 具体实现细节属于 Claude Code,但原则——简单存储结合智能检索——适用于任何需要跨会话持久化知识的 Agent。四类分类法(用户、反馈、项目、参考)和可推导性测试(“这能否从当前项目状态重新推导出来?”)是可复用的设计启发式方法。
远程执行的非对称读写通道。 当读取是高频流而写入是低频 RPC 时,无论具体传输协议如何,将它们分离都是正确的做法。
用于搜索的位图预过滤器。 任何搜索大型文件索引的 Agent 都能从 26 位字母位图预过滤器中受益。每个条目四字节,每个候选项一次整数比较——性价比极高。
Prompt Cache 稳定性作为架构考量。 如果你的 Agent 使用支持 Prompt Caching 的 API,将稳定内容置于 Prompt 前面、易变内容置于后面就不再是优化——而是决定成本结构的架构决策。
Claude Code 特有的规模化模式
Forked 终端渲染器。 Claude Code Fork 了 Ink 并使用紧凑的类型数组、基于池的内部化(interning)和单元格级 Diff 重写了渲染流水线,因为它需要在终端中实现 60fps 的流式渲染。大多数 Agent 渲染到 Web 界面或简单的日志输出。只有当终端渲染是你的主要 UI 且你需要高频流式输出时,这种工程投入才有意义。
50+ 启动分析检查点。 当你拥有数十万用户且 0.5% 的采样就能产生统计显著数据时,这才有意义。对于较小的 Agent,简单的计时系统就足够了。
八种 MCP 传输类型。 Claude Code 支持 stdio、SSE、HTTP、WebSocket、SDK、两种 IDE 变体和 Claude.ai 代理,因为它必须集成各种部署拓扑。大多数 Agent 只需要 stdio 和 HTTP。
Hooks 快照安全模型。 在启动时冻结 Hook 配置且不再隐式重新读取,是为了防御特定威胁:恶意仓库代码在用户接受信任对话框后修改 Hooks。当你的 Agent 在包含不受信任 .claude/ 配置的任意仓库中运行时,这一点至关重要。仅在受信任环境中运行的 Agent 可以使用更简单的 Hook 管理。
复杂性的代价
近两千个文件。这换来了什么,又付出了什么?
文件数量作为复杂性指标具有误导性。其中大部分是测试基础设施、类型定义、配置 Schema 和 Fork 的 Ink 渲染器。实际的行为复杂性集中在少数高密度文件中:query.ts(1,700 行,Agent 循环)、hooks.ts(4,900 行,生命周期拦截系统)、REPL.tsx(5,000 行,交互式编排器)以及记忆系统的 Prompt 构建函数。
复杂性来自三个方面,各具不同特征:
协议多样性。 支持五种终端键盘协议、八种 MCP 传输类型、四种远程执行拓扑和七种配置作用域本身就非常复杂。每增加一种协议都是对代码库的线性增加,而非指数级增加——但总和很大。这种复杂性在 Brooks 意义上是偶然的:它来自环境(终端碎片化、MCP 传输演进、远程部署拓扑),而非来自要解决的问题本身。
性能优化。 基于池的渲染、位图搜索预过滤器、粘性缓存锁存器和推测性工具执行,每一项都以增加复杂性为代价换取可衡量的性能提升。这种复杂性是由度量证明合理的——每项优化之前都有识别瓶颈的性能分析数据。风险在于优化会累积并以使热路径更难修改的方式相互作用。
行为调优。 记忆系统的 Prompt 指令、陈旧警告、验证协议、“忽略记忆”反模式指令——这些不是代码复杂性。它们是 Prompt 复杂性,带来不同的维护负担。当模型行为在版本间发生变化时,通过 Eval 精心调优的 Prompt 指令可能需要重新调整。Eval 基础设施(在整个代码库中以用例编号和 Eval 分数引用)是防止回归的防线,但它需要持续投入。
该系统的维护负担是沉重的。阅读代码库的新工程师不仅要理解代码路径,还要理解促使特定 Prompt 措辞的 Eval 结果、促使特定安全检查的生产事故,以及促使特定优化的性能分析数据。代码注释很详尽——许多包含 Eval 用例编号和优化前后的测量值——但在近两千个文件中,详尽的注释本身就是一种阅读负担。
Agentic 系统的未来走向
从 Claude Code 的模式中可以观察到四个趋势,它们指明了该领域的发展方向。
MCP 作为通用协议
第 15 章将 Claude Code 描述为最完整的 MCP Client 之一。其重要性不在于 Claude Code 的实现——而在于 MCP 的存在。标准化的工具发现和调用协议意味着为一个 Agent 构建的工具可以与任何 Agent 配合使用。生态效应显而易见:一旦构建了 Postgres 的 MCP Server,它就可以服务于所有支持 MCP 的 Agent。开发者在工具集成上的投资是可移植的。
对 Agent 构建者的启示:如果你正在定义自定义工具协议,你可能犯了一个错误。MCP 已经足够好,而且还在不断改进,标准协议的生态优势会随时间复合增长。构建一个 MCP Client,为规范做贡献,让协议通过社区反馈演进。
多 Agent 协调
Claude Code 的子 Agent 系统(第 8 章)、任务协调(第 10 章)和 Fork 机制(第 9 章)是多 Agent 模式的早期实现。它们解决了特定问题——缓存共享、并行探索、结构化验证——但也揭示了根本挑战:协调开销。
Agent 之间的每条消息都会消耗 Token。每个 Fork 虽然共享缓存,但会增加一个父 Agent 最终必须调和的对话分支。Task 系统的状态机(queued、running、completed、failed、cancelled)是增加了复杂性但未增加能力的协调机制。随着 Agent 能力增强,压力将从“我们如何协调多个 Agent?”转移到“我们如何让单个 Agent 足够强大以至于不需要协调?”
目前的证据表明这两种方法将共存。简单任务使用单 Agent。复杂任务使用协调的多 Agent 系统。工程挑战在于将协调开销降低到足够低,使得交叉点有利于真正并行工作的多 Agent 模式,而不仅仅是因为任务复杂。
持久化记忆
Claude Code 的记忆系统是持久化 Agent 记忆的 v1 版本。基于文件的设计、四类分类法、LLM 驱动的召回、陈旧系统以及用于长时间运行会话的 KAIROS 模式,都是针对一个将显著演进的问题的第一代解决方案。
未来的记忆系统可能会增加结构化检索(当前系统检索整个文件;未来系统可能检索特定事实)、跨项目迁移学习(适用于所有地方的用户偏好、不适用的项目约定)以及协作记忆(第 11 章的团队记忆是第一步,但同步、冲突解决和访问控制都很基础)。
悬而未决的问题是文件方式是否具备可扩展性。在每个项目 200 条记忆时,它是有效的。在每个项目 2,000 条记忆时,Sonnet 侧查询的清单变得过大,整合变得过于昂贵,索引超出上限。随着使用量增长,文件优于数据库的架构赌注将面临最严峻的考验。
自主运行
KAIROS 模式、后台记忆提取 Agent、自动梦境整合、推测性工具执行——这些都是迈向自主运行的步骤。Agent 在被要求之前就做有用的工作:它记住你忘记让它记住的内容,在你睡觉时整合自己的知识,在当前响应完成之前就开始执行下一个工具。
轨迹是清晰的。未来的 Agent 将更少被动反应,更多主动出击。它们会发现用户未描述的模式,建议用户未请求的修正,并在没有显式 /remember 命令的情况下维护自己的知识。Claude Code 的记忆系统,凭借其后台提取安全网和经过 Prompt Engineering 的“保存什么”启发式规则,是这一未来的原型。
约束在于信任。自主运行要求用户信任 Agent 在无人值守时会做正确的事。基于文件的记忆、可观测的 Hook 系统、陈旧警告、权限对话框——所有这些存在都是因为信任必须被赢得,而非被假定。通往更自主 Agent 的道路,必经更透明的 Agent。
结语
十七章。六个核心抽象。中心是一个生成器循环,工具向外延伸,记忆向后追溯时间,Hooks 守卫边界,渲染引擎将一切转化为屏幕上的字符,MCP 将其连接到代码库之外的世界。
Claude Code 中最深刻的模式并非任何单一技术。而是反复出现的将复杂性推向边界的决策。渲染系统将复杂性推向池和 Diff——在流水线内部,一切都是整数比较。输入系统将复杂性推向 Tokenizer 和键绑定解析器——在处理器内部,一切都是类型化的 Action。记忆系统将复杂性推向写入协议和召回选择器——在对话内部,一切都是上下文。Agent 循环将复杂性推向终止状态和工具系统——在循环内部,仅仅是:流式输出、收集、执行、追加、重复。
每个边界吸收混乱并导出秩序。原始字节变为 ParsedKey。Markdown 文件变为被召回的记忆。MCP JSON-RPC 变为 Tool 对象。Hook 退出码变为权限决策。在每个边界的一侧,世界是混乱的——五种键盘协议、脆弱的 OAuth 服务器、陈旧的记忆、不受信任的仓库 Hooks。在另一侧,世界是类型化的、有界的且被穷尽处理的。
如果你正在构建 Agentic 系统,这就是可迁移的经验教训。不是具体的技术——你可能不需要基于池的渲染、KAIROS 模式或八种 MCP 传输。而是原则:定义你的边界,在那里吸收复杂性,并保持边界之间的一切整洁。边界是工程艰难之处。内部是工程愉悦之处。为愉悦的内部而设计,将你的复杂性预算投入到边缘。
源代码是开放的。寻宝图就在你手中。去读吧。