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