Praval Tool System Specification๏ƒ

Overview๏ƒ

The Praval Tool System provides a comprehensive framework for creating, registering, and managing tools that can be used by agents. This system extends Pravalโ€™s decorator-based architecture to support modular, reusable tools that can be shared across multiple agents.

Key Features๏ƒ

1. Declarative Tool Definition๏ƒ

Tools are defined using the @tool decorator, similar to how agents are defined with @agent:

@tool(
    tool_name="mathematical_add",
    owned_by="calculator",
    description="Add two numbers together",
    category="arithmetic"
)
def add_numbers(x: float, y: float) -> float:
    """Add two numbers and return the result."""
    return x + y

2. Tool Registry๏ƒ

A centralized registry manages all tools and their relationships to agents:

from praval import get_tool_registry

registry = get_tool_registry()
tools = registry.get_tools_for_agent("calculator")
all_tools = registry.list_all_tools()

3. Agent-Tool Association๏ƒ

Tools can be associated with agents in multiple ways:

  • Direct Ownership: owned_by="agent_name"

  • Shared Tools: shared=True (available to all agents)

  • Category-based: category="math" (agents can request tools by category)

  • Runtime Assignment: Tools can be dynamically assigned to agents

4. Type Safety and Validation๏ƒ

All tools must include proper type hints and validation:

@tool("validator", owned_by="data_processor")
def validate_email(email: str) -> bool:
    """Validate email address format."""
    import re
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return bool(re.match(pattern, email))

Architecture๏ƒ

Core Components๏ƒ

  1. ToolRegistry: Central registry for all tools

  2. Tool: Individual tool wrapper with metadata

  3. ToolCollection: Group of related tools

  4. @tool Decorator: Function decorator for tool registration

  5. Agent Integration: Seamless integration with existing agent system

Tool Metadata Structure๏ƒ

@dataclass
class ToolMetadata:
    tool_name: str
    owned_by: Optional[str] = None
    description: str = ""
    category: str = "general"
    shared: bool = False
    version: str = "1.0.0"
    author: str = ""
    tags: List[str] = field(default_factory=list)
    parameters: Dict[str, Any] = field(default_factory=dict)
    return_type: str = "Any"

Tool Registry Interface๏ƒ

class ToolRegistry:
    def register_tool(self, tool: Tool) -> None
    def get_tool(self, tool_name: str) -> Optional[Tool]
    def get_tools_for_agent(self, agent_name: str) -> List[Tool]
    def get_tools_by_category(self, category: str) -> List[Tool]
    def get_shared_tools(self) -> List[Tool]
    def list_all_tools(self) -> List[Tool]
    def assign_tool_to_agent(self, tool_name: str, agent_name: str) -> bool
    def remove_tool_from_agent(self, tool_name: str, agent_name: str) -> bool
    def clear_registry(self) -> None

Usage Patterns๏ƒ

Pattern 1: Agent-Specific Tools๏ƒ

from praval import agent, tool

@tool("calculator_add", owned_by="calculator")
def add(x: float, y: float) -> float:
    return x + y

@tool("calculator_multiply", owned_by="calculator")
def multiply(x: float, y: float) -> float:
    return x * y

@agent("calculator")
def calculator_agent(spore):
    # Tools are automatically available to this agent
    query = spore.knowledge.get("query", "")
    return {"result": "calculation complete"}

Pattern 2: Shared Tools๏ƒ

@tool("logger", shared=True, category="utility")
def log_message(level: str, message: str) -> str:
    import logging
    logger = logging.getLogger("praval.tools")
    getattr(logger, level.lower())(message)
    return f"Logged: {message}"

# Available to all agents automatically
@agent("data_processor")
def processor_agent(spore):
    # Can use the logger tool
    pass

@agent("analyzer")
def analyzer_agent(spore):
    # Can also use the logger tool
    pass

Pattern 3: Category-Based Tools๏ƒ

@tool("sin", category="trigonometry", owned_by="math_agent")
def sine(angle: float) -> float:
    import math
    return math.sin(math.radians(angle))

@tool("cos", category="trigonometry", owned_by="math_agent")
def cosine(angle: float) -> float:
    import math
    return math.cos(math.radians(angle))

# Agent can request all trigonometry tools
@agent("scientific_calculator")
def sci_calc_agent(spore):
    # Automatically gets access to trigonometry category tools
    pass

