Skip to content

Using Tools

Tools are functions that agents can use to perform specific tasks. They provide agents with capabilities beyond just language processing, allowing them to perform calculations, access time information, send messages between agents, and more.

Tool Architecture

classDiagram
    class BaseTool {
        +name: str
        +description: str
        +execute()
        +get_schema()
        #_execute_impl()
    }

    class CalculatorTool {
        +name: str
        +description: str
        #_execute_impl()
    }

    class CurrentTimeTool {
        +name: str
        +description: str
        #_execute_impl()
    }

    class SendMessageTool {
        +name: str
        +description: str
        +valid_recipients: List[str]
        +agency: Agency
        #_execute_impl()
    }

    BaseTool <|-- CalculatorTool
    BaseTool <|-- CurrentTimeTool
    BaseTool <|-- SendMessageTool

Built-in Tools

1. Calculator Tool

from bedrock_swarm.tools.calculator import CalculatorTool

calculator = CalculatorTool()
result = calculator.execute(expression="15 * 7")
print(result)  # "105"

2. Current Time Tool

from bedrock_swarm.tools.time import CurrentTimeTool

time_tool = CurrentTimeTool()
time = time_tool.execute(timezone="Asia/Tokyo")
print(time)  # "10:30 PM JST"

3. Send Message Tool

from bedrock_swarm.tools.send_message import SendMessageTool

send_tool = SendMessageTool(
    valid_recipients=["calculator", "time_expert"]
)
response = send_tool.execute(
    recipient="calculator",
    message="Calculate 15 * 7"
)

Creating Custom Tools

Basic Tool Creation

from bedrock_swarm.tools.base import BaseTool
from typing import Any, Dict

class CustomTool(BaseTool):
    def __init__(
        self,
        name: str = "custom_tool",
        description: str = "Does something specific"
    ) -> None:
        self._name = name
        self._description = description

    @property
    def name(self) -> str:
        return self._name

    @property
    def description(self) -> str:
        return self._description

    def get_schema(self) -> Dict[str, Any]:
        return {
            "name": self.name,
            "description": self.description,
            "parameters": {
                "type": "object",
                "properties": {
                    "input_param": {
                        "type": "string",
                        "description": "Input parameter description"
                    }
                },
                "required": ["input_param"]
            }
        }

    def _execute_impl(self, *, input_param: str, **kwargs: Any) -> str:
        # Implement tool logic here
        return f"Processed: {input_param}"

Tool with Error Handling

class RobustTool(BaseTool):
    def _execute_impl(self, **kwargs: Any) -> str:
        try:
            # Tool logic here
            result = self._process(**kwargs)
            return str(result)
        except ValueError as e:
            raise ToolError(f"Invalid input: {str(e)}")
        except Exception as e:
            raise ToolExecutionError(f"Error during execution: {str(e)}")

Tool Usage Patterns

1. Direct Usage

# Create and use tool directly
calculator = CalculatorTool()
result = calculator.execute(expression="2 + 2")

2. Through Agents

# Create agent with tools
agent = BedrockAgent(
    tools=[CalculatorTool(), CurrentTimeTool()],
    system_prompt="Use tools as needed"
)

# Agent automatically decides when to use tools
response = agent.generate("What is 15 * 7?")

3. Tool Chaining

sequenceDiagram
    participant User
    participant Agent
    participant Tool1
    participant Tool2

    User->>Agent: Complex Query
    Agent->>Tool1: First Operation
    Tool1-->>Agent: Result 1
    Agent->>Tool2: Second Operation
    Tool2-->>Agent: Result 2
    Agent->>User: Final Response

Best Practices

1. Tool Design

# Good: Clear, single responsibility
class TimezoneTool(BaseTool):
    def _execute_impl(self, *, timezone: str) -> str:
        return get_time_in_timezone(timezone)

# Bad: Too many responsibilities
class MultiTool(BaseTool):
    def _execute_impl(self, *, action: str, data: str) -> str:
        if action == "time":
            return get_time()
        elif action == "calculate":
            return calculate(data)
        # etc...

2. Parameter Validation

class ValidatedTool(BaseTool):
    def get_schema(self) -> Dict[str, Any]:
        return {
            "name": self.name,
            "description": self.description,
            "parameters": {
                "type": "object",
                "properties": {
                    "number": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 100
                    }
                },
                "required": ["number"]
            }
        }

3. Error Messages

class UserFriendlyTool(BaseTool):
    def _execute_impl(self, *, expression: str) -> str:
        try:
            # Validate input
            if not self._is_safe_expression(expression):
                raise ValueError(
                    "Invalid expression. Only use numbers and "
                    "basic operators (+, -, *, /)"
                )

            # Process
            result = eval(expression, {"__builtins__": {}})
            return str(result)

        except Exception as e:
            raise ToolError(
                f"Could not calculate '{expression}'. "
                f"Error: {str(e)}"
            )

Advanced Topics

1. Stateful Tools

class StatefulTool(BaseTool):
    def __init__(self) -> None:
        self._state = {}
        self._name = "stateful_tool"
        self._description = "Maintains state between calls"

    def _execute_impl(
        self,
        *,
        action: str,
        key: str,
        value: Optional[str] = None
    ) -> str:
        if action == "set":
            self._state[key] = value
            return f"Stored {value} at {key}"
        elif action == "get":
            return self._state.get(key, "Not found")

3. Tool Composition

class ComposedTool(BaseTool):
    def __init__(self) -> None:
        self._calculator = CalculatorTool()
        self._time_tool = CurrentTimeTool()

    def _execute_impl(self, *, operation: str, **kwargs: Any) -> str:
        if operation == "calculate":
            return self._calculator.execute(**kwargs)
        elif operation == "time":
            return self._time_tool.execute(**kwargs)