Agent 工具注册与发现机制:从硬编码到插件式动态加载的架构演进

发布时间:2026/7/11 2:20:52

Agent 工具注册与发现机制:从硬编码到插件式动态加载的架构演进 Agent 工具注册与发现机制从硬编码到插件式动态加载的架构演进一、硬编码工具的维护灾难每新增一个工具都要改核心代码Agent 的核心能力是调用工具——搜索信息、查询数据库、发送邮件、执行脚本。最初的实现通常是硬编码的 switch-casedef call_tool(tool_name, params): if tool_name search: return search_web(params[query]) elif tool_name calculator: return calculate(params[expression]) elif tool_name weather: return get_weather(params[city]) # 每次新增工具都要加一个 elif改核心代码当工具只有 3 个时这段代码没问题。当工具增加到 15 个时switch-case 变成了一面难以维护的代码墙。更糟的是——新增工具需要修改核心 Agent 逻辑、重新部署、重启服务。这在开源项目中意味着外部贡献者无法独立开发新工具每个工具 PR 都要等核心维护者合并。工具注册与发现机制是解药。它的核心思想是Agent 不关心工具的具体实现只关心工具的签名——名称、描述、参数 Schema。工具提供方按规范注册工具Agent 运行时动态发现并调用。graph TB subgraph Discovery[工具发现] D1[本地插件目录] D2[远程注册中心] D3[环境变量配置] end D1 -- R[工具注册表 Registry] D2 -- R D3 -- R R -- S[Schema 索引br/名称 描述 参数] subgraph Agent[Agent 运行时] A1[用户查询] -- A2[LLM 分析意图] A2 -- A3[从 Registry 匹配工具] A3 -- A4{工具存在?} A4 --|是| A5[调用工具执行] A4 --|否| A6[降级: 告知用户不支持] end subgraph Tools[已注册工具] T1[SearchTool] T2[CalculatorTool] T3[WeatherTool] T4[自定义插件 Tool] end S -- A3 A5 -- T1 A5 -- T2 A5 -- T3 A5 -- T4 style A6 fill:#ffd43b,color:#000 style A5 fill:#51cf66,color:#fff二、工具注册的核心抽象Tool 接口与 Register 机制工具注册系统的核心是一个统一的Tool接口和Registry容器。接口定义了每个工具必须具备的能力interface Tool { name: string; // 唯一标识 description: string; // 给 LLM 看的描述 parameters: JSONSchema; // 参数定义 execute(params: any): Promiseany; // 执行逻辑 }Registry负责管理所有已注册的工具提供注册、注销、查询三个能力。关键设计工具注册发生在 Agent 启动时而非运行时。这意味着工具的加载是一次性的不存在运行时动态替换的安全风险。三、完整的工具注册系统实现// tool-registry.ts import { z } from zod; // 工具接口定义 export interface ToolTInput any, TOutput any { name: string; description: string; schema: z.ZodSchemaTInput; execute(input: TInput): PromiseTOutput; } // 工具注册表 export class ToolRegistry { private tools: Mapstring, Tool new Map(); private loadedPlugins: Setstring new Set(); // 注册单个工具 register(tool: Tool): void { if (this.tools.has(tool.name)) { throw new Error(Tool ${tool.name} is already registered); } this.tools.set(tool.name, tool); } // 注销工具 unregister(name: string): void { this.tools.delete(name); } // 获取工具 get(name: string): Tool | undefined { return this.tools.get(name); } // 生成给 LLM 的 Function Calling Schema generateLLMSchema(): Array{ type: function; function: { name: string; description: string; parameters: any }; } { const schemas: Arrayany []; for (const tool of this.tools.values()) { schemas.push({ type: function, function: { name: tool.name, description: tool.description, parameters: this.zodToJSONSchema(tool.schema), }, }); } return schemas; } // 执行工具调用 async execute(name: string, input: any): Promiseany { const tool this.tools.get(name); if (!tool) { throw new Error(Tool ${name} not found. Available: ${this.listNames()}); } // 参数验证 const parsed tool.schema.safeParse(input); if (!parsed.success) { throw new Error(Invalid parameters for ${name}: ${parsed.error.message}); } return tool.execute(parsed.data); } listNames(): string[] { return Array.from(this.tools.keys()); } size(): number { return this.tools.size; } // Zod Schema 转 JSON Schema用于 Function Calling private zodToJSONSchema(schema: z.ZodSchema): any { // 使用 zod-to-json-schema 库转换 // 简化实现 return zodToJsonSchema(schema); } // 从插件目录加载工具动态发现 async loadPlugins(pluginDir: string): Promisenumber { const fs require(fs); const path require(path); if (!fs.existsSync(pluginDir)) return 0; const files fs.readdirSync(pluginDir); let loaded 0; for (const file of files) { if (!file.endsWith(.js) !file.endsWith(.ts)) continue; const pluginPath path.join(pluginDir, file); if (this.loadedPlugins.has(pluginPath)) continue; try { const plugin require(pluginPath); if (plugin.default typeof plugin.default object) { this.register(plugin.default); this.loadedPlugins.add(pluginPath); loaded; } } catch (err) { console.error(Failed to load plugin ${file}:, err); } } return loaded; } } // 工具实现示例 // 搜索工具 export const SearchTool: Tool { name: web_search, description: 搜索互联网信息。当需要查找实时信息时使用。, schema: z.object({ query: z.string().describe(搜索关键词), max_results: z.number().optional().default(5).describe(最大结果数), }), async execute(input) { // 实际调用搜索引擎 API const response await fetch(https://api.search.example.com?q${encodeURIComponent(input.query)}n${input.max_results}); return response.json(); }, }; // 计算器工具 export const CalculatorTool: Tool { name: calculator, description: 执行数学计算。支持 、-、*、/ 和括号。, schema: z.object({ expression: z.string().describe(数学表达式如 2 3 * 4), }), async execute(input) { try { // 安全计算使用 Function 在一些场景不安全实际应使用 math.js const result Function(use strict; return (${input.expression}))(); return { expression: input.expression, result }; } catch (err) { return { error: 计算失败: ${err.message} }; } }, }; // Agent 中使用 import OpenAI from openai; class Agent { private registry: ToolRegistry; private openai: OpenAI; constructor(registry: ToolRegistry) { this.registry registry; this.openai new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); } async chat(userMessage: string) { const messages: any[] [ { role: system, content: 你是一个 AI 助手可以使用工具帮助用户。 }, { role: user, content: userMessage }, ]; const response await this.openai.chat.completions.create({ model: gpt-4o, messages, tools: this.registry.generateLLMSchema(), }); const choice response.choices[0]; // 处理工具调用 if (choice.finish_reason tool_calls choice.message.tool_calls) { for (const toolCall of choice.message.tool_calls) { const args JSON.parse(toolCall.function.arguments); const result await this.registry.execute(toolCall.function.name, args); messages.push(choice.message); messages.push({ role: tool, tool_call_id: toolCall.id, content: JSON.stringify(result), }); } // 继续对话 const finalResponse await this.openai.chat.completions.create({ model: gpt-4o, messages, }); return finalResponse.choices[0].message.content; } return choice.message.content; } }四、动态加载的安全考量插件隔离动态加载的插件代码运行在 Agent 进程中没有沙箱隔离。恶意插件可以访问文件系统、环境变量、网络。安全策略插件应有签名验证通过 checksum 或 GPG 签名生产环境推荐使用 WASM 沙箱或子进程隔离加载前做静态分析AST 扫描是否访问敏感 API不适用场景工具数量固定且不需要外部贡献 5 个工具金融等严格安全审查的场景不允许动态加载未知代码Serverless 环境启动时间不包含插件扫描五、总结工具注册与发现机制将 Agent 的工具管理从硬编码升级为插件化。核心只有两个组件Tool接口定义工具契约和Registry管理工具生命周期。落地路径先定义Tool接口将现有的 3-5 个工具迁移到接口模式然后实现Registry和插件加载最后在 Agent 的 Function Calling 中集成。少即是多。不要在 3 个工具的时候就上插件系统——等到工具数量超过 8 个、或者有外部贡献者需要独立开发工具时再引入。

相关新闻