跳转到主要内容
AG-Kit 提供了一个强大的框架,用于创建扩展AI代理能力的自定义工具。通过完整的类型安全和无缝集成,为您的特定用例构建专业化工具。

工具创建模式

函数工具

从TypeScript/JavaScript函数创建工具:

工具包

将相关工具组织成可重用的工具包:

快速入门

基础工具创建

创建一个简单的自定义工具:
import { tool } from '@ag-kit/tools';
import { z } from 'zod';

const weatherTool = tool(
  async ({ city, units }) => {
    try {
      // 获取天气数据
      const response = await fetch(
        `https://api.weather.com/v1/current?city=${city}&units=${units}`
      );

      if (!response.ok) {
        return {
          success: false,
          error: `Weather API error: ${response.statusText}`,
          error_type: 'network'
        };
      }

      const data = await response.json();

      return {
        success: true,
        data: {
          city,
          temperature: data.temperature,
          condition: data.condition,
          humidity: data.humidity,
          units
        }
      };
    } catch (error) {
      return {
        success: false,
        error: error.message,
        error_type: 'execution'
      };
    }
  },
  {
    name: 'get_weather',
    description: '获取城市的当前天气信息',
    schema: z.object({
      city: z.string().describe('城市名称'),
      units: z.enum(['celsius', 'fahrenheit']).default('celsius')
    })
  }
);

工具集成

将自定义工具与代理一起使用:
import { Agent } from '@ag-kit/core';
import { OpenAIProvider } from '@ag-kit/providers/openai';

const provider = new OpenAIProvider({
  apiKey: process.env.OPENAI_API_KEY!,
  model: 'gpt-4'
});

const agent = new Agent({
  name: 'weather-agent',
  model: provider,
  tools: [weatherTool],
  instructions: '您可以查询任何城市的天气信息。'
});

const response = await agent.run({
  input: '旧金山的天气如何?'
});

工具架构

BaseTool接口

所有工具都实现了标准化接口:
interface BaseTool<TInput = any, TOutput = any> {
  name: string;
  description: string;
  schema: ZodSchema<TInput>;
  invoke(input: TInput): Promise<ToolResult<TOutput>>;
}

工具结果结构

所有工具的一致结果格式:
interface ToolResult<T = any> {
  success: boolean;
  data?: T;
  error?: string;
  error_type?: "validation" | "execution" | "permission" | "network";
  executionTime?: number;
}

模式验证

使用Zod进行输入验证:
import { z } from 'zod';

const complexSchema = z.object({
  // 必填字段
  id: z.string().uuid(),
  name: z.string().min(1).max(100),

  // 带默认值的可选字段
  priority: z.enum(['low', 'medium', 'high']).default('medium'),
  tags: z.array(z.string()).default([]),

  // 嵌套对象
  metadata: z.object({
    created: z.date(),
    author: z.string()
  }).optional(),

  // 条件验证
  config: z.union([
    z.object({ type: z.literal('simple'), value: z.string() }),
    z.object({ type: z.literal('complex'), settings: z.record(z.any()) })
  ])
});

工具包架构

自定义工具包

创建自定义工具包以组织相关工具:
import { BaseToolkit, tool } from '@ag-kit/tools';
import { z } from 'zod';

class WeatherToolkit extends BaseToolkit {
  constructor() {
    super({
      name: 'weather-toolkit',
      description: 'Comprehensive weather information toolkit'
    });
  }

  protected async onInitialize(): Promise<void> {
    // Add weather tools
    this.addTool(this.createCurrentWeatherTool());
    this.addTool(this.createForecastTool());
    this.addTool(this.createHistoricalTool());
  }

  private createCurrentWeatherTool() {
    return tool(
      async ({ city, units }) => {
        // Implementation
        return { success: true, data: { temperature: 22, condition: 'sunny' } };
      },
      {
        name: 'current_weather',
        description: 'Get current weather conditions',
        schema: z.object({
          city: z.string(),
          units: z.enum(['celsius', 'fahrenheit']).default('celsius')
        })
      }
    );
  }

  private createForecastTool() {
    return tool(
      async ({ city, days }) => {
        // Implementation
        return { success: true, data: { forecast: [] } };
      },
      {
        name: 'weather_forecast',
        description: 'Get weather forecast',
        schema: z.object({
          city: z.string(),
          days: z.number().min(1).max(14).default(5)
        })
      }
    );
  }

  private createHistoricalTool() {
    return tool(
      async ({ city, date }) => {
        // Implementation
        return { success: true, data: { historical: [] } };
      },
      {
        name: 'weather_historical',
        description: 'Get historical weather data',
        schema: z.object({
          city: z.string(),
          date: z.string()
        })
      }
    );
  }
}

// 使用工具包
const weatherKit = new WeatherToolkit();
await weatherKit.initialize();
const tools = weatherKit.getTools();

使用自定义工具包

初始化自定义工具包并与Agent一起使用:
// Create and initialize toolkit
const weatherToolkit = new WeatherToolkit();
await weatherToolkit.initialize();

// Get all tools from toolkit
const weatherTools = weatherToolkit.getTools();

工具包管理

使用工具包管理器进行集中式工具包管理:
import { ToolkitManager } from '@ag-kit/tools';

const toolkitManager = new ToolkitManager();
// Register toolkit
toolkitManager.register(weatherToolkit);

// Get toolkit by name
const toolkit = toolkitManager.getToolkit('weather-toolkit');

// Get all tools from all registered toolkits
const allTools = toolkitManager.getAllTools();

// Find specific tool across all toolkits
const currentWeatherTools = toolkitManager.findTool('current_weather');

