feat: Add docs
This commit is contained in:
457
docs/TOOL_REGISTRY_GUIDE.md
Normal file
457
docs/TOOL_REGISTRY_GUIDE.md
Normal file
@@ -0,0 +1,457 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user