352 lines
10 KiB
Markdown
352 lines
10 KiB
Markdown
# Guía: Usar ToolRegistry para crear herramientas en NaliiaBot
|
|
|
|
El patrón **Registry** permite gestionar de forma centralizada todas las herramientas del agente. Aquí te muestro cómo usarlo.
|
|
|
|
## 1. Estructura básica
|
|
|
|
El proyecto tiene esta estructura de tools:
|
|
|
|
```
|
|
src/naliiabot/bot/tools/
|
|
├── __init__.py
|
|
├── tool_registry.py # Base + Registry
|
|
└── tools.py # Tus herramientas personalizadas
|
|
```
|
|
|
|
## 2. Crear una herramienta personalizada
|
|
|
|
En el archivo `src/naliiabot/bot/tools/tools.py`, define tu herramienta:
|
|
|
|
```python
|
|
from typing import Any, Dict
|
|
from .tool_registry import BaseTool
|
|
|
|
class ObtenerAgendaTool(BaseTool):
|
|
"""Obtiene los eventos de la agenda del usuario."""
|
|
|
|
@property
|
|
def name(self) -> str:
|
|
return "obtener_agenda"
|
|
|
|
@property
|
|
def description(self) -> str:
|
|
return "Obtiene los eventos programados en la agenda del usuario para una fecha específica."
|
|
|
|
@property
|
|
def args_schema(self) -> Dict[str, Any]:
|
|
return {
|
|
"type": "object",
|
|
"properties": {
|
|
"fecha": {
|
|
"type": "string",
|
|
"description": "Fecha en formato YYYY-MM-DD"
|
|
},
|
|
"usuario_id": {
|
|
"type": "string",
|
|
"description": "ID del usuario"
|
|
}
|
|
},
|
|
"required": ["fecha", "usuario_id"]
|
|
}
|
|
|
|
def invoke(self, **kwargs) -> str:
|
|
fecha = kwargs.get("fecha")
|
|
usuario_id = kwargs.get("usuario_id")
|
|
|
|
# Aquí va la lógica real: consultar DB, API, etc.
|
|
# Por ahora es un ejemplo simulado
|
|
return f"Eventos para {usuario_id} el {fecha}: Reunión 10:00, Almuerzo 12:30"
|
|
```
|
|
|
|
## 3. Registrar y usar herramientas
|
|
|
|
### Opción A: En el código principal
|
|
|
|
```python
|
|
from src.naliiabot.bot.tools.tool_registry import ToolRegistry
|
|
from src.naliiabot.bot.tools.tools import ObtenerAgendaTool, OtraHerramienta
|
|
from src.naliiabot.bot.agent.agent import Agent
|
|
|
|
# Crear el registry
|
|
registry = ToolRegistry()
|
|
|
|
# Registrar las herramientas
|
|
registry.register(ObtenerAgendaTool())
|
|
registry.register(OtraHerramienta())
|
|
|
|
# Obtener en formato LangChain
|
|
herramientas_langchain = registry.get_all_tools_as_langchain()
|
|
|
|
# Crear el agente con las herramientas
|
|
agent = Agent(model=mi_modelo, tools=herramientas_langchain)
|
|
```
|
|
|
|
### Opción B: Crear una factory para simplificar
|
|
|
|
Crea un archivo `src/naliiabot/bot/tools/factory.py`:
|
|
|
|
```python
|
|
from .tool_registry import ToolRegistry
|
|
from .tools import ObtenerAgendaTool, OtraHerramienta
|
|
|
|
def create_tool_registry() -> ToolRegistry:
|
|
"""Factory para crear el registry con todas las tools."""
|
|
registry = ToolRegistry()
|
|
registry.register(ObtenerAgendaTool())
|
|
registry.register(OtraHerramienta())
|
|
return registry
|
|
```
|
|
|
|
Luego úsalo así:
|
|
|
|
```python
|
|
from src.naliiabot.bot.tools.factory import create_tool_registry
|
|
from src.naliiabot.bot.agent.agent import Agent
|
|
|
|
registry = create_tool_registry()
|
|
tools = registry.get_all_tools_as_langchain()
|
|
|
|
agent = Agent(model=mi_modelo, tools=tools)
|
|
```
|
|
|
|
## 4. Métodos disponibles del ToolRegistry
|
|
|
|
```python
|
|
registry = ToolRegistry()
|
|
|
|
# Registrar una herramienta
|
|
registry.register(MiHerramienta())
|
|
|
|
# Obtener una herramienta específica
|
|
herramienta = registry.get_tool("obtener_agenda")
|
|
|
|
# Obtener todas las herramientas como BaseTool
|
|
todas = registry.get_all_tools()
|
|
|
|
# Obtener todas como LangChain Tools (para usar con Agent)
|
|
herramientas_lc = registry.get_all_tools_as_langchain()
|
|
|
|
# Verificar si existe una herramienta
|
|
existe = registry.has_tool("obtener_agenda")
|
|
|
|
# Listar nombres de todas las herramientas
|
|
nombres = registry.list_tool_names()
|
|
|
|
# Obtener descripción de una herramienta
|
|
desc = registry.get_tool_description("obtener_agenda")
|
|
```
|
|
|
|
## 5. Ejemplo completo: Herramienta para gestionar agenda
|
|
|
|
```python
|
|
from typing import Any, Dict
|
|
from .tool_registry import BaseTool
|
|
from datetime import datetime
|
|
|
|
class AgregarEventoTool(BaseTool):
|
|
"""Agrega un evento a la agenda del usuario."""
|
|
|
|
@property
|
|
def name(self) -> str:
|
|
return "agregar_evento"
|
|
|
|
@property
|
|
def description(self) -> str:
|
|
return "Agrega un nuevo evento a la agenda del usuario."
|
|
|
|
@property
|
|
def args_schema(self) -> Dict[str, Any]:
|
|
return {
|
|
"type": "object",
|
|
"properties": {
|
|
"usuario_id": {
|
|
"type": "string",
|
|
"description": "ID del usuario"
|
|
},
|
|
"titulo": {
|
|
"type": "string",
|
|
"description": "Título del evento"
|
|
},
|
|
"fecha": {
|
|
"type": "string",
|
|
"description": "Fecha en formato YYYY-MM-DD"
|
|
},
|
|
"hora_inicio": {
|
|
"type": "string",
|
|
"description": "Hora de inicio en formato HH:MM"
|
|
},
|
|
"duracion_minutos": {
|
|
"type": "integer",
|
|
"description": "Duración del evento en minutos",
|
|
"default": 30
|
|
},
|
|
"descripcion": {
|
|
"type": "string",
|
|
"description": "Descripción del evento (opcional)"
|
|
}
|
|
},
|
|
"required": ["usuario_id", "titulo", "fecha", "hora_inicio"]
|
|
}
|
|
|
|
def invoke(self, **kwargs) -> str:
|
|
usuario_id = kwargs.get("usuario_id")
|
|
titulo = kwargs.get("titulo")
|
|
fecha = kwargs.get("fecha")
|
|
hora = kwargs.get("hora_inicio")
|
|
duracion = kwargs.get("duracion_minutos", 30)
|
|
descripcion = kwargs.get("descripcion", "")
|
|
|
|
try:
|
|
# Aquí iría la lógica para guardar en BD
|
|
# base_de_datos.agregar_evento(...)
|
|
|
|
return (
|
|
f"✓ Evento '{titulo}' agregado con éxito\n"
|
|
f"Fecha: {fecha}\n"
|
|
f"Hora: {hora}\n"
|
|
f"Duración: {duracion} minutos"
|
|
)
|
|
except Exception as e:
|
|
return f"Error al agregar evento: {str(e)}"
|
|
|
|
|
|
class ModificarEventoTool(BaseTool):
|
|
"""Modifica un evento existente."""
|
|
|
|
@property
|
|
def name(self) -> str:
|
|
return "modificar_evento"
|
|
|
|
@property
|
|
def description(self) -> str:
|
|
return "Modifica los detalles de un evento existente en la agenda."
|
|
|
|
@property
|
|
def args_schema(self) -> Dict[str, Any]:
|
|
return {
|
|
"type": "object",
|
|
"properties": {
|
|
"evento_id": {
|
|
"type": "string",
|
|
"description": "ID del evento a modificar"
|
|
},
|
|
"titulo": {
|
|
"type": "string",
|
|
"description": "Nuevo título (opcional)"
|
|
},
|
|
"hora_inicio": {
|
|
"type": "string",
|
|
"description": "Nueva hora en formato HH:MM (opcional)"
|
|
},
|
|
"duracion_minutos": {
|
|
"type": "integer",
|
|
"description": "Nueva duración en minutos (opcional)"
|
|
}
|
|
},
|
|
"required": ["evento_id"]
|
|
}
|
|
|
|
def invoke(self, **kwargs) -> str:
|
|
evento_id = kwargs.get("evento_id")
|
|
|
|
try:
|
|
# Lógica para actualizar en BD
|
|
return f"✓ Evento {evento_id} modificado correctamente"
|
|
except Exception as e:
|
|
return f"Error al modificar evento: {str(e)}"
|
|
|
|
|
|
class EliminarEventoTool(BaseTool):
|
|
"""Elimina un evento de la agenda."""
|
|
|
|
@property
|
|
def name(self) -> str:
|
|
return "eliminar_evento"
|
|
|
|
@property
|
|
def description(self) -> str:
|
|
return "Elimina un evento de la agenda del usuario."
|
|
|
|
@property
|
|
def args_schema(self) -> Dict[str, Any]:
|
|
return {
|
|
"type": "object",
|
|
"properties": {
|
|
"evento_id": {
|
|
"type": "string",
|
|
"description": "ID del evento a eliminar"
|
|
},
|
|
"usuario_id": {
|
|
"type": "string",
|
|
"description": "ID del usuario propietario"
|
|
}
|
|
},
|
|
"required": ["evento_id", "usuario_id"]
|
|
}
|
|
|
|
def invoke(self, **kwargs) -> str:
|
|
evento_id = kwargs.get("evento_id")
|
|
|
|
try:
|
|
# Lógica para eliminar de BD
|
|
return f"✓ Evento {evento_id} eliminado correctamente"
|
|
except Exception as e:
|
|
return f"Error al eliminar evento: {str(e)}"
|
|
```
|
|
|
|
## 6. Usar con el Agent
|
|
|
|
Una vez que has creado tus herramientas, úsalas con el agente:
|
|
|
|
```python
|
|
from src.naliiabot.bot.tools.tool_registry import ToolRegistry
|
|
from src.naliiabot.bot.tools.tools import (
|
|
ObtenerAgendaTool,
|
|
AgregarEventoTool,
|
|
ModificarEventoTool,
|
|
EliminarEventoTool
|
|
)
|
|
from src.naliiabot.bot.agent.agent import Agent
|
|
from langchain_anthropic import ChatAnthropic
|
|
|
|
# Crear modelo
|
|
modelo = ChatAnthropic(model="claude-3-5-sonnet-20241022")
|
|
|
|
# Crear registry y registrar herramientas
|
|
registry = ToolRegistry()
|
|
registry.register(ObtenerAgendaTool())
|
|
registry.register(AgregarEventoTool())
|
|
registry.register(ModificarEventoTool())
|
|
registry.register(EliminarEventoTool())
|
|
|
|
# Crear agente con las herramientas
|
|
agent = Agent(
|
|
model=modelo,
|
|
tools=registry.get_all_tools_as_langchain()
|
|
)
|
|
|
|
# Usar el agente
|
|
resultado = agent.invoke({
|
|
"messages": [
|
|
HumanMessage(content="Agrega una reunión el 15 de febrero a las 10:00")
|
|
]
|
|
})
|
|
```
|
|
|
|
## 7. Ventajas del patrón Registry
|
|
|
|
✅ **Centralización**: Todas las tools en un solo lugar
|
|
✅ **Escalabilidad**: Fácil agregar nuevas herramientas
|
|
✅ **Testabilidad**: Cada tool se prueba independientemente
|
|
✅ **Mantenibilidad**: Cambios en una tool no afectan otras
|
|
✅ **Reusabilidad**: Las tools se pueden usar en múltiples agentes
|
|
✅ **Validación**: Verificación automática de esquemas
|
|
✅ **Inspección**: Fácil listar y documentar tools disponibles
|
|
|
|
## 8. Próximos pasos
|
|
|
|
1. Define tus herramientas específicas en `tools.py`
|
|
2. Crea un factory o inicializar el registry en tu main
|
|
3. Prueba con tests unitarios (los tests ya están en `test_naliia_agent_tools.py`)
|
|
4. Integra con el API FastAPI en `src/naliiabotapi/main.py`
|