Files
OnAPB-Carrere_Demartin/docs/superpowers/specs/2026-04-09-genius-agent-design.md
Laucha1312 90c5f85512 2
2026-06-04 15:15:23 -03:00

8.3 KiB
Raw Permalink Blame History

OnAPB Genius Agent — Spec de Diseño

Fecha: 2026-04-09
Rama: feature/genius-agent
Stack: Laravel 12, Prism PHP (prism-php/prism), Google Gemini 1.5 Flash, MySQL, Alpine.js


1. Objetivo

Implementar un agente de IA conversacional ("OnAPB Genius") integrado en onapb.com con dos modos de operación según el rol del usuario:

  • Público (no logueado, aficionado, jugador): asistente de navegación y consultas usando el MANUAL_USUARIO.md como contexto RAG simplificado. Sin tools. Sin persistencia entre sesiones.
  • Admin (SuperAdmin rol=1 / GeneralAdmin rol=2): automatización de tareas mediante function calling (Tools). Historial persistente en MySQL, auto-purgado a los 30 días.

2. Decisiones de Arquitectura

Decisión Elección Razón
SDK de AI prism-php/prism Soporte probado de Gemini 1.5 Flash + tools. Más estable que laravel/ai (nuevo).
Modelo gemini-1.5-flash Velocidad (25s), costo bajo, function calling.
Streaming No — JSON completo Hosting compartido Hostinger con límites PHP desconocidos. Evita problemas de output buffer.
Memoria admin MySQL (agent_threads) Sin Redis. JSON column para mensajes. Purge automático 30 días.
Memoria público Session PHP Stateless entre sesiones. Simple, sin overhead de BD.
Tools Solo si admin_logged_in Validado en GeniusAgentService, no en Gemini.

3. Estructura de Archivos

app/
├── AI/
│   ├── Tools/
│   │   ├── CrearPartidoTool.php
│   │   ├── CargarPuntajeTool.php
│   │   ├── RedactarNoticiaTool.php
│   │   ├── ListarEquiposTool.php
│   │   └── ListarEventosTool.php
│   └── Prompts/
│       ├── SystemPromptAdmin.php
│       └── SystemPromptPublic.php
├── Services/
│   └── GeniusAgentService.php
├── Http/Controllers/
│   └── GeniusAgentController.php
├── Models/
│   └── AgentThread.php
└── Console/Commands/
    └── PurgeAgentThreads.php

database/migrations/
└── xxxx_create_agent_threads_table.php

resources/views/components/
└── genius-chat.blade.php

routes/web.php
└── POST /agent/chat  (throttle: 20/min por IP)

4. Schema de Base de Datos

CREATE TABLE agent_threads (
    id          BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    thread_id   VARCHAR(36) NOT NULL UNIQUE,   -- UUID generado en frontend
    admin_id    INT NOT NULL,                  -- session('admin_id')
    messages    JSON NOT NULL,                 -- array [{role, content}]
    created_at  TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at  TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    expires_at  TIMESTAMP NOT NULL             -- created_at + 30 días
);

5. Tools Disponibles (Admin)

Cada tool implementa la interfaz Prism Tool. El agent loop permite que Gemini llame la misma tool N veces para operaciones batch (ej. cargar puntajes de todos los partidos de una jornada).

Tool Parámetros Acción en BD
CrearPartidoTool id_equipo_local, id_equipo_visitante, fecha_evento, hora_inicio, hora_fin, sede, id_torneo Evento::create(...)
CargarPuntajeTool id_evento, marcador_local, marcador_visitante Evento::find()->update(...)
RedactarNoticiaTool titulo, contenido, id_torneo?, categoria? Noticia::create(...)
ListarEquiposTool id_torneo?, id_club?, grupo? Equipo::query() con join a pivot torneo_equipo filtrando por grupo (solo lectura)
ListarEventosTool fecha_desde?, fecha_hasta?, id_torneo? Evento::query()->get() (solo lectura)

Nota sobre grupo: No es un modelo separado. Es una columna en la tabla pivot torneo_equipo (relación Torneo::equipos()->withPivot('grupo')). ListarEquiposTool filtra con wherePivot('grupo', $grupo).

Agregar una nueva tool en el futuro:

  1. Crear app/AI/Tools/NuevaTool.php
  2. Registrarla en GeniusAgentService::getAdminTools()

