Tools are the superpowers that make AI agents truly useful. While language models are great at understanding and generating text, tools allow agents to take real actions in the world - like searching the web, calling APIs, accessing databases, or running calculations.

What are Tools? (Simple Explanation)

Think of tools as apps that your AI agent can use. Just like you might use a calculator app, weather app, or email app on your phone, AI agents can use tools to:
  • Get Information: Search the web, query databases, read files
  • Perform Actions: Send emails, create calendar events, make API calls
  • Process Data: Run calculations, analyze spreadsheets, generate reports
  • Interact with Services: Book appointments, place orders, update records

Before and After Tools

Without Tools:
User: "What's the weather like in New York today?"
Agent: "I don't have access to current weather data. You might want to check a weather website."
With Weather Tool:
User: "What's the weather like in New York today?"
Agent: [Uses weather tool] "It's currently 72Β°F and sunny in New York with a light breeze. Perfect day to go outside!"

How Tools Work

For Business Users

Types of Tools

Information Tools

Web search, database queries, document retrieval

Communication Tools

Email, Slack, SMS, notifications

Productivity Tools

Calendar management, task creation, file operations

Business Tools

CRM updates, inventory management, analytics

Business Use Cases

Customer Support Agent Tools

Tools Available:
βœ… Order lookup tool
βœ… Inventory checker
βœ… Refund processor
βœ… Email sender
βœ… Knowledge base search

Customer: "I want to return my order"
Agent Actions:
1. Uses order lookup tool β†’ Finds order details
2. Uses refund processor β†’ Initiates return
3. Uses email sender β†’ Sends confirmation
4. Provides complete, helpful response

Sales Assistant Tools

Tools Available:
βœ… CRM integration
βœ… Product catalog
βœ… Pricing calculator
βœ… Calendar booking
βœ… Quote generator

Lead: "I need a quote for 100 units"
Agent Actions:
1. Uses product catalog β†’ Gets product details
2. Uses pricing calculator β†’ Calculates bulk pricing
3. Uses quote generator β†’ Creates professional quote
4. Uses calendar booking β†’ Schedules follow-up call

Benefits for Your Business

  • πŸš€ Increased Efficiency: Agents handle complex tasks automatically
  • πŸ“Š Better Data Access: Real-time information from your systems
  • πŸ”— System Integration: Connect all your business tools
  • ⚑ Faster Response Times: Instant access to information and actions
  • πŸ“ˆ Scalability: Handle more requests without hiring more staff

For Developers

Tool Architecture

Tool Schema Definition

Tools are defined using JSON Schema to specify their interface:
{
  "name": "weather_tool",
  "description": "Get current weather information for a specific city",
  "parameters": {
    "type": "object",
    "properties": {
      "city": {
        "type": "string",
        "description": "The city name to get weather for"
      },
      "units": {
        "type": "string",
        "enum": ["celsius", "fahrenheit"],
        "default": "celsius",
        "description": "Temperature units"
      }
    },
    "required": ["city"]
  },
  "returns": {
    "type": "object",
    "properties": {
      "temperature": {"type": "number"},
      "condition": {"type": "string"},
      "humidity": {"type": "number"},
      "wind_speed": {"type": "number"}
    }
  }
}

Creating Custom Tools

Simple HTTP API Tool

import requests
from typing import Dict, Any

class HTTPTool:
    def __init__(self, name: str, base_url: str, headers: Dict[str, str] = None):
        self.name = name
        self.base_url = base_url
        self.headers = headers or {}
    
    def get(self, endpoint: str, params: Dict[str, Any] = None) -> Dict[str, Any]:
        """Make GET request to API"""
        url = f"{self.base_url}/{endpoint.lstrip('/')}"
        response = requests.get(url, params=params, headers=self.headers)
        response.raise_for_status()
        return response.json()
    
    def post(self, endpoint: str, data: Dict[str, Any]) -> Dict[str, Any]:
        """Make POST request to API"""
        url = f"{self.base_url}/{endpoint.lstrip('/')}"
        response = requests.post(url, json=data, headers=self.headers)
        response.raise_for_status()
        return response.json()

# Example: GitHub API Tool
github_tool = HTTPTool(
    name="github_api",
    base_url="https://api.github.com",
    headers={"Authorization": "token YOUR_GITHUB_TOKEN"}
)

