乐于分享
好东西不私藏

Agent越来越臃肿?插件+钩子让它无限扩展

Agent越来越臃肿?插件+钩子让它无限扩展

第十一章 · 扩展机制

前十章,我们一步步搭出了一个功能完备的 Agent:它能思考、能用工具、有记忆、有权限控制、能分工协作、能错误恢复、还能流式输出。

但有个问题越来越突出:每加一个功能,核心代码就要改一次。今天加搜索、明天加代码格式化、后天加 Git 操作……Agent 越来越臃肿,维护越来越痛苦。

这一章,我们给 Agent 装上扩展机制——钩子系统 + 插件架构 + MCP 协议,三套机制让 Agent 像乐高一样可插拔、可组合、无限扩展。

📌 本文你将掌握

✅ 理解扩展机制的三层架构(核心引擎→钩子→插件)

✅ 设计钩子系统:事件、优先级、管道模式、错误隔离

✅ 实现插件架构:清单声明 + 生命周期管理

✅ 理解 MCP 协议:Agent 与外部服务的标准化通信

✅ 对比 Claude Code 的扩展系统设计

✅ 掌握扩展机制的五大设计原则

01 为什么需要扩展机制

想象一个场景:你的 Agent 今天需要搜索网页,明天要格式化代码,后天要操作 Git 仓库。如果每次新增功能都改核心代码,很快就会变成一个维护噩梦

扩展机制遵循两个核心原则:

① 关注点分离:核心逻辑与扩展功能解耦,各自独立演化

② 开闭原则:对扩展开放,对修改关闭——不改核心代码就能加功能

扩展机制分三层,从下到上依次叠加:

┌─────────────────────────────────────────┐│              应用层 (Application)         ││  ┌───────────────────────────────────┐   ││  │       插件系统 (Plugin System)     │   ││  │  ┌──────┐  ┌──────┐  ┌──────┐    │   ││  │  │插件A │  │插件B │  │插件C │    │   ││  │  └──────┘  └──────┘  └──────┘    │   ││  └───────────────────────────────────┘   ││                    ▼                      ││  ┌───────────────────────────────────┐   ││  │       钩子系统 (Hook System)       │   ││  │  ┌─────┐ ┌─────┐ ┌───────────┐   │   ││  │  │前置  │ │后置  │ │错误/完成   │   │   ││  │  └─────┘ └─────┘ └───────────┘   │   ││  └───────────────────────────────────┘   ││                    ▼                      ││  ┌───────────────────────────────────┐   ││  │       核心引擎 (Core Engine)       │   ││  │  Tool Call │ LLM Call │ Agent Loop │   ││  └───────────────────────────────────┘   │└─────────────────────────────────────────┘

💡 一句话理解:核心引擎是"发动机",钩子系统是"传感网络",插件系统是"配件商城"。三层各管各的,互不干扰。

层次
职责
扩展粒度
核心引擎
基本运行循环
不可扩展
钩子系统
关键节点插入逻辑
细粒度(单个事件)
插件系统
工具+钩子+提示打包
粗粒度(完整能力)

02 钩子系统:在关键节点"埋伏兵"

钩子系统是扩展机制的基石。它在 Agent 生命周期中定义一系列事件,外部代码注册处理函数到这些事件上,触发时依次执行。

Agent 执行流程中的钩子触发点:

┌──────────┐    ┌──────────────┐    ┌──────────┐│ 接收任务  │    │  调用工具     │    │ 调用 LLM  │└────┬─────┘    └──────┬───────┘    └────┬─────┘     │                 │                 │     ▼                 ▼                 ▼┌──────────┐  ┌────────────┐  ┌──────────────┐│beforeAll │  │beforeTool  │  │beforeLlmCall │└──────────┘  └──────┬─────┘  └──────┬───────┘                     │               │                     ▼               ▼              ┌────────────┐  ┌──────────────┐              │  工具执行   │  │  LLM 响应    │              └──────┬─────┘  └──────┬───────┘                     │               │                     ▼               ▼              ┌────────────┐  ┌──────────────┐              │afterTool   │  │afterLlmCall  │              └──────┬─────┘  └──────┬───────┘                     │               │                     └───────┬───────┘                             │                    ┌────────┴────────┐                    ▼                 ▼              ┌──────────┐    ┌────────────┐              │ onError  │    │ onComplete │              └──────────┘    └────────────┘

钩子系统有三大核心特性:

① 优先级排序:每个钩子注册时指定优先级(数值越小越先执行),关键校验设高优先级,日志记录设低优先级

② 管道模式:钩子可以修改上下文对象,后续钩子接收的是修改后的数据(类似 HTTP 中间件的"洋葱模型")

③ 错误隔离:单个钩子失败不影响其他钩子,系统捕获异常后继续执行

