上一章我们搞定了 Agent 的「循环」——让它能自己决定下一步做什么。但问题来了:如果 Agent 只有大脑没有双手,它依然什么也做不了。
这一章,我们给 Agent 装上「手」——工具系统。工具决定了 Agent 能做什么、不能做什么。设计好不好,直接决定 Agent 是「万能助手」还是「花瓶」。
📌 本文你将掌握
✅ 理解工具系统在 Agent 架构中的核心地位
✅ 设计工具定义接口(含分类、结果标准化)
✅ 实现 Tool Registry 注册中心
✅ 掌握工具描述的最佳实践
✅ 三种工具组合模式(编排 / 组合 / 管道)
01 工具是 Agent 能力的边界
先记住这个公式:
Agent 的能力 = LLM 推理能力 × 工具系统覆盖范围推理能力(LLM)= 大脑工具系统(Tools)= 双手没有工具的 Agent → 只是聊天机器人没有 LLM 的工具 → 只是普通函数
回顾第 1 章的例子:
客服 Agent = 工单界面 + 工具集(查订单、退款、发邮件)+ LLM 推理循环Claude Code 内置的工具集长这样:
Claude Code 的核心是 Registry 模式——所有工具先注册到注册中心,Agent 循环通过注册中心查找和执行。好处有四个:
① 松耦合:工具定义和 Agent 逻辑分离
② 可扩展:随时添加新工具,不改核心逻辑
③ 可审计:所有调用经过注册中心,便于日志
④ 统一执行:执行接口标准化,Agent 不关心实现
02 工具长什么样?接口设计
Anthropic API 的工具定义格式很简洁:
interface APITool {name: string; // 工具名(LLM 选择的依据)description: string; // 描述(LLM 理解用途的关键)input_schema: { // 参数定义(JSON Schema)type: "object";properties: Record;required: string[];};}
但光有这些还不够。我们在 API 格式基础上,增加了执行处理器和分类信息:
// 工具分类enumToolCategory {READ = "READ", // 只读,无副作用WRITE = "WRITE", // 写入,有副作用EXECUTE = "EXECUTE", // 执行命令,高风险}// 完整工具定义interfaceToolDefinition {name: string;description: string;category: ToolCategory;inputSchema: { type: "object"; properties: ...; required: ... };handler: (input: any) =>Promise; // 执行函数}// 标准化结果interfaceToolResult {success: boolean; // 是否成功data?: unknown; // 成功时的数据error?: string; // 失败时的错误信息meta?: { // 元信息(调试/监控)duration: number;toolName: string;};}
为什么要分类? 因为不同工具的风险等级不同:
💡 这个分类在第 5 章权限系统中会发挥关键作用——READ 自动放行,WRITE 需确认,EXECUTE 严格审批。
03 Tool Registry:工具的「调度中心」
所有工具注册到一个中心,Agent 只跟这个中心打交道。核心结构:
classToolRegistry{private tools: Map; // 名称 → 定义private categoryIndex: Map; // 分类 → 名称列表register(tool: ToolDefinition): void; // 注册unregister(name: string): boolean; // 注销get(name: string): ToolDefinition; // 查找list(): ToolDefinition[]; // 全部列出listByCategory(cat: ToolCategory): ...; // 按分类列出execute(name: string, input: any): Promise; // 执行toAPIFormat(): Tool[]; // 转为 API 格式}
执行流程一目了然:
Agent 循环 ToolRegistry│ ││ 1. LLM 返回 tool_use ││ 2. 提取工具名 + 参数 ││──────────────▶ 3. get(name) ││◀────────────── 4. 返回定义 ││──────────────▶ 5. execute() ││ ├─ 检查参数 ││ ├─ 调用 handler││ └─ 返回结果 ││◀────────────── 6. 返回 ToolResult││ 7. 注入结果到消息 │
💡 关键设计:Agent 不直接调用工具函数,而是通过 Registry 间接调用。这层间接性带来了权限控制、日志记录、结果标准化的能力。
04 工具描述:写给 LLM 看的「说明书」
工具的
description先看个反例 vs 正例:
// ❌ 坏描述 —— 太简略{name: "search_code",description: "搜索代码",}// ✅ 好描述 —— 说清用途、时机、限制{name: "search_code",description: "在项目中搜索代码。当你需要查找函数定义、变量引用、或确认某段代码是否存在时使用。支持按关键词和文件类型过滤。注意:搜索范围仅限于已索引的项目文件。",}
五条写作原则,记住就行:
高级技巧:用描述引导 LLM 的工作顺序:
{name: "generate_report",description: "生成分析报告。注意:在调用此工具前,请确保已通过 read_file、search_code 等工具收集了足够的信息。如果信息不足,请先收集信息。",}
这样 LLM 会先收集信息,再生成报告,而不是在信息不足时就急着输出——一句话的描述,改变 Agent 的行为模式。
05 三种工具组合模式
实际业务中,一个操作往往需要多个工具配合。比如退款 = 查订单 + 检查库存 + 创建退款 + 发邮件。怎么组合?三种模式:
简单查询:LLM → query_order("ORD-001") → 订单信息
复杂操作:LLM → process_refund → 查订单 → 检查库存 → 创建退款 → 发邮件
模式 1:编排模式(Agent 驱动)
LLM 思考:"需要退款,先查订单,再退款,最后通知"→ 第1步: query_order("ORD-001")→ 第2步: refund_order("ORD-001")→ 第3步: send_email("user@example.com", "退款成功")
Agent 自己决定调哪些工具、什么顺序。最灵活,也是默认方式。
模式 2:组合模式(工具内部调工具)
LLM: process_refund("ORD-001")↓process_refund handler 内部:1. query_order("ORD-001") → 验证订单2. check_inventory("P001") → 更新库存3. create_refund_record(...) → 创建退款4. send_email("user@...") → 通知用户5. return { success: true }
一个「高级工具」内部调用多个「基础工具」,对 LLM 只暴露为一个工具。降低 LLM 决策负担。
模式 3:管道模式(数据流)
read_config → parse_config → validate_config → apply_config↓ ↓ ↓ ↓原始 JSON 解析对象 验证结果 应用结果
一个工具的输出是下一个工具的输入,形成数据管道。适合数据处理流水线场景。
06 从 Claude Code 学到四个设计技巧
Claude Code 的 ToolRegistry 有四个值得借鉴的设计:
📁 按域分组:fileTools、bashTools、customTools,按业务领域隔离
⏳ 延迟初始化:需要时才注册,不一次性加载所有工具
🔗 工具拦截器:执行前后加 hook,统一日志/权限/监控
🛡️ 结果验证层:handler 返回任意格式 → 标准化 → 大结果截断 → 敏感信息脱敏
拦截器的代码特别优雅:
executeTool(name, input) {preHooks.forEach(hook =>hook(name, input)); // 前置钩子const result = awaithandler(input); // 执行postHooks.forEach(hook =>hook(name, result)); // 后置钩子return result;}
权限检查、日志记录、性能监控——全部通过 hook 插入,不改工具本身的代码。这就是松耦合的力量。
07 三个文件,三个台阶
📦 01-tool-registry.ts — 注册中心 + 6 个示例工具
↓
🔧 02-business-tools.ts — 真实业务场景(客服工具集)
↓
🤖 03-tool-composition.ts — 工具组合(编排 + 组合 + 管道)
08 本章总结
工具系统架构,一张图概括:
ToolDefinition(接口定义)├─ name ← LLM 选择依据├─ description ← LLM 理解依据├─ inputSchema ← 参数验证├─ category ← 安全分类(READ/WRITE/EXECUTE)└─ handler ← 实际执行ToolRegistry(注册中心)├─ register/unregister ← 动态管理├─ get/list ← 查找工具├─ execute ← 统一执行入口└─ toAPIFormat ← 转为 API 格式ToolResult(结果标准化)├─ success: boolean ← 执行状态├─ data/error ← 成功/失败数据└─ meta ← 元信息(耗时等)
五条设计原则:
① 工具描述写给 LLM 看 — 清晰描述 = 正确的工具选择
② 分类管理 — READ/WRITE/EXECUTE 为权限系统打基础
③ 结果标准化 — 统一 ToolResult 格式简化处理逻辑
④ 注册中心模式 — 松耦合、可扩展、可审计
⑤ 组合优于单一 — 复杂业务通过工具组合实现
🎯 下一章预告:上下文管理与优化——Agent 对话越来越长,Token 怎么管?如何让 Agent 在长对话中不丢上下文、不超限制?
📝 课后练习
1️⃣ 在 01-tool-registry.ts 中添加一个 delete_file 工具(WRITE 类别)
2️⃣ 在 02-business-tools.ts 中添加 cancel_order,让它内部调用 send_email
3️⃣ 用管道模式实现:read_file → parse_json → validate → apply
4️⃣ 思考:50+ 个工具时,LLM 选择准确率会下降。你有哪些策略?
👇 关注我,一起从零搭建 AI Agent
每周更新,带你从原理到实战不懂的地方随时留言,我会一一回复
觉得有帮助?点个在看 👁 你的支持是我持续更新的动力
夜雨聆风