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 accordinglyObservations
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,247The 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