def search_repositories(query: str, limit: int = 5) -> Dict[str, Any]:
    """Search GitHub repositories"""
    params = {"q": query, "per_page": limit, "sort": "stars"}
    return github_tool.get("search/repositories", params)

Database Query Tool

import sqlite3
import pandas as pd
from typing import List, Dict, Any

class DatabaseTool:
    def __init__(self, database_path: str):
        self.database_path = database_path
    
    def execute_query(self, query: str, params: tuple = None) -> List[Dict[str, Any]]:
        """Execute SQL query and return results"""
        with sqlite3.connect(self.database_path) as conn:
            conn.row_factory = sqlite3.Row  # Return dict-like rows
            cursor = conn.cursor()
            
            if params:
                cursor.execute(query, params)
            else:
                cursor.execute(query)
            
            # Convert to list of dictionaries
            rows = cursor.fetchall()
            return [dict(row) for row in rows]
    
    def get_table_schema(self, table_name: str) -> List[Dict[str, str]]:
        """Get table schema information"""
        query = f"PRAGMA table_info({table_name})"
        return self.execute_query(query)
    
    def search_customers(self, name: str = None, email: str = None) -> List[Dict[str, Any]]:
        """Search customers by name or email"""
        conditions = []
        params = []
        
        if name:
            conditions.append("name LIKE ?")
            params.append(f"%{name}%")
        
        if email:
            conditions.append("email LIKE ?")
            params.append(f"%{email}%")
        
        if not conditions:
            return []
        
        query = f"SELECT * FROM customers WHERE {' AND '.join(conditions)}"
        return self.execute_query(query, tuple(params))

# Example usage
db_tool = DatabaseTool("company.db")

# Agent can use this tool to search for customers
customer_results = db_tool.search_customers(name="John", email="john@")

File Processing Tool

import os
import pandas as pd
from pathlib import Path
from typing import Union, Dict, Any, List
import json
import yaml

class FileProcessingTool:
    def __init__(self, base_directory: str):
        self.base_directory = Path(base_directory)
        self.supported_formats = ['.csv', '.json', '.yaml', '.yml', '.txt', '.xlsx']
    
    def read_file(self, file_path: str) -> Dict[str, Any]:
        """Read file and return structured data"""
        full_path = self.base_directory / file_path
        
        if not full_path.exists():
            return {"error": f"File {file_path} not found"}
        
        if not full_path.is_file():
            return {"error": f"{file_path} is not a file"}
        
        suffix = full_path.suffix.lower()
        
        try:
            if suffix == '.csv':
                df = pd.read_csv(full_path)
                return {
                    "type": "csv",
                    "rows": len(df),
                    "columns": list(df.columns),
                    "data": df.to_dict(orient='records')[:100]  # Limit to first 100 rows
                }
            
            elif suffix == '.json':
                with open(full_path, 'r') as f:
                    data = json.load(f)
                return {
                    "type": "json",
                    "data": data
                }
            
            elif suffix in ['.yaml', '.yml']:
                with open(full_path, 'r') as f:
                    data = yaml.safe_load(f)
                return {
                    "type": "yaml",
                    "data": data
                }
            
            elif suffix == '.txt':
                with open(full_path, 'r') as f:
                    content = f.read()
                return {
                    "type": "text",
                    "length": len(content),
                    "content": content[:1000]  # First 1000 chars
                }
            
            elif suffix == '.xlsx':
                df = pd.read_excel(full_path)
                return {
                    "type": "excel",
                    "rows": len(df),
                    "columns": list(df.columns),
                    "data": df.to_dict(orient='records')[:100]
                }
            
            else:
                return {"error": f"Unsupported file format: {suffix}"}
                
        except Exception as e:
            return {"error": f"Error reading file: {str(e)}"}
    
    def list_files(self, directory: str = "", pattern: str = "*") -> List[Dict[str, Any]]:
        """List files in directory"""
        target_dir = self.base_directory / directory
        
        if not target_dir.exists():
            return [{"error": f"Directory {directory} not found"}]
        
        files = []
        for file_path in target_dir.glob(pattern):
            if file_path.is_file():
                stat = file_path.stat()
                files.append({
                    "name": file_path.name,
                    "path": str(file_path.relative_to(self.base_directory)),
                    "size": stat.st_size,
                    "modified": stat.st_mtime,
                    "extension": file_path.suffix
                })
        
        return sorted(files, key=lambda x: x['modified'], reverse=True)
    
    def analyze_csv(self, file_path: str) -> Dict[str, Any]:
        """Analyze CSV file structure and content"""
        full_path = self.base_directory / file_path
        
        try:
            df = pd.read_csv(full_path)
            
            analysis = {
                "shape": df.shape,
                "columns": list(df.columns),
                "dtypes": df.dtypes.to_dict(),
                "null_counts": df.isnull().sum().to_dict(),
                "sample_data": df.head().to_dict(orient='records')
            }
            
            # Add statistics for numeric columns
            numeric_cols = df.select_dtypes(include=['number']).columns
            if len(numeric_cols) > 0:
                analysis["statistics"] = df[numeric_cols].describe().to_dict()
            
            return analysis
            
        except Exception as e:
            return {"error": f"Error analyzing CSV: {str(e)}"}

