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

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

  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