解剖一只"爪子":84个Hook构建的编排层
第一个Hook只花了四分钟编写。它阻止模型在纯Anthropic工作流中推荐OpenAI产品。两个月后,这一个Hook变成了84个。84个Hook连接着43个技能、19个专用智能体和30个库模块。在某个节点上,这些脚本集合不再只是一组脚本,而是演变成了一个编排层。
我并非有意如此设计。没有人会坐下来说”我要构建15,000行智能体基础设施”。你解决一个问题,然后再解决一个,接着解决这些问题之间的交互问题。当你注意到架构的存在时,它已经形成了。
Andrej Karpathy也注意到了这一点。2026年2月,他将”Claws”描述为一个新的计算层:编排、调度、上下文管理和工具路由,构建在LLM智能体之上,正如智能体构建在LLM之上一样。1这一框架概念将实践者一直在构建却未曾命名的东西具象化了。本文是对这样一个系统的解剖:它包含什么、如何成长、哪里有效、哪里失败。
TL;DR
Karpathy的”Claws”层描述的是构建在智能体CLI之上的编排系统。我在Claude Code上用两个月时间有机地构建了一个:84个Hook覆盖15种事件类型、43个技能、19个智能体和30多个库模块。该系统清晰地映射到Claws的五大功能(编排、调度、上下文管理、工具路由、质量执行),但存在一个显著缺口(声明式工作流定义)。关键发现:规划与执行的分离是基于Hook编排的自然属性,而非设计目标。Lattner的观察——“判断力和抽象能力仍然是核心,而AI自动化的是实现”——直接映射到Hook架构:治理Hook行使判断,自动化Hook执行实现。
Claws分类体系
Karpathy的描述确定了Claws层执行的五大功能。每项功能在我过去两个月在Claude Code上构建的Hook系统中都有直接对应。1
| Claws功能 | 描述 | 实现 |
|---|---|---|
| 编排 | 协调多个智能体实现目标 | Ralph自主循环、审议系统 |
| 调度 | 确定任务的执行时机 | Cron Hook、activity-heartbeat.sh、夜间安全扫描 |
| 上下文管理 | 跨轮次维护相关信息 | 提示词调度器、理念注入器、记忆胶囊 |
| 工具路由 | 将工具调用引导至适当的处理器 | 84个Hook覆盖PreToolUse、PostToolUse、UserPromptSubmit事件 |
| 质量执行 | 验证输出是否符合标准 | 质量门控、证据要求、7个审查智能体 |
这一分类体系之所以有用,是因为它分离了实践者倾向于混合构建的关注点。我早期的Hook将上下文管理与质量执行混在一起。成本跟踪Hook既注入预算上下文(上下文管理),又阻止昂贵操作(质量执行)。将它们分离为不同的Hook提高了可靠性,因为每个Hook可以独立失败而不破坏另一个功能。
完整系统
截至2026年2月的数据:
| 组件 | 数量 | 用途 |
|---|---|---|
| Hook | 84 | 覆盖15种Hook事件类型的事件驱动函数 |
| 技能 | 43 | 按名称调用的可复用能力模块 |
| 智能体 | 19 | 用于审查、探索、开发的专用子智能体 |
| 库模块 | 30+ | 共享的Python和Bash工具集 |
| 代码行数 | ~15,000 | 分布在Hook、技能、智能体、库和配置中 |
Hook在各事件类型上的分布揭示了编排复杂度的集中区域:
| 事件类型 | Hook数量 | 示例 |
|---|---|---|
| UserPromptSubmit | 9(通过调度器) | 上下文注入、成本跟踪、使用分析 |
| PreToolUse:Bash | 12 | 安全扫描、凭证检查、敏感命令拦截 |
| PostToolUse:Bash | 6 | 输出扫描、部署验证 |
| PreToolUse:Write | 4 | 凭证检测、路径验证 |
| PreToolUse:Edit | 3 | 模式强制执行 |
| PreToolUse:Task | 3 | 递归守卫、生成预算 |
| PreCompact | 1 | 记忆胶囊、死循环检测 |
| SessionStart | 1 | 环境初始化 |
| WorktreeCreate | 1 | 隔离分支的环境设置 |
| WorktreeRemove | 1 | 清理前的安全检查 |
| 其他事件类型 | ~43 | 分布在PreToolUse:Read、PostToolUse:Write、PreToolUse:WebFetch、NotebookEdit及另外8种事件类型中 |
UserPromptSubmit承载最大权重,因为它在每条用户消息时触发。调度器(prompt-dispatcher.sh)在每个提示词上按顺序运行九个Hook:安全过滤、分析、使用跟踪、系统监控、目标注入、时间估算拦截、上下文注入、记忆主题注入和上下文压力监控。2
每个Hook都会增加延迟。九个顺序执行的Hook总共增加了实测200毫秒的每次提示延迟。调度器按顺序(非并行)运行它们,因为早期测试中并发Hook写入共享JSON状态文件导致了数据损坏。两个Hook同时写入jiro.state.json产生了截断的JSON,破坏了所有下游Hook。顺序执行更慢但更安全。200毫秒的开销对用户不可感知,因为人类打字速度才是瓶颈,而非Hook延迟。
系统如何成长
增长并非线性的。它遵循了”问题-解决方案-整合”的循环模式。
第一阶段:单用途Hook(第1-2周)。 每个Hook解决一个问题。enforce-opus-model.sh拦截非Opus模型请求。no-time-estimates.sh从响应中移除工作量估算。filter-sensitive.sh捕获工具调用中的凭证。这些Hook独立运作,没有任何Hook知道其他Hook的存在。
第二阶段:协调问题(第3-4周)。 Hook开始互相干扰。凭证过滤器拦截了合法的API调用。模型强制器与子智能体生成产生冲突。解决方案是:调度器。一个单一入口点(prompt-dispatcher.sh)取代了七个独立的UserPromptSubmit Hook,通过缓存的stdin管道控制执行顺序并共享状态。
第三阶段:复合能力(第5-8周)。 单个Hook组合成系统。质量循环将预工具Hook(在问题发生前捕获它们)和后工具Hook(在结果产生后验证它们)通过共享状态文件(jiro.state.json)连接起来。审议系统使用递归守卫、生成预算和共识协议来协调多个智能体,避免无限循环。Ralph(自主开发循环)在单一编排管线中将PRD文件连接到Claude生成、测试验证和代码审查。
第四阶段:自我感知(第9周以后)。 系统变得足够庞大,需要工具来理解自身。跨Hook系统的语义搜索(/find技能)让智能体能按用途而非文件名发现Hook。性能监控(/perf技能)跟踪系统自身的开销是否在降低机器性能。上下文压力监控器在编排层注入的上下文消耗过多模型上下文窗口时发出警告。
从单用途Hook到自我监控基础设施的演进,映射了Chris Lattner在审查Claude C编译器项目时发现的模式:”优秀的软件依赖于判断力、沟通和清晰的抽象。AI放大了这一点。”3 Hook系统的架构揭示了同样的真理。最有价值的Hook不是那些自动化任务的Hook,而是那些编码了关于何时以及如何自动化任务的判断力的Hook。
判断Hook与自动化Hook
Lattner对Claude C编译器的审查区分了AI擅长自动化的部分(实现)和本质上仍属于人类的部分(判断力和抽象)。3这一区分直接映射到Hook系统。
判断Hook决定某件事是否应该发生。它们编码的是策略,而非流程。
| Hook | 判断 |
|---|---|
quality-gate.sh |
“这项工作是否足够完整可以报告?” |
filter-sensitive.sh |
“这个命令是否有暴露凭证的风险?” |
recursion-guard.sh |
“智能体是否生成了过多的子智能体?” |
context-pressure.sh |
“上下文窗口是否太满而无法有效继续?” |
cost-gate.sh |
“此会话是否已超出预算阈值?” |
自动化Hook执行预定操作。它们编码的是流程,而非策略。
| Hook | 自动化 |
|---|---|
inject-context.sh |
将日期、时间、工作目录、分支注入每个提示词 |
track-usage.sh |
记录Token计数和会话指标 |
sysmon-snapshot.sh |
捕获CPU、内存、磁盘状态 |
memory-capsule-inject.sh |
压缩后恢复上下文 |
activity-heartbeat.sh |
更新会话活跃指示器 |
判断Hook更难编写、更难测试,但更有价值。quality-gate.sh需要7种命名的失败模式、6项证据标准和一个模糊语言检测器。inject-context.sh只需要5行Bash代码。但两者都不可或缺。自动化Hook提供判断Hook评估所需的数据。sysmon-snapshot.sh(自动化)向性能监控器输入数据,后者决定是否建议降低智能体数量(判断)。
比例关系很重要。在健康的编排层中,判断Hook应多于自动化Hook。如果大多数Hook只是注入数据或记录指标,系统自动化做得好但治理做得差。当前系统的验证计数:35个判断Hook,44个自动化Hook,大约4:5。自动化仍然领先。该比例从约1:6开始(几乎全是注入和日志Hook),在两个月内随着遇到纯自动化无法防止的故障而添加治理约束,逐渐向判断方向偏移。该比例尚未达到均衡,这本身就是一个有用的信号:这个系统的治理仍然少于其自动化。
规划与执行的分离
Boris Tane的”我如何使用Claude Code”一文在Hacker News上获得了936分,文中描述了一种工作流模式:将规划与执行分离。4用一个Claude会话进行规划(研究、大纲、设计),然后用一个新的会话接收规划作为结构化输入来执行。这一模式引起了共鸣,因为它解决了一个实际问题:规划和执行争夺上下文窗口空间。
Hook系统通过不同的路径得出了相同的分离。审议系统生成专用智能体来研究和讨论方案。输出是结构化的PRD(产品需求文档),包含故事、验收标准和验证类型。Ralph循环读取PRD并生成新的Claude实例来实现每个故事。规划智能体从不实现。实现智能体从不规划。
这种分离并非设计目标,它源于两个独立约束:
-
上下文窗口压力。 规划需要读取大量文件并探索选项。实现需要专注于当前任务的上下文。将两者放在同一个上下文窗口中意味着两者都得不到足够空间。分离的会话让每个阶段都拥有完整的上下文。
-
质量验证的独立性。 如果同一个智能体既规划又实现,它就无法客观地根据计划验证自己的实现。一个只拥有计划和代码的新智能体提供了独立验证。Ralph循环强制执行这一点:实现智能体运行测试,但三个独立的审查智能体(正确性、安全性、规范性)验证结果。
Tane的手动工作流与自动化Hook系统之间的趋同表明,规划-执行分离是智能体系统的自然属性,而非仅仅是实践者的偏好。任何管理上下文窗口并验证输出的系统最终都会将规划与执行分离,因为替代方案(在一个上下文中完成两者)会在两个阶段都产生更差的结果。
Hook系统的不足之处
该架构有三个显著弱点,专门构建的编排框架可以解决这些问题。
没有声明式工作流定义。 每个工作流都以命令式方式编码在Bash脚本中。Ralph循环是1,320行Bash代码,编码了特定的序列:读取PRD、选择故事、收集上下文、生成Claude、运行测试、运行审查、处理失败、更新状态。更改工作流意味着编辑Bash。声明式系统会将工作流定义为数据(YAML、JSON),由解释器执行。声明式工作流更容易修改、组合和可视化。命令式脚本初期更容易编写,但随着增长更难维护。
Hook排序脆弱。 提示词调度器按硬编码顺序运行Hook。将memory-capsule-inject.sh移到inject-context.sh之前会破坏胶囊注入,因为它依赖于inject-context.sh解析的会话ID。这些依赖关系是隐式的(编码在调度器的排序中),而非显式的(声明为Hook之间的依赖关系)。专门构建的系统会将Hook依赖表达为DAG并进行拓扑排序。
没有工作流可视化。 有84个Hook,理解任何用户操作的完整执行路径需要手动阅读调度器代码并追踪Hook链。没有工具可以展示”当用户输入消息时,这9个Hook按此顺序触发,Hook 3调用库函数X并写入状态文件Y”。系统通过日志可观察,但不通过结构可观察。专门构建的编排框架会提供Hook依赖、数据流和执行路径的可视化图表。
这些弱点有一个共同原因:系统是从解决单个问题中有机生长出来的,而非作为一个连贯的编排层来设计的。有机生长产生的系统是可工作的(所有84个Hook在生产环境中均正常运行),但难以作为整体来推理。这一权衡是真实的:预先设计编排层会产生更好的结构但更差的能力,因为许多能力(记忆胶囊、输出白名单、生成预算)都是为响应那些无法预先预测的故障而发明的。
实践者应该带走什么
如果您正在智能体CLI之上构建编排层,本系统的三个模式可以直接迁移。
从调度器开始,而非单个Hook。 最大的架构改进是用一个按顺序运行的调度器替换了七个独立的UserPromptSubmit Hook。如果您预计任何事件类型上会有超过三个Hook,请先构建调度器。花30分钟编写调度器可以节省数小时的Hook交互问题调试。最小化模式:
#!/bin/bash
# dispatcher.sh — sequential hook execution with shared stdin
HANDLERS=("inject-context.sh" "track-usage.sh" "quality-gate.sh")
HOOK_DIR="$(dirname "$0")/handlers"
INPUT=$(cat) # Cache stdin once (each handler gets the same input)
for handler in "${HANDLERS[@]}"; do
[ -x "$HOOK_DIR/$handler" ] && echo "$INPUT" | "$HOOK_DIR/$handler"
done
将此单一调度器注册为您的Hook入口点。在构建时向数组中添加处理器。每个处理器读取相同的缓存stdin(Hook事件负载)并独立写入stdout。
尽早分离判断与自动化。 编写新Hook时,请问自己:”这个Hook是决定某事是否应该发生,还是执行预定操作?”判断Hook需要更多测试、更多边界情况处理和更多迭代。自动化Hook需要可靠性和性能。将两者同等对待会导致判断Hook测试不足和自动化Hook过度工程化。
让规划-执行分离自然涌现。 不要在第一天就强制分离。构建最简单的可工作方案。当您注意到智能体的上下文窗口对于规划和实现来说都太满时,将它们分开。当您注意到智能体无法客观验证自己的工作时,添加独立的审查智能体。当约束要求时,分离会显得理所当然。
基于Hook的方法相对于专门构建的编排框架有一个优势:零承诺。每个Hook都是独立的。您可以采用一个Hook、十个Hook或八十四个Hook。您可以删除任何Hook而不破坏其他Hook(前提是维护调度器)。没有需要学习的框架,没有需要管理的依赖,没有需要运维的运行时。编排层就是文件。
Karpathy称之为新的计算层。实现比名称更古老。自从实践者第一次编写Shell脚本来包装智能体CLI调用以来,他们就一直在构建Claws。Shell脚本和编排层之间的区别不是类型上的区别,而是你解决了多少问题、以及这些解决方案中有多少不得不互相解决的区别。
参考来源
-
Andrej Karpathy,”Claws”讨论,2026年2月,x.com/karpathy/status/2024987174077432126。经由Simon Willison转载,simonwillison.net/2026/Feb/21/claws/。 ↩↩
-
上下文注入架构详见”Context Is Architecture“。 ↩
-
Chris Lattner,”The Claude C Compiler: What It Reveals About the Future of Software“,Modular博客,2026年2月。经由Simon Willison转载,simonwillison.net/2026/Feb/22/ccc/。 ↩↩
-
Boris Tane,”How I use Claude Code“,boristane.com,2026年2月。Hacker News上获得936分、569条评论。 ↩