rss2/docs/LLM_CATEGORIZER.md

8.2 KiB

Sistema de Categorización Automática con LLM

Descripción

Este sistema utiliza ExLlamaV2 con un modelo de lenguaje local (LLM) para categorizar automáticamente las noticias del feed RSS.

¿Qué hace?

  1. Recopila 10 noticias sin categorizar de la base de datos
  2. Envía al LLM local con un prompt especializado
  3. El LLM discrimina/categoriza cada noticia en una de las categorías predefinidas
  4. Actualiza la base de datos con las categorías asignadas

Ventajas

  • 100% Local: No envía datos a APIs externas
  • Optimizado para RTX 3060 12GB: Modelos cuantizados eficientes
  • Categorización inteligente: Entiende contexto, no solo keywords
  • Escalable: Procesa lotes de 10 noticias automáticamente
  • Integrado: Se ejecuta como un worker más del sistema

Instalación

Paso 1: Descargar el Modelo

El sistema necesita un modelo LLM compatible. Recomendamos Mistral-7B-Instruct GPTQ para RTX 3060 12GB.

# Ejecutar el script de descarga
./scripts/download_llm_model.sh

El script te mostrará opciones:

  1. Mistral-7B-Instruct-v0.2 (GPTQ) - RECOMENDADO
  2. Mistral-7B-Instruct-v0.2 (EXL2)
  3. OpenHermes-2.5-Mistral-7B (GPTQ)
  4. Neural-Chat-7B (GPTQ)

Tiempo estimado de descarga: 10-30 minutos (según conexión)

Espacio en disco: ~4.5 GB

Paso 2: Verificar la instalación

# Verificar que el modelo se descargó correctamente
ls -lh models/llm/

# Deberías ver archivos como:
# - model.safetensors o *.safetensors
# - config.json
# - tokenizer.json
# - etc.

Paso 3: Probar el sistema (opcional)

Antes de levantar el contenedor, puedes probar que funciona:

# Instalar dependencias localmente (solo para prueba)
pip3 install exllamav2 torch

# Ejecutar script de prueba
python3 scripts/test_llm_categorizer.py

Uso

Iniciar el servicio

# Construir y levantar el contenedor
docker compose up -d llm-categorizer

# Ver logs en tiempo real
docker compose logs -f llm-categorizer

Verificar funcionamiento

# Ver estado del contenedor
docker compose ps llm-categorizer

# Ver últimas 50 líneas de log
docker compose logs --tail=50 llm-categorizer

# Ver categorías asignadas en la base de datos
docker exec -it rss2_db psql -U rss -d rss -c \
  "SELECT llm_categoria, COUNT(*) FROM noticias WHERE llm_processed = TRUE GROUP BY llm_categoria;"

Detener el servicio

docker compose stop llm-categorizer

Configuración

Variables de Entorno

Puedes ajustar el comportamiento editando docker-compose.yml:

environment:
  # Número de noticias a procesar por lote (default: 10)
  LLM_BATCH_SIZE: 10
  
  # Tiempo de espera cuando no hay noticias (segundos, default: 30)
  LLM_SLEEP_IDLE: 30
  
  # Longitud máxima de contexto (default: 4096)
  LLM_MAX_SEQ_LEN: 4096
  
  # Modo de caché: FP16 o Q4 (default: FP16)
  # Q4 usa menos VRAM pero puede ser más lento
  LLM_CACHE_MODE: FP16
  
  # Distribución de GPU: "auto" para single GPU
  LLM_GPU_SPLIT: auto

Categorías

Las categorías están definidas en workers/llm_categorizer_worker.py:

CATEGORIES = [
    "Política",
    "Economía",
    "Tecnología",
    "Ciencia",
    "Salud",
    "Deportes",
    "Entretenimiento",
    "Internacional",
    "Nacional",
    "Sociedad",
    "Cultura",
    "Medio Ambiente",
    "Educación",
    "Seguridad",
    "Otros"
]

Para modificarlas, edita el archivo y reconstruye el contenedor:

docker compose up -d --build llm-categorizer

Base de Datos

Nuevas columnas en noticias

El worker añade automáticamente estas columnas:

  • llm_categoria (VARCHAR): Categoría asignada
  • llm_confianza (FLOAT): Nivel de confianza (0.0 - 1.0)
  • llm_processed (BOOLEAN): Si ya fue procesada
  • llm_processed_at (TIMESTAMP): Fecha de procesamiento

Consultas útiles

-- Ver distribución de categorías
SELECT llm_categoria, COUNT(*) as total, AVG(llm_confianza) as confianza_media
FROM noticias 
WHERE llm_processed = TRUE 
GROUP BY llm_categoria
ORDER BY total DESC;

