🛠️ 聊天工具模块
聊天工具模块允许前端应用向后端 AI 应用传递工具定义,并在对话过程中动态调用这些工具。这为 AI 助手提供了强大的扩展能力,使其能够执行各种自定义操作。
📋 概述
工具模块的核心功能:
- 工具定义: 在前端定义可供 AI 使用的工具
- 工具传递: 将工具定义传递给后端 AI 应用
- 动态调用: AI 在对话中根据需要调用相应工具
- 结果处理: 处理工具执行结果并继续对话
🚀 快速开始
基础工具定义
ts
import { scp } from '@your-org/scp-sdk';
// 定义一个简单的计算器工具
const calculatorTool = {
name: 'calculator',
description: '执行基本的数学计算',
parameters: {
type: 'object',
properties: {
expression: {
type: 'string',
description: '要计算的数学表达式,例如: "2 + 3 * 4"'
}
},
required: ['expression']
},
handler: async (params: { expression: string }) => {
try {
// 安全的数学表达式计算
const result = eval(params.expression);
return {
success: true,
result: result,
message: `计算结果: ${params.expression} = ${result}`
};
} catch (error) {
return {
success: false,
error: '无效的数学表达式',
message: '请提供有效的数学表达式'
};
}
}
};
// 注册工具到聊天模块
scp.chat.registerTool(calculatorTool);🔧 工具定义规范
工具结构
每个工具必须包含以下属性:
ts
interface ChatTool {
name: string; // 工具名称(唯一标识)
description: string; // 工具描述(AI 用于理解工具用途)
parameters: JSONSchema; // 参数模式定义
handler: ToolHandler; // 工具执行函数
category?: string; // 工具分类(可选)
version?: string; // 工具版本(可选)
}
interface ToolHandler {
(params: any): Promise<ToolResult> | ToolResult;
}
interface ToolResult {
success: boolean;
result?: any;
error?: string;
message?: string;
data?: any;
}参数模式定义
使用 JSON Schema 定义工具参数:
ts
// 简单参数
const simpleParameters = {
type: 'object',
properties: {
message: {
type: 'string',
description: '要发送的消息内容'
}
},
required: ['message']
};
// 复杂参数
const complexParameters = {
type: 'object',
properties: {
user: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
email: { type: 'string', format: 'email' }
},
required: ['id', 'name']
},
options: {
type: 'object',
properties: {
priority: {
type: 'string',
enum: ['low', 'medium', 'high'],
default: 'medium'
},
tags: {
type: 'array',
items: { type: 'string' }
}
}
}
},
required: ['user']
};🛠️ 常用工具示例
1. 文件操作工具
ts
const fileOperationTool = {
name: 'file_operations',
description: '执行文件相关操作,如读取、写入、删除文件',
parameters: {
type: 'object',
properties: {
operation: {
type: 'string',
enum: ['read', 'write', 'delete', 'list'],
description: '要执行的操作类型'
},
path: {
type: 'string',
description: '文件或目录路径'
},
content: {
type: 'string',
description: '写入文件时的内容(仅在 operation 为 write 时需要)'
}
},
required: ['operation', 'path']
},
handler: async (params) => {
const { operation, path, content } = params;
try {
switch (operation) {
case 'read':
const fileContent = await readFile(path);
return {
success: true,
result: fileContent,
message: `成功读取文件: ${path}`
};
case 'write':
await writeFile(path, content);
return {
success: true,
message: `成功写入文件: ${path}`
};
case 'delete':
await deleteFile(path);
return {
success: true,
message: `成功删除文件: ${path}`
};
case 'list':
const files = await listFiles(path);
return {
success: true,
result: files,
message: `目录 ${path} 包含 ${files.length} 个文件`
};
default:
return {
success: false,
error: '不支持的操作类型'
};
}
} catch (error) {
return {
success: false,
error: error.message,
message: `文件操作失败: ${error.message}`
};
}
}
};2. API 调用工具
ts
const apiCallTool = {
name: 'api_request',
description: '发起 HTTP API 请求',
parameters: {
type: 'object',
properties: {
url: {
type: 'string',
format: 'uri',
description: 'API 请求的 URL'
},
method: {
type: 'string',
enum: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
default: 'GET',
description: 'HTTP 请求方法'
},
headers: {
type: 'object',
description: '请求头'
},
body: {
type: 'object',
description: '请求体数据'
}
},
required: ['url']
},
handler: async (params) => {
const { url, method = 'GET', headers = {}, body } = params;
try {
const response = await fetch(url, {
method,
headers: {
'Content-Type': 'application/json',
...headers
},
body: body ? JSON.stringify(body) : undefined
});
const data = await response.json();
return {
success: response.ok,
result: data,
message: `API 请求${response.ok ? '成功' : '失败'}: ${response.status}`
};
} catch (error) {
return {
success: false,
error: error.message,
message: `API 请求失败: ${error.message}`
};
}
}
};3. 数据库查询工具
ts
const databaseTool = {
name: 'database_query',
description: '执行数据库查询操作',
parameters: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'SQL 查询语句'
},
params: {
type: 'array',
items: { type: 'string' },
description: '查询参数'
}
},
required: ['query']
},
handler: async (params) => {
const { query, params: queryParams = [] } = params;
try {
// 这里应该使用你的数据库连接
const result = await executeQuery(query, queryParams);
return {
success: true,
result: result,
message: `查询成功,返回 ${result.length} 条记录`
};
} catch (error) {
return {
success: false,
error: error.message,
message: `数据库查询失败: ${error.message}`
};
}
}
};🔄 前端到后端工具传递
1. 工具注册与传递
ts
// 前端工具注册
class ChatToolManager {
private tools: Map<string, ChatTool> = new Map();
// 注册工具
registerTool(tool: ChatTool) {
this.tools.set(tool.name, tool);
// 将工具定义发送给后端
this.sendToolDefinitionToBackend(tool);
}
// 向后端发送工具定义
private async sendToolDefinitionToBackend(tool: ChatTool) {
const toolDefinition = {
name: tool.name,
description: tool.description,
parameters: tool.parameters,
category: tool.category,
version: tool.version
};
try {
await scp.api.post('/chat/tools/register', {
tool: toolDefinition
});
console.log(`工具 ${tool.name} 已注册到后端`);
} catch (error) {
console.error(`工具注册失败: ${error.message}`);
}
}
// 执行工具调用
async executeTool(toolName: string, params: any): Promise<ToolResult> {
const tool = this.tools.get(toolName);
if (!tool) {
return {
success: false,
error: `工具 ${toolName} 未找到`
};
}
try {
const result = await tool.handler(params);
return result;
} catch (error) {
return {
success: false,
error: error.message,
message: `工具执行失败: ${error.message}`
};
}
}
}
// 全局工具管理器实例
const toolManager = new ChatToolManager();
// SCP 聊天模块扩展
scp.chat.registerTool = (tool: ChatTool) => {
toolManager.registerTool(tool);
};
scp.chat.executeTool = (toolName: string, params: any) => {
return toolManager.executeTool(toolName, params);
};2. 聊天会话中的工具集成
ts
// 启动聊天会话时传递工具列表
const startChatSession = async () => {
const sessionConfig = {
sessionId: generateSessionId(),
userId: getCurrentUserId(),
// 传递可用工具列表
availableTools: Array.from(toolManager.tools.keys()),
// 工具执行回调
onToolCall: async (toolName: string, params: any) => {
const result = await toolManager.executeTool(toolName, params);
return result;
}
};
const session = await scp.chat.createSession(sessionConfig);
return session;
};3. WebSocket 工具调用处理
ts
// WebSocket 消息处理
scp.chat.onMessage((message) => {
if (message.type === 'tool_call') {
const { toolName, params, callId } = message.data;
// 执行工具调用
toolManager.executeTool(toolName, params)
.then(result => {
// 将结果发送回后端
scp.chat.sendMessage({
type: 'tool_result',
data: {
callId,
result
}
});
})
.catch(error => {
// 发送错误结果
scp.chat.sendMessage({
type: 'tool_result',
data: {
callId,
result: {
success: false,
error: error.message
}
}
});
});
}
});💬 对话中的工具使用
1. AI 工具调用流程
mermaid
sequenceDiagram
participant User as 用户
participant Frontend as 前端应用
participant Backend as 后端 AI
participant Tool as 工具处理器
User->>Frontend: 发送消息
Frontend->>Backend: 转发消息 + 可用工具列表
Backend->>Backend: AI 分析是否需要使用工具
Backend->>Frontend: 发送工具调用请求
Frontend->>Tool: 执行工具
Tool->>Frontend: 返回执行结果
Frontend->>Backend: 发送工具执行结果
Backend->>Frontend: 基于工具结果生成回复
Frontend->>User: 显示 AI 回复2. 工具调用示例对话
ts
// 用户消息: "帮我计算 15 * 23 + 7 的结果"
// AI 分析后决定使用计算器工具
const toolCall = {
type: 'tool_call',
data: {
toolName: 'calculator',
params: {
expression: '15 * 23 + 7'
},
callId: 'call_123456'
}
};
// 前端执行工具并返回结果
const toolResult = {
type: 'tool_result',
data: {
callId: 'call_123456',
result: {
success: true,
result: 352,
message: '计算结果: 15 * 23 + 7 = 352'
}
}
};
// AI 基于工具结果生成最终回复
const aiResponse = {
type: 'message',
data: {
content: '根据计算,15 × 23 + 7 = 352。计算过程是:先算乘法 15 × 23 = 345,然后加上 7,最终结果是 352。',
toolCalls: [
{
toolName: 'calculator',
params: { expression: '15 * 23 + 7' },
result: 352
}
]
}
};3. 多工具协作
ts
// 复杂场景:AI 需要查询数据库然后发送邮件
// 用户消息: "给所有 VIP 用户发送促销邮件"
// 第一步:查询 VIP 用户
const step1 = {
type: 'tool_call',
data: {
toolName: 'database_query',
params: {
query: 'SELECT email, name FROM users WHERE user_type = ?',
params: ['VIP']
},
callId: 'call_001'
}
};
// 第二步:基于查询结果发送邮件
const step2 = {
type: 'tool_call',
data: {
toolName: 'send_email',
params: {
recipients: ['user1@example.com', 'user2@example.com'], // 从第一步获取
subject: '专属VIP促销活动',
template: 'vip_promotion',
data: {
promotionCode: 'VIP2024',
discount: '30%'
}
},
callId: 'call_002'
}
};🎯 高级特性
1. 工具权限控制
ts
interface ToolPermission {
userId?: string;
roles?: string[];
permissions?: string[];
restrictions?: {
maxCallsPerHour?: number;
allowedParams?: string[];
deniedParams?: string[];
};
}
const restrictedTool = {
name: 'admin_operations',
description: '管理员操作工具',
permissions: {
roles: ['admin', 'super_admin'],
restrictions: {
maxCallsPerHour: 10
}
},
parameters: {
type: 'object',
properties: {
operation: {
type: 'string',
enum: ['delete_user', 'modify_permissions', 'system_backup']
}
}
},
handler: async (params, context) => {
// 检查权限
if (!context.user.roles.includes('admin')) {
return {
success: false,
error: '权限不足',
message: '此操作需要管理员权限'
};
}
// 执行操作
// ...
}
};2. 工具链式调用
ts
const workflowTool = {
name: 'data_analysis_workflow',
description: '数据分析工作流',
parameters: {
type: 'object',
properties: {
dataSource: { type: 'string' },
analysisType: { type: 'string' }
}
},
handler: async (params) => {
const workflow = [
{ tool: 'data_fetch', params: { source: params.dataSource } },
{ tool: 'data_clean', params: { method: 'standard' } },
{ tool: 'data_analyze', params: { type: params.analysisType } },
{ tool: 'generate_report', params: { format: 'pdf' } }
];
let results = [];
let previousResult = null;
for (const step of workflow) {
const result = await scp.chat.executeTool(step.tool, {
...step.params,
previousResult
});
if (!result.success) {
return {
success: false,
error: `工作流在步骤 ${step.tool} 失败`,
partialResults: results
};
}
results.push(result);
previousResult = result.result;
}
return {
success: true,
result: results,
message: '数据分析工作流执行完成'
};
}
};3. 实时工具状态
ts
// 长时间运行的工具支持进度回调
const longRunningTool = {
name: 'batch_process',
description: '批量处理工具',
parameters: {
type: 'object',
properties: {
items: {
type: 'array',
items: { type: 'string' }
}
}
},
handler: async (params, context) => {
const { items } = params;
const total = items.length;
let processed = 0;
// 发送初始状态
context.sendProgress({
status: 'running',
progress: 0,
message: `开始处理 ${total} 个项目`
});
const results = [];
for (const item of items) {
// 处理单个项目
const result = await processItem(item);
results.push(result);
processed++;
// 发送进度更新
context.sendProgress({
status: 'running',
progress: (processed / total) * 100,
message: `已处理 ${processed}/${total} 个项目`
});
}
// 发送完成状态
context.sendProgress({
status: 'completed',
progress: 100,
message: '批量处理完成'
});
return {
success: true,
result: results,
message: `成功处理 ${processed} 个项目`
};
}
};📚 最佳实践
1. 工具设计原则
ts
// ✅ 好的工具设计
const goodTool = {
name: 'user_search',
description: '根据条件搜索用户。支持按姓名、邮箱、用户类型等条件查询用户信息。',
parameters: {
type: 'object',
properties: {
query: {
type: 'string',
description: '搜索关键词,可以是姓名或邮箱的一部分'
},
userType: {
type: 'string',
enum: ['regular', 'vip', 'admin'],
description: '用户类型筛选'
},
limit: {
type: 'integer',
minimum: 1,
maximum: 100,
default: 10,
description: '返回结果数量限制'
}
},
required: ['query']
},
handler: async (params) => {
// 参数验证
if (!params.query || params.query.trim().length < 2) {
return {
success: false,
error: '搜索关键词至少需要2个字符'
};
}
try {
const users = await searchUsers(params);
return {
success: true,
result: users,
message: `找到 ${users.length} 个匹配的用户`
};
} catch (error) {
return {
success: false,
error: error.message,
message: '用户搜索失败'
};
}
}
};
// ❌ 不好的工具设计
const badTool = {
name: 'do_stuff', // 名称不明确
description: '做一些事情', // 描述不清楚
parameters: {
type: 'object',
properties: {
data: { type: 'any' } // 参数类型不明确
}
},
handler: async (params) => {
// 没有错误处理,返回格式不一致
return someFunction(params.data);
}
};2. 错误处理策略
ts
// 统一的错误处理包装器
function withErrorHandling(handler: ToolHandler): ToolHandler {
return async (params, context) => {
try {
const result = await handler(params, context);
return result;
} catch (error) {
console.error('工具执行错误:', error);
return {
success: false,
error: error.message,
message: '工具执行过程中发生错误',
errorCode: error.code || 'UNKNOWN_ERROR'
};
}
};
}
// 使用错误处理包装器
const safeApiTool = {
name: 'safe_api_call',
description: '安全的 API 调用工具',
parameters: { /* ... */ },
handler: withErrorHandling(async (params) => {
// 可能抛出异常的代码
const response = await fetch(params.url);
const data = await response.json();
return {
success: true,
result: data
};
})
};3. 性能优化
ts
// 工具结果缓存
class ToolCache {
private cache = new Map<string, { result: any; timestamp: number; ttl: number }>();
getCacheKey(toolName: string, params: any): string {
return `${toolName}:${JSON.stringify(params)}`;
}
get(toolName: string, params: any): any | null {
const key = this.getCacheKey(toolName, params);
const cached = this.cache.get(key);
if (!cached) return null;
if (Date.now() - cached.timestamp > cached.ttl) {
this.cache.delete(key);
return null;
}
return cached.result;
}
set(toolName: string, params: any, result: any, ttl: number = 300000) {
const key = this.getCacheKey(toolName, params);
this.cache.set(key, {
result,
timestamp: Date.now(),
ttl
});
}
}
const toolCache = new ToolCache();
// 带缓存的工具包装器
function withCache(handler: ToolHandler, cacheTTL: number = 300000): ToolHandler {
return async (params, context) => {
const toolName = context.toolName;
// 尝试从缓存获取
const cached = toolCache.get(toolName, params);
if (cached) {
return {
success: true,
result: cached,
message: '从缓存返回结果',
cached: true
};
}
// 执行工具
const result = await handler(params, context);
// 缓存成功结果
if (result.success) {
toolCache.set(toolName, params, result.result, cacheTTL);
}
return result;
};
}4. 工具测试
ts
// 工具测试框架
class ToolTester {
async testTool(tool: ChatTool, testCases: Array<{
name: string;
params: any;
expected: Partial<ToolResult>;
}>) {
const results = [];
for (const testCase of testCases) {
try {
const result = await tool.handler(testCase.params);
const passed = this.validateResult(result, testCase.expected);
results.push({
name: testCase.name,
passed,
result,
expected: testCase.expected
});
} catch (error) {
results.push({
name: testCase.name,
passed: false,
error: error.message
});
}
}
return results;
}
private validateResult(actual: ToolResult, expected: Partial<ToolResult>): boolean {
for (const [key, value] of Object.entries(expected)) {
if (actual[key] !== value) {
return false;
}
}
return true;
}
}
// 测试示例
const tester = new ToolTester();
const testResults = await tester.testTool(calculatorTool, [
{
name: '基本加法',
params: { expression: '2 + 3' },
expected: { success: true, result: 5 }
},
{
name: '复杂表达式',
params: { expression: '(10 + 5) * 2' },
expected: { success: true, result: 30 }
},
{
name: '无效表达式',
params: { expression: 'invalid' },
expected: { success: false }
}
]);🐛 常见问题与解决方案
Q1: 工具调用超时怎么办?
A: 实现超时处理机制:
ts
function withTimeout(handler: ToolHandler, timeoutMs: number = 30000): ToolHandler {
return async (params, context) => {
return Promise.race([
handler(params, context),
new Promise<ToolResult>((_, reject) => {
setTimeout(() => {
reject(new Error(`工具执行超时 (${timeoutMs}ms)`));
}, timeoutMs);
})
]);
};
}Q2: 如何处理工具权限验证?
A: 在工具处理器中添加权限检查:
ts
const secureHandler = async (params, context) => {
// 权限检查
if (!context.user.hasPermission('tool:execute')) {
return {
success: false,
error: 'PERMISSION_DENIED',
message: '没有执行此工具的权限'
};
}
// 执行工具逻辑
// ...
};Q3: 工具参数验证失败?
A: 使用 JSON Schema 验证器:
ts
import Ajv from 'ajv';
const ajv = new Ajv();
function validateParams(params: any, schema: any): { valid: boolean; errors?: string[] } {
const validate = ajv.compile(schema);
const valid = validate(params);
if (!valid) {
return {
valid: false,
errors: validate.errors?.map(err => `${err.instancePath} ${err.message}`)
};
}
return { valid: true };
}Q4: 如何调试工具执行?
A: 添加详细的日志记录:
ts
const debugTool = {
name: 'debug_example',
handler: async (params, context) => {
console.log('工具开始执行:', {
toolName: context.toolName,
params,
userId: context.user.id,
timestamp: new Date().toISOString()
});
try {
const result = await actualHandler(params);
console.log('工具执行成功:', {
toolName: context.toolName,
result,
duration: Date.now() - context.startTime
});
return result;
} catch (error) {
console.error('工具执行失败:', {
toolName: context.toolName,
error: error.message,
stack: error.stack,
params
});
throw error;
}
}
};🔗 与其他框架的集成
AG-UI 风格集成
ts
// 参考 AG-UI 的工具定义模式
const agStyleTool = {
name: 'data_visualization',
description: '创建数据可视化图表',
category: 'visualization',
icon: 'chart-bar',
parameters: {
type: 'object',
properties: {
data: {
type: 'array',
items: {
type: 'object',
properties: {
label: { type: 'string' },
value: { type: 'number' }
}
}
},
chartType: {
type: 'string',
enum: ['bar', 'line', 'pie', 'scatter'],
default: 'bar'
},
title: { type: 'string' },
theme: {
type: 'string',
enum: ['light', 'dark'],
default: 'light'
}
},
required: ['data']
},
handler: async (params) => {
const chart = await generateChart(params);
return {
success: true,
result: {
chartUrl: chart.url,
chartId: chart.id,
metadata: {
type: params.chartType,
dataPoints: params.data.length,
theme: params.theme
}
},
message: `成功创建 ${params.chartType} 图表`,
// AG-UI 风格的 UI 组件返回
ui: {
type: 'chart',
props: {
data: params.data,
type: params.chartType,
title: params.title,
theme: params.theme
}
}
};
}
};CopilotKit 风格集成
ts
// 参考 CopilotKit 的 Action 模式
const copilotStyleAction = {
name: 'create_document',
description: '创建新文档',
parameters: {
type: 'object',
properties: {
title: {
type: 'string',
description: '文档标题'
},
content: {
type: 'string',
description: '文档内容'
},
template: {
type: 'string',
enum: ['blank', 'report', 'proposal', 'memo'],
description: '文档模板'
}
},
required: ['title', 'content']
},
handler: async (params, context) => {
// CopilotKit 风格的状态更新
context.updateState('document.creating', true);
try {
const document = await createDocument({
title: params.title,
content: params.content,
template: params.template || 'blank',
userId: context.user.id
});
// 更新应用状态
context.updateState('documents', (prev) => [...prev, document]);
context.updateState('document.creating', false);
return {
success: true,
result: document,
message: `文档 "${params.title}" 创建成功`,
// CopilotKit 风格的 UI 更新
uiUpdates: [
{
type: 'navigate',
path: `/documents/${document.id}`
},
{
type: 'notification',
message: '文档创建成功',
type: 'success'
}
]
};
} catch (error) {
context.updateState('document.creating', false);
throw error;
}
}
};📖 相关文档
🎯 总结
聊天工具模块为 AI 助手提供了强大的扩展能力,通过合理的工具设计和集成,可以让 AI 助手执行各种复杂的操作。关键要点:
- 清晰的工具定义: 明确的名称、描述和参数规范
- 可靠的错误处理: 统一的错误处理和用户友好的错误信息
- 安全的权限控制: 适当的权限验证和访问控制
- 高效的性能优化: 缓存、超时处理和资源管理
- 完善的测试覆盖: 全面的测试用例和调试工具
通过遵循这些最佳实践,你可以创建出强大、可靠且用户友好的 AI 工具集成系统。