Botovis

Tools

Built-in database tools, custom tool creation, and the tool execution system

Botovis uses a tool-based architecture where the AI agent calls specific tools to interact with your database. Each tool has a defined purpose, parameter schema, and execution logic. The agent decides which tools to use based on the user's question.

Built-in Tools

Botovis ships with 8 built-in tools, divided into read and write categories:

Read Tools

Read tools execute immediately without user confirmation.

search_records

Searches for records in a table with filtering, sorting, and pagination.

{
  "name": "search_records",
  "parameters": {
    "table": "orders",
    "conditions": [
      { "column": "status", "operator": "=", "value": "completed" },
      { "column": "total", "operator": ">", "value": 100 }
    ],
    "sort_by": "created_at",
    "sort_direction": "desc",
    "limit": 10
  }
}

Supports operators: =, !=, >, <, >=, <=, LIKE, IN, NOT IN, IS NULL, IS NOT NULL.

count_records

Counts records in a table with optional conditions.

{
  "name": "count_records",
  "parameters": {
    "table": "users",
    "conditions": [
      { "column": "is_active", "operator": "=", "value": true }
    ]
  }
}

get_sample_data

Retrieves a sample of records from a table — useful for the agent to understand data patterns.

{
  "name": "get_sample_data",
  "parameters": {
    "table": "products",
    "limit": 5
  }
}

get_column_stats

Gets statistical information about a column: min, max, average, distinct count, null count, etc.

{
  "name": "get_column_stats",
  "parameters": {
    "table": "orders",
    "column": "total"
  }
}

aggregate

Performs aggregation operations (SUM, AVG, COUNT, MIN, MAX) with optional grouping.

{
  "name": "aggregate",
  "parameters": {
    "table": "orders",
    "function": "SUM",
    "column": "total",
    "group_by": "status",
    "conditions": [
      { "column": "created_at", "operator": ">", "value": "2025-01-01" }
    ]
  }
}

Supports functions: SUM, AVG, COUNT, MIN, MAX.

Write Tools

Write tools pause the agent and require user confirmation before executing (when require_confirmation includes the action type).

create_record

Creates a new record in a table.

{
  "name": "create_record",
  "parameters": {
    "table": "products",
    "data": {
      "name": "New Product",
      "price": 29.99,
      "category_id": 3
    }
  }
}

update_record

Updates an existing record by ID.

{
  "name": "update_record",
  "parameters": {
    "table": "products",
    "id": 42,
    "data": {
      "price": 24.99,
      "updated_at": "2025-02-19"
    }
  }
}

delete_record

Deletes a record by ID.

{
  "name": "delete_record",
  "parameters": {
    "table": "products",
    "id": 42
  }
}

Tool Execution Flow

User asks a question


Agent receives user message + schema + tools


Agent decides which tool(s) to call

    ├── Read tool? → Execute immediately → Return observation to agent

    └── Write tool? → Pause agent → Send confirmation to widget

                          ├── User confirms → Execute → Return observation → Resume agent
                          └── User rejects → Agent receives rejection → Responds accordingly

Observations

After a tool executes, the result is formatted as an "observation" and fed back to the agent. The agent uses this observation to decide its next action or formulate a final answer.

For example, a count_records call might return:

Count of records in "orders" where status = completed: 1,247

The agent then uses this information to respond: "You have 1,247 completed orders."

Parallel Tool Calls

Modern LLMs can return multiple tool calls in a single response. Botovis handles this natively:

Agent response:
  ├── tool_call: count_records(table: "orders", conditions: [{status: "completed"}])
  ├── tool_call: count_records(table: "orders", conditions: [{status: "pending"}])
  └── tool_call: count_records(table: "orders", conditions: [{status: "cancelled"}])

All three execute in the same step, and the agent receives all three observations at once. This significantly reduces latency for multi-part queries.

Step Counting

All tool calls in a parallel batch count as a single agent step, not multiple steps. This means the agent can do more within the max_steps limit.

Creating Custom Tools

You can extend Botovis with custom tools by implementing the ToolInterface:

1. Implement the Interface

<?php

namespace App\Botovis\Tools;

use Botovis\Core\Tools\ToolInterface;
use Botovis\Core\Tools\ToolResult;

class WeatherTool implements ToolInterface
{
    public function name(): string
    {
        return 'get_weather';
    }

    public function description(): string
    {
        return 'Get the current weather for a given city';
    }

    public function parameters(): array
    {
        return [
            'type' => 'object',
            'properties' => [
                'city' => [
                    'type' => 'string',
                    'description' => 'The city name to get weather for',
                ],
            ],
            'required' => ['city'],
        ];
    }

    public function requiresConfirmation(): bool
    {
        return false; // Read-only tool, no confirmation needed
    }

    public function execute(array $params): ToolResult
    {
        $city = $params['city'];

        // Your logic here — call an API, query a service, etc.
        $weather = Http::get("https://api.weather.com/current?city={$city}")->json();

        return ToolResult::ok(
            "Weather in {$city}: {$weather['temp']}°C, {$weather['condition']}"
        );
    }
}

2. Define Parameters with JSON Schema

The parameters() method returns a JSON Schema object that describes the tool's input. This schema is sent to the LLM so it knows how to call your tool:

public function parameters(): array
{
    return [
        'type' => 'object',
        'properties' => [
            'query' => [
                'type' => 'string',
                'description' => 'The search query',
            ],
            'limit' => [
                'type' => 'integer',
                'description' => 'Maximum results to return',
                'default' => 10,
            ],
            'categories' => [
                'type' => 'array',
                'items' => ['type' => 'string'],
                'description' => 'Filter by categories',
            ],
        ],
        'required' => ['query'],
    ];
}

3. Return Results with ToolResult

// Success with data
return ToolResult::ok("Found 42 matching records");

// Success with structured observation
return ToolResult::ok("Found records", [
    'total' => 42,
    'records' => $records,
]);

// Error
return ToolResult::fail("Table 'nonexistent' not found");

4. Register Your Tool

Register custom tools in a service provider:

// In AppServiceProvider or a dedicated BotovisServiceProvider

use Botovis\Core\Tools\ToolRegistry;

public function boot(): void
{
    $this->app->afterResolving(ToolRegistry::class, function (ToolRegistry $registry) {
        $registry->register(new \App\Botovis\Tools\WeatherTool());
        $registry->register(new \App\Botovis\Tools\InventoryCheckTool());
        $registry->register(new \App\Botovis\Tools\ReportGeneratorTool());
    });
}

Tool Naming

Tool names should be snake_case and descriptive. The LLM uses the name and description to decide when to call your tool, so be specific. Avoid generic names like do_thing or helper.

Tool Registry

The ToolRegistry manages all available tools:

$registry = app(ToolRegistry::class);

// List all registered tools
$tools = $registry->all();

// Get a specific tool
$tool = $registry->get('search_records');

// Check if a tool exists
$exists = $registry->has('custom_tool');

// Get JSON Schema definitions for all tools (sent to LLM)
$definitions = $registry->toFunctionDefinitions();

Permission-Aware Execution

All built-in tools respect the current user's permissions:

  • Read tools check that the user has read access to the target table
  • Write tools check that the user has write access to the target table
  • If a user without write permissions asks to create a record, the tool returns an error message that the agent relays to the user
  • Custom tools should implement their own permission checks if needed

On this page