6. Flujo de Datos

Usuario público

POST /agent/chat { message }
→ session()->get('agent_messages', [])
→ GeniusAgentService::chatPublic(message, history)
  → System prompt: navegación + MANUAL_USUARIO.md completo como contexto (el archivo es pequeño; si crece >50KB usar solo las primeras 200 líneas)
  → Prism → Gemini (sin tools)
  → Respuesta de texto
→ session()->put('agent_messages', [...])
→ return JSON { reply }

Admin

POST /agent/chat { message, thread_id? }
→ AgentThread::findOrCreate(thread_id, admin_id)
→ GeniusAgentService::chatAdmin(message, thread)
  → System prompt: automatización de tareas OnAPB
  → Prism → Gemini con tools
    → [si Gemini llama tool] → Tool::handle() → resultado
    → [Gemini puede llamar N tools] → agent loop
    → Respuesta final de texto
→ thread->appendMessages([...])
→ thread->save()
→ return JSON { reply, thread_id }

7. Seguridad

  • Tools bloqueadas a no-admins: GeniusAgentService valida session('admin_logged_in') antes de incluir tools.
  • Rate limiting: throttle:20,1 en la ruta /agent/chat.
  • Validación de input: message requerido, string, máx. 1000 caracteres.
  • Aislamiento de threads: cada thread valida admin_id = session('admin_id'). No hay acceso cruzado entre admins.
  • API key: solo en .env (GEMINI_API_KEY). Nunca en código ni en BD.

8. Manejo de Errores

Caso Comportamiento
Gemini timeout / 5xx catch RequestException → JSON { error: "El agente no responde, reintentá en un momento." }
Tool falla (ej. evento no existe) Tool retorna { error: "..." } → Gemini lo incorpora en su respuesta
Thread expirado / no encontrado Se crea un nuevo thread automáticamente
API key inválida / quota excedida Log en Laravel + respuesta genérica (sin exponer detalles de la API)
Timeout de Hostinger set_time_limit(120) al inicio del controller action

9. Purge de Threads

Comando php artisan agent:purge-threads que elimina agent_threads donde expires_at < NOW().

Schedule: Se registra en routes/console.php para ejecutarse diariamente. En Hostinger sin queue worker, se puede configurar como cron en hPanel:

# Reemplazar /home/TU_USUARIO/public_html con la ruta real de tu cuenta Hostinger
0 3 * * * cd /home/TU_USUARIO/public_html && php artisan agent:purge-threads >> /dev/null 2>&1

10. Frontend — Chat Bubble

Componente Blade genius-chat.blade.php incluido al final de resources/views/layouts/app.blade.php:

  • Botón flotante (bottom-right) con ícono de chat
  • Panel slide-up con historial de mensajes de la sesión actual
  • Alpine.js para estado local (open/closed, messages, loading spinner)
  • Tailwind para estilos (sin dependencias adicionales)
  • Rol-aware: el frontend no diferencia, la diferencia la hace el backend

11. Comandos de Deploy

Instalación inicial (local y producción)

# 1. Instalar Prism PHP
composer require prism-php/prism

# 2. Publicar config de Prism (opcional)
php artisan vendor:publish --provider="EchoLabs\Prism\PrismServiceProvider"

# 3. Agregar en .env
GEMINI_API_KEY=tu_api_key_aqui

# 4. Ejecutar migration de agent_threads
php artisan migrate

# 5. Registrar el comando de purge (se hace automáticamente con el código)
# Verificar que está en routes/console.php

# 6. Limpiar cachés después del deploy
php artisan config:clear
php artisan cache:clear
php artisan view:clear
php artisan route:clear

# 7. Re-cachear para producción
php artisan config:cache
php artisan route:cache
php artisan view:cache

Comandos de mantenimiento

# Purge manual de threads expirados
php artisan agent:purge-threads

# Ver logs del agente
php artisan pail --filter="GeniusAgent"

# Limpiar todos los threads (emergencia)
php artisan tinker --execute="App\Models\AgentThread::truncate();"

Deploy en Hostinger (via SSH o File Manager)

# Subir archivos nuevos y correr:
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache

12. Configuración .env requerida

GEMINI_API_KEY=tu_api_key_de_google_ai_studio

# Opcional: ajustar timeout HTTP de Prism
PRISM_HTTP_TIMEOUT=30