管道模式的执行流程:

输入上下文    │    ▼┌─────────┐     ┌─────────┐     ┌─────────┐│ 钩子 A   │ ──► │ 钩子 B   │ ──► │ 钩子 C   │ ──► 输出│(优先级 5)│     │(优先级10)│     │(优先级15)│└─────────┘     └─────────┘     └─────────┘

钩子系统核心 API:

// 注册钩子hookSystem.register(event, handler, priority?);// 执行钩子链const context = await hookSystem.execute(event, initialData);// 移除钩子hookSystem.unregister(event, handler);hookSystem.unregisterByName(name);

完整使用示例——三个钩子协同工作:

// 创建钩子系统实例consthookSystem = newHookSystem();// 注册日志钩子(低优先级,后执行)hookSystem.register("beforeToolCall", loggingHook, 20);hookSystem.register("afterToolCall", loggingHook, 20);// 注册验证钩子(高优先级,先执行)hookSystem.register("beforeToolCall", validationHook, 5);// 执行时,先验证后记录日志constresult = await hookSystem.execute("beforeToolCall", {data: { toolName"search"args: { query"AI Agent" } },});

💡 执行顺序:validationHook(优先级 5)先跑校验 → loggingHook(优先级 20)后记日志。如果校验失败,可以拦截后续执行。

03 插件架构:把能力打包成"即插即用"

如果说钩子是"点",插件就是"面"——一个插件可以包含多个钩子 + 多个工具 + 系统提示,打包成一个可复用的整体。

每个插件通过一个清单文件声明自己的能力:

