1. 工具系统设计概述
在AI Agent开发领域,工具系统是连接大语言模型(LLM)与现实世界的桥梁。Schoober AI SDK的工具系统设计采用了ReAct范式,其中LLM负责"思考"(推理和决策),工具负责"执行"(具体操作)。这种分工模式决定了工具系统的质量直接影响Agent的能力边界。
提示:ReAct(Reasoning and Acting)是一种让LLM通过循环执行"思考-行动-观察"来完成复杂任务的范式,工具系统是实现"行动"环节的关键基础设施。
工具系统需要解决三个核心问题:
- 定义问题:如何让LLM理解工具的功能和使用方式
- 执行问题:如何在流式交互中调用工具并反馈结果
- 管理问题:如何高效地组织、缓存和销毁工具实例
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Tool接口:工具系统的契约
2.1 接口定义解析
所有工具都必须实现Tool接口,这是系统的基础契约:
typescript复制interface Tool {
name: string; // 工具唯一标识符
displayName?: string; // 面向用户的展示名称
getDescription(): Promise<ToolDescription>; // 获取工具描述
getParameters(): Promise<z.ZodSchema>; // 获取参数定义
execute(params, context, isPartial): Promise<void>; // 执行逻辑
validate(params): Promise<ValidationResult>; // 参数验证
}
2.2 关键设计决策
异步获取描述和参数:
- 工具描述和参数可能依赖运行时状态(如数据库连接)
- 示例:数据库查询工具的表名列表需要实时获取
- 避免了硬编码带来的维护问题
使用Zod Schema:
- 统一了TypeScript类型定义和运行时校验
- 示例:
z.object({ city: z.string() })同时提供:- TypeScript类型提示
- 运行时参数校验能力
- 自动生成JSON Schema供LLM理解
无返回值设计:
- 通过两条独立通道传递信息:
mermaid复制graph LR A[工具执行] --> B[setToolResult] A --> C[sendToolStatus] B --> D[消息历史] C --> E[用户界面] setToolResult:结构化数据供LLM使用sendToolStatus:友好提示供用户查看
3. BaseTool:工具实现的基类
3.1 基础结构
BaseTool抽象类封装了与TaskExecutor交互的通用逻辑:
typescript复制abstract class BaseTool implements Tool {
abstract name: string;
abstract getDescription(): Promise<ToolDescription>;
abstract getParameters(): Promise<z.ZodSchema>;
abstract execute(params, context, isPartial): Promise<void>;
}
开发者只需实现这三个抽象方法即可创建新工具。
3.2 工具状态机
工具执行遵循明确的状态流转:
code复制WAIT → DOING → SUCCESS
↘ ERROR
状态管理实现要点:
- 状态信息累积合并而非覆盖
- 终态(SUCCESS/ERROR)自动清理缓存
- 示例状态流转代码:
typescript复制// 等待状态
await this.sendToolStatus(requestId, ToolStatus.WAIT, {
showTip: '准备查询中...',
params: { city: '北京' }
});
// 执行状态
await this.sendToolStatus(requestId, ToolStatus.DOING, {
showTip: '查询北京天气中...'
});
// 成功状态
await this.sendToolStatus(requestId, ToolStatus.SUCCESS, {
result: { temperature: 22, condition: '晴' }
});
3.3 流式参数处理
isPartial机制处理流式参数解析:
- 当LLM响应未完成时,
isPartial=true - 工具可提前展示部分信息
- 完整执行流程:
typescript复制async execute(params, context, isPartial) {
if (isPartial) {
await this.sendToolStatus(context.requestId, ToolStatus.WAIT, {
showTip: '参数解析中...',
params // 可能不完整
});
return;
}
// 完整执行逻辑
await this.sendToolStatus(context.requestId, ToolStatus.DOING);
const result = await fetchWeather(params.city);
await this.setToolResult(JSON.stringify(result));
await this.sendToolStatus(context.requestId, ToolStatus.SUCCESS, {
result
});
}
4. 工具注册与管理
4.1 ToolRegistry设计
采用工厂模式注册工具:
typescript复制class DefaultToolRegistry implements ToolRegistry {
private factories: Map<string, ToolFactory> = new Map();
registerFactory(name: string, factory: ToolFactory): void {
this.factories.set(name, factory);
}
async get(name: string): Promise<Tool | undefined> {
const factory = this.factories.get(name);
return factory ? await factory(undefined) : undefined;
}
}
注册方式示例:
typescript复制// 方式1:直接注册类
agent.registerTool(WeatherTool);
// 方式2:带依赖注入的工厂函数
agent.registerTool({
name: 'database_query',
factory: async (context) => {
const tool = new DatabaseQueryTool();
await tool.connect(process.env.DB_URL);
return tool;
}
});
4.2 两级缓存策略
ToolManager实现高效实例管理:
code复制执行流程:
1. 检查ToolUse.id缓存 → 命中则复用实例
2. 未命中 → 调用ToolRegistry工厂创建新实例
3. 缓存新实例供后续使用
缓存设计要点:
- 一级缓存:基于
ToolUse.id的短期缓存 - 二级缓存:工厂函数创建的实例
- 系统工具作为兜底方案
4.3 系统工具与用户工具
内置系统工具:
attempt_completion:标记任务完成new_task:创建子任务
覆盖规则:
- 用户工具优先于系统工具
- 覆盖在prompt生成和执行层面都生效
5. 工具执行细节
5.1 参数校验与修正
自动处理常见类型问题:
- 字符串"true"/"false"转布尔值
- 递归处理嵌套对象和数组
- 校验失败时提供友好错误信息
示例修正逻辑:
typescript复制function convertBooleanStrings(params: any): any {
if (typeof params === 'string') {
if (params === 'true') return true;
if (params === 'false') return false;
}
// 递归处理对象和数组...
return params;
}
5.2 执行超时控制
默认10分钟超时机制:
typescript复制private async executeWithTimeout(tool, params, context, isPartial) {
await Promise.race([
tool.execute(params, context, isPartial),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), this.timeout)
)
]);
}
5.3 执行统计
收集的关键指标:
- 执行次数
- 成功率
- 平均耗时
- 失败模式分析
6. 工具快捷创建
createSimpleTool简化无状态工具开发:
typescript复制const calculator = createSimpleTool({
name: 'calculator',
description: {
displayName: '计算器',
description: '执行数学运算'
},
parameters: z.object({
expression: z.string().describe('数学表达式')
}),
execute: async (params) => {
return eval(params.expression); // 实际项目应使用安全计算库
}
});
注意事项:
- 仅适用于简单工具
- 无法使用
this相关方法 - 复杂工具仍需继承
BaseTool
7. 工具与Prompt生成
工具定义到prompt的转换流程:
code复制Tool实例
→ getDescription() → 名称/描述
→ getParameters() → Zod Schema → JSON Schema
→ 生成Markdown格式定义
示例生成的prompt片段:
markdown复制## get_weather
Display Name: 天气查询
Description: 查询指定城市天气
Parameters:
- city: string (required) - 城市名称
- unit: enum (optional) - 温度单位(celsius/fahrenheit)
8. 设计原则总结
-
双通道通信原则
- LLM通道:结构化数据
- 用户通道:友好状态提示
-
延迟实例化原则
- 工厂函数注册
- 按需创建实例
-
流式友好设计
- 支持部分参数执行
- 渐进式UI反馈
-
用户优先策略
- 自定义工具覆盖系统工具
- 统一的prompt和执行处理
实际应用建议:
- 复杂工具继承
BaseTool - 简单工具使用
createSimpleTool - 合理设置执行超时
- 监控工具执行指标