# Example usage
file_tool = FileProcessingTool("/data/uploads")

# Agent can use these tools
files = file_tool.list_files("customer_data", "*.csv")
customer_data = file_tool.read_file("customer_data/customers.csv")
analysis = file_tool.analyze_csv("sales_data/monthly_sales.csv")

Tool Registration and Management

from typing import Dict, Callable, Any
from functools import wraps
import inspect

class ToolRegistry:
    def __init__(self):
        self.tools: Dict[str, Dict[str, Any]] = {}
    
    def register_tool(self, name: str, description: str, parameters: Dict[str, Any]):
        """Decorator to register a tool"""
        def decorator(func: Callable):
            # Extract function signature
            sig = inspect.signature(func)
            
            tool_info = {
                "name": name,
                "description": description,
                "parameters": parameters,
                "function": func,
                "signature": str(sig)
            }
            
            self.tools[name] = tool_info
            
            @wraps(func)
            def wrapper(*args, **kwargs):
                # Add logging, validation, etc.
                try:
                    result = func(*args, **kwargs)
                    return {"success": True, "result": result}
                except Exception as e:
                    return {"success": False, "error": str(e)}
            
            return wrapper
        return decorator
    
    def get_tool(self, name: str) -> Dict[str, Any]:
        """Get tool information"""
        return self.tools.get(name)
    
    def list_tools(self) -> List[Dict[str, Any]]:
        """List all available tools"""
        return [
            {
                "name": tool_info["name"],
                "description": tool_info["description"],
                "parameters": tool_info["parameters"]
            }
            for tool_info in self.tools.values()
        ]
    
    def execute_tool(self, name: str, **kwargs) -> Dict[str, Any]:
        """Execute a tool with given parameters"""
        if name not in self.tools:
            return {"success": False, "error": f"Tool '{name}' not found"}
        
        tool = self.tools[name]
        try:
            result = tool["function"](**kwargs)
            return {"success": True, "result": result}
        except Exception as e:
            return {"success": False, "error": str(e)}

# Usage example
registry = ToolRegistry()

@registry.register_tool(
    name="calculate_tip",
    description="Calculate tip amount for a bill",
    parameters={
        "type": "object",
        "properties": {
            "bill_amount": {"type": "number", "description": "Total bill amount"},
            "tip_percentage": {"type": "number", "default": 15, "description": "Tip percentage"}
        },
        "required": ["bill_amount"]
    }
)
def calculate_tip(bill_amount: float, tip_percentage: float = 15) -> Dict[str, float]:
    """Calculate tip and total amount"""
    tip_amount = bill_amount * (tip_percentage / 100)
    total_amount = bill_amount + tip_amount
    
    return {
        "bill_amount": bill_amount,
        "tip_percentage": tip_percentage,
        "tip_amount": tip_amount,
        "total_amount": total_amount
    }

# Agent can now use this tool
result = registry.execute_tool("calculate_tip", bill_amount=50.0, tip_percentage=20)

Error Handling and Validation

from pydantic import BaseModel, ValidationError
from typing import Any, Dict
import logging