// Initialize all registered toolkits
await toolkitManager.initializeAll();

// Cleanup all toolkits
await toolkitManager.destroyAll();

工具包事件

监听工具包生命周期事件:
weatherToolkit.addEventListener((event) => {
  switch (event.type) {
    case 'toolkit_initialized':
      console.log(`Toolkit ${event.toolkit.name} initialized`);
      break;
    case 'tool_added':
      console.log(`Tool ${event.tool.name} added`);
      break;
    case 'tool_executed':
      console.log(`Tool ${event.toolName} executed`);
      break;
    case 'toolkit_destroyed':
      console.log(`Toolkit ${event.toolkit.name} destroyed`);
      break;
  }
});

工具测试

单元测试

全面测试自定义工具:
import { describe, it, expect } from 'jest/globals';

describe('weatherTool', () => {
  it('should return weather data for valid city', async () => {
    const result = await weatherTool.invoke({
      city: 'San Francisco',
      units: 'celsius'
    });

    expect(result.success).toBe(true);
    expect(result.data).toHaveProperty('temperature');
    expect(result.data.city).toBe('San Francisco');
    expect(result.data.units).toBe('celsius');
  });

  it('should handle invalid city gracefully', async () => {
    const result = await weatherTool.invoke({
      city: 'InvalidCity123',
      units: 'celsius'
    });

    expect(result.success).toBe(false);
    expect(result.error_type).toBe('network');
  });

  it('should validate input schema', async () => {
    const result = await weatherTool.invoke({
      city: '', // Invalid empty city
      units: 'celsius'
    });

    expect(result.success).toBe(false);
    expect(result.error_type).toBe('validation');
  });
});

工具包测试

全面测试自定义工具包:
import { describe, it, expect, beforeEach, afterEach } from 'jest/globals';

describe('WeatherToolkit', () => {
  let weatherToolkit: WeatherToolkit;

  beforeEach(async () => {
    weatherToolkit = new WeatherToolkit();
    await weatherToolkit.initialize();
  });

  afterEach(async () => {
    await weatherToolkit.destroy();
  });

  it('should initialize with correct tools', () => {
    const toolNames = weatherToolkit.getToolNames();
    expect(toolNames).toContain('current_weather');
    expect(toolNames).toContain('weather_forecast');
    expect(toolNames).toContain('historical_weather');
    expect(weatherToolkit.getTools()).toHaveLength(3);
  });

  it('should execute tools correctly', async () => {
    const result = await weatherToolkit.invokeTool('current_weather', {
      city: 'San Francisco',
      units: 'celsius'
    });

    expect(result.success).toBe(true);
    expect(result.data).toHaveProperty('temperature');
  });

  it('should handle batch tool execution', async () => {
    const results = await weatherToolkit.invokeTools([
      { toolName: 'current_weather', input: { city: 'Tokyo' } },
      { toolName: 'weather_forecast', input: { city: 'Tokyo', days: 3 } }
    ]);

    expect(results).toHaveLength(2);
    expect(results.every(r => r.success)).toBe(true);
  });

  it('should validate toolkit integrity', () => {
    const validation = weatherToolkit.validate();
    expect(validation.valid).toBe(true);
    expect(validation.errors).toHaveLength(0);
  });

  it('should emit events correctly', async () => {
    const events: any[] = [];

    const newToolkit = new WeatherToolkit();
    newToolkit.addEventListener((event) => {
      events.push(event);
    });

    await newToolkit.initialize();
    await newToolkit.invokeTool('current_weather', { city: 'London' });
    await newToolkit.destroy();

    expect(events.some(e => e.type === 'toolkit_initialized')).toBe(true);
    expect(events.some(e => e.type === 'tool_executed')).toBe(true);
    expect(events.some(e => e.type === 'toolkit_destroyed')).toBe(true);
  });
});

性能优化

缓存

为昂贵的操作实现缓存:
const cache = new Map();

const cachedWeatherTool = tool(
  async ({ city, units }) => {
    const cacheKey = `${city}-${units}`;
    const cached = cache.get(cacheKey);

    if (cached && Date.now() - cached.timestamp < 300000) { // 5 minutes
      return {
        success: true,
        data: { ...cached.data, cached: true }
      };
    }

    const result = await fetchWeatherData(city, units);

    if (result.success) {
      cache.set(cacheKey, {
        data: result.data,
        timestamp: Date.now()
      });
    }

    return result;
  },
  {
    name: 'get_weather_cached',
    description: 'Get weather with caching',
    schema: z.object({
      city: z.string(),
      units: z.enum(['celsius', 'fahrenheit']).default('celsius')
    })
  }
);

连接池

重用连接以获得更好的性能:
class DatabaseConnectionPool {
  private pool: any[] = [];

  async getConnection() {
    if (this.pool.length > 0) {
      return this.pool.pop();
    }
    return await createNewConnection();
  }

  releaseConnection(connection: any) {
    if (this.pool.length < 10) {
      this.pool.push(connection);
    } else {
      connection.close();
    }
  }
}

const pool = new DatabaseConnectionPool();

const optimizedDbTool = tool(
  async ({ query, parameters }) => {
    const connection = await pool.getConnection();

    try {
      const result = await connection.query(query, parameters);
      return { success: true, data: result };
    } finally {
      pool.releaseConnection(connection);
    }
  },
  {
    name: 'optimized_db_query',
    description: 'Database query with connection pooling',
    schema: z.object({
      query: z.string(),
      parameters: z.array(z.any()).default([])
    })
  }
);

后续步骤