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- Sumasubtract- Restamultiply- Multiplicacióndivide- División
GreeterTool
Proporciona saludos personalizados.
tool = GreeterTool()
result = tool.invoke(name="Alice", tone="formal")
Tonos disponibles:
formal- Saludo formalcasual- Saludo casualfriendly- 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:
- TestBaseTool - Pruebas de herramientas individuales
- TestToolRegistry - Pruebas del registro
- TestToolRegistryBuilder - Pruebas del builder
- TestAgentWithRegistry - Integración con Agent
- 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.