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?
- Recopila 10 noticias sin categorizar de la base de datos
- Envía al LLM local con un prompt especializado
- El LLM discrimina/categoriza cada noticia en una de las categorías predefinidas
- 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:
- Mistral-7B-Instruct-v0.2 (GPTQ) - RECOMENDADO
- Mistral-7B-Instruct-v0.2 (EXL2)
- OpenHermes-2.5-Mistral-7B (GPTQ)
- 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 asignadallm_confianza(FLOAT): Nivel de confianza (0.0 - 1.0)llm_processed(BOOLEAN): Si ya fue procesadallm_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:
- Usa un modelo más pequeño (ej: EXL2 con menor bpw)
- Reduce el batch size:
LLM_BATCH_SIZE: 5 - 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:
- Ajustar el prompt en
workers/llm_categorizer_worker.py(método_build_prompt) - Probar un modelo diferente (ej: OpenHermes es mejor generalista)
- 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:
-
Aumentar batch size (si sobra VRAM):
LLM_BATCH_SIZE: 20 -
Cache Q4 (menos VRAM, ligeramente más lento):
LLM_CACHE_MODE: Q4 -
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:
- Revisar logs:
docker compose logs llm-categorizer - Verificar GPU:
nvidia-smi - 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).