Hooks & Plugins — 行为拦截与扩展
上下文视角:Hooks 以编程方式在上下文流动的关键节点执行用户逻辑——拦截操作、修改上下文内容、记录日志。Plugins 是 Agent 的第三方扩展包,通过编程接口扩展能力,远不只是挂载 hooks。
前面的节点讲了各种往上下文里"放东西"的方式:System Instructions 放规则,MCP 放工具,Skills 放知识,CLI Tools 放能力。
但有些需求,靠写文件、写配置这种静态方式解决不了:
- Agent 执行
rm -rf之前,你想自动拦截。 - Agent 完成任务后,你想收到桌面通知。
- Session 压缩时,你想改写压缩指令,强制保留特定上下文。
- 构建 system prompt 时,你想根据运行时状态动态追加规则。
- 每次工具调用,你想记录日志到外部系统。
这些需求需要一种新机制:不是静态地放内容,而是编程式地介入上下文流动过程。
Hooks — 事件驱动的行为控制
Agent 运行时会经过一系列生命周期事件——开始 session、用户提交 prompt、调用工具、完成任务。Hook 让你在这些事件上挂载自己的逻辑。
跟 HTTP middleware 的模式一样:拦截 → 检查 → 放行、拒绝或修改。
上面展示的是 hooks 最直观的场景——拦截工具调用。但 hooks 的用途远不止于此:桌面通知、token 统计、压缩控制、动态注入规则,都是 hooks。拦截只是其中一种。
守门人模式
Agent 干活很快,能力也强——但它没有"这个操作太危险,我该先问一下"的本能。 你需要一个守门人,在它真正执行高风险操作之前拦住它。
- 读操作 (
ls,cat):直接放行。 - 低风险写操作 (
npm install):可以放行,但最好静默检查一下,比如npm install前先确认package-lock.json没变化。 - 高风险写操作 (
rm -rf,git push --force):必须拦住,弹个窗问你“确定吗?”
这就是 Hook 的核心价值:在 Agent 和真实世界之间,加一道可编程的防火墙。
生命周期事件
不同 Agent 工具支持的事件集合和命名各异。以下是代表性类型——工具调用前后和 Agent 停止最为通用;会话开始、提示提交、系统指令变换和会话压缩则因工具而异:
| 事件类型 | 时机 | 你能做什么 |
|---|---|---|
| 会话开始 | Session 启动 | 初始化环境、注入环境变量 |
| 提示提交 | 用户提交 prompt | 拦截或改写 prompt |
| 工具调用前 | 工具即将执行 | 拦截危险命令、自动审批安全操作 |
| 工具调用后 | 工具执行完毕 | 日志记录、结果校验、修改返回值 |
| 系统指令变换 | 构建 system prompt 时 | 动态修改 system prompt 内容 |
| 会话压缩 | 上下文即将被压缩 | 注入压缩指令、强制保留特定信息 |
| Agent 停止 | 任务完成或暂停 | 通知、统计、清理 |
注意"系统指令变换"和"会话压缩"——这两类 hook 直接修改上下文内容。所以 hooks 不仅仅是"旁路拦截",它也能"往上下文里放东西"。
"会话压缩" hook 是其中较特殊的一类——并非所有工具都支持,但如果你用的工具有这个功能,值得好好利用。
长对话中 agent 自动压缩早期历史时,你的核心约束可能被压没——agent 在后半段突然"忘了"规则,不是它笨,是那条规则已经不在上下文里了。压缩 hook 让你从机制层面解决这个问题:指定哪些信息必须原样保留,哪些可以被摘要。比起靠人工每隔几轮重申一次约束,这是更可靠的做法。
事件处理模式
不同 Agent 工具的 hook 实现方式差异很大——有的用 shell 脚本配合 JSON 通信,有的用 TypeScript 异步函数,有的用声明式配置。但概念模式是通用的:
核心要素:
- 事件:在哪个生命周期节点挂载
- 匹配:过滤条件——只处理特定工具或操作(比如只拦截 bash 命令,不管文件读取)
- 处理:执行什么逻辑——可以是 shell 命令、一段代码、甚至启动一个子 agent 来判断
Hook 的两种形式
同一套机制,可以做两种截然不同的事:
旁路式——Agent 和 LLM 完全不知情。Hook 在事件流旁边默默执行,不修改任何上下文。桌面通知、日志记录、token 统计——纯粹的副作用。
介入式——直接改变上下文内容或 Agent 行为。Hook 拦截工具调用并阻断,或者修改 system prompt 注入新规则,或者改写工具的输入参数。这些改变会被 LLM "看到"(体现在上下文中),虽然 LLM 不知道变化来自 hook。
Plugins — 第三方扩展包
如果说 Hook 是一个事件处理器,Plugin 就是一个完整的扩展包。
Plugin 的能力范围远超"hooks 的集合"。一个 Plugin 可以同时:
- 挂载多个 Hooks(生命周期事件处理)
- 注册新的 工具(扩展 Agent 的可用工具集)
- 提供 Slash Commands(快捷指令,部分工具支持)
- 注入 Skills(领域知识,部分工具支持)
- 提供认证流程、输出样式、配置修改等
说白了,Plugin 就是一个拥有编程能力的 Agent 扩展点——它能做的事取决于 Agent 工具暴露了哪些接口。
安装与分发
两种主流模式正在平行演进:
Marketplace 模式:从集中式仓库浏览和安装预打包的插件。官方仓库和社区仓库并存,一键安装,自动生效。适合通用场景——代码审查、前端设计、TDD 工作流等。
本地文件 / 包管理器模式:直接写代码文件放到约定目录,或通过包管理器安装。适合个人定制——你的项目有独特需求,社区没有现成的插件。有些框架还会自动扫描 plugins/ 目录下的所有文件——放进去就生效,不需要手动注册。
两种模式不互斥。你可以同时用 marketplace 装的通用插件和自己写的定制插件。
生态现状
Plugin 生态还在早期。各 Agent 工具的插件接口、分发机制、安全模型都在快速迭代。方向很清楚:用户和社区可以通过编程方式扩展 Agent 的能力,不再只能等开发者更新。
这跟浏览器扩展、编辑器插件的演进路径一样——先有核心功能,然后开放扩展接口,最后生态爆发。
Hooks + Plugins vs. Skills vs. MCP
三者都能扩展 Agent 的能力,区别在于方式和能力边界:
| Skills | MCP | Hooks / Plugins | |
|---|---|---|---|
| 做什么 | 注入知识 | 扩展工具 | 编程式扩展(拦截 + 修改 + 注册 + …) |
| 实现方式 | 声明式(写文档) | 协议式(实现 server) | 编程式(写代码) |
| 触发方式 | 主动加载 | LLM 决定调用 | 事件自动触发 |
| LLM 知道它存在吗 | ✅ 内容直接可见 | ✅ 工具定义可见 | ❌ 机制本身不可见 |
| 能修改上下文吗 | ✅ 追加到 system prompt | ✅ 工具返回值进入上下文 | ✅ 可修改 system prompt、工具输入输出 |
注意最后一行:Hooks 修改的结果会体现在上下文中(LLM 能看到修改后的 system prompt),但 Hook 本身的存在对 LLM 不可见。
Skills 告诉 LLM "该怎么做"。MCP 给 LLM "能做什么"。Hooks / Plugins 以编程方式控制 Agent 的行为边界——LLM 不知道有代码在替它做决定。
三者可以组合。一个 Plugin 里同时注入 Skill(Git 规范知识)+ 挂载 Hook(commit 前自动 lint)是完全正常的做法。
信任与权限分级
如果你关心安全,会发现一条贯穿好几个节点的线索:权限是分级的。
内置工具最安全,Agent 开发商帮你兜底。 MCP 其次,权限靠协议约束,但你得自己看一眼服务商的节操。 Hooks 和 Plugins 权限最大,也最危险。它们就是代码,能干任何事。
权限越大,审查责任越重。用内置工具可以无脑,用 MCP 得瞅一眼权限声明,用 Hooks 和 Plugins 就得像做 code review 一样,逐行审查。
本节小结
- 上下文流动:Hooks 在上下文流动的关键节点执行——工具调用前后、system prompt 构建时、session 压缩时。它既能做旁路(不影响上下文),也能直接修改上下文内容。Plugin 打包了多种上下文操控手段。这是用户可用的最细粒度的上下文控制机制。
- 风险:Hook 和 Plugin 代码有极高的系统权限——一个写坏的工具调用前 hook 可以阻断所有工具调用,让 Agent 彻底瘫痪。这不是 prompt 级别的"写错了改就好",而是代码级别的"写错了就崩"。Plugin 的来源也需要审查——你在安装一段会在每次 Agent 运行时执行的代码。
- 可审计性:Hook 执行日志是最细粒度的可观测性数据——每个事件、每次拦截、每个决策都有时间戳。但需要你主动实现日志逻辑。
下一节进入「知识喂养」——回顾前面所有载体,统一回答"我有一堆知识,怎么让 Agent 知道这些"。