Creating a Custom Tool

While AgentB excels at integrating with APIs via OpenAPI, you'll often want to give your agents custom capabilities – perhaps to interact with a database, a local file system, or a proprietary internal service that doesn't have an OpenAPI spec.

Goal: Create a simple custom tool that an agent can use, register it with AgentB, and see it in action. We'll make a tool that "calculates" the square of a number.

Prerequisites

Step 1: Define Your Custom Tool (ITool interface)

A custom tool in AgentB needs to implement the ITool interface. This involves two main methods:

  • getDefinition(): Returns an IToolDefinition object describing the tool's name, purpose, and parameters for the LLM.

  • execute(): Contains the actual logic of the tool.

Create a new file, e.g., customToolAgent.ts.

import * as dotenv from 'dotenv';
dotenv.config();

import {
  AgentB,
  LLMMessage,
  ITool,
  IToolDefinition,
  IToolParameter,
  IToolResult,
  IToolProvider, // We'll need this to provide our custom tool
  ToolProviderSourceConfig // For registering via AgentB facade (conceptual for custom)
} from '@ulifeai/agentb';
import readline from 'readline/promises';

// 1. Define the Custom Tool
class SquareCalculatorTool implements ITool {
  async getDefinition(): Promise<IToolDefinition> {
    return {
      name: "calculateSquare", // Name the LLM will use
      description: "Calculates the square of a given number.",
      parameters: [
        {
          name: "number",
          type: "number", // The type of the input parameter
          description: "The number to be squared.",
          required: true,
          schema: { type: "number" } // JSON schema for the parameter
        }
      ]
    };
  }

  // The 'input' will be an object matching the 'parameters' in the definition.
  // For this tool, it will be: { number: 123 }
  async execute(input: { number: number }): Promise<IToolResult> {
    if (typeof input.number !== 'number') {
      return {
        success: false,
        data: null,
        error: "Invalid input: 'number' must be a number."
      };
    }

    const result = input.number * input.number;
    console.log(`[SquareCalculatorTool] Calculated ${input.number}^2 = ${result}`);

    return {
      success: true,
      data: `The square of ${input.number} is ${result}.`, // Data returned to the LLM
    };
  }
}

Step 2: Create a Custom Tool Provider

To make this custom tool available to an agent, it needs to be provided by an IToolProvider.

Step 3: Registering the Custom Tool Provider with AgentB

The AgentB facade's registerToolProvider method is primarily designed for ToolProviderSourceConfig which describes how to create a provider (usually an OpenAPIConnector). It doesn't have a direct method to register an instance of a custom IToolProvider.

Option A: Using ApiInteractionManager directly (More Flexible for Custom Providers) For full control and easier registration of custom provider instances, you'd typically use the ApiInteractionManager directly instead of the AgentB facade. This is covered in the "In-Depth Guides".

Option B: Conceptual Registration with Facade (for this tutorial's simplicity) To keep this tutorial focused on the facade, we'll illustrate conceptually. In a real scenario with the current facade, you might: a. Extend AgentB.initialize or AgentB itself to accept custom provider instances. b. Use a "dummy" ToolProviderSourceConfig if the facade strictly requires it, and then have a mechanism to replace/augment the provider created by the internal ToolsetOrchestrator. This is complex.

For this tutorial, let's assume AgentB.initialize or a similar mechanism could take pre-instantiated providers or a more flexible configuration. We will proceed as if our custom tool provider is part of the toolProviders array, understanding this might require a slight adaptation of the real AgentB.initialize or direct use of ApiInteractionManager.

The key takeaway is how to define and use the ITool and IToolProvider interfaces.

Let's refine the AgentB.initialize part to reflect how you might integrate this if AgentB were extended or if you used ApiInteractionManager.

Step 4: Run and Interact

  1. Save customToolAgent.ts.

  2. Compile: npx tsc customToolAgent.ts

  3. Run: node customToolAgent.js (or npx ts-node customToolAgent.ts)

Example Interaction:

Key Takeaways

  • ITool Interface:

    • getDefinition(): You described your tool (name, description, parameters, parameter schema) so the LLM knows how and when to use it. The name here (calculateSquare) is what the LLM will try to call.

    • execute(): You implemented the tool's logic. The input object's structure is determined by the LLM based on your parameters definition. The IToolResult tells the agent system if the tool succeeded and what data to pass back to the LLM.

  • IToolProvider Interface:

    • You created MyCustomToolProvider to make your SquareCalculatorTool (and potentially others) available.

  • Agent Context is Key: For an agent to use a tool, its IAgentContext must contain an IToolProvider that can supply that tool. In this tutorial, we simulated a more direct agent setup to ensure our customProviderInstance was used.

  • LLM Invokes the Tool: The LLM, guided by the tool definition, decided to call calculateSquare and correctly formulated the arguments ({"number":7}).

Integrating Custom Tool Providers with the AgentB Facade More Seamlessly:

The AgentB facade currently focuses on ToolProviderSourceConfig which is geared towards OpenAPI. To make custom IToolProvider instances first-class citizens with the facade:

  1. AgentB.initialize or a new method like AgentB.registerCustomProvider(id: string, provider: IToolProvider) could be added.

  2. The internal ApiInteractionManager and ToolsetOrchestrator would need to be aware of these "custom instance" providers and integrate them into the available toolsets or directly into the agent's context.

For now, if you have many custom tools, using ApiInteractionManager directly gives you more immediate flexibility in constructing the IAgentContext with your custom tool providers.

This tutorial shows the fundamental pattern for custom tool creation, which is a powerful way to extend your agent's abilities beyond pre-defined API interactions.

Last updated