Files
NaliiaBot/docs/TOOL_REGISTRY_GUIDE.md
2026-02-15 16:03:09 -05:00

9.0 KiB

Tool Registry Pattern - Documentación

📋 Descripción General

El Tool Registry es un patrón de diseño que centraliza la gestión de herramientas (tools) del agente conversacional. Proporciona:

  • Registro centralizado de herramientas
  • Validación de herramientas
  • Gestión dinámica (añadir/remover tools)
  • Interfaz fluida con Builder
  • Fácil integración con el Agent
  • Conversión a formato LangChain

🏗️ Arquitectura

Componentes Principales

BaseTool (ABC)
    ├── CalculatorTool
    ├── GreeterTool
    ├── WeatherTool
    └── TimeTool

ToolRegistry
    └── Gestiona instancias de BaseTool

ToolRegistryBuilder
    └── Interfaz fluida para crear registros

Agent
    └── Integración con ToolRegistry

📝 Uso Básico

1. Crear un Registry Básico

from src.naliiabot.bot.tools.tool_registry import ToolRegistry
from src.naliiabot.bot.tools.tools import CalculatorTool, GreeterTool

# Crear el registro
registry = ToolRegistry()

# Registrar herramientas
registry.register(CalculatorTool())
registry.register(GreeterTool())

# Verificar
print(f"Tools registradas: {len(registry)}")  # Output: 2

2. Usar el Builder Pattern

from src.naliiabot.bot.tools.tool_registry import ToolRegistryBuilder

registry = (ToolRegistryBuilder()
    .add(CalculatorTool())
    .add(GreeterTool())
    .add(WeatherTool())
    .build())

3. Integrar con el Agent

from src.naliiabot.bot.agent.agent import Agent, AgentConfig

registry = (ToolRegistryBuilder()
    .add(CalculatorTool())
    .add(GreeterTool())
    .build())

config = AgentConfig(
    system_prompt="You are a helpful assistant",
    max_iterations=5
)

agent = Agent(model=your_model, config=config, tools=registry)

🔧 API del ToolRegistry

Métodos Principales

register(tool: BaseTool) -> None

Registra una nueva herramienta.

registry.register(CalculatorTool())

register_multiple(tools: List[BaseTool]) -> None

Registra múltiples herramientas de una vez.

registry.register_multiple([
    CalculatorTool(),
    GreeterTool(),
    WeatherTool()
])

unregister(tool_name: str) -> None

Desregistra una herramienta.

registry.unregister("calculator")

get_tool(tool_name: str) -> Optional[BaseTool]

Obtiene una herramienta específica.

calculator = registry.get_tool("calculator")
result = calculator.invoke(a=5, b=3, operation="add")

get_all_tools() -> List[BaseTool]

Obtiene todas las herramientas registradas.

all_tools = registry.get_all_tools()
for tool in all_tools:
    print(f"- {tool.name}")

get_langchain_tools() -> List[Tool]

Convierte a formato LangChain Tool.

langchain_tools = registry.get_langchain_tools()

has_tool(tool_name: str) -> bool

Verifica si una herramienta existe.

if registry.has_tool("calculator"):
    print("Calculator disponible")

list_tools() -> Dict[str, str]

Lista todas las herramientas con descripción.

tools_info = registry.list_tools()
# {"calculator": "Performs math operations", ...}

clear() -> None

Limpia el registro.

registry.clear()

🛠️ Crear una Nueva Tool

Paso 1: Extender BaseTool

from src.naliiabot.bot.tools.tool_registry import BaseTool
from typing import Any, Dict

class MiHerramienta(BaseTool):
    @property
    def name(self) -> str:
        return "mi_herramienta"
    
    @property
    def description(self) -> str:
        return "Descripción de qué hace mi herramienta"
    
    @property
    def args_schema(self) -> Dict[str, Any]:
        return {
            "type": "object",
            "properties": {
                "param1": {
                    "type": "string",
                    "description": "Parámetro 1"
                }
            },
            "required": ["param1"]
        }
    
    def invoke(self, **kwargs) -> str:
        param1 = kwargs.get("param1")
        # Implementar lógica
        return f"Resultado: {param1}"

Paso 2: Registrar

registry = ToolRegistry()
registry.register(MiHerramienta())

📊 Herramientas Incluidas

CalculatorTool

Realiza operaciones matemáticas básicas.

tool = CalculatorTool()
result = tool.invoke(a=10, b=5, operation="add")  # "Result: 10 add 5 = 15"

