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

458 lines
9.0 KiB
Markdown

# 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
```python
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
```python
from src.naliiabot.bot.tools.tool_registry import ToolRegistryBuilder
registry = (ToolRegistryBuilder()
.add(CalculatorTool())
.add(GreeterTool())
.add(WeatherTool())
.build())
```
### 3. Integrar con el Agent
```python
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.
```python
registry.register(CalculatorTool())
```
#### `register_multiple(tools: List[BaseTool]) -> None`
Registra múltiples herramientas de una vez.
```python
registry.register_multiple([
CalculatorTool(),
GreeterTool(),
WeatherTool()
])
```
#### `unregister(tool_name: str) -> None`
Desregistra una herramienta.
```python
registry.unregister("calculator")
```
#### `get_tool(tool_name: str) -> Optional[BaseTool]`
Obtiene una herramienta específica.
```python
calculator = registry.get_tool("calculator")
result = calculator.invoke(a=5, b=3, operation="add")
```
#### `get_all_tools() -> List[BaseTool]`
Obtiene todas las herramientas registradas.
```python
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.
```python
langchain_tools = registry.get_langchain_tools()
```
#### `has_tool(tool_name: str) -> bool`
Verifica si una herramienta existe.
```python
if registry.has_tool("calculator"):
print("Calculator disponible")
```
#### `list_tools() -> Dict[str, str]`
Lista todas las herramientas con descripción.
```python
tools_info = registry.list_tools()
# {"calculator": "Performs math operations", ...}
```
#### `clear() -> None`
Limpia el registro.
```python
registry.clear()
```
---
## 🛠️ Crear una Nueva Tool
### Paso 1: Extender BaseTool
```python
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
```python
registry = ToolRegistry()
registry.register(MiHerramienta())
```
---
## 📊 Herramientas Incluidas
### CalculatorTool
Realiza operaciones matemáticas básicas.
```python
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.
```python
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.
```python
tool = WeatherTool()
result = tool.invoke(location="Madrid", units="celsius")
```
### TimeTool
Obtiene la hora actual.
```python
tool = TimeTool()
result = tool.invoke(format="24h", timezone="UTC")
```
---
## 🧪 Testing
### Tests Disponibles
```bash
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
```python
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
```python
customer_service_registry = (ToolRegistryBuilder()
.add(GreeterTool())
.add(WeatherTool())
.add(TimeTool())
.build())
```
### Caso 2: Agente Matemático
```python
math_agent_registry = (ToolRegistryBuilder()
.add(CalculatorTool())
.build())
```
### Caso 3: Agente Multipropósito
```python
general_registry = (ToolRegistryBuilder()
.add_multiple([
CalculatorTool(),
GreeterTool(),
WeatherTool(),
TimeTool()
])
.build())
```
### Caso 4: Registro Dinámico
```python
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
```bash
python examples/tool_registry_examples.py
```
---
## 🎯 Mejores Prácticas
### 1. Nombres Descriptivos
```python
# ✅ Bien
tool.name = "get_weather"
tool.name = "calculate_math"
# ❌ Evitar
tool.name = "tool1"
tool.name = "t"
```
### 2. Descripciones Claras
```python
# ✅ Bien
description = "Gets weather info for a location. Returns temperature, humidity, conditions."
# ❌ Evitar
description = "Gets weather"
```
### 3. Usar Builder para Registros Complejos
```python
# ✅ Bien
registry = (ToolRegistryBuilder()
.add(Tool1())
.add(Tool2())
.build())
# ❌ Evitar
registry = ToolRegistry()
registry.register(Tool1())
registry.register(Tool2())
```
### 4. Validar Argumentos
```python
# ✅ Bien
def invoke(self, **kwargs) -> str:
a = kwargs.get("a")
if a is None:
return "Error: Missing parameter 'a'"
return str(a * 2)
```
---
## 📚 Referencias
- [BaseTool](src/naliiabot/bot/tools/tool_registry.py#L8)
- [ToolRegistry](src/naliiabot/bot/tools/tool_registry.py#L60)
- [ToolRegistryBuilder](src/naliiabot/bot/tools/tool_registry.py#L190)
- [Herramientas](src/naliiabot/bot/tools/tools.py)
- [Tests](tests/test_tool_registry.py)
- [Ejemplos](examples/tool_registry_examples.py)
---
## ❓ 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.