-- Ver noticias de una categoría específica
SELECT id, titulo, llm_categoria, llm_confianza, fecha
FROM noticias
WHERE llm_categoria = 'Tecnología'
  AND llm_processed = TRUE
ORDER BY fecha DESC
LIMIT 20;

-- Ver noticias con baja confianza (revisar manualmente)
SELECT id, titulo, llm_categoria, llm_confianza
FROM noticias
WHERE llm_processed = TRUE
  AND llm_confianza < 0.6
ORDER BY llm_confianza ASC
LIMIT 20;

-- Resetear procesamiento (para reprocesar)
UPDATE noticias SET llm_processed = FALSE WHERE llm_categoria = 'Otros';

Monitorización

Prometheus/Grafana

El worker está integrado con el stack de monitorización. Puedes ver:

  • Uso de GPU (VRAM)
  • Tiempo de procesamiento por lote
  • Tasa de categorización

Accede a Grafana: http://localhost:3001

Logs

# Ver logs en tiempo real
docker compose logs -f llm-categorizer

# Buscar errores
docker compose logs llm-categorizer | grep ERROR

# Ver estadísticas de categorización
docker compose logs llm-categorizer | grep "Distribución"

Troubleshooting

Error: "Out of memory"

Causa: El modelo es demasiado grande para tu GPU.

Solución:

  1. Usa un modelo más pequeño (ej: EXL2 con menor bpw)
  2. Reduce el batch size: LLM_BATCH_SIZE: 5
  3. Usa cache Q4 en lugar de FP16: LLM_CACHE_MODE: Q4
environment:
  LLM_BATCH_SIZE: 5
  LLM_CACHE_MODE: Q4

Error: "Model not found"

Causa: El modelo no se descargó correctamente.

Solución:

# Verificar directorio
ls -la models/llm/

# Debería contener config.json y archivos .safetensors
# Si está vacío, ejecutar de nuevo:
./scripts/download_llm_model.sh

El worker no procesa noticias

Causa: Posiblemente ya están todas procesadas.

Solución:

# Verificar cuántas noticias faltan
docker exec -it rss2_db psql -U rss -d rss -c \
  "SELECT COUNT(*) FROM noticias WHERE llm_processed = FALSE;"

# Si es 0, resetear algunas para probar
docker exec -it rss2_db psql -U rss -d rss -c \
  "UPDATE noticias SET llm_processed = FALSE WHERE id IN (SELECT id FROM noticias ORDER BY fecha DESC LIMIT 20);"

Categorización incorrecta

Causa: El prompt puede necesitar ajustes o el modelo no es adecuado.

Soluciones:

  1. Ajustar el prompt en workers/llm_categorizer_worker.py (método _build_prompt)
  2. Probar un modelo diferente (ej: OpenHermes es mejor generalista)
  3. Ajustar la temperatura (más baja = más determinista):
self.settings.temperature = 0.05  # Muy determinista

Rendimiento

RTX 3060 12GB

  • Modelo recomendado: Mistral-7B-Instruct GPTQ 4-bit
  • VRAM utilizada: ~6-7 GB
  • Tiempo por noticia: ~2-5 segundos
  • Throughput: ~120-300 noticias/hora

Optimizaciones

Para mejorar el rendimiento:

  1. Aumentar batch size (si sobra VRAM):

    LLM_BATCH_SIZE: 20
    
  2. Cache Q4 (menos VRAM, ligeramente más lento):

    LLM_CACHE_MODE: Q4
    
  3. Modelo EXL2 optimizado:

    • Usar Mistral EXL2 4.0bpw
    • Es más rápido que GPTQ en ExLlamaV2

Integración con la Web

Para mostrar las categorías en la interfaz web, modifica routers/search.py o crea una nueva vista:

# Ejemplo de endpoint para estadísticas
@app.route('/api/categories/stats')
def category_stats():
    query = """
        SELECT llm_categoria, COUNT(*) as total
        FROM noticias
        WHERE llm_processed = TRUE
        GROUP BY llm_categoria
        ORDER BY total DESC
    """
    # ... ejecutar query y devolver JSON

Roadmap

Posibles mejoras futuras:

  • Subcategorías automáticas
  • Detección de temas trending
  • Resúmenes automáticos por categoría
  • Alertas personalizadas por categoría
  • API REST para categorización bajo demanda
  • Fine-tuning del modelo con feedback de usuario

Soporte

Para problemas o preguntas:

  1. Revisar logs: docker compose logs llm-categorizer
  2. Verificar GPU: nvidia-smi
  3. Consultar documentación de ExLlamaV2: https://github.com/turboderp/exllamav2

Licencia

Este componente se distribuye bajo la misma licencia que el proyecto principal RSS2.

Los modelos LLM tienen sus propias licencias (generalmente Apache 2.0 o MIT para los recomendados).