A workflow demonstrating how to create and use class-based tools that can be integrated with AI agents to extend their capabilities with custom functionality.
Quick Start
Install Package
First, install the PraisonAI Agents package: pip install praisonaiagents
Set API Key
Set your OpenAI API key and EXA API key as environment variables in your terminal: export OPENAI_API_KEY = your_api_key_here
export EXA_API_KEY = your_exa_api_key_here
Create a file
Create a new file app.py
with the basic setup: from praisonaiagents import Agent, Task, PraisonAIAgents
import os
import requests
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field
class EXASearchTool ( BaseModel ):
"""Wrapper for EXA Search API."""
search_url: str = "https://api.exa.ai/search"
headers: Dict = {
"accept" : "application/json" ,
"content-type" : "application/json" ,
}
max_results: Optional[ int ] = None
def run ( self , query : str ) -> str :
"""Run query through EXA and return concatenated results."""
payload = {
"query" : query,
"type" : "magic" ,
}
headers = self .headers.copy()
headers[ "x-api-key" ] = os.environ[ 'EXA_API_KEY' ]
response = requests.post( self .search_url, json = payload, headers = headers)
results = response.json()
if 'results' in results:
return self ._parse_results(results[ 'results' ])
return ""
def results ( self , query : str , max_results : Optional[ int ] = None ) -> List[Dict[ str , Any]]:
"""Run query through EXA and return metadata."""
payload = {
"query" : query,
"type" : "magic" ,
}
headers = self .headers.copy()
headers[ "x-api-key" ] = os.environ[ 'EXA_API_KEY' ]
response = requests.post( self .search_url, json = payload, headers = headers)
results = response.json()
if 'results' in results:
return results[ 'results' ][:max_results] if max_results else results[ 'results' ]
return []
def _parse_results ( self , results : List[Dict[ str , Any]]) -> str :
"""Parse results into a readable string format."""
strings = []
for result in results:
try :
strings.append( ' \n ' .join([
f "Title: { result[ 'title' ] } " ,
f "Score: { result[ 'score' ] } " ,
f "Url: { result[ 'url' ] } " ,
f "ID: { result[ 'id' ] } " ,
"---"
]))
except KeyError :
continue
content = ' \n ' .join(strings)
return f " \n Search results: { content } \n "
# Create an agent with the tool
agent = Agent(
name = "SearchAgent" ,
role = "Research Assistant" ,
goal = "Search for information about 'AI Agents Framework'" ,
backstory = "I am an AI assistant that can search GitHub." ,
tools = [EXASearchTool],
self_reflect = False
)
# Create task to demonstrate the tool
task = Task(
name = "search_task" ,
description = "Search for information about 'AI Agents Framework'" ,
expected_output = "Information about AI Agents Framework" ,
agent = agent
)
# Create and start the workflow
agents = PraisonAIAgents(
agents = [agent],
tasks = [task],
verbose = True
)
agents.start()
Start Agents
Type this in your terminal to run your agents:
Requirements
Python 3.10 or higher
OpenAI API key. Generate OpenAI API key here
EXA API key for search functionality
Basic understanding of Python and Pydantic
What are Class-based Tools? Class-based tools enable:
Custom functionality encapsulation
Reusable tool components
Type-safe tool interfaces
Complex API integrations
Features
Pydantic Integration Built-in validation and type safety with Pydantic models.
API Wrapping Easily wrap external APIs as agent tools.
Method Flexibility Support for multiple methods within a single tool.
Type Hints Strong typing for better code reliability.
Configuration Options
# Create a custom tool class
class CustomTool ( BaseModel ):
"""Custom tool with configuration options."""
api_url: str = Field( default = "https://api.example.com" )
headers: Dict[ str , str ] = Field( default_factory = dict )
max_retries: int = Field( default = 3 )
def run ( self , input_data : str ) -> str :
"""Main execution method."""
# Tool implementation
return "Result"
def configure ( self , ** kwargs ):
"""Update tool configuration."""
for key, value in kwargs.items():
if hasattr ( self , key):
setattr ( self , key, value)
# Use the tool with an agent
agent = Agent(
name = "CustomAgent" ,
role = "Tool User" ,
goal = "Use custom tool functionality" ,
tools = [CustomTool],
verbose = True
)
Troubleshooting
Tool Issues If tool execution fails:
Check API credentials
Verify network connectivity
Enable verbose logging
Type Errors If type validation fails:
Review input types
Check Pydantic model
Verify method signatures
Next Steps
For optimal results, ensure your tool classes are well-documented and follow Pydantic best practices for model definition.