Deleted unused files
This commit is contained in:
148
README_TOOLS.md
148
README_TOOLS.md
@@ -1,148 +0,0 @@
|
|||||||
# 🚀 NaliiaBot - Patrón Registry para Herramientas
|
|
||||||
|
|
||||||
Bienvenido al sistema de **herramientas (tools)** de NaliiaBot.
|
|
||||||
|
|
||||||
## 📚 Documentación
|
|
||||||
|
|
||||||
Toda la documentación está en la carpeta **[`docs/`](docs/)**
|
|
||||||
|
|
||||||
### ⭐ Comienza aquí:
|
|
||||||
|
|
||||||
1. **[docs/INICIO.txt](docs/INICIO.txt)** - Bienvenida y guía visual (2 min)
|
|
||||||
2. **[docs/TOOLS_DOCUMENTATION_INDEX.md](docs/TOOLS_DOCUMENTATION_INDEX.md)** - Índice maestro (10 min)
|
|
||||||
3. **[docs/QUICK_START.py](docs/QUICK_START.py)** - Tutorial interactivo (5 min)
|
|
||||||
|
|
||||||
### 📖 Documentación completa:
|
|
||||||
|
|
||||||
| Archivo | Descripción |
|
|
||||||
|---------|-----------|
|
|
||||||
| [docs/INICIO.txt](docs/INICIO.txt) | Bienvenida con instrucciones visuales |
|
|
||||||
| [docs/TOOLS_DOCUMENTATION_INDEX.md](docs/TOOLS_DOCUMENTATION_INDEX.md) | Índice y navegación de toda la documentación |
|
|
||||||
| [docs/QUICK_START.py](docs/QUICK_START.py) | Tutorial de 5 minutos para empezar |
|
|
||||||
| [docs/TOOLS_GUIDE.md](docs/TOOLS_GUIDE.md) | Guía completa del patrón Registry |
|
|
||||||
| [docs/AGENDA_TOOLS_EXAMPLE.py](docs/AGENDA_TOOLS_EXAMPLE.py) | 5 herramientas reales de ejemplo |
|
|
||||||
| [docs/REGISTRY_IMPLEMENTATION_SUMMARY.md](docs/REGISTRY_IMPLEMENTATION_SUMMARY.md) | Resumen de la implementación |
|
|
||||||
| [docs/README_REGISTRY.md](docs/README_REGISTRY.md) | Resumen ejecutivo |
|
|
||||||
| [docs/TOOL_REGISTRY_GUIDE.md](docs/TOOL_REGISTRY_GUIDE.md) | Guía de patrones y mejores prácticas |
|
|
||||||
| [docs/CHANGES_SUMMARY.md](docs/CHANGES_SUMMARY.md) | Qué cambió en el sistema |
|
|
||||||
|
|
||||||
## 🏗️ Estructura del proyecto
|
|
||||||
|
|
||||||
```
|
|
||||||
NaliiaBot/
|
|
||||||
├── 📁 docs/ ← Toda la documentación
|
|
||||||
│ ├── INICIO.txt
|
|
||||||
│ ├── TOOLS_DOCUMENTATION_INDEX.md
|
|
||||||
│ ├── QUICK_START.py
|
|
||||||
│ ├── TOOLS_GUIDE.md
|
|
||||||
│ ├── AGENDA_TOOLS_EXAMPLE.py
|
|
||||||
│ └── ... (más documentación)
|
|
||||||
│
|
|
||||||
├── 📁 src/naliiabot/bot/tools/
|
|
||||||
│ ├── __init__.py
|
|
||||||
│ ├── tool_registry.py (infraestructura)
|
|
||||||
│ └── tools.py (← Aquí van TUS herramientas)
|
|
||||||
│
|
|
||||||
└── 📁 tests/
|
|
||||||
└── test_naliia_agent_tools.py
|
|
||||||
```
|
|
||||||
|
|
||||||
## ✨ ¿Qué es el patrón Registry?
|
|
||||||
|
|
||||||
Sistema centralizado para registrar y gestionar herramientas del agente:
|
|
||||||
|
|
||||||
1. **Definir** - Crear una herramienta que herede de `BaseTool`
|
|
||||||
2. **Registrar** - Agregar a `ToolRegistry`
|
|
||||||
3. **Usar** - Pasar al `Agent` para que el LLM las use
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Crear
|
|
||||||
class MiTool(BaseTool):
|
|
||||||
def name(self): return "mi_herramienta"
|
|
||||||
# ...
|
|
||||||
|
|
||||||
# Registrar
|
|
||||||
registry = ToolRegistry()
|
|
||||||
registry.register(MiTool())
|
|
||||||
|
|
||||||
# Usar
|
|
||||||
agent = Agent(model=modelo, tools=registry.get_all_tools_as_langchain())
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🎯 Próximos pasos
|
|
||||||
|
|
||||||
1. **Lee** la bienvenida: [docs/INICIO.txt](docs/INICIO.txt)
|
|
||||||
2. **Entiende** la estructura: [docs/TOOLS_DOCUMENTATION_INDEX.md](docs/TOOLS_DOCUMENTATION_INDEX.md)
|
|
||||||
3. **Sigue** el tutorial: [docs/QUICK_START.py](docs/QUICK_START.py)
|
|
||||||
4. **Crea** tus herramientas en: `src/naliiabot/bot/tools/tools.py`
|
|
||||||
|
|
||||||
## 📝 Crear tu primera herramienta
|
|
||||||
|
|
||||||
En `src/naliiabot/bot/tools/tools.py`:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from typing import Any, Dict
|
|
||||||
from .tool_registry import BaseTool
|
|
||||||
|
|
||||||
class MiPrimeraHerramienta(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": "Un parámetro"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["param1"]
|
|
||||||
}
|
|
||||||
|
|
||||||
def invoke(self, **kwargs) -> str:
|
|
||||||
param = kwargs.get("param1")
|
|
||||||
return f"Resultado para {param}"
|
|
||||||
```
|
|
||||||
|
|
||||||
Luego regístrala:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from src.naliiabot.bot.tools import ToolRegistry
|
|
||||||
from src.naliiabot.bot.tools.tools import MiPrimeraHerramienta
|
|
||||||
|
|
||||||
registry = ToolRegistry()
|
|
||||||
registry.register(MiPrimeraHerramienta())
|
|
||||||
tools = registry.get_all_tools_as_langchain()
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔗 Enlaces rápidos
|
|
||||||
|
|
||||||
| Necesito... | Ir a... |
|
|
||||||
|-----------|---------|
|
|
||||||
| Entender el sistema | [docs/TOOLS_DOCUMENTATION_INDEX.md](docs/TOOLS_DOCUMENTATION_INDEX.md) |
|
|
||||||
| Un tutorial rápido | [docs/QUICK_START.py](docs/QUICK_START.py) |
|
|
||||||
| Crear una herramienta | [docs/TOOLS_GUIDE.md](docs/TOOLS_GUIDE.md#2-crear-una-herramienta-personalizada) |
|
|
||||||
| Ver ejemplos | [docs/AGENDA_TOOLS_EXAMPLE.py](docs/AGENDA_TOOLS_EXAMPLE.py) |
|
|
||||||
| Registrar tools | [docs/TOOLS_GUIDE.md](docs/TOOLS_GUIDE.md#3-registrar-y-usar-herramientas) |
|
|
||||||
| Métodos disponibles | [docs/TOOLS_GUIDE.md](docs/TOOLS_GUIDE.md#4-métodos-disponibles-del-toolregistry) |
|
|
||||||
| Hacer tests | [docs/TOOLS_GUIDE.md](docs/TOOLS_GUIDE.md#7-mejores-prácticas) |
|
|
||||||
|
|
||||||
## ✅ ¿Qué obtienes?
|
|
||||||
|
|
||||||
✅ Sistema centralizado de herramientas
|
|
||||||
✅ Documentación completa y detallada
|
|
||||||
✅ Ejemplos funcionales
|
|
||||||
✅ Tests automáticos
|
|
||||||
✅ Guías paso a paso
|
|
||||||
✅ Listo para producción
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Comenzar:** Lee [docs/INICIO.txt](docs/INICIO.txt) ahora (2 minutos)
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
╔════════════════════════════════════════════════════════════════════════════╗
|
|
||||||
║ ESTRUCTURA DEL PROYECTO ║
|
|
||||||
║ ║
|
|
||||||
║ NaliiaBot - Patrón Registry ║
|
|
||||||
╚════════════════════════════════════════════════════════════════════════════╝
|
|
||||||
|
|
||||||
|
|
||||||
📁 RAÍZ DEL PROYECTO
|
|
||||||
═══════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
README.md ← Empieza aquí (proyecto)
|
|
||||||
README_TOOLS.md ← Documentación de tools
|
|
||||||
pyproject.toml
|
|
||||||
htmlcov/
|
|
||||||
|
|
||||||
|
|
||||||
📁 docs/ 📚 DOCUMENTACIÓN COMPLETA
|
|
||||||
═══════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
INICIO.txt ← Bienvenida (2 min) ⭐
|
|
||||||
|
|
||||||
TOOLS_DOCUMENTATION_INDEX.md ← Índice maestro (10 min) ⭐
|
|
||||||
|
|
||||||
QUICK_START.py ← Tutorial interactivo (5 min) ⭐
|
|
||||||
|
|
||||||
TOOLS_GUIDE.md ← Guía completa (20 min)
|
|
||||||
|
|
||||||
AGENDA_TOOLS_EXAMPLE.py ← 5 herramientas reales (15 min)
|
|
||||||
|
|
||||||
REGISTRY_IMPLEMENTATION_SUMMARY.md
|
|
||||||
|
|
||||||
README_REGISTRY.md
|
|
||||||
|
|
||||||
TOOL_REGISTRY_GUIDE.md
|
|
||||||
|
|
||||||
CHANGES_SUMMARY.md
|
|
||||||
|
|
||||||
|
|
||||||
📁 src/naliiabot/bot/tools/ 🔧 CÓDIGO DEL REGISTRY
|
|
||||||
═══════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
__init__.py ← Exporta: ToolRegistry, BaseTool
|
|
||||||
|
|
||||||
tool_registry.py ← Infraestructura (BaseTool + Registry)
|
|
||||||
|
|
||||||
tools.py ← ← ← TUS HERRAMIENTAS VAN AQUÍ
|
|
||||||
|
|
||||||
|
|
||||||
📁 src/naliiabot/bot/agent/ 🤖 AGENTE
|
|
||||||
═══════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
agent.py ← Agente principal
|
|
||||||
|
|
||||||
schemas.py ← Schemas del agente
|
|
||||||
|
|
||||||
|
|
||||||
📁 tests/ ✅ TESTS
|
|
||||||
═══════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
test_naliia_agent_tools.py ← Tests de las tools
|
|
||||||
|
|
||||||
test_agent.py ← Tests del agente
|
|
||||||
|
|
||||||
|
|
||||||
═══════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
🚀 COMIENZA AQUÍ:
|
|
||||||
|
|
||||||
1. Lee: README.md
|
|
||||||
2. Lee: docs/TOOLS_DOCUMENTATION_INDEX.md
|
|
||||||
3. Lee: docs/QUICK_START.py
|
|
||||||
4. Crea: src/naliiabot/bot/tools/tools.py (tus herramientas)
|
|
||||||
5. Testa: pytest tests/test_naliia_agent_tools.py
|
|
||||||
|
|
||||||
═══════════════════════════════════════════════════════════════════════════
|
|
||||||
@@ -1,484 +0,0 @@
|
|||||||
"""
|
|
||||||
Ejemplo práctico: Herramientas para gestión de agenda.
|
|
||||||
|
|
||||||
Este archivo muestra cómo implementar tools reales para el agente Naliia
|
|
||||||
que está enfocado en gestión de agenda.
|
|
||||||
|
|
||||||
NOTA: Este es un archivo de ejemplo para entender la estructura.
|
|
||||||
Para usar estas herramientas reales, cópilas al archivo:
|
|
||||||
src/naliiabot/bot/tools/tools.py
|
|
||||||
"""
|
|
||||||
|
|
||||||
from typing import Any, Dict
|
|
||||||
from src.naliiabot.bot.tools.tool_registry import BaseTool
|
|
||||||
|
|
||||||
|
|
||||||
class ObtenerAgendaTool(BaseTool):
|
|
||||||
"""
|
|
||||||
Obtiene los eventos de la agenda del usuario para una fecha.
|
|
||||||
|
|
||||||
Ejemplo de uso por el LLM:
|
|
||||||
- "¿Qué tengo programado para el 15 de febrero?"
|
|
||||||
- "Muéstrame mi agenda de hoy"
|
|
||||||
"""
|
|
||||||
|
|
||||||
@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. Retorna una lista con hora, título "
|
|
||||||
"y descripción de cada evento."
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def args_schema(self) -> Dict[str, Any]:
|
|
||||||
return {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"fecha": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Fecha en formato YYYY-MM-DD (ej: 2026-02-15)",
|
|
||||||
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
|
|
||||||
},
|
|
||||||
"usuario_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "ID único del usuario"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["fecha", "usuario_id"]
|
|
||||||
}
|
|
||||||
|
|
||||||
def invoke(self, **kwargs) -> str:
|
|
||||||
"""
|
|
||||||
Ejecuta la obtención de eventos.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
fecha: Fecha a consultar (YYYY-MM-DD)
|
|
||||||
usuario_id: ID del usuario
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
String con los eventos o mensaje de error
|
|
||||||
"""
|
|
||||||
fecha = kwargs.get("fecha", "")
|
|
||||||
usuario_id = kwargs.get("usuario_id", "")
|
|
||||||
|
|
||||||
if not fecha or not usuario_id:
|
|
||||||
return "Error: Se requieren 'fecha' y 'usuario_id'"
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Aquí iría la consulta real a base de datos
|
|
||||||
# eventos = db.obtener_eventos(usuario_id, fecha)
|
|
||||||
|
|
||||||
# Simulación para el ejemplo
|
|
||||||
eventos_simulados = {
|
|
||||||
"2026-02-15": [
|
|
||||||
{"hora": "09:00", "titulo": "Standup", "duracion": 30},
|
|
||||||
{"hora": "10:30", "titulo": "Reunión con equipo", "duracion": 60},
|
|
||||||
{"hora": "14:00", "titulo": "Review", "duracion": 45},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
eventos = eventos_simulados.get(fecha, [])
|
|
||||||
|
|
||||||
if not eventos:
|
|
||||||
return f"No hay eventos programados para {fecha}"
|
|
||||||
|
|
||||||
resultado = f"Agenda para {fecha}:\n"
|
|
||||||
for evento in eventos:
|
|
||||||
resultado += f" • {evento['hora']}: {evento['titulo']} ({evento['duracion']} min)\n"
|
|
||||||
|
|
||||||
return resultado
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
return f"Error al obtener agenda: {str(e)}"
|
|
||||||
|
|
||||||
|
|
||||||
class AgregarEventoTool(BaseTool):
|
|
||||||
"""
|
|
||||||
Agrega un nuevo evento a la agenda del usuario.
|
|
||||||
|
|
||||||
Ejemplo de uso:
|
|
||||||
- "Agrega una reunión el 15 de febrero a las 10:00"
|
|
||||||
- "Programa una llamada el 20/02 a las 14:30 por 45 minutos"
|
|
||||||
"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self) -> str:
|
|
||||||
return "agregar_evento"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def description(self) -> str:
|
|
||||||
return (
|
|
||||||
"Agrega un nuevo evento a la agenda del usuario. "
|
|
||||||
"Requiere título, fecha, hora de inicio y duración."
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def args_schema(self) -> Dict[str, Any]:
|
|
||||||
return {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"usuario_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "ID del usuario propietario de la agenda"
|
|
||||||
},
|
|
||||||
"titulo": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Título o nombre del evento"
|
|
||||||
},
|
|
||||||
"fecha": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Fecha en formato YYYY-MM-DD",
|
|
||||||
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
|
|
||||||
},
|
|
||||||
"hora_inicio": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Hora de inicio en formato HH:MM (24h)",
|
|
||||||
"pattern": "^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$"
|
|
||||||
},
|
|
||||||
"duracion_minutos": {
|
|
||||||
"type": "integer",
|
|
||||||
"description": "Duración del evento en minutos",
|
|
||||||
"default": 30,
|
|
||||||
"minimum": 5,
|
|
||||||
"maximum": 480
|
|
||||||
},
|
|
||||||
"descripcion": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Descripción adicional del evento (opcional)"
|
|
||||||
},
|
|
||||||
"participantes": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {"type": "string"},
|
|
||||||
"description": "Lista de emails de participantes (opcional)"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["usuario_id", "titulo", "fecha", "hora_inicio"]
|
|
||||||
}
|
|
||||||
|
|
||||||
def invoke(self, **kwargs) -> str:
|
|
||||||
"""
|
|
||||||
Agrega un evento a la agenda.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Confirmación del evento agregado o mensaje de error
|
|
||||||
"""
|
|
||||||
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", "")
|
|
||||||
participantes = kwargs.get("participantes", [])
|
|
||||||
|
|
||||||
# Validaciones
|
|
||||||
if not all([usuario_id, titulo, fecha, hora]):
|
|
||||||
return "Error: Faltan parámetros requeridos"
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Aquí iría la lógica para guardar en BD
|
|
||||||
# db.agregar_evento(usuario_id, titulo, fecha, hora, duracion, etc)
|
|
||||||
|
|
||||||
resultado = (
|
|
||||||
f"✓ Evento agregado correctamente\n"
|
|
||||||
f" Título: {titulo}\n"
|
|
||||||
f" Fecha: {fecha}\n"
|
|
||||||
f" Hora: {hora}\n"
|
|
||||||
f" Duración: {duracion} minutos"
|
|
||||||
)
|
|
||||||
|
|
||||||
if descripcion:
|
|
||||||
resultado += f"\n Descripción: {descripcion}"
|
|
||||||
|
|
||||||
if participantes:
|
|
||||||
resultado += f"\n Participantes: {', '.join(participantes)}"
|
|
||||||
|
|
||||||
return resultado
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
return f"Error al agregar evento: {str(e)}"
|
|
||||||
|
|
||||||
|
|
||||||
class ModificarEventoTool(BaseTool):
|
|
||||||
"""
|
|
||||||
Modifica los detalles de un evento existente.
|
|
||||||
|
|
||||||
Ejemplo de uso:
|
|
||||||
- "Cambia la reunión de las 10:00 a las 11:00"
|
|
||||||
- "Aumenta la duración de la reunión a 60 minutos"
|
|
||||||
"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self) -> str:
|
|
||||||
return "modificar_evento"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def description(self) -> str:
|
|
||||||
return (
|
|
||||||
"Modifica los detalles de un evento existente en la agenda. "
|
|
||||||
"Se puede cambiar hora, duración, título o descripción."
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def args_schema(self) -> Dict[str, Any]:
|
|
||||||
return {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"evento_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "ID único del evento a modificar"
|
|
||||||
},
|
|
||||||
"usuario_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "ID del usuario propietario"
|
|
||||||
},
|
|
||||||
"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)"
|
|
||||||
},
|
|
||||||
"descripcion": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Nueva descripción (opcional)"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["evento_id", "usuario_id"]
|
|
||||||
}
|
|
||||||
|
|
||||||
def invoke(self, **kwargs) -> str:
|
|
||||||
evento_id = kwargs.get("evento_id")
|
|
||||||
usuario_id = kwargs.get("usuario_id")
|
|
||||||
|
|
||||||
cambios = {}
|
|
||||||
if "titulo" in kwargs:
|
|
||||||
cambios["titulo"] = kwargs["titulo"]
|
|
||||||
if "hora_inicio" in kwargs:
|
|
||||||
cambios["hora_inicio"] = kwargs["hora_inicio"]
|
|
||||||
if "duracion_minutos" in kwargs:
|
|
||||||
cambios["duracion_minutos"] = kwargs["duracion_minutos"]
|
|
||||||
if "descripcion" in kwargs:
|
|
||||||
cambios["descripcion"] = kwargs["descripcion"]
|
|
||||||
|
|
||||||
if not cambios:
|
|
||||||
return "Error: No hay cambios para aplicar"
|
|
||||||
|
|
||||||
try:
|
|
||||||
# db.modificar_evento(usuario_id, evento_id, cambios)
|
|
||||||
|
|
||||||
resultado = f"✓ Evento {evento_id} modificado:\n"
|
|
||||||
for campo, valor in cambios.items():
|
|
||||||
resultado += f" • {campo}: {valor}\n"
|
|
||||||
|
|
||||||
return resultado
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
return f"Error al modificar evento: {str(e)}"
|
|
||||||
|
|
||||||
|
|
||||||
class EliminarEventoTool(BaseTool):
|
|
||||||
"""
|
|
||||||
Elimina un evento de la agenda.
|
|
||||||
|
|
||||||
Ejemplo de uso:
|
|
||||||
- "Elimina la reunión de las 10:00"
|
|
||||||
- "Cancela el evento 'Standup' del 15 de febrero"
|
|
||||||
"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self) -> str:
|
|
||||||
return "eliminar_evento"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def description(self) -> str:
|
|
||||||
return (
|
|
||||||
"Elimina un evento de la agenda del usuario. "
|
|
||||||
"Requiere confirmación para evitar eliminaciones accidentales."
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def args_schema(self) -> Dict[str, Any]:
|
|
||||||
return {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"evento_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "ID único del evento a eliminar"
|
|
||||||
},
|
|
||||||
"usuario_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "ID del usuario propietario"
|
|
||||||
},
|
|
||||||
"confirmar": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Confirmación de eliminación",
|
|
||||||
"default": False
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["evento_id", "usuario_id", "confirmar"]
|
|
||||||
}
|
|
||||||
|
|
||||||
def invoke(self, **kwargs) -> str:
|
|
||||||
evento_id = kwargs.get("evento_id")
|
|
||||||
usuario_id = kwargs.get("usuario_id")
|
|
||||||
confirmar = kwargs.get("confirmar", False)
|
|
||||||
|
|
||||||
if not confirmar:
|
|
||||||
return (
|
|
||||||
f"⚠️ Confirmación requerida para eliminar evento {evento_id}. "
|
|
||||||
f"Por favor, confirma: confirmar=true"
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
# db.eliminar_evento(usuario_id, evento_id)
|
|
||||||
|
|
||||||
return f"✓ Evento {evento_id} eliminado correctamente de la agenda"
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
return f"Error al eliminar evento: {str(e)}"
|
|
||||||
|
|
||||||
|
|
||||||
class ListarAgendaTool(BaseTool):
|
|
||||||
"""
|
|
||||||
Lista todos los eventos del usuario en un rango de fechas.
|
|
||||||
|
|
||||||
Ejemplo de uso:
|
|
||||||
- "Muestra mi agenda de la próxima semana"
|
|
||||||
- "¿Qué tengo programado entre el 10 y el 20 de febrero?"
|
|
||||||
"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self) -> str:
|
|
||||||
return "listar_agenda"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def description(self) -> str:
|
|
||||||
return (
|
|
||||||
"Lista todos los eventos de la agenda del usuario "
|
|
||||||
"dentro de un rango de fechas especificado."
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def args_schema(self) -> Dict[str, Any]:
|
|
||||||
return {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"usuario_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "ID del usuario"
|
|
||||||
},
|
|
||||||
"fecha_inicio": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Fecha de inicio en formato YYYY-MM-DD"
|
|
||||||
},
|
|
||||||
"fecha_fin": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Fecha de fin en formato YYYY-MM-DD"
|
|
||||||
},
|
|
||||||
"filtro_titulo": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Filtrar por título del evento (opcional)"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["usuario_id", "fecha_inicio", "fecha_fin"]
|
|
||||||
}
|
|
||||||
|
|
||||||
def invoke(self, **kwargs) -> str:
|
|
||||||
usuario_id = kwargs.get("usuario_id")
|
|
||||||
fecha_inicio = kwargs.get("fecha_inicio")
|
|
||||||
fecha_fin = kwargs.get("fecha_fin")
|
|
||||||
filtro = kwargs.get("filtro_titulo", "")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# eventos = db.listar_eventos(usuario_id, fecha_inicio, fecha_fin, filtro)
|
|
||||||
|
|
||||||
resultado = (
|
|
||||||
f"Agenda de {fecha_inicio} a {fecha_fin}:\n"
|
|
||||||
f" • Total de eventos: 3\n"
|
|
||||||
f" • Horas ocupadas: 9\n"
|
|
||||||
f" • Tiempo libre: 7 horas"
|
|
||||||
)
|
|
||||||
|
|
||||||
return resultado
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
return f"Error al listar agenda: {str(e)}"
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# CÓMO USAR ESTAS HERRAMIENTAS
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
"""
|
|
||||||
OPCIÓN 1: Copiar a tools.py
|
|
||||||
|
|
||||||
1. Copia las clases de herramientas a: src/naliiabot/bot/tools/tools.py
|
|
||||||
2. En tu código principal:
|
|
||||||
|
|
||||||
from src.naliiabot.bot.tools import ToolRegistry
|
|
||||||
from src.naliiabot.bot.tools.tools import (
|
|
||||||
ObtenerAgendaTool,
|
|
||||||
AgregarEventoTool,
|
|
||||||
ModificarEventoTool,
|
|
||||||
EliminarEventoTool,
|
|
||||||
ListarAgendaTool
|
|
||||||
)
|
|
||||||
from src.naliiabot.bot.agent.agent import Agent
|
|
||||||
from langchain_anthropic import ChatAnthropic
|
|
||||||
|
|
||||||
# Configurar
|
|
||||||
modelo = ChatAnthropic(model="claude-3-5-sonnet-20241022")
|
|
||||||
registry = ToolRegistry()
|
|
||||||
|
|
||||||
# Registrar herramientas
|
|
||||||
registry.register(ObtenerAgendaTool())
|
|
||||||
registry.register(AgregarEventoTool())
|
|
||||||
registry.register(ModificarEventoTool())
|
|
||||||
registry.register(EliminarEventoTool())
|
|
||||||
registry.register(ListarAgendaTool())
|
|
||||||
|
|
||||||
# Crear agente
|
|
||||||
agent = Agent(
|
|
||||||
model=modelo,
|
|
||||||
tools=registry.get_all_tools_as_langchain()
|
|
||||||
)
|
|
||||||
|
|
||||||
OPCIÓN 2: Usar factory pattern
|
|
||||||
|
|
||||||
Crea src/naliiabot/bot/tools/factory.py:
|
|
||||||
|
|
||||||
from .tool_registry import ToolRegistry
|
|
||||||
from .tools import (
|
|
||||||
ObtenerAgendaTool,
|
|
||||||
AgregarEventoTool,
|
|
||||||
ModificarEventoTool,
|
|
||||||
EliminarEventoTool,
|
|
||||||
ListarAgendaTool
|
|
||||||
)
|
|
||||||
|
|
||||||
def crear_registry_agenda() -> ToolRegistry:
|
|
||||||
registry = ToolRegistry()
|
|
||||||
registry.register(ObtenerAgendaTool())
|
|
||||||
registry.register(AgregarEventoTool())
|
|
||||||
registry.register(ModificarEventoTool())
|
|
||||||
registry.register(EliminarEventoTool())
|
|
||||||
registry.register(ListarAgendaTool())
|
|
||||||
return registry
|
|
||||||
|
|
||||||
Luego úsalo:
|
|
||||||
|
|
||||||
from src.naliiabot.bot.tools.factory import crear_registry_agenda
|
|
||||||
|
|
||||||
registry = crear_registry_agenda()
|
|
||||||
tools = registry.get_all_tools_as_langchain()
|
|
||||||
"""
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
# Cambios realizados: Patrón Registry para Tools
|
|
||||||
|
|
||||||
## ✅ Cambios completados
|
|
||||||
|
|
||||||
### 1. **tools.py** - Limpiado ✓
|
|
||||||
**Antes:** Contenía herramientas de ejemplo (Calculator, Greeter, Weather, Time)
|
|
||||||
**Ahora:** Vacío y listo para tus herramientas personalizadas
|
|
||||||
|
|
||||||
```python
|
|
||||||
"""
|
|
||||||
Implementaciones concretas de herramientas del agente.
|
|
||||||
Define aquí tus herramientas personalizadas que extienden BaseTool
|
|
||||||
"""
|
|
||||||
|
|
||||||
from typing import Any, Dict
|
|
||||||
from .tool_registry import BaseTool
|
|
||||||
|
|
||||||
# Define aquí tus herramientas personalizadas
|
|
||||||
# Ejemplo: class MiHerramienta(BaseTool): ...
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. **__init__.py** - Actualizado ✓
|
|
||||||
**Antes:** Importaba CalculatorTool, GreeterTool
|
|
||||||
**Ahora:** Solo exporta BaseTool y ToolRegistry
|
|
||||||
|
|
||||||
```python
|
|
||||||
from .tool_registry import ToolRegistry, BaseTool
|
|
||||||
|
|
||||||
__all__ = ["ToolRegistry", "BaseTool"]
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. **tool_registry.py** - Mantiene la infraestructura ✓
|
|
||||||
Contiene:
|
|
||||||
- `BaseTool`: Clase abstracta para todas las tools
|
|
||||||
- `ToolRegistry`: Clase para registrar y gestionar tools
|
|
||||||
|
|
||||||
### 4. **Documentación - Nueva ✓**
|
|
||||||
Creado: `TOOLS_GUIDE.md` con ejemplos completos de cómo:
|
|
||||||
- Crear herramientas personalizadas
|
|
||||||
- Usar el ToolRegistry
|
|
||||||
- Integrar con el Agent
|
|
||||||
- Ejemplos de tools para gestionar agenda
|
|
||||||
|
|
||||||
## 📁 Estructura final
|
|
||||||
|
|
||||||
```
|
|
||||||
src/naliiabot/bot/tools/
|
|
||||||
├── __init__.py # Exporta ToolRegistry y BaseTool
|
|
||||||
├── tool_registry.py # Base + Registry (infraestructura)
|
|
||||||
└── tools.py # TUS HERRAMIENTAS AQUÍ (vacío, listo para usar)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🚀 Próximos pasos
|
|
||||||
|
|
||||||
1. **Define tus herramientas** en `tools.py`:
|
|
||||||
```python
|
|
||||||
class ObtenerAgendaTool(BaseTool):
|
|
||||||
@property
|
|
||||||
def name(self) -> str:
|
|
||||||
return "obtener_agenda"
|
|
||||||
# ... más detalles en TOOLS_GUIDE.md
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Registra y usa** en tu código:
|
|
||||||
```python
|
|
||||||
from src.naliiabot.bot.tools import ToolRegistry
|
|
||||||
from src.naliiabot.bot.tools.tools import ObtenerAgendaTool
|
|
||||||
|
|
||||||
registry = ToolRegistry()
|
|
||||||
registry.register(ObtenerAgendaTool())
|
|
||||||
tools = registry.get_all_tools_as_langchain()
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Lee TOOLS_GUIDE.md** para ejemplos detallados con:
|
|
||||||
- Herramientas para gestión de agenda
|
|
||||||
- Cómo validar argumentos
|
|
||||||
- Patrones recomendados
|
|
||||||
- Testing de tools
|
|
||||||
|
|
||||||
## 📚 Referencias
|
|
||||||
|
|
||||||
- [TOOLS_GUIDE.md](./TOOLS_GUIDE.md) - Guía completa con ejemplos
|
|
||||||
- [tool_registry.py](./src/naliiabot/bot/tools/tool_registry.py) - Código de la infraestructura
|
|
||||||
- [tests/test_naliia_agent_tools.py](./tests/test_naliia_agent_tools.py) - Tests de tools
|
|
||||||
|
|
||||||
## ✨ Beneficios del patrón Registry
|
|
||||||
|
|
||||||
✅ **Centralizado**: Un único lugar para todas las tools
|
|
||||||
✅ **Extensible**: Fácil agregar nuevas tools
|
|
||||||
✅ **Testeable**: Cada tool se prueba de forma independiente
|
|
||||||
✅ **Mantenible**: Cambios aislados en cada tool
|
|
||||||
✅ **Reutilizable**: Mismo registry en múltiples agentes
|
|
||||||
✅ **Validado**: Schema automático para argumentos
|
|
||||||
✅ **Documentado**: Inspección de tools disponibles
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Nota:** Las herramientas de ejemplo (Calculator, Greeter, Weather, Time) han sido removidas.
|
|
||||||
Ahora el proyecto está limpio y listo para que implementes las tools que necesitas.
|
|
||||||
133
docs/INICIO.txt
133
docs/INICIO.txt
@@ -1,133 +0,0 @@
|
|||||||
╔════════════════════════════════════════════════════════════════════════════╗
|
|
||||||
║ ║
|
|
||||||
║ 🎉 SISTEMA DE TOOLS IMPLEMENTADO 🎉 ║
|
|
||||||
║ ║
|
|
||||||
║ ✅ Patrón Registry para NaliiaBot ║
|
|
||||||
║ ║
|
|
||||||
╚════════════════════════════════════════════════════════════════════════════╝
|
|
||||||
|
|
||||||
📚 DOCUMENTACIÓN DISPONIBLE:
|
|
||||||
|
|
||||||
🗺️ TOOLS_DOCUMENTATION_INDEX.md
|
|
||||||
└─ Punto de partida. Lee esto primero. (10 min)
|
|
||||||
|
|
||||||
🚀 QUICK_START.py
|
|
||||||
└─ Tutorial interactivo. Ejecutar y aprender. (5 min)
|
|
||||||
|
|
||||||
📖 TOOLS_GUIDE.md
|
|
||||||
└─ Guía completa con ejemplos. (20 min)
|
|
||||||
|
|
||||||
💼 AGENDA_TOOLS_EXAMPLE.py
|
|
||||||
└─ 5 herramientas reales funcionando. (15 min)
|
|
||||||
|
|
||||||
📋 REGISTRY_IMPLEMENTATION_SUMMARY.md
|
|
||||||
└─ Qué se hizo y cómo. (10 min)
|
|
||||||
|
|
||||||
|
|
||||||
🏗️ ESTRUCTURA DEL PROYECTO:
|
|
||||||
|
|
||||||
src/naliiabot/bot/tools/
|
|
||||||
├── __init__.py
|
|
||||||
├── tool_registry.py (infraestructura)
|
|
||||||
└── tools.py (← TUS HERRAMIENTAS AQUÍ)
|
|
||||||
|
|
||||||
tests/
|
|
||||||
└── test_naliia_agent_tools.py (tests automáticos)
|
|
||||||
|
|
||||||
|
|
||||||
🚀 COMIENZA CON ESTOS 3 PASOS:
|
|
||||||
|
|
||||||
1️⃣ Abre: TOOLS_DOCUMENTATION_INDEX.md
|
|
||||||
└─ Entiende la estructura
|
|
||||||
|
|
||||||
2️⃣ Abre: QUICK_START.py
|
|
||||||
└─ Crea tu primera herramienta
|
|
||||||
|
|
||||||
3️⃣ Abre: src/naliiabot/bot/tools/tools.py
|
|
||||||
└─ Agrega tus herramientas aquí
|
|
||||||
|
|
||||||
|
|
||||||
📊 ¿QUÉ ES EL PATRÓN REGISTRY?
|
|
||||||
|
|
||||||
Un sistema para registrar y gestionar herramientas de forma centralizada.
|
|
||||||
|
|
||||||
Flujo:
|
|
||||||
1. Define una herramienta (clase que hereda de BaseTool)
|
|
||||||
2. Regístrala en el ToolRegistry
|
|
||||||
3. Pásala al Agent
|
|
||||||
4. El LLM ahora puede usarla
|
|
||||||
|
|
||||||
|
|
||||||
✨ BENEFICIOS:
|
|
||||||
|
|
||||||
✅ Centralizado - Un único lugar para todas las tools
|
|
||||||
✅ Extensible - Agregar tools sin tocar código existente
|
|
||||||
✅ Testeable - Cada tool se prueba independientemente
|
|
||||||
✅ Mantenible - Cambios aislados
|
|
||||||
✅ Escalable - Funciona con 1 o 100 tools
|
|
||||||
✅ Validado - Schema automático de argumentos
|
|
||||||
|
|
||||||
|
|
||||||
💻 EJEMPLO RÁPIDO (copiar y pegar):
|
|
||||||
|
|
||||||
# 1. Crear una herramienta
|
|
||||||
class MiTool(BaseTool):
|
|
||||||
@property
|
|
||||||
def name(self) -> str:
|
|
||||||
return "mi_herramienta"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def description(self) -> str:
|
|
||||||
return "Descripción"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def args_schema(self):
|
|
||||||
return {"type": "object", "properties": {}}
|
|
||||||
|
|
||||||
def invoke(self, **kwargs) -> str:
|
|
||||||
return "resultado"
|
|
||||||
|
|
||||||
# 2. Registrar
|
|
||||||
registry = ToolRegistry()
|
|
||||||
registry.register(MiTool())
|
|
||||||
tools = registry.get_all_tools_as_langchain()
|
|
||||||
|
|
||||||
# 3. Usar con Agent
|
|
||||||
agent = Agent(model=modelo, tools=tools)
|
|
||||||
|
|
||||||
|
|
||||||
🎯 PRÓXIMOS PASOS:
|
|
||||||
|
|
||||||
Ahora mismo:
|
|
||||||
[ ] Abre TOOLS_DOCUMENTATION_INDEX.md
|
|
||||||
[ ] Lee QUICK_START.py
|
|
||||||
[ ] Intenta crear una herramienta
|
|
||||||
|
|
||||||
Después:
|
|
||||||
[ ] Revisa ejemplos en AGENDA_TOOLS_EXAMPLE.py
|
|
||||||
[ ] Crea tus herramientas personalizadas
|
|
||||||
[ ] Escribe tests
|
|
||||||
|
|
||||||
|
|
||||||
❓ ¿PREGUNTAS?
|
|
||||||
|
|
||||||
¿Cómo creo una tool?
|
|
||||||
→ TOOLS_GUIDE.md sección 2
|
|
||||||
|
|
||||||
¿Cómo registro tools?
|
|
||||||
→ TOOLS_GUIDE.md sección 3
|
|
||||||
|
|
||||||
¿Cuáles son los métodos disponibles?
|
|
||||||
→ TOOLS_GUIDE.md sección 4
|
|
||||||
|
|
||||||
¿Necesito ejemplos?
|
|
||||||
→ AGENDA_TOOLS_EXAMPLE.py
|
|
||||||
|
|
||||||
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
🎓 TODO LISTO PARA EMPEZAR
|
|
||||||
|
|
||||||
👉 Comienza por: TOOLS_DOCUMENTATION_INDEX.md
|
|
||||||
|
|
||||||
═══════════════════════════════════════════════════════════════════════════════
|
|
||||||
@@ -1,345 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
QUICK START: Cómo crear tu primera herramienta en 5 minutos
|
|
||||||
|
|
||||||
Este archivo es un tutorial paso a paso que puedes ejecutar.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# PASO 1: Entender la estructura
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
"""
|
|
||||||
El patrón Registry funciona así:
|
|
||||||
|
|
||||||
1. DEFINIR una herramienta (en tools.py):
|
|
||||||
- Crear una clase que herede de BaseTool
|
|
||||||
- Implementar: name, description, args_schema, invoke()
|
|
||||||
|
|
||||||
2. REGISTRAR la herramienta:
|
|
||||||
- Crear ToolRegistry()
|
|
||||||
- Llamar registry.register(MiHerramienta())
|
|
||||||
|
|
||||||
3. USAR las herramientas:
|
|
||||||
- Pasar registry.get_all_tools_as_langchain() al Agent
|
|
||||||
- El Agent ahora puede usar tus tools
|
|
||||||
"""
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# PASO 2: Crear tu primera herramienta
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
"""
|
|
||||||
Abre: src/naliiabot/bot/tools/tools.py
|
|
||||||
|
|
||||||
Y pega este código (reemplaza el contenido):
|
|
||||||
"""
|
|
||||||
|
|
||||||
EJEMPLO_HERRAMIENTA = '''
|
|
||||||
"""
|
|
||||||
Implementaciones concretas de herramientas del agente.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from typing import Any, Dict
|
|
||||||
from .tool_registry import BaseTool
|
|
||||||
|
|
||||||
|
|
||||||
class ObtenerHoraActualTool(BaseTool):
|
|
||||||
"""
|
|
||||||
Herramienta simple que retorna la hora actual.
|
|
||||||
|
|
||||||
El LLM puede usar esto para responder: "¿Qué hora es?"
|
|
||||||
"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self) -> str:
|
|
||||||
return "obtener_hora"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def description(self) -> str:
|
|
||||||
return "Retorna la hora actual del sistema."
|
|
||||||
|
|
||||||
@property
|
|
||||||
def args_schema(self) -> Dict[str, Any]:
|
|
||||||
return {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"formato": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": ["12h", "24h"],
|
|
||||||
"description": "Formato de hora deseado",
|
|
||||||
"default": "24h"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": []
|
|
||||||
}
|
|
||||||
|
|
||||||
def invoke(self, **kwargs) -> str:
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
formato = kwargs.get("formato", "24h")
|
|
||||||
ahora = datetime.now()
|
|
||||||
|
|
||||||
if formato == "12h":
|
|
||||||
return ahora.strftime("%I:%M %p")
|
|
||||||
else:
|
|
||||||
return ahora.strftime("%H:%M:%S")
|
|
||||||
|
|
||||||
|
|
||||||
class SaludarTool(BaseTool):
|
|
||||||
"""
|
|
||||||
Herramienta para saludar al usuario.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self) -> str:
|
|
||||||
return "saludar"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def description(self) -> str:
|
|
||||||
return "Saluda al usuario de forma amable."
|
|
||||||
|
|
||||||
@property
|
|
||||||
def args_schema(self) -> Dict[str, Any]:
|
|
||||||
return {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"nombre": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Nombre de la persona a saludar"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["nombre"]
|
|
||||||
}
|
|
||||||
|
|
||||||
def invoke(self, **kwargs) -> str:
|
|
||||||
nombre = kwargs.get("nombre", "Usuario")
|
|
||||||
return f"¡Hola {nombre}! 👋 Bienvenido a Naliia"
|
|
||||||
'''
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# PASO 3: Usar tus herramientas
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
"""
|
|
||||||
En tu código principal (ejemplo en main.py):
|
|
||||||
"""
|
|
||||||
|
|
||||||
EJEMPLO_USO = '''
|
|
||||||
from src.naliiabot.bot.tools import ToolRegistry
|
|
||||||
from src.naliiabot.bot.tools.tools import ObtenerHoraActualTool, SaludarTool
|
|
||||||
from src.naliiabot.bot.agent.agent import Agent
|
|
||||||
from langchain_anthropic import ChatAnthropic
|
|
||||||
|
|
||||||
# 1. Crear el modelo LLM
|
|
||||||
modelo = ChatAnthropic(model="claude-3-5-sonnet-20241022")
|
|
||||||
|
|
||||||
# 2. Crear el registry de tools
|
|
||||||
registry = ToolRegistry()
|
|
||||||
|
|
||||||
# 3. Registrar las herramientas
|
|
||||||
registry.register(ObtenerHoraActualTool())
|
|
||||||
registry.register(SaludarTool())
|
|
||||||
|
|
||||||
# 4. Crear el agente con las tools
|
|
||||||
agent = Agent(
|
|
||||||
model=modelo,
|
|
||||||
tools=registry.get_all_tools_as_langchain()
|
|
||||||
)
|
|
||||||
|
|
||||||
# 5. Usar el agente
|
|
||||||
from langchain_core.messages import HumanMessage
|
|
||||||
|
|
||||||
resultado = agent.invoke({
|
|
||||||
"messages": [
|
|
||||||
HumanMessage(content="Hola, ¿qué hora es?")
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
print(resultado)
|
|
||||||
'''
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# PASO 4: Verificar que funciona
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
"""
|
|
||||||
Ejecuta en la terminal:
|
|
||||||
|
|
||||||
cd /home/aserrador/Desktop/01-OneCluster/02-Desarrollo/03-Naliia/NaliiaBot
|
|
||||||
python -m pytest tests/test_naliia_agent_tools.py -v
|
|
||||||
|
|
||||||
Deberías ver los tests pasando.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# PASO 5: Crear herramientas más complejas
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
"""
|
|
||||||
Para herramientas con lógica real:
|
|
||||||
|
|
||||||
1. Conectar a base de datos:
|
|
||||||
- Importa el cliente de tu BD
|
|
||||||
- En invoke(), haz la consulta
|
|
||||||
- Retorna el resultado como string
|
|
||||||
|
|
||||||
2. Manejar errores:
|
|
||||||
- Try/except en invoke()
|
|
||||||
- Retorna un mensaje de error claro
|
|
||||||
|
|
||||||
3. Validar argumentos:
|
|
||||||
- Usa el args_schema para definir requerimientos
|
|
||||||
- El LLM sabe qué argumentos necesita
|
|
||||||
|
|
||||||
Ejemplo con BD:
|
|
||||||
"""
|
|
||||||
|
|
||||||
EJEMPLO_CON_BD = '''
|
|
||||||
from typing import Any, Dict
|
|
||||||
from .tool_registry import BaseTool
|
|
||||||
from tu_modulo.database import get_db
|
|
||||||
|
|
||||||
class ObtenerClienteTool(BaseTool):
|
|
||||||
@property
|
|
||||||
def name(self) -> str:
|
|
||||||
return "obtener_cliente"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def description(self) -> str:
|
|
||||||
return "Obtiene información de un cliente por ID"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def args_schema(self) -> Dict[str, Any]:
|
|
||||||
return {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"cliente_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "ID del cliente"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["cliente_id"]
|
|
||||||
}
|
|
||||||
|
|
||||||
def invoke(self, **kwargs) -> str:
|
|
||||||
cliente_id = kwargs.get("cliente_id")
|
|
||||||
|
|
||||||
try:
|
|
||||||
db = get_db()
|
|
||||||
cliente = db.clientes.find_one({"_id": cliente_id})
|
|
||||||
|
|
||||||
if not cliente:
|
|
||||||
return f"Cliente {cliente_id} no encontrado"
|
|
||||||
|
|
||||||
return f"Nombre: {cliente['nombre']}, Email: {cliente['email']}"
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
return f"Error al obtener cliente: {str(e)}"
|
|
||||||
'''
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# TIPS Y BUENAS PRÁCTICAS
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
TIPS = """
|
|
||||||
✅ BUENAS PRÁCTICAS:
|
|
||||||
|
|
||||||
1. **Nombres claros**: "obtener_agenda" mejor que "get_agenda_info"
|
|
||||||
|
|
||||||
2. **Descripciones detalladas**: El LLM las lee para decidir cuándo usar la tool
|
|
||||||
|
|
||||||
3. **Args schema completo**:
|
|
||||||
- Describe qué es cada parámetro
|
|
||||||
- Especifica si es requerido
|
|
||||||
- Usa enum para valores limitados
|
|
||||||
- Añade ejemplos en description
|
|
||||||
|
|
||||||
4. **Manejo de errores**: Siempre captura excepciones en invoke()
|
|
||||||
|
|
||||||
5. **Retorna strings**: invoke() debe retornar texto plano, no objetos
|
|
||||||
|
|
||||||
6. **Validación**: Valida argumentos en invoke() antes de usarlos
|
|
||||||
|
|
||||||
7. **Logging**: Usa logging para debugging en producción
|
|
||||||
|
|
||||||
8. **Tests**: Crea tests unitarios para cada tool
|
|
||||||
|
|
||||||
❌ ERRORES COMUNES:
|
|
||||||
|
|
||||||
- No retornar string desde invoke()
|
|
||||||
- Olvidar el args_schema completo
|
|
||||||
- No manejar excepciones
|
|
||||||
- Nombres de tools con espacios o caracteres especiales
|
|
||||||
- Tools que hacen demasiadas cosas (una responsabilidad)
|
|
||||||
|
|
||||||
🔍 DEBUGGING:
|
|
||||||
|
|
||||||
# Ver todas las tools registradas
|
|
||||||
print(registry.list_tool_names())
|
|
||||||
|
|
||||||
# Ver schema de una tool
|
|
||||||
herramienta = registry.get_tool("mi_herramienta")
|
|
||||||
print(herramienta.args_schema)
|
|
||||||
|
|
||||||
# Ver descripción
|
|
||||||
print(registry.get_tool_description("mi_herramienta"))
|
|
||||||
"""
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# ESTRUCTURA FINAL
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
ESTRUCTURA = """
|
|
||||||
Después de seguir estos pasos, tu proyecto tendrá:
|
|
||||||
|
|
||||||
src/naliiabot/bot/tools/
|
|
||||||
├── __init__.py
|
|
||||||
│ └── Exporta: ToolRegistry, BaseTool
|
|
||||||
├── tool_registry.py
|
|
||||||
│ └── Contiene: BaseTool (clase abstracta), ToolRegistry (registro)
|
|
||||||
└── tools.py
|
|
||||||
└── TUS herramientas (ObtenerHoraActualTool, SaludarTool, etc.)
|
|
||||||
|
|
||||||
tests/
|
|
||||||
└── test_naliia_agent_tools.py
|
|
||||||
└── Tests para las tools (ya creado)
|
|
||||||
|
|
||||||
src/naliiabotapi/
|
|
||||||
└── main.py
|
|
||||||
└── Crea el registry, inicializa el agent, lanza el API
|
|
||||||
|
|
||||||
DOCUMENTACIÓN:
|
|
||||||
├── TOOLS_GUIDE.md ← Lee primero
|
|
||||||
├── AGENDA_TOOLS_EXAMPLE.py ← Ejemplos reales
|
|
||||||
├── REGISTRY_IMPLEMENTATION_SUMMARY.md ← Resumen
|
|
||||||
├── CHANGES_SUMMARY.md ← Qué cambió
|
|
||||||
└── QUICK_START.md (este archivo) ← Tutorial
|
|
||||||
"""
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
print("=" * 80)
|
|
||||||
print("QUICK START: Patrón Registry para Tools en NaliiaBot")
|
|
||||||
print("=" * 80)
|
|
||||||
print("\n📖 PASOS:")
|
|
||||||
print("\n1️⃣ CREA tu primera herramienta")
|
|
||||||
print(" → Abre: src/naliiabot/bot/tools/tools.py")
|
|
||||||
print(" → Pega el código de EJEMPLO_HERRAMIENTA\n")
|
|
||||||
|
|
||||||
print("2️⃣ REGISTRA la herramienta")
|
|
||||||
print(" → Crea ToolRegistry()")
|
|
||||||
print(" → Llama registry.register(MiHerramienta())\n")
|
|
||||||
|
|
||||||
print("3️⃣ USA con el Agent")
|
|
||||||
print(" → Pasa registry.get_all_tools_as_langchain() a Agent\n")
|
|
||||||
|
|
||||||
print("4️⃣ TESTA tus tools")
|
|
||||||
print(" → python -m pytest tests/test_naliia_agent_tools.py -v\n")
|
|
||||||
|
|
||||||
print("5️⃣ LEE la documentación")
|
|
||||||
print(" → TOOLS_GUIDE.md - Guía completa")
|
|
||||||
print(" → AGENDA_TOOLS_EXAMPLE.py - Ejemplos reales\n")
|
|
||||||
|
|
||||||
print("=" * 80)
|
|
||||||
print("¡Listo! Ya tienes todo lo que necesitas para empezar 🚀")
|
|
||||||
print("=" * 80)
|
|
||||||
@@ -1,280 +0,0 @@
|
|||||||
# ✅ RESUMEN: Implementación completada del Patrón Registry
|
|
||||||
|
|
||||||
## 🎉 ¿Qué se logró?
|
|
||||||
|
|
||||||
Se implementó un **sistema profesional de herramientas** para el agente Naliia usando el patrón **Registry**. El proyecto está completamente documentado y listo para que agregues tus propias tools.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📊 Cambios realizados
|
|
||||||
|
|
||||||
### ✅ Código modificado
|
|
||||||
|
|
||||||
| Archivo | Cambio | Razón |
|
|
||||||
|---------|--------|-------|
|
|
||||||
| `src/naliiabot/bot/tools/tools.py` | Limpiado de tools de ejemplo | Tools de ejemplo no son aplicables a tu caso |
|
|
||||||
| `src/naliiabot/bot/tools/__init__.py` | Removidas importaciones de ejemplos | Mantener el módulo limpio |
|
|
||||||
|
|
||||||
### ✅ Infraestructura existente
|
|
||||||
|
|
||||||
Estos archivos ya estaban y funcionan perfectamente:
|
|
||||||
|
|
||||||
| Archivo | Descripción |
|
|
||||||
|---------|-----------|
|
|
||||||
| `src/naliiabot/bot/tools/tool_registry.py` | Patrón Registry (BaseTool + ToolRegistry) |
|
|
||||||
| `tests/test_naliia_agent_tools.py` | Tests para validar tools |
|
|
||||||
|
|
||||||
### ✅ Documentación nueva (8 archivos)
|
|
||||||
|
|
||||||
| Archivo | Propósito | Tiempo de lectura |
|
|
||||||
|---------|----------|-----------------|
|
|
||||||
| **TOOLS_DOCUMENTATION_INDEX.md** | 🗺️ Índice y navegación | 10 min |
|
|
||||||
| **QUICK_START.py** | 🚀 Tutorial interactivo | 5 min |
|
|
||||||
| **TOOLS_GUIDE.md** | 📖 Guía completa | 20 min |
|
|
||||||
| **AGENDA_TOOLS_EXAMPLE.py** | 💼 Ejemplos reales de 5 tools | 15 min |
|
|
||||||
| **REGISTRY_IMPLEMENTATION_SUMMARY.md** | 📋 Resumen de implementación | 10 min |
|
|
||||||
| **CHANGES_SUMMARY.md** | 📝 Qué cambió | 5 min |
|
|
||||||
| **TOOL_REGISTRY_GUIDE.md** | 📚 Guía de patrones | 20 min |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🏗️ Estructura actual
|
|
||||||
|
|
||||||
```
|
|
||||||
NaliiaBot/
|
|
||||||
│
|
|
||||||
├── 📚 DOCUMENTACIÓN (nueva)
|
|
||||||
│ ├── TOOLS_DOCUMENTATION_INDEX.md ← EMPIEZA POR AQUÍ
|
|
||||||
│ ├── QUICK_START.py
|
|
||||||
│ ├── TOOLS_GUIDE.md
|
|
||||||
│ ├── AGENDA_TOOLS_EXAMPLE.py
|
|
||||||
│ ├── REGISTRY_IMPLEMENTATION_SUMMARY.md
|
|
||||||
│ ├── CHANGES_SUMMARY.md
|
|
||||||
│ └── TOOL_REGISTRY_GUIDE.md
|
|
||||||
│
|
|
||||||
├── 📁 src/naliiabot/bot/tools/
|
|
||||||
│ ├── __init__.py ✨ (actualizado)
|
|
||||||
│ ├── tool_registry.py (infraestructura existente)
|
|
||||||
│ └── tools.py ✨ (limpiado, listo para tus tools)
|
|
||||||
│
|
|
||||||
└── 📁 tests/
|
|
||||||
└── test_naliia_agent_tools.py (tests completos)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 Próximos pasos
|
|
||||||
|
|
||||||
### Opción 1: Aprender el sistema (20 minutos)
|
|
||||||
```
|
|
||||||
1. Abre: TOOLS_DOCUMENTATION_INDEX.md
|
|
||||||
2. Lee: QUICK_START.py
|
|
||||||
3. Revisa: TOOLS_GUIDE.md
|
|
||||||
4. Entiendido ✅
|
|
||||||
```
|
|
||||||
|
|
||||||
### Opción 2: Empezar con un ejemplo (15 minutos)
|
|
||||||
```
|
|
||||||
1. Abre: AGENDA_TOOLS_EXAMPLE.py
|
|
||||||
2. Copia una de las 5 herramientas
|
|
||||||
3. Pégala en: src/naliiabot/bot/tools/tools.py
|
|
||||||
4. Registra y usa ✅
|
|
||||||
```
|
|
||||||
|
|
||||||
### Opción 3: Crear tu primera tool (10 minutos)
|
|
||||||
```
|
|
||||||
1. Abre: src/naliiabot/bot/tools/tools.py
|
|
||||||
2. Define una clase que herede de BaseTool
|
|
||||||
3. Implementa: name, description, args_schema, invoke()
|
|
||||||
4. Registra en tu main.py ✅
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📖 Recomendación de lectura
|
|
||||||
|
|
||||||
**Primero (5 min):**
|
|
||||||
```
|
|
||||||
TOOLS_DOCUMENTATION_INDEX.md
|
|
||||||
```
|
|
||||||
Es el índice maestro. Te orienta hacia lo que necesitas.
|
|
||||||
|
|
||||||
**Segundo (5-20 min):**
|
|
||||||
Elige según tu necesidad:
|
|
||||||
- Rápido: **QUICK_START.py**
|
|
||||||
- Completo: **TOOLS_GUIDE.md**
|
|
||||||
- Práctico: **AGENDA_TOOLS_EXAMPLE.py**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✨ Lo que ahora puedes hacer
|
|
||||||
|
|
||||||
✅ **Crear herramientas personalizadas**
|
|
||||||
- Documentación paso a paso
|
|
||||||
- Ejemplos reales funcionales
|
|
||||||
- Tests automáticos
|
|
||||||
|
|
||||||
✅ **Registrar herramientas de forma centralizada**
|
|
||||||
- Un único `ToolRegistry`
|
|
||||||
- Fácil de escalar
|
|
||||||
- Validación automática
|
|
||||||
|
|
||||||
✅ **Integrar con el Agent**
|
|
||||||
- Herramientas listas para LangChain
|
|
||||||
- Compatible con modelos Anthropic
|
|
||||||
- Flujo completo documentado
|
|
||||||
|
|
||||||
✅ **Testear herramientas**
|
|
||||||
- Tests unitarios incluidos
|
|
||||||
- Cobertura completa
|
|
||||||
- Fácil de mantener
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚀 ¿Qué sigue?
|
|
||||||
|
|
||||||
### Corto plazo (hoy)
|
|
||||||
- [ ] Lee TOOLS_DOCUMENTATION_INDEX.md (10 min)
|
|
||||||
- [ ] Sigue QUICK_START.py (5 min)
|
|
||||||
- [ ] Crea tu primera tool simple (10 min)
|
|
||||||
|
|
||||||
### Mediano plazo (esta semana)
|
|
||||||
- [ ] Revisa AGENDA_TOOLS_EXAMPLE.py
|
|
||||||
- [ ] Crea tools para tu caso de uso
|
|
||||||
- [ ] Escribe tests
|
|
||||||
|
|
||||||
### Largo plazo (próximas semanas)
|
|
||||||
- [ ] Integra con BD real
|
|
||||||
- [ ] Crea factory de tools
|
|
||||||
- [ ] Deploy en producción
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📊 Estadísticas
|
|
||||||
|
|
||||||
| Métrica | Valor |
|
|
||||||
|---------|-------|
|
|
||||||
| Archivos modificados | 2 |
|
|
||||||
| Archivos documentación creados | 8 |
|
|
||||||
| Líneas de documentación | ~800 |
|
|
||||||
| Ejemplos de herramientas | 7 (6 en ejemplos + tests) |
|
|
||||||
| Métodos de ToolRegistry | 6+ |
|
|
||||||
| Tests incluidos | 10+ casos |
|
|
||||||
| Tiempo para aprender | 20-30 min |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎓 Ventajas del patrón Registry implementado
|
|
||||||
|
|
||||||
| Ventaja | Beneficio |
|
|
||||||
|---------|-----------|
|
|
||||||
| ✅ **Centralizado** | Una fuente única de verdad |
|
|
||||||
| ✅ **Extensible** | Agregar tools sin tocar código existente |
|
|
||||||
| ✅ **Testeable** | Cada tool se prueba independientemente |
|
|
||||||
| ✅ **Mantenible** | Cambios aislados |
|
|
||||||
| ✅ **Escalable** | Funciona con 1 o 100 tools |
|
|
||||||
| ✅ **Validado** | Schema automático |
|
|
||||||
| ✅ **Documentado** | Fácil inspeccionar |
|
|
||||||
| ✅ **Profesional** | Patrón estándar de la industria |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 💡 Ejemplos rápidos
|
|
||||||
|
|
||||||
### Crear una tool (3 minutos)
|
|
||||||
```python
|
|
||||||
from typing import Any, Dict
|
|
||||||
from .tool_registry import BaseTool
|
|
||||||
|
|
||||||
class MiTool(BaseTool):
|
|
||||||
@property
|
|
||||||
def name(self) -> str:
|
|
||||||
return "mi_herramienta"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def description(self) -> str:
|
|
||||||
return "Descripción de qué hace"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def args_schema(self) -> Dict[str, Any]:
|
|
||||||
return {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {"param": {"type": "string"}},
|
|
||||||
"required": ["param"]
|
|
||||||
}
|
|
||||||
|
|
||||||
def invoke(self, **kwargs) -> str:
|
|
||||||
return f"Resultado para {kwargs.get('param')}"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Registrar una tool (2 minutos)
|
|
||||||
```python
|
|
||||||
from src.naliiabot.bot.tools import ToolRegistry
|
|
||||||
from src.naliiabot.bot.tools.tools import MiTool
|
|
||||||
|
|
||||||
registry = ToolRegistry()
|
|
||||||
registry.register(MiTool())
|
|
||||||
tools = registry.get_all_tools_as_langchain()
|
|
||||||
```
|
|
||||||
|
|
||||||
### Usar con Agent (2 minutos)
|
|
||||||
```python
|
|
||||||
from src.naliiabot.bot.agent.agent import Agent
|
|
||||||
|
|
||||||
agent = Agent(model=modelo, tools=tools)
|
|
||||||
resultado = agent.invoke({"messages": [...]})
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ❓ FAQ Rápido
|
|
||||||
|
|
||||||
**P: ¿Por dónde empiezo?**
|
|
||||||
R: Lee `TOOLS_DOCUMENTATION_INDEX.md`
|
|
||||||
|
|
||||||
**P: ¿Quiero un ejemplo simple?**
|
|
||||||
R: Abre `QUICK_START.py`
|
|
||||||
|
|
||||||
**P: ¿Necesito ejemplos reales?**
|
|
||||||
R: Mira `AGENDA_TOOLS_EXAMPLE.py`
|
|
||||||
|
|
||||||
**P: ¿Cómo conecto con BD?**
|
|
||||||
R: Lee sección "Conectar a base de datos" en `TOOLS_GUIDE.md`
|
|
||||||
|
|
||||||
**P: ¿Cómo testo mis tools?**
|
|
||||||
R: Usa `tests/test_naliia_agent_tools.py` como referencia
|
|
||||||
|
|
||||||
**P: ¿Es seguro modificar tools.py?**
|
|
||||||
R: Sí, es exactamente para eso
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ Checklist de implementación
|
|
||||||
|
|
||||||
- [x] Infraestructura de Registry implementada
|
|
||||||
- [x] Documentación completa creada
|
|
||||||
- [x] Ejemplos funcionales provistos
|
|
||||||
- [x] Tests incluidos
|
|
||||||
- [x] Code limpiado
|
|
||||||
- [x] Estructura lista para usar
|
|
||||||
- [x] Guías de inicio rápido
|
|
||||||
- [x] Índice de navegación
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 Conclusión
|
|
||||||
|
|
||||||
**Tu proyecto está listo.**
|
|
||||||
|
|
||||||
El patrón Registry está implementado, documentado y listo para que crees tus herramientas personalizadas. La documentación es completa, con ejemplos prácticos y tests.
|
|
||||||
|
|
||||||
### Comienza aquí:
|
|
||||||
```
|
|
||||||
👉 TOOLS_DOCUMENTATION_INDEX.md
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Implementación completada:** 15 Feb 2026
|
|
||||||
**Status:** ✅ Listo para producción
|
|
||||||
**Soporte:** Ver documentación incluida
|
|
||||||
@@ -1,171 +0,0 @@
|
|||||||
# ✅ Resumen: Implementación del Patrón Registry para Tools
|
|
||||||
|
|
||||||
## 📋 Qué se hizo
|
|
||||||
|
|
||||||
Se implementó el **patrón Registry** para gestionar las herramientas del agente Naliia de forma centralizada, escalable y fácil de mantener.
|
|
||||||
|
|
||||||
### Archivos modificados:
|
|
||||||
|
|
||||||
| Archivo | Cambio | Estado |
|
|
||||||
|---------|--------|--------|
|
|
||||||
| `src/naliiabot/bot/tools/tools.py` | Removidas tools de ejemplo (Calculator, Greeter, Weather, Time) | ✅ Limpio |
|
|
||||||
| `src/naliiabot/bot/tools/__init__.py` | Removidas importaciones de tools de ejemplo | ✅ Actualizado |
|
|
||||||
|
|
||||||
### Archivos ya existentes (infraestructura):
|
|
||||||
|
|
||||||
| Archivo | Descripción |
|
|
||||||
|---------|-----------|
|
|
||||||
| `src/naliiabot/bot/tools/tool_registry.py` | Patrón Registry: `BaseTool` + `ToolRegistry` |
|
|
||||||
|
|
||||||
### Archivos de documentación y ejemplos creados:
|
|
||||||
|
|
||||||
| Archivo | Contenido |
|
|
||||||
|---------|-----------|
|
|
||||||
| **TOOLS_GUIDE.md** | Guía completa con ejemplos de uso |
|
|
||||||
| **AGENDA_TOOLS_EXAMPLE.py** | 5 herramientas reales para gestión de agenda |
|
|
||||||
| **CHANGES_SUMMARY.md** | Resumen de cambios |
|
|
||||||
|
|
||||||
## 🎯 Estado actual
|
|
||||||
|
|
||||||
El proyecto está listo para que **definas tus propias herramientas** en:
|
|
||||||
|
|
||||||
```
|
|
||||||
src/naliiabot/bot/tools/tools.py ← Aquí van TUS tools
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📚 Cómo empezar
|
|
||||||
|
|
||||||
### 1. Lee la documentación
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Abre estos archivos en orden:
|
|
||||||
1. CHANGES_SUMMARY.md # Resumen rápido
|
|
||||||
2. TOOLS_GUIDE.md # Guía detallada
|
|
||||||
3. AGENDA_TOOLS_EXAMPLE.py # Ejemplos prácticos
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Define una herramienta simple
|
|
||||||
|
|
||||||
En `src/naliiabot/bot/tools/tools.py`:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from typing import Any, Dict
|
|
||||||
from .tool_registry import BaseTool
|
|
||||||
|
|
||||||
class MiPrimeraHerramienta(BaseTool):
|
|
||||||
@property
|
|
||||||
def name(self) -> str:
|
|
||||||
return "mi_herramienta"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def description(self) -> str:
|
|
||||||
return "Descripción de qué hace"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def args_schema(self) -> Dict[str, Any]:
|
|
||||||
return {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"param1": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Un parámetro"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["param1"]
|
|
||||||
}
|
|
||||||
|
|
||||||
def invoke(self, **kwargs) -> str:
|
|
||||||
param = kwargs.get("param1")
|
|
||||||
return f"Resultado para {param}"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Registra y usa
|
|
||||||
|
|
||||||
```python
|
|
||||||
from src.naliiabot.bot.tools import ToolRegistry
|
|
||||||
from src.naliiabot.bot.tools.tools import MiPrimeraHerramienta
|
|
||||||
|
|
||||||
registry = ToolRegistry()
|
|
||||||
registry.register(MiPrimeraHerramienta())
|
|
||||||
tools = registry.get_all_tools_as_langchain()
|
|
||||||
|
|
||||||
# Usar con el Agent
|
|
||||||
agent = Agent(model=modelo, tools=tools)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔍 Inspeccionar el ToolRegistry
|
|
||||||
|
|
||||||
```python
|
|
||||||
registry = ToolRegistry()
|
|
||||||
registry.register(MiHerramienta())
|
|
||||||
|
|
||||||
# Ver todas las tools
|
|
||||||
tools = registry.get_all_tools()
|
|
||||||
|
|
||||||
# Ver nombres disponibles
|
|
||||||
nombres = registry.list_tool_names() # ['mi_herramienta', ...]
|
|
||||||
|
|
||||||
# Obtener una específica
|
|
||||||
herramienta = registry.get_tool("mi_herramienta")
|
|
||||||
|
|
||||||
# Verificar si existe
|
|
||||||
existe = registry.has_tool("mi_herramienta") # True
|
|
||||||
|
|
||||||
# Obtener descripción
|
|
||||||
desc = registry.get_tool_description("mi_herramienta")
|
|
||||||
```
|
|
||||||
|
|
||||||
## ✨ Ventajas
|
|
||||||
|
|
||||||
✅ **Centralizado** - Una única fuente de verdad para todas las tools
|
|
||||||
✅ **Extensible** - Agregar nuevas tools sin tocar código existente
|
|
||||||
✅ **Testeable** - Cada tool se prueba de forma independiente
|
|
||||||
✅ **Mantenible** - Cambios aislados en cada tool
|
|
||||||
✅ **Validado** - Schema automático de argumentos
|
|
||||||
✅ **Documentado** - Fácil inspeccionar tools disponibles
|
|
||||||
✅ **Seguro** - Sin tools de ejemplo conflictivas
|
|
||||||
|
|
||||||
## 📁 Estructura de directorios
|
|
||||||
|
|
||||||
```
|
|
||||||
NaliiaBot/
|
|
||||||
├── src/naliiabot/bot/tools/
|
|
||||||
│ ├── __init__.py # Exporta ToolRegistry, BaseTool
|
|
||||||
│ ├── tool_registry.py # Infraestructura (BaseTool + ToolRegistry)
|
|
||||||
│ └── tools.py # ← TUS HERRAMIENTAS AQUÍ (vacío y listo)
|
|
||||||
│
|
|
||||||
├── tests/
|
|
||||||
│ └── test_naliia_agent_tools.py # Tests de tools (ya creados)
|
|
||||||
│
|
|
||||||
├── TOOLS_GUIDE.md # Guía completa
|
|
||||||
├── AGENDA_TOOLS_EXAMPLE.py # Ejemplos reales para agenda
|
|
||||||
├── CHANGES_SUMMARY.md # Resumen de cambios
|
|
||||||
└── README.md
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🚀 Próximos pasos sugeridos
|
|
||||||
|
|
||||||
1. **Lee TOOLS_GUIDE.md** para entender completamente el patrón
|
|
||||||
2. **Copia ejemplos de AGENDA_TOOLS_EXAMPLE.py** si necesitas tools de agenda
|
|
||||||
3. **Crea tus propias herramientas** en `src/naliiabot/bot/tools/tools.py`
|
|
||||||
4. **Escribe tests** para tus tools (usa test_naliia_agent_tools.py como referencia)
|
|
||||||
5. **Integra con el API** en `src/naliiabotapi/main.py`
|
|
||||||
|
|
||||||
## ❓ Preguntas frecuentes
|
|
||||||
|
|
||||||
**P: ¿Cómo agrego una nueva herramienta?**
|
|
||||||
R: Define una clase que herede de `BaseTool` en `tools.py` y registra en `ToolRegistry`
|
|
||||||
|
|
||||||
**P: ¿Puedo usar varias registries?**
|
|
||||||
R: Sí, puedes crear múltiples `ToolRegistry` si necesitas diferentes conjuntos de tools
|
|
||||||
|
|
||||||
**P: ¿Cómo conecto una tool con una BD real?**
|
|
||||||
R: En el método `invoke()`, hace la consulta a tu BD. Los ejemplos aquí usan datos simulados.
|
|
||||||
|
|
||||||
**P: ¿Necesito modificar agent.py?**
|
|
||||||
R: No, `agent.py` ya soporta tools. Solo necesitas crear las tools y pasar a `Agent(tools=...)`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Última actualización:** 15 Feb 2026
|
|
||||||
**Status:** ✅ Implementación completada y documentada
|
|
||||||
@@ -1,229 +0,0 @@
|
|||||||
# 📚 Índice de Documentación: Patrón Registry para Tools
|
|
||||||
|
|
||||||
Bienvenido al sistema de Tools de **NaliiaBot** usando el patrón Registry.
|
|
||||||
|
|
||||||
## 🚀 Comienza por aquí
|
|
||||||
|
|
||||||
**Si es tu primer día con esto:**
|
|
||||||
|
|
||||||
1. **[QUICK_START.py](QUICK_START.py)** ⭐ (5 minutos)
|
|
||||||
- Tutorial interactivo paso a paso
|
|
||||||
- Ejemplo simple que funciona inmediatamente
|
|
||||||
- Puedes ejecutarlo: `python QUICK_START.py`
|
|
||||||
|
|
||||||
2. **[REGISTRY_IMPLEMENTATION_SUMMARY.md](REGISTRY_IMPLEMENTATION_SUMMARY.md)** (10 minutos)
|
|
||||||
- Qué se hizo y por qué
|
|
||||||
- Cómo está organizado ahora
|
|
||||||
- Próximos pasos
|
|
||||||
|
|
||||||
3. **[TOOLS_GUIDE.md](TOOLS_GUIDE.md)** (20 minutos)
|
|
||||||
- Guía completa del patrón Registry
|
|
||||||
- Métodos disponibles
|
|
||||||
- Mejores prácticas
|
|
||||||
|
|
||||||
## 📖 Documentación por tema
|
|
||||||
|
|
||||||
### Para crear herramientas
|
|
||||||
|
|
||||||
| Documento | Contenido |
|
|
||||||
|-----------|-----------|
|
|
||||||
| **[TOOLS_GUIDE.md](TOOLS_GUIDE.md#2-crear-una-herramienta-personalizada)** | Cómo crear una herramienta básica |
|
|
||||||
| **[TOOLS_GUIDE.md](TOOLS_GUIDE.md#5-ejemplo-completo)** | Ejemplo completo con validaciones |
|
|
||||||
| **[AGENDA_TOOLS_EXAMPLE.py](AGENDA_TOOLS_EXAMPLE.py)** | 5 herramientas reales para agenda |
|
|
||||||
|
|
||||||
### Para usar tools
|
|
||||||
|
|
||||||
| Documento | Contenido |
|
|
||||||
|-----------|-----------|
|
|
||||||
| **[TOOLS_GUIDE.md](TOOLS_GUIDE.md#3-registrar-y-usar-herramientas)** | Cómo registrar y usar tools |
|
|
||||||
| **[TOOLS_GUIDE.md](TOOLS_GUIDE.md#4-métodos-disponibles)** | API completo de ToolRegistry |
|
|
||||||
| **[TOOLS_GUIDE.md](TOOLS_GUIDE.md#6-usar-con-el-agent)** | Integración con Agent |
|
|
||||||
|
|
||||||
### Para entender la arquitectura
|
|
||||||
|
|
||||||
| Documento | Contenido |
|
|
||||||
|-----------|-----------|
|
|
||||||
| **[REGISTRY_IMPLEMENTATION_SUMMARY.md](REGISTRY_IMPLEMENTATION_SUMMARY.md)** | Estructura general |
|
|
||||||
| **[CHANGES_SUMMARY.md](CHANGES_SUMMARY.md)** | Qué cambió respecto a antes |
|
|
||||||
| **[src/naliiabot/bot/tools/tool_registry.py](src/naliiabot/bot/tools/tool_registry.py)** | Código fuente del Registry |
|
|
||||||
|
|
||||||
## 🎯 Casos de uso comunes
|
|
||||||
|
|
||||||
### Caso 1: Crear una herramienta simple
|
|
||||||
```
|
|
||||||
1. Lee: QUICK_START.py
|
|
||||||
2. Lee: TOOLS_GUIDE.md (sección 2)
|
|
||||||
3. Copia código de QUICK_START.py a tools.py
|
|
||||||
4. Listo ✅
|
|
||||||
```
|
|
||||||
|
|
||||||
### Caso 2: Crear herramientas para gestionar agenda
|
|
||||||
```
|
|
||||||
1. Lee: AGENDA_TOOLS_EXAMPLE.py
|
|
||||||
2. Adapta los ejemplos a tu lógica
|
|
||||||
3. Copia a tools.py
|
|
||||||
4. Registra en tu main.py
|
|
||||||
5. Listo ✅
|
|
||||||
```
|
|
||||||
|
|
||||||
### Caso 3: Entender cómo funciona el Registry
|
|
||||||
```
|
|
||||||
1. Lee: REGISTRY_IMPLEMENTATION_SUMMARY.md
|
|
||||||
2. Revisa: tool_registry.py
|
|
||||||
3. Lee: TOOLS_GUIDE.md (sección 4)
|
|
||||||
4. Experimenta con los métodos
|
|
||||||
5. Entiendido ✅
|
|
||||||
```
|
|
||||||
|
|
||||||
### Caso 4: Hacer pruebas de tools
|
|
||||||
```
|
|
||||||
1. Abre: tests/test_naliia_agent_tools.py
|
|
||||||
2. Agrega tus tests
|
|
||||||
3. Ejecuta: pytest tests/test_naliia_agent_tools.py -v
|
|
||||||
4. Testeado ✅
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📂 Estructura de archivos
|
|
||||||
|
|
||||||
```
|
|
||||||
NaliiaBot/
|
|
||||||
│
|
|
||||||
├── 📄 QUICK_START.py ← EMPIEZA AQUÍ
|
|
||||||
├── 📄 REGISTRY_IMPLEMENTATION_SUMMARY.md ← Resumen
|
|
||||||
├── 📄 TOOLS_GUIDE.md ← Guía completa
|
|
||||||
├── 📄 AGENDA_TOOLS_EXAMPLE.py ← Ejemplos reales
|
|
||||||
├── 📄 CHANGES_SUMMARY.md ← Qué cambió
|
|
||||||
├── 📄 TOOLS_DOCUMENTATION_INDEX.md ← Este archivo
|
|
||||||
│
|
|
||||||
├── 📁 src/naliiabot/bot/tools/
|
|
||||||
│ ├── __init__.py ← Exporta BaseTool, ToolRegistry
|
|
||||||
│ ├── tool_registry.py ← Infraestructura (código fuente)
|
|
||||||
│ └── tools.py ← ← TUS HERRAMIENTAS AQUÍ
|
|
||||||
│
|
|
||||||
└── 📁 tests/
|
|
||||||
└── test_naliia_agent_tools.py ← Tests de tools
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔍 Búsqueda rápida
|
|
||||||
|
|
||||||
### Necesito...
|
|
||||||
|
|
||||||
- **Crear mi primera tool** → [QUICK_START.py](QUICK_START.py)
|
|
||||||
- **Entender el patrón Registry** → [TOOLS_GUIDE.md](TOOLS_GUIDE.md)
|
|
||||||
- **Ver ejemplos de tools reales** → [AGENDA_TOOLS_EXAMPLE.py](AGENDA_TOOLS_EXAMPLE.py)
|
|
||||||
- **Saber qué cambió** → [CHANGES_SUMMARY.md](CHANGES_SUMMARY.md)
|
|
||||||
- **Registrar herramientas** → [TOOLS_GUIDE.md#3](TOOLS_GUIDE.md#3-registrar-y-usar-herramientas)
|
|
||||||
- **Ver métodos de ToolRegistry** → [TOOLS_GUIDE.md#4](TOOLS_GUIDE.md#4-métodos-disponibles-del-toolregistry)
|
|
||||||
- **Hacer tests** → [tests/test_naliia_agent_tools.py](tests/test_naliia_agent_tools.py)
|
|
||||||
- **Ver el código del Registry** → [src/naliiabot/bot/tools/tool_registry.py](src/naliiabot/bot/tools/tool_registry.py)
|
|
||||||
|
|
||||||
## 📊 Recomendación de lectura
|
|
||||||
|
|
||||||
### Para nuevos usuarios (20 minutos)
|
|
||||||
```
|
|
||||||
1. QUICK_START.py (5 min)
|
|
||||||
2. TOOLS_GUIDE.md (15 min)
|
|
||||||
↓
|
|
||||||
Ahora puedes crear tools
|
|
||||||
```
|
|
||||||
|
|
||||||
### Para desarrollo completo (1 hora)
|
|
||||||
```
|
|
||||||
1. QUICK_START.py (5 min)
|
|
||||||
2. REGISTRY_IMPLEMENTATION_SUMMARY.md (10 min)
|
|
||||||
3. TOOLS_GUIDE.md (20 min)
|
|
||||||
4. AGENDA_TOOLS_EXAMPLE.py (15 min)
|
|
||||||
5. Revisar tests (10 min)
|
|
||||||
↓
|
|
||||||
Experto en el patrón
|
|
||||||
```
|
|
||||||
|
|
||||||
### Para entender la arquitectura (30 minutos)
|
|
||||||
```
|
|
||||||
1. CHANGES_SUMMARY.md (10 min)
|
|
||||||
2. tool_registry.py (15 min)
|
|
||||||
3. tests/test_naliia_agent_tools.py (5 min)
|
|
||||||
↓
|
|
||||||
Entiendes cómo está construido
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🎓 Conceptos clave
|
|
||||||
|
|
||||||
### BaseTool
|
|
||||||
Clase abstracta que define qué es una herramienta:
|
|
||||||
- `name`: Identificador único
|
|
||||||
- `description`: Para que el LLM sepa cuándo usarla
|
|
||||||
- `args_schema`: Qué argumentos acepta (JSON Schema)
|
|
||||||
- `invoke()`: Cómo se ejecuta
|
|
||||||
|
|
||||||
**Leer en:** [TOOLS_GUIDE.md#estructura-básica](TOOLS_GUIDE.md#1-estructura-básica)
|
|
||||||
|
|
||||||
### ToolRegistry
|
|
||||||
Clase que administra todas las herramientas:
|
|
||||||
- `register()`: Agregar una herramienta
|
|
||||||
- `get_tool()`: Obtener una específica
|
|
||||||
- `get_all_tools()`: Obtener todas (BaseTool)
|
|
||||||
- `get_all_tools_as_langchain()`: Obtener en formato LangChain
|
|
||||||
|
|
||||||
**Leer en:** [TOOLS_GUIDE.md#métodos-disponibles](TOOLS_GUIDE.md#4-métodos-disponibles-del-toolregistry)
|
|
||||||
|
|
||||||
### El flujo completo
|
|
||||||
```
|
|
||||||
Tool (BaseTool)
|
|
||||||
↓
|
|
||||||
Registry (ToolRegistry)
|
|
||||||
↓
|
|
||||||
Agent (recibe tools)
|
|
||||||
↓
|
|
||||||
LLM (usa las tools)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Leer en:** [TOOLS_GUIDE.md#uso-con-el-agent](TOOLS_GUIDE.md#6-usar-con-el-agent)
|
|
||||||
|
|
||||||
## ✅ Checklist: Empezar a usar Tools
|
|
||||||
|
|
||||||
- [ ] Leí QUICK_START.py
|
|
||||||
- [ ] Entiendo qué es BaseTool
|
|
||||||
- [ ] Entiendo qué es ToolRegistry
|
|
||||||
- [ ] Creé una herramienta simple en tools.py
|
|
||||||
- [ ] Registré la herramienta
|
|
||||||
- [ ] Usé la herramienta con Agent
|
|
||||||
- [ ] Los tests pasan
|
|
||||||
- [ ] Leí ejemplos de agenda (AGENDA_TOOLS_EXAMPLE.py)
|
|
||||||
- [ ] Sé cómo conectar con BD
|
|
||||||
- [ ] Sé cómo manejar errores
|
|
||||||
|
|
||||||
Si marcaste todo ✅, ¡estás listo para trabajar con el sistema de tools!
|
|
||||||
|
|
||||||
## 🆘 Necesito ayuda
|
|
||||||
|
|
||||||
- **¿Cómo creo una tool?** → [TOOLS_GUIDE.md#2](TOOLS_GUIDE.md#2-crear-una-herramienta-personalizada)
|
|
||||||
- **¿Cuáles son los métodos?** → [TOOLS_GUIDE.md#4](TOOLS_GUIDE.md#4-métodos-disponibles-del-toolregistry)
|
|
||||||
- **¿Tengo un error?** → [TOOLS_GUIDE.md#7](TOOLS_GUIDE.md#7-ventajas-del-patrón-registry)
|
|
||||||
- **¿Quiero ejemplos?** → [AGENDA_TOOLS_EXAMPLE.py](AGENDA_TOOLS_EXAMPLE.py)
|
|
||||||
|
|
||||||
## 📞 Resumen rápido
|
|
||||||
|
|
||||||
### Lo esencial (30 segundos)
|
|
||||||
|
|
||||||
```python
|
|
||||||
# 1. Crear una tool
|
|
||||||
class MiTool(BaseTool):
|
|
||||||
def name(self) -> str:
|
|
||||||
return "mi_tool"
|
|
||||||
# ... más detalles
|
|
||||||
|
|
||||||
# 2. Registrar
|
|
||||||
registry = ToolRegistry()
|
|
||||||
registry.register(MiTool())
|
|
||||||
|
|
||||||
# 3. Usar
|
|
||||||
agent = Agent(model=modelo, tools=registry.get_all_tools_as_langchain())
|
|
||||||
```
|
|
||||||
|
|
||||||
Eso es todo. El resto está en la documentación.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Última actualización:** 15 Feb 2026
|
|
||||||
**Status:** ✅ Documentación completa
|
|
||||||
@@ -1,351 +0,0 @@
|
|||||||
# 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`
|
|
||||||
@@ -1,457 +0,0 @@
|
|||||||
# 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.
|
|
||||||
@@ -1,210 +0,0 @@
|
|||||||
"""
|
|
||||||
Ejemplos de uso del patrón Registry con el Agent.
|
|
||||||
|
|
||||||
Muestra diferentes formas de usar el ToolRegistry con el agente.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from src.naliiabot.bot.tools.tool_registry import ToolRegistry, ToolRegistryBuilder
|
|
||||||
from src.naliiabot.bot.tools.tools import (
|
|
||||||
CalculatorTool,
|
|
||||||
GreeterTool,
|
|
||||||
WeatherTool,
|
|
||||||
TimeTool
|
|
||||||
)
|
|
||||||
from src.naliiabot.bot.agent.agent import Agent, AgentConfig
|
|
||||||
|
|
||||||
|
|
||||||
def example_1_basic_registry():
|
|
||||||
"""Ejemplo 1: Crear un registry básico y usarlo."""
|
|
||||||
print("\n" + "="*60)
|
|
||||||
print("EJEMPLO 1: Registry Básico")
|
|
||||||
print("="*60)
|
|
||||||
|
|
||||||
# Crear el registro
|
|
||||||
registry = ToolRegistry()
|
|
||||||
|
|
||||||
# Registrar herramientas
|
|
||||||
registry.register(CalculatorTool())
|
|
||||||
registry.register(GreeterTool())
|
|
||||||
|
|
||||||
# Ver herramientas disponibles
|
|
||||||
print(f"\nHerramientas disponibles: {len(registry)}")
|
|
||||||
for name, description in registry.list_tools().items():
|
|
||||||
print(f" - {name}: {description[:50]}...")
|
|
||||||
|
|
||||||
# Obtener una herramienta específica
|
|
||||||
calc = registry.get_tool("calculator")
|
|
||||||
result = calc.invoke(a=10, b=5, operation="add")
|
|
||||||
print(f"\nResultado de cálculo: {result}")
|
|
||||||
|
|
||||||
|
|
||||||
def example_2_builder_pattern():
|
|
||||||
"""Ejemplo 2: Usar el builder para crear un registry."""
|
|
||||||
print("\n" + "="*60)
|
|
||||||
print("EJEMPLO 2: Builder Pattern")
|
|
||||||
print("="*60)
|
|
||||||
|
|
||||||
# Usar el builder fluido
|
|
||||||
registry = (ToolRegistryBuilder()
|
|
||||||
.add(CalculatorTool())
|
|
||||||
.add(GreeterTool())
|
|
||||||
.add(WeatherTool())
|
|
||||||
.add(TimeTool())
|
|
||||||
.build())
|
|
||||||
|
|
||||||
print(f"\nRegistro creado con {len(registry)} herramientas")
|
|
||||||
print(f"Contenido: {registry}")
|
|
||||||
|
|
||||||
|
|
||||||
def example_3_agent_integration():
|
|
||||||
"""Ejemplo 3: Integrar registry con Agent."""
|
|
||||||
print("\n" + "="*60)
|
|
||||||
print("EJEMPLO 3: Integración con Agent")
|
|
||||||
print("="*60)
|
|
||||||
|
|
||||||
# Crear registro de tools
|
|
||||||
registry = (ToolRegistryBuilder()
|
|
||||||
.add(CalculatorTool())
|
|
||||||
.add(GreeterTool())
|
|
||||||
.add(WeatherTool())
|
|
||||||
.build())
|
|
||||||
|
|
||||||
# Crear configuración
|
|
||||||
config = AgentConfig(
|
|
||||||
system_prompt="You are a helpful assistant with access to tools.",
|
|
||||||
max_iterations=5
|
|
||||||
)
|
|
||||||
|
|
||||||
# Aquí iría el modelo real (mock para ejemplo)
|
|
||||||
# agent = Agent(model=your_model, config=config, tools=registry)
|
|
||||||
print(f"Agent configurado con {len(registry.get_all_tools())} herramientas")
|
|
||||||
print("Tools disponibles:")
|
|
||||||
for tool in registry.get_all_tools():
|
|
||||||
print(f" - {tool.name}: {tool.description}")
|
|
||||||
|
|
||||||
|
|
||||||
def example_4_dynamic_tools():
|
|
||||||
"""Ejemplo 4: Registración dinámica de herramientas."""
|
|
||||||
print("\n" + "="*60)
|
|
||||||
print("EJEMPLO 4: Registración Dinámica")
|
|
||||||
print("="*60)
|
|
||||||
|
|
||||||
registry = ToolRegistry()
|
|
||||||
|
|
||||||
# Fase 1: herramientas básicas
|
|
||||||
registry.register(CalculatorTool())
|
|
||||||
print(f"Paso 1 - Herramientas: {len(registry)}")
|
|
||||||
|
|
||||||
# Fase 2: añadir más
|
|
||||||
registry.register(GreeterTool())
|
|
||||||
registry.register(WeatherTool())
|
|
||||||
print(f"Paso 2 - Herramientas: {len(registry)}")
|
|
||||||
|
|
||||||
# Fase 3: remover según necesidad
|
|
||||||
registry.unregister("calculator")
|
|
||||||
print(f"Paso 3 - Herramientas: {len(registry)}")
|
|
||||||
|
|
||||||
# Ver estado final
|
|
||||||
print(f"\nHerramientas finales:")
|
|
||||||
for tool in registry.get_all_tools():
|
|
||||||
print(f" - {tool.name}")
|
|
||||||
|
|
||||||
|
|
||||||
def example_5_error_handling():
|
|
||||||
"""Ejemplo 5: Manejo de errores."""
|
|
||||||
print("\n" + "="*60)
|
|
||||||
print("EJEMPLO 5: Manejo de Errores")
|
|
||||||
print("="*60)
|
|
||||||
|
|
||||||
registry = ToolRegistry()
|
|
||||||
|
|
||||||
# Intentar operación inválida
|
|
||||||
try:
|
|
||||||
registry.register("not a tool")
|
|
||||||
except TypeError as e:
|
|
||||||
print(f"Error esperado: {e}")
|
|
||||||
|
|
||||||
# Registrar herramienta
|
|
||||||
registry.register(CalculatorTool())
|
|
||||||
|
|
||||||
# Intentar registrar duplicada
|
|
||||||
try:
|
|
||||||
registry.register(CalculatorTool())
|
|
||||||
except ValueError as e:
|
|
||||||
print(f"Error esperado: {e}")
|
|
||||||
|
|
||||||
# Intentar desregistrar inexistente
|
|
||||||
try:
|
|
||||||
registry.unregister("nonexistent")
|
|
||||||
except KeyError as e:
|
|
||||||
print(f"Error esperado: {e}")
|
|
||||||
|
|
||||||
|
|
||||||
def example_6_tool_execution():
|
|
||||||
"""Ejemplo 6: Ejecutar herramientas desde el registry."""
|
|
||||||
print("\n" + "="*60)
|
|
||||||
print("EJEMPLO 6: Ejecución de Herramientas")
|
|
||||||
print("="*60)
|
|
||||||
|
|
||||||
registry = (ToolRegistryBuilder()
|
|
||||||
.add(CalculatorTool())
|
|
||||||
.add(GreeterTool())
|
|
||||||
.add(WeatherTool())
|
|
||||||
.add(TimeTool())
|
|
||||||
.build())
|
|
||||||
|
|
||||||
# Ejecutar calculator
|
|
||||||
calc_tool = registry.get_tool("calculator")
|
|
||||||
calc_result = calc_tool.invoke(a=15, b=3, operation="multiply")
|
|
||||||
print(f"\nCalculadora: {calc_result}")
|
|
||||||
|
|
||||||
# Ejecutar greeter
|
|
||||||
greeter = registry.get_tool("greeter")
|
|
||||||
greeting = greeter.invoke(name="Alice", tone="formal")
|
|
||||||
print(f"Saludo: {greeting}")
|
|
||||||
|
|
||||||
# Ejecutar weather
|
|
||||||
weather = registry.get_tool("get_weather")
|
|
||||||
weather_result = weather.invoke(location="Barcelona", units="celsius")
|
|
||||||
print(f"Clima: {weather_result}")
|
|
||||||
|
|
||||||
# Ejecutar time
|
|
||||||
time_tool = registry.get_tool("get_time")
|
|
||||||
time_result = time_tool.invoke(format="24h", timezone="CET")
|
|
||||||
print(f"Hora: {time_result}")
|
|
||||||
|
|
||||||
|
|
||||||
def example_7_langchain_format():
|
|
||||||
"""Ejemplo 7: Convertir a formato LangChain."""
|
|
||||||
print("\n" + "="*60)
|
|
||||||
print("EJEMPLO 7: Formato LangChain")
|
|
||||||
print("="*60)
|
|
||||||
|
|
||||||
registry = (ToolRegistryBuilder()
|
|
||||||
.add(CalculatorTool())
|
|
||||||
.add(GreeterTool())
|
|
||||||
.build())
|
|
||||||
|
|
||||||
# Obtener herramientas en formato LangChain
|
|
||||||
langchain_tools = registry.get_langchain_tools()
|
|
||||||
|
|
||||||
print(f"\nHerramientas en formato LangChain: {len(langchain_tools)}")
|
|
||||||
for tool in langchain_tools:
|
|
||||||
print(f" - Nombre: {tool.name}")
|
|
||||||
print(f" Descripción: {tool.description[:50]}...")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
print("\n🚀 EJEMPLOS DE USO DEL TOOL REGISTRY")
|
|
||||||
|
|
||||||
example_1_basic_registry()
|
|
||||||
example_2_builder_pattern()
|
|
||||||
example_3_agent_integration()
|
|
||||||
example_4_dynamic_tools()
|
|
||||||
example_5_error_handling()
|
|
||||||
example_6_tool_execution()
|
|
||||||
example_7_langchain_format()
|
|
||||||
|
|
||||||
print("\n" + "="*60)
|
|
||||||
print("✅ Ejemplos completados")
|
|
||||||
print("="*60 + "\n")
|
|
||||||
Reference in New Issue
Block a user