class ToolExecutor:
    def __init__(self):
        self.logger = logging.getLogger(__name__)
    
    def validate_input(self, tool_schema: Dict[str, Any], input_data: Dict[str, Any]) -> tuple[bool, str]:
        """Validate input against tool schema"""
        try:
            # Create Pydantic model from schema
            properties = tool_schema.get("parameters", {}).get("properties", {})
            required = tool_schema.get("parameters", {}).get("required", [])
            
            # Check required fields
            for field in required:
                if field not in input_data:
                    return False, f"Missing required field: {field}"
            
            # Type validation would go here
            return True, "Valid"
            
        except Exception as e:
            return False, f"Validation error: {str(e)}"
    
    def execute_with_retries(self, tool_func: Callable, max_retries: int = 3, **kwargs) -> Dict[str, Any]:
        """Execute tool with retry logic"""
        last_error = None
        
        for attempt in range(max_retries):
            try:
                result = tool_func(**kwargs)
                self.logger.info(f"Tool executed successfully on attempt {attempt + 1}")
                return {"success": True, "result": result, "attempts": attempt + 1}
                
            except Exception as e:
                last_error = e
                self.logger.warning(f"Tool execution failed on attempt {attempt + 1}: {str(e)}")
                
                if attempt < max_retries - 1:
                    time.sleep(2 ** attempt)  # Exponential backoff
        
        return {
            "success": False, 
            "error": f"Tool failed after {max_retries} attempts: {str(last_error)}",
            "attempts": max_retries
        }
    
    def execute_with_timeout(self, tool_func: Callable, timeout_seconds: int = 30, **kwargs) -> Dict[str, Any]:
        """Execute tool with timeout"""
        import signal
        
        def timeout_handler(signum, frame):
            raise TimeoutError(f"Tool execution timed out after {timeout_seconds} seconds")
        
        # Set up timeout
        old_handler = signal.signal(signal.SIGALRM, timeout_handler)
        signal.alarm(timeout_seconds)
        
        try:
            result = tool_func(**kwargs)
            return {"success": True, "result": result}
        except TimeoutError as e:
            return {"success": False, "error": str(e)}
        except Exception as e:
            return {"success": False, "error": f"Tool execution failed: {str(e)}"}
        finally:
            signal.alarm(0)  # Cancel timeout
            signal.signal(signal.SIGALRM, old_handler)  # Restore handler

Tool Categories

Built-in Tools

Definable.ai provides several built-in tools out of the box:

Web Search

Search the internet for current information

Calculator

Perform mathematical calculations and conversions

Code Executor

Run Python, JavaScript, and other code snippets

File Reader

Read and process various file formats

Integration Tools

Connect with popular services and platforms:
  • CRM Integration: Salesforce, HubSpot, Pipedrive
  • Communication: Slack, Microsoft Teams, Discord
  • Productivity: Google Workspace, Microsoft 365
  • E-commerce: Shopify, WooCommerce, Amazon
  • Analytics: Google Analytics, Mixpanel
  • Databases: PostgreSQL, MySQL, MongoDB

Custom Tools

Build tools specific to your business needs:
  • Internal APIs: Connect to your company’s systems
  • Proprietary Data: Access your unique data sources
  • Business Logic: Implement your specific workflows
  • Third-party Services: Integrate with specialized tools

Best Practices

Tool Design

  1. Single Responsibility: Each tool should do one thing well
  2. Clear Documentation: Provide detailed descriptions and examples
  3. Error Handling: Implement robust error handling and recovery
  4. Parameter Validation: Validate inputs before processing

Security

  1. Authentication: Secure access to sensitive tools
  2. Authorization: Control which agents can use which tools
  3. Input Sanitization: Validate and sanitize all inputs
  4. Audit Logging: Track tool usage and results

Performance

  1. Caching: Cache results when appropriate
  2. Timeouts: Set reasonable timeout limits
  3. Rate Limiting: Prevent abuse and overuse
  4. Async Processing: Use async operations for I/O operations

Testing

  1. Unit Tests: Test each tool function individually
  2. Integration Tests: Test tool interactions with external services
  3. Error Scenarios: Test failure cases and error handling
  4. Performance Tests: Ensure tools perform within acceptable limits

Troubleshooting

Common Issues

Issue: Tool returns incorrect results Solutions:
  • Validate input parameters
  • Check API documentation for changes
  • Add more comprehensive error handling
  • Test with different input combinations
Issue: Tool is too slow Solutions:
  • Implement caching for repeated requests
  • Use async operations where possible
  • Optimize database queries
  • Consider pagination for large datasets
Issue: Tool fails intermittently Solutions:
  • Add retry logic with exponential backoff
  • Implement circuit breaker pattern
  • Check network connectivity and timeouts
  • Monitor external service status

Next Steps

Now that you understand Tools, explore how they integrate with other concepts: Ready to create your first tool? Check out the Tools API Reference or start with our Getting Started Guide.