Pattern 4: Dynamic Tool Assignment๏ƒ

from praval import get_tool_registry

# Runtime tool assignment
registry = get_tool_registry()
registry.assign_tool_to_agent("advanced_math", "calculator")
registry.assign_tool_to_agent("data_export", "calculator")

Integration with Existing Agent System๏ƒ

Enhanced @agent Decorator๏ƒ

The existing @agent decorator will be enhanced to support tool integration:

@agent(
    "calculator",
    tools=["math_add", "math_multiply"],  # Specific tools
    tool_categories=["arithmetic", "geometry"],  # Tool categories
    auto_discover_tools=True  # Automatically find owned tools
)
def calculator_agent(spore):
    pass

Tool Method Exposure๏ƒ

The @agent decorator will expose tool-related methods:

@agent("processor")
def data_processor(spore):
    pass

# Enhanced tool access
data_processor.add_tool("csv_parser")
data_processor.remove_tool("xml_parser")
data_processor.list_tools()
data_processor.tool_exists("json_parser")

Error Handling and Validation๏ƒ

Tool Validation๏ƒ

class ToolValidationError(Exception):
    pass

# Automatic validation on registration
@tool("invalid_tool", owned_by="agent1")
def bad_tool(x, y):  # Missing type hints
    return x + y  # Will raise ToolValidationError

Runtime Error Handling๏ƒ

@tool("safe_divide", owned_by="calculator")
def divide(x: float, y: float) -> float:
    if y == 0:
        raise ValueError("Division by zero")
    return x / y

# Agent automatically handles tool errors
@agent("calculator")
def calc_agent(spore):
    try:
        # Tool execution is automatically wrapped in error handling
        pass
    except ValueError as e:
        return {"error": str(e)}

Tool Discovery and Introspection๏ƒ

Tool Discovery๏ƒ

from praval import discover_tools

# Discover tools in a module
tools = discover_tools("my_project.math_tools")

# Discover tools with pattern
tools = discover_tools(pattern="*_tool.py")

# Discover tools by category
math_tools = discover_tools(category="mathematics")

Tool Introspection๏ƒ

tool_info = registry.get_tool("calculator_add")
print(f"Name: {tool_info.metadata.tool_name}")
print(f"Owner: {tool_info.metadata.owned_by}")
print(f"Category: {tool_info.metadata.category}")
print(f"Parameters: {tool_info.metadata.parameters}")

Backward Compatibility๏ƒ

The new tool system maintains full backward compatibility with existing code:

  1. Existing agent.tool decorator continues to work

  2. Tools registered with old system are automatically migrated

  3. Agent behavior remains unchanged for existing implementations

Testing Requirements๏ƒ

Unit Tests๏ƒ

  • Tool registration and retrieval

  • Agent-tool association

  • Tool validation and error handling

  • Registry operations (add, remove, list, search)

Integration Tests๏ƒ

  • Tool execution within agent context

  • Cross-agent tool sharing

  • Category-based tool assignment

  • Runtime tool management

Performance Tests๏ƒ

  • Tool lookup performance with large registries

  • Memory usage with many tools

  • Tool execution overhead

Implementation Phases๏ƒ

Phase 1: Core Infrastructure๏ƒ

  • ToolRegistry implementation

  • Tool metadata classes

  • Basic @tool decorator

  • Agent integration points

Phase 2: Advanced Features๏ƒ

  • Category-based tool management

  • Shared tools system

  • Tool discovery mechanisms

  • Enhanced error handling

Phase 3: Developer Experience๏ƒ

  • CLI tools for tool management

  • Tool documentation generation

  • IDE integration helpers

  • Performance monitoring

Migration Guide๏ƒ

For Existing Code๏ƒ

# Before (current approach)
@agent("calculator")
def calc_agent(spore):
    pass

@calc_agent._praval_agent.tool
def add(x: float, y: float) -> float:
    return x + y

# After (new approach)
@tool("add", owned_by="calculator")
def add(x: float, y: float) -> float:
    return x + y

@agent("calculator")
def calc_agent(spore):
    pass

Benefits of Migration๏ƒ

  • Cleaner, more modular code

  • Better tool reusability

  • Enhanced type safety

  • Improved debugging and introspection

  • Better testing capabilities

This specification provides the foundation for a robust, scalable tool system that enhances Pravalโ€™s capabilities while maintaining its simple, decorator-based philosophy.