第 8 章 · Hooks 与生命周期扩展
04 · Hook 系统的产品价值
Hook 不是炫技——它让 agent 的行为更稳定、更规范、更容易治理。
Hook 解决的三类问题
回顾这一章的三个 hook 示例,它们分别解决了三类问题:
| 问题 | Hook | 时机 |
|---|---|---|
| 代码格式不规范 | auto_format | after_edit |
| 操作不可追溯 | audit_log | after_tool |
| 危险操作需拦截 | before_tool skip | before_tool |
这三类问题在真实的 coding agent 产品中普遍存在。Claude Code 用 hooks 来实现"编辑后自动 lint"、"提交前检查"等能力。Codex CLI 用类似的机制做沙箱隔离和操作审计。
Hook 和其他系统的关系
到第 8 章结束时,agent 已经有了多个可扩展的系统:
| 系统 | 解决什么 | 扩展方式 |
|---|---|---|
| 工具(第 3 章) | agent 能做什么 | 注册新 Tool |
| 权限(第 4 章) | 什么操作需要确认 | PermissionRule 配置 |
| 事件(第 5 章) | agent 的状态如何展示 | 注册 EventListener |
| 记忆(第 6 章) | agent 记住什么 | ContextManager 配置 |
| Skill(第 7 章) | 不同任务怎么工作 | 注册新 Skill |
| Hook(第 8 章) | 执行过程中插入逻辑 | 注册新 Hook |
每个系统都是正交的——它们解决不同的问题,可以独立使用。RunAgentOptions 是它们的统一入口:
export interface RunAgentOptions {
maxIterations?: number;
permissionGuard?: PermissionGuard; // 第 4 章
emitter?: AgentEmitter; // 第 5 章
contextManager?: ContextManager; // 第 6 章
projectRules?: string; // 第 6 章
skill?: Skill; // 第 7 章
hookManager?: HookManager; // 第 8 章
}所有扩展点都是可选的。不传就保持默认行为——这在教学项目中很重要,每一章的代码都能独立运行。
Hook 的设计取舍
为什么 hook 用类而不是函数数组? HookManager 用 Map 按 timing 分组,比一个大数组更高效——不需要在每次触发时过滤。
为什么 hook handler 是异步的? 大部分 hook 需要做 I/O(执行格式化命令、写日志文件)。同步 hook 无法支持这些操作。
为什么 after_edit 是单独的时机? 可以只用 after_tool,然后在 handler 里检查 ctx.tool === "write_file"。但 after_edit 更语义化——看到 after_edit 就知道这是"文件编辑后的处理",不需要看 handler 内部的判断逻辑。
版本更新
main.ts 的版本号从 v0.7.0 升级到 v0.8.0。
本章回顾
第 8 章实现了 hook 系统,让 agent 具备可扩展的生命周期能力:
| 新增 | 文件 | 作用 |
|---|---|---|
| Hook 类型 | src/hooks/types.ts | 定义 hook 的时机、上下文和结果 |
| HookManager | src/hooks/manager.ts | 注册和执行 hook 链 |
| 内置 hook | src/hooks/builtins.ts | 审计日志、自动格式化、编辑验证 |
| hook 集成 | src/agent.ts | 在四个时机调用 hook |
| hook 使用 | src/main.ts | 注册审计日志 hook |
加上之前章节的测试,整个项目现在有 168 个测试用例。
登录以继续阅读
解锁完整文档、代码示例及更多高级功能。