interfacePluginManifest {namestring;           // 插件名称(唯一标识)versionstring;        // 语义化版本号descriptionstring;    // 功能描述  hooks?: Array<{         // 钩子注册列表eventstring;handlerFunction;  }>;  tools?: ToolDefinition[];  // 提供的工具  systemPrompt?: string;     // 追加到系统提示的文本}

插件从注册到卸载,经历完整的生命周期:

注册插件┌───────────┐ load(加载) ── 读取清单,验证依赖└─────┬─────┘┌────────────┐init(初始化)── 分配资源,建立连接└─────┬──────┘┌──────────────┐activate(激活)── 注册钩子/工具,开始服务└──────┬───────┘┌────┴────┐运行中    停用/卸载┌──────────────┐ deactivate   ── 释放资源,取消注册└──────────────┘

两个实战插件示例:

CodeFormatter 插件:

const codeFormatterPlugin: PluginManifest = {  name: "code-formatter",  version: "1.0.0",  description: "代码格式化能力",  tools: [{    name: "format_code",    description: "格式化代码",    inputSchema: { ... },    execute: async (args) => { ... },  }],  hooks: [{    event: "beforeToolCall",    handler: (ctx) => { /* 验证输入 */ },  }],  systemPrompt: "你拥有代码格式化能力...",};

GitHelper 插件:

const gitHelperPlugin: PluginManifest = {  name: "git-helper",  version: "2.1.0",  description: "Git 版本控制辅助",  tools: [    { name: "git_status", ... },    { name: "git_commit", ... },  ],  hooks: [{    event: "afterToolCall",    handler: (ctx) => { /* 记录操作日志 */ },  }],  systemPrompt: "你具备 Git 版本控制辅助能力...",};

💡 关键点:注册这两个插件后,Agent 自动获得代码格式化和 Git 操作能力,核心代码不需要任何修改。这就是"对扩展开放,对修改关闭"。

04 MCP 协议:Agent 与外部世界的"普通话"

MCP(Model Context Protocol)是 Anthropic 提出的开放协议,定义了 AI 模型与外部工具/数据源之间的标准化交互方式

如果说我们的插件系统是 Agent 内部的扩展机制,那么 MCP 就是 Agent 之间以及 Agent 与外部服务之间的扩展机制:

┌───────────────┐       MCP 协议       ┌───────────────┐│               │ ◄══════════════════► │               ││  MCP 客户端    │                      │  MCP 服务器    ││  (AI Agent)   │                      │ (工具/数据源)  ││               │                      │               ││  发现能力      │ ── 列出工具/资源 ──► │               ││  调用工具      │ ── 执行工具调用 ───► │               ││  获取资源      │ ── 读取资源/数据 ──► │               ││  订阅变更      │ ◄── 状态变更通知 ──── │               │└───────────────┘                      └───────────────┘

MCP 的核心概念与我们的插件系统高度相似:

概念
我们的插件系统
MCP
能力声明
PluginManifest
JSON-RPC 发现
工具注册
tools 数组
tools/list
工具调用
execute 方法
tools/call
钩子机制
HookSystem
notifications
扩展方式
插件动态加载
服务器发现

MCP 不是要替代我们的插件系统,而是互补关系:

插件系统:Agent 进程内的扩展 → 适合与 Agent 生命周期绑定的能力(如代码格式化)

MCP:Agent 进程间的扩展 → 适合独立运行的外部服务(如数据库、文件系统、云服务)

💡 最佳实践:在实际框架中,MCP 服务器适配器本身就可以实现为一个插件。这样插件系统成为 MCP 的"网关",Agent 同时拥有进程内的高效集成和进程间的灵活扩展。

05 对比 Claude Code:Anthropic 怎么做扩展

Claude Code 是 Anthropic 官方的命令行 AI Agent,它的扩展机制设计非常值得借鉴:

┌─────────────────────────────────────────┐ClaudeCodeCLI├─────────────────────────────────────────┤  插件 /SlashCommands┌─────────┐┌─────────┐/review │  │ /fix    ...└─────────┘└─────────┘  工具 (Tools)                            ┌────┐┌────┐┌────┐┌────┐BashReadEditgrep...└────┘└────┘└────┘└────┘MCP 服务器                              ┌────────────────────────┐ 外部 MCPServer(filesys)...└────────────────────────┘  钩子 (Hooks)                            ┌────────┐┌────────┐前置钩子 后置钩子 ...└────────┘└────────┘├─────────────────────────────────────────┤       settings.json /CLAUDE.md          └─────────────────────────────────────────┘
特性
Claude Code
本章实现
钩子系统
settings.json 配置
HookSystem + 优先级
插件机制
MCP 服务器扩展
PluginSystem + Manifest
生命周期
无显式生命周期
load→init→activate→deactivate
工具定义
内置 + MCP 工具
ToolDefinition 聚合
系统提示
CLAUDE.md 文件
systemPrompt 聚合
错误隔离
MCP 服务器隔离
钩子错误隔离

从 Claude Code 学到的四条设计启示:

① 配置驱动 > 代码驱动:通过 manifest 文件声明扩展,而不是直接改代码

② 分层扩展:不同需求用不同机制(钩子 vs 插件 vs MCP),不一刀切

③ 进程隔离:不稳定的第三方工具用独立进程运行(MCP)

④ 渐进式复杂度:简单场景用钩子,复杂场景用插件,跨进程用 MCP

06 十一章合一:Agent 完整能力图谱

从第一章到第十一章,我们一步步搭出了一个完整的 Agent 系统:

章节
主题
比喻
核心能力
第1章
基础概念
出生
环境搭建 + 80行原型
第2章
Agent循环
思考
Think→Act→Observe
第3章
工具系统
双手
工具注册 + 描述
第4章
上下文管理
短期记忆
压缩 + Token预算
第5章
权限安全
刹车
权限分级 + 审批
第6章
子代理
分身
任务拆分 + 并行
第7章
记忆系统
长期记忆
文件存储 + 跨会话
第8章
结构化输出
说数据
Zod + JSON验证
第9章
错误处理
免疫系统
重试 + 熔断 + 降级
第10章
流式处理
说话
SSE + 逐Token渲染
第11章
扩展机制
长骨骼
钩子 + 插件 + MCP

从"出生"到"长骨骼",十一章的内容构成了一个完整的 Agent 架构体系。扩展机制是最后一块拼图——它让 Agent 不再是一个固定的程序,而是一个可无限生长的平台

07 总结:五条扩展设计原则

原则一:核心引擎只做最基础的事,能力通过扩展注入

原则二:钩子是"点",插件是"面",MCP 是"跨进程"

原则三:每个扩展点都要有错误隔离,一个插件崩不能带垮全局

原则四:配置驱动优于代码驱动,声明式注册优于硬编码

原则五:渐进式复杂度——简单用钩子,复杂用插件,跨进程用 MCP

扩展机制的核心思想是"关注点分离"——核心引擎专注基础运行,具体能力通过钩子和插件动态组合。这种架构让 Agent 从小巧的核心逐步成长,适应越来越复杂的应用场景。

下一章预告

第十二章我们将深入探讨 Agent 的性能优化与生产化部署——如何让 Agent 跑得更快、更稳、更省 Token,从"能跑"到"能上线"。

课后练习

练习 1:实现一个 RateLimitHook 钩子,在 beforeToolCall 中限制调用频率(1秒内不超过3次)

练习 2:为 PluginSystem 添加 getTool(name) 和 hasPlugin(name) 方法

练习 3:构建一个翻译插件,提供 translate 工具 + beforeToolCall 验证 + afterToolCall 统计

练习 4(选做):设计 MCPPluginAdapter,将 MCP 服务器包装为插件

如果这篇文章对你有帮助

点个「在看」分享给更多人关注公众号,下一章讲性能优化与生产化部署

AI Agent 从零到一系列 · 第十一章