Spaces:
Sleeping
Sleeping
Tools Module - Complete Working Guide
Location: src/agents/tools.py
Purpose: Atomic actions that the agentic system can execute
Status: Production-ready with 15+ built-in tools
ποΈ Architecture Overview
Component Hierarchy
Tool (Abstract Base Class)
βββ Built-in Tools (15+)
β βββ SystemStatusTool
β βββ SetTimerTool
β βββ WebSearchTool
β βββ LaunchAppTool
β βββ SendEmailTool
β βββ SendSlackMessage
β βββ OpenBrowserTool
β βββ ReadFileContent
β βββ WriteFileTool
β βββ ListDirectoryTool
β βββ CreateGmailDraft
β βββ CreateNotion
β βββ [Others...]
β
βββ Custom Tools (User-defined)
βββ Your Custom Tool 1
βββ Your Custom Tool 2
ToolRegistry (Management Layer)
βββ Register/Unregister tools
βββ Discovery & lookup
βββ Category filtering
βββ Safe execution with validation
π Core Classes
1. ToolParameter - Defines What Tool Expects
class ToolParameter:
"""Describes a single parameter for a tool"""
name: str # "duration_seconds"
type: str # "string" | "number" | "boolean" | "array" | "object"
description: str # Human-readable description
required: bool # True/False
default: Optional[Any] # Default value if not provided
enum: Optional[List[Any]] # Valid values to restrict input
items_type: Optional[str] # Type of items in array
Example:
ToolParameter(
name="duration_seconds",
type="number",
description="Duration in seconds",
required=True
)
ToolParameter(
name="info_type",
type="string",
description="Type of information to get",
required=False,
default="all",
enum=["cpu", "memory", "disk", "battery", "all"]
)
2. ToolResult - What Tool Returns
@dataclass
class ToolResult:
"""Result of tool execution"""
success: bool # True if succeeded
data: Optional[Any] # Result data
message: Optional[str] # Human-readable message
error: Optional[str] # Error message if failed
execution_time_ms: float # How long it took
metadata: Dict[str, Any] # Additional metadata
Example:
# Success result
ToolResult(
success=True,
data={
"timer_id": "timer_123",
"duration_seconds": 300,
"end_time": 1234567890
},
message="Timer set successfully",
execution_time_ms=12.5
)
# Failure result
ToolResult(
success=False,
error="Missing required parameter: duration_seconds",
execution_time_ms=2.1
)
3. ToolDescription - Metadata for Planning
@dataclass
class ToolDescription:
"""How the planner knows about a tool"""
name: str # "set_timer"
description: str # What it does
category: ToolCategory # SYSTEM, COMMUNICATION, etc.
parameters: List[ToolParameter] # What it needs
requires_confirmation: bool # Needs user approval?
examples: List[str] # Usage examples
Used by LLM to plan:
Available tools:
- set_timer(duration_seconds: number, label?: string): Set a countdown timer
- web_search(query: string, max_results?: number): Search the web
- system_status(info_type?: string): Get system information
4. Tool - Abstract Base Class
All tools inherit from this:
class Tool(ABC):
# Class-level metadata
name: str = "base_tool"
description: str = "Base tool"
category: ToolCategory = ToolCategory.CUSTOM
requires_confirmation: bool = False
def _setup_parameters(self) -> None:
"""Override to define parameters"""
pass
@abstractmethod
def execute(self, **params: Any) -> ToolResult:
"""Override to implement tool logic"""
pass
def validate_params(self, params: Dict) -> Optional[str]:
"""Checks parameters are valid"""
pass
def safe_execute(self, **params: Any) -> ToolResult:
"""Execute with validation + error handling"""
pass
def get_description(self) -> ToolDescription:
"""Get metadata for planner"""
pass
5. ToolRegistry - Manages All Tools
class ToolRegistry:
"""Central registry for all available tools"""
def register(self, tool: Tool) -> None:
"""Register a new tool"""
def unregister(self, tool_name: str) -> bool:
"""Unregister a tool"""
def get(self, tool_name: str) -> Optional[Tool]:
"""Get a specific tool"""
def list_available(self) -> List[ToolDescription]:
"""List all tools with descriptions"""
def list_by_category(self, category: ToolCategory) -> List[ToolDescription]:
"""Get tools in a category"""
def get_tools_for_prompt(self) -> str:
"""Format all tools for LLM prompts"""
def execute(self, tool_name: str, **params: Any) -> ToolResult:
"""Execute a tool by name"""
def get_stats(self) -> Dict[str, Any]:
"""Get registry statistics"""
π§ How It Works - Step by Step
Flow 1: Simple Execution
User: "Set a timer for 5 minutes"
β
NLU β Intent: set_timer
β
Planner retrieves tool description from Registry
ββ Name: set_timer
ββ Params: [duration_seconds: number, label?: string]
ββ Requires confirmation: false
ββ Examples: ["Set a timer for 5 minutes", ...]
β
Planner creates step: set_timer(duration_seconds=300, label="Timer")
β
StreamingExecutor calls:
registry.execute("set_timer", duration_seconds=300, label="Timer")
β
Registry gets the SetTimerTool and calls: tool.safe_execute(...)
ββ Validates parameters
β ββ "duration_seconds" is required β
β ββ "label" is string β
ββ Measures execution time
ββ Calls: tool.execute(duration_seconds=300, label="Timer")
ββ Returns: ToolResult(success=True, data={...})
β
StreamingExecutor emits: step_completed event
β
Metrics collector records success
β
"Timer set for 5 minutes" (audio + text)
Flow 2: Complex Multi-Tool Plan
User: "Search for Python tutorials and send results to my email"
β
Planner creates 3-step plan:
Step 1: web_search(query="Python tutorials", max_results=5)
Step 2: create_gmail_draft(to="user@gmail.com", subject="Python Tutorials", body="{results from step 1}")
Step 3: send_email(draft_id="{draft_id from step 2}")
β
Executor runs sequentially:
Step 1: web_search
ββ Registry gets WebSearchTool
ββ Validates: query="Python tutorials" β, max_results=5 β
ββ Tool tries Tavily API (if key exists)
ββ Falls back to DuckDuckGo if needed
ββ Returns: ToolResult with [{"title": "...", "link": "...", "snippet": "..."}, ...]
Step 2: create_gmail_draft (depends on Step 1)
ββ Registry gets CreateGmailDraftTool
ββ Validates: to, subject, body β
ββ Uses Google Gmail API
ββ Returns: ToolResult with draft_id and preview
Step 3: send_email (depends on Step 2)
ββ Registry gets SendEmailTool
ββ Validates: draft_id β
ββ Calls Gmail API to send
ββ Returns: ToolResult with message_id and confirmation
β
"Found 5 tutorials and sent them to your email"
Flow 3: Tool with Confirmation
User: "Delete the file important.txt"
β
Planner creates: delete_file(path="/path/to/important.txt")
β
Registry gets DeleteFileTool (requires_confirmation=True)
β
Safety Guardrails check:
ββ Action: delete_file
ββ Risk level: CRITICAL
ββ Requires confirmation: YES
ββ Reason: Destructive operation on system file
β
StreamingExecutor emits: confirmation_needed event
β
WebSocket sends to UI: "Please confirm: Delete file important.txt?"
β
User clicks "Confirm" or "Cancel"
ββ If Confirm:
β ββ ExecutionFeedbackAnalyzer records:
β - Action: delete_file
β - Result: success
β - Confirmation: approved
β - AutonomousDecisionMaker updates trust score
β
ββ If Cancel:
ββ Step marked CANCELLED, plan continues (if possible)
π Built-in Tools Reference
System Tools (ToolCategory.SYSTEM)
# 1. SystemStatusTool
get_system_info(info_type: "cpu" | "memory" | "disk" | "battery" | "all")
β ToolResult: {cpu_percent, memory_percent, disk_free_gb, battery_percent, ...}
# 2. LaunchAppTool
launch_app(app_name: str, wait: bool?)
β ToolResult: {success, message, process_id}
# 3. GetCurrentTimeTool
get_current_time()
β ToolResult: {timestamp, formatted_time, date, day_of_week}
Productivity Tools (ToolCategory.PRODUCTIVITY)
# 1. SetTimerTool
set_timer(duration_seconds: number, label: string?)
β ToolResult: {message, duration_seconds, end_time}
# 2. ReadFileContentTool
read_file(path: str, encoding: "utf-8"?)
β ToolResult: {content, size_bytes, last_modified}
# 3. WriteFileTool
write_file(path: str, content: str, append: bool?)
β ToolResult: {success, bytes_written, file_path}
# 4. ListDirectoryTool
list_directory(path: str, recursive: bool?)
β ToolResult: {files, directories, total_items}
Communication Tools (ToolCategory.COMMUNICATION)
# 1. SendEmailTool
send_email(to: str, subject: str, body: str, cc?: str, bcc?: str)
β ToolResult: {message_id, timestamp, status}
# 2. SendSlackMessageTool
send_slack_message(channel: str, message: str, thread_ts?: str)
β ToolResult: {timestamp, channel, message_text}
# 3. SendDiscordMessageTool
send_discord_message(channel_id: str, message: str)
β ToolResult: {message_id, channel_id, timestamp}
Information Tools (ToolCategory.INFORMATION)
# 1. WebSearchTool
web_search(query: str, max_results: number?)
β ToolResult: {results: [{title, link, snippet}, ...], source: "tavily" | "duckduckgo"}
# 2. WeatherTool
get_weather(location: str, units: "C" | "F"?)
β ToolResult: {temperature, description, humidity, wind_speed}
# 3. GetCurrentWeatherTool
get_current_weather(location: str, units?: string)
β ToolResult: {current_weather_data}
π» Creating Custom Tools
Example: Custom "AnalyzeSentiment" Tool
from src.agents.tools import Tool, ToolCategory, ToolParameter, ToolResult
class AnalyzeSentimentTool(Tool):
"""Analyze sentiment of text"""
name = "analyze_sentiment"
description = "Analyze the sentiment (positive, negative, neutral) of text"
category = ToolCategory.INFORMATION
requires_confirmation = False
def _setup_parameters(self) -> None:
"""Define what parameters this tool needs"""
self._parameters = [
ToolParameter(
name="text",
type="string",
description="Text to analyze",
required=True
),
ToolParameter(
name="language",
type="string",
description="Language of text",
required=False,
default="en",
enum=["en", "es", "fr", "de"]
)
]
self._examples = [
"Analyze sentiment of: I love this product!",
"What's the sentiment of this review?",
"Is this negative feedback?"
]
def execute(self, text: str, language: str = "en", **params) -> ToolResult:
"""Implement the tool logic"""
try:
# Use TextBlob for sentiment analysis
from textblob import TextBlob
blob = TextBlob(text)
polarity = blob.sentiment.polarity # -1 to 1
subjectivity = blob.sentiment.subjectivity # 0 to 1
# Classify sentiment
if polarity > 0.1:
sentiment = "positive"
elif polarity < -0.1:
sentiment = "negative"
else:
sentiment = "neutral"
return ToolResult(
success=True,
data={
"text": text[:100], # First 100 chars
"sentiment": sentiment,
"polarity": round(polarity, 3),
"subjectivity": round(subjectivity, 3),
"language": language
},
message=f"Text sentiment: {sentiment}"
)
except Exception as e:
return ToolResult(
success=False,
error=f"Failed to analyze sentiment: {str(e)}"
)
# Register the custom tool
from src.agents.tools import ToolRegistry
registry = ToolRegistry()
registry.register(AnalyzeSentimentTool())
# Now it's available for the agent to use!
result = registry.execute(
"analyze_sentiment",
text="I absolutely love this amazing product!",
language="en"
)
print(result.data)
# Output: {
# "text": "I absolutely love this amazing product!",
# "sentiment": "positive",
# "polarity": 0.875,
# "subjectivity": 0.75,
# "language": "en"
# }
π Integration with Agentic System
How Registry Integrates
# 1. In AgenticPlanner
planner = AgenticPlanner(tool_registry=registry)
# 2. When creating plans, planner calls:
tools_prompt = registry.get_tools_for_prompt()
# Returns:
# Available tools:
# - set_timer(duration_seconds: number, label?: string): Set a countdown timer
# - web_search(query: string, max_results?: number): Search the web
# ...
# 3. LLM sees available tools and includes in plan
# 4. During execution, StreamingExecutor calls:
result = registry.execute(tool_name, **params)
# 5. ExecutionFeedbackAnalyzer tracks:
feedback.record_failure(step, result) # If failed
metrics.record_step_execution(tool_name, success, latency)
π Tool Lifecycle
1. DEFINITION (Build time)
ββ Extend Tool class
ββ Define name, category, requires_confirmation
ββ Implement _setup_parameters()
ββ Implement execute() method
2. REGISTRATION (Startup time)
ββ registry.register(MyTool())
ββ Tool added to registry
ββ Indexed by category
3. DISCOVERY (Plan generation time)
ββ registry.list_available()
ββ registry.list_by_category(COMMUNICATION)
ββ registry.get_tools_for_prompt()
ββ LLM sees available tools
4. EXECUTION (Runtime)
ββ Planner includes tool in plan
ββ Executor calls registry.execute(tool_name, **params)
ββ Tool.safe_execute() validates, measures time, handles errors
ββ Returns ToolResult
5. LEARNING (Post-execution)
ββ ExecutionFeedbackAnalyzer tracks failure patterns
ββ AgentMetrics records tool reliability
ββ AutonomousDecisionMaker builds trust score
ββ Next time, system is smarter
π― Tool Execution Statistics
Tracked per Tool
metrics.get_tool_metrics("send_email")
# Returns:
{
"tool": "send_email",
"executions": 45,
"successes": 42,
"failures": 3,
"success_rate": "93.3%",
"avg_latency_ms": "1245.3",
"p95_latency_ms": "2500.1",
"top_errors": [
"SMTP connection timeout (2 times)",
"Invalid recipient address (1 time)"
]
}
Top Failing Tools
metrics.get_top_failing_tools(top_n=5)
# Returns tools sorted by failure rate:
[
{"tool": "open_slack", "failures": 12, "failure_rate": "18%"},
{"tool": "browser_automation", "failures": 8, "failure_rate": "15%"},
{"tool": "send_teams_message", "failures": 5, "failure_rate": "12%"},
...
]
π§ͺ Testing & Debugging
Test a Tool
from src.agents.tools import ToolRegistry, SetTimerTool
# Create and register
registry = ToolRegistry()
registry.register(SetTimerTool())
# Test execution
result = registry.execute(
"set_timer",
duration_seconds=300,
label="Work Break"
)
# Check result
print(f"Success: {result.success}")
print(f"Data: {result.data}")
print(f"Time taken: {result.execution_time_ms}ms")
assert result.success == True
assert result.data["duration_seconds"] == 300
assert result.execution_time_ms < 50 # Should be instant
Validate Parameters
tool = SetTimerTool()
# Test validation
error = tool.validate_params({
"duration_seconds": 300
})
print(error) # None (valid)
error = tool.validate_params({
"label": "Test"
# Missing duration_seconds!
})
print(error) # "Missing required parameter: duration_seconds"
π Performance Considerations
Execution Time Tracking
# Each tool tracks own execution time
result.execution_time_ms # How long tool took
# Metrics aggregate by tool
metrics.get_tool_metrics("web_search")
# "avg_latency_ms": "2145.3"
# "p95_latency_ms": "5342.1" # 95th percentile
Best Practices
- Keep tools focused - One thing per tool
- Validate early - Fail fast with good errors
- Handle timeouts - Don't hang forever
- Log errors - Help with debugging
- Return metadata - Include timing, IDs, etc.
π Summary
βββββββββββββββββββββββββββββββββββββββββββ
β Tools Architecture β
βββββββββββββββββββββββββββββββββββββββββββ€
β β
β Tool (ABC) β
β ββ 15+ Built-in Tools β
β ββ Unlimited Custom Tools β
β β
β ToolParameter (defines inputs) β
β ToolResult (defines outputs) β
β ToolDescription (for planner) β
β β
β ToolRegistry (central management) β
β ββ register(tool) β
β ββ execute(name, **params) β
β ββ list_available() β
β ββ get_stats() β
β β
β Integration Points: β
β ββ Planner (gets tool descriptions) β
β ββ Executor (executes tools) β
β ββ Feedback (tracks failures) β
β ββ Metrics (measures performance) β
β β
βββββββββββββββββββββββββββββββββββββββββββ
Everything is production-ready and extensible!