Custom Tools¶
Create powerful tools for your agents using the @tool decorator.
Basic Tool Creation¶
from reactive_agents import tool
@tool()
async def search(query: str) -> str:
"""Search the web for information.
Args:
query: The search query
"""
# Implementation
return f"Results for: {query}"
The decorator automatically:
- Extracts parameter types from type hints
- Extracts descriptions from docstrings
- Creates the tool schema for LLM function calling
Tool Parameters¶
Type Hints¶
Type hints are converted to JSON schema types:
@tool()
async def example(
text: str, # -> "string"
count: int, # -> "integer"
value: float, # -> "number"
flag: bool, # -> "boolean"
items: list, # -> "array"
data: dict, # -> "object"
) -> str:
"""Example with all types."""
pass
Optional Parameters¶
Parameters with defaults are optional:
@tool()
async def search(
query: str, # Required
limit: int = 10, # Optional (default: 10)
sort: str = "date", # Optional (default: "date")
) -> str:
"""Search with options."""
pass
Descriptions from Docstrings¶
Use Google-style docstrings for parameter descriptions:
@tool()
async def calculate(
expression: str,
precision: int = 2,
) -> str:
"""Evaluate a mathematical expression.
Args:
expression: The math expression to evaluate (e.g., "2 + 2")
precision: Number of decimal places in the result
"""
result = round(eval(expression), precision)
return str(result)
Decorator Options¶
@tool(
description="Override the docstring description",
category="math", # Tool category
requires_confirmation=True, # Requires user confirmation
cacheable=True, # Results can be cached
cache_ttl=300, # Cache time-to-live (seconds)
)
async def my_tool(param: str) -> str:
"""This description is overridden."""
pass
Synchronous Tools¶
Both sync and async functions work:
@tool()
def sync_tool(param: str) -> str:
"""A synchronous tool."""
return "result"
@tool()
async def async_tool(param: str) -> str:
"""An asynchronous tool."""
return await some_async_operation(param)
Tool Categories¶
Organize tools by category:
@tool(category="search")
async def web_search(query: str) -> str:
"""Search the web."""
pass
@tool(category="math")
async def calculate(expr: str) -> str:
"""Calculate math."""
pass
@tool(category="file")
async def read_file(path: str) -> str:
"""Read a file."""
pass
Confirmation Tools¶
For dangerous or irreversible operations:
@tool(requires_confirmation=True)
async def delete_file(path: str) -> str:
"""Delete a file (requires confirmation)."""
import os
os.remove(path)
return f"Deleted: {path}"
When used with a confirmation callback:
async def confirm(description: str, details: dict) -> bool:
print(f"Confirm: {description}")
return input("(y/n): ").lower() == "y"
agent = await (
ReactiveAgentBuilder()
.with_custom_tools([delete_file])
.with_confirmation_callback(confirm)
.build()
)
Cacheable Tools¶
For expensive operations that produce deterministic results:
@tool(cacheable=True, cache_ttl=3600) # Cache for 1 hour
async def expensive_api_call(query: str) -> str:
"""Make an expensive API call."""
# This result will be cached
return await call_expensive_api(query)
Registering Tools¶
With Builder¶
Programmatic Creation¶
from reactive_agents import create_tool_from_function
async def my_function(query: str) -> str:
return f"Result: {query}"
tool = create_tool_from_function(
my_function,
description="Custom description",
category="custom"
)
Error Handling in Tools¶
Tools should handle errors gracefully:
@tool()
async def safe_divide(a: float, b: float) -> str:
"""Divide two numbers safely.
Args:
a: The dividend
b: The divisor
"""
if b == 0:
return "Error: Cannot divide by zero"
return str(a / b)
Tool Schema Access¶
Access the generated schema:
@tool()
async def my_tool(param: str) -> str:
"""My tool."""
pass
print(my_tool.tool_definition)
# {
# "type": "function",
# "function": {
# "name": "my_tool",
# "description": "My tool.",
# "parameters": {
# "type": "object",
# "properties": {
# "param": {"type": "string", "description": "param"}
# },
# "required": ["param"]
# }
# }
# }
Best Practices¶
- Clear descriptions - Write descriptive docstrings for better LLM understanding
- Type hints - Always include type hints for parameters
- Error handling - Return error messages instead of raising exceptions
- Specific names - Use descriptive function names (e.g.,
search_webnotsearch) - Return strings - Tools should return string results for the LLM