10 KiB
10 KiB
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:
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
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:
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í:
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
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
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:
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
- Define tus herramientas específicas en
tools.py - Crea un factory o inicializar el registry en tu main
- Prueba con tests unitarios (los tests ya están en
test_naliia_agent_tools.py) - Integra con el API FastAPI en
src/naliiabotapi/main.py