Operaciones soportadas:

  • add - Suma
  • subtract - Resta
  • multiply - Multiplicación
  • divide - División

GreeterTool

Proporciona saludos personalizados.

tool = GreeterTool()
result = tool.invoke(name="Alice", tone="formal")

Tonos disponibles:

  • formal - Saludo formal
  • casual - Saludo casual
  • friendly - Saludo amigable

WeatherTool

Obtiene información del clima.

tool = WeatherTool()
result = tool.invoke(location="Madrid", units="celsius")

TimeTool

Obtiene la hora actual.

tool = TimeTool()
result = tool.invoke(format="24h", timezone="UTC")

🧪 Testing

Tests Disponibles

pytest tests/test_tool_registry.py -v

Categorías de tests:

  1. TestBaseTool - Pruebas de herramientas individuales
  2. TestToolRegistry - Pruebas del registro
  3. TestToolRegistryBuilder - Pruebas del builder
  4. TestAgentWithRegistry - Integración con Agent
  5. TestPracticalUseCases - Casos de uso prácticos

Escribir un Test

def test_mi_herramienta():
    """Prueba mi herramienta."""
    registry = ToolRegistry()
    registry.register(MiHerramienta())
    
    tool = registry.get_tool("mi_herramienta")
    result = tool.invoke(param1="test")
    
    assert "test" in result

🔄 Casos de Uso

Caso 1: Agente de Servicio al Cliente

customer_service_registry = (ToolRegistryBuilder()
    .add(GreeterTool())
    .add(WeatherTool())
    .add(TimeTool())
    .build())

Caso 2: Agente Matemático

math_agent_registry = (ToolRegistryBuilder()
    .add(CalculatorTool())
    .build())

Caso 3: Agente Multipropósito

general_registry = (ToolRegistryBuilder()
    .add_multiple([
        CalculatorTool(),
        GreeterTool(),
        WeatherTool(),
        TimeTool()
    ])
    .build())

Caso 4: Registro Dinámico

registry = ToolRegistry()

# Añadir basado en configuración
if config.enable_calculator:
    registry.register(CalculatorTool())

if config.enable_greeting:
    registry.register(GreeterTool())

Ventajas del Patrón

1. Centralización

Todas las tools en un lugar.

2. Extensibilidad

Fácil añadir nuevas tools sin modificar el Agent.

3. Validación

Valida que las tools sean de tipo correcto.

4. Mantenibilidad

Código limpio y organizado.

5. Testing

Fácil de testear y mockear.

6. Reutilización

Registros pueden compartirse entre agentes.


🚀 Ejemplos Completos

Ver examples/tool_registry_examples.py para:

  • Ejemplo 1: Registry Básico
  • Ejemplo 2: Builder Pattern
  • Ejemplo 3: Integración con Agent
  • Ejemplo 4: Registración Dinámica
  • Ejemplo 5: Manejo de Errores
  • Ejemplo 6: Ejecución de Herramientas
  • Ejemplo 7: Formato LangChain
python examples/tool_registry_examples.py

🎯 Mejores Prácticas

1. Nombres Descriptivos

# ✅ Bien
tool.name = "get_weather"
tool.name = "calculate_math"

# ❌ Evitar
tool.name = "tool1"
tool.name = "t"

2. Descripciones Claras

# ✅ Bien
description = "Gets weather info for a location. Returns temperature, humidity, conditions."

# ❌ Evitar
description = "Gets weather"

3. Usar Builder para Registros Complejos

# ✅ Bien
registry = (ToolRegistryBuilder()
    .add(Tool1())
    .add(Tool2())
    .build())

# ❌ Evitar
registry = ToolRegistry()
registry.register(Tool1())
registry.register(Tool2())

4. Validar Argumentos

# ✅ Bien
def invoke(self, **kwargs) -> str:
    a = kwargs.get("a")
    if a is None:
        return "Error: Missing parameter 'a'"
    return str(a * 2)

📚 Referencias


FAQ

P: ¿Puedo cambiar el nombre de una herramienta? R: No directamente. Desregistra con el nombre antiguo y registra una nueva instancia.

P: ¿Qué pasa si dos tools tienen el mismo nombre? R: Se lanza ValueError. Cada tool debe tener un nombre único.

P: ¿Puedo heredar de BaseTool múltiples veces? R: No es recomendado. Usa composición en su lugar.

P: ¿Cómo convertir todo a formato LangChain? R: Usa registry.get_langchain_tools().


📞 Soporte

Para reportar bugs o sugerencias, abre un issue en el repositorio.