rss2/docs/LLM_CATEGORIZER.md

370 lines
8.2 KiB
Markdown

# 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.
```bash
# 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
```bash
# 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:
```bash
# Instalar dependencias localmente (solo para prueba)
pip3 install exllamav2 torch
# Ejecutar script de prueba
python3 scripts/test_llm_categorizer.py
```
---
## Uso
### Iniciar el servicio
```bash
# Construir y levantar el contenedor
docker compose up -d llm-categorizer
# Ver logs en tiempo real
docker compose logs -f llm-categorizer
```
### Verificar funcionamiento
```bash
# 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
```bash
docker compose stop llm-categorizer
```
---
## Configuración
### Variables de Entorno
Puedes ajustar el comportamiento editando `docker-compose.yml`:
```yaml
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`:
```python
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:
```bash
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
```sql
-- 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
```bash
# 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`
```yaml
environment:
LLM_BATCH_SIZE: 5
LLM_CACHE_MODE: Q4
```
### Error: "Model not found"
**Causa**: El modelo no se descargó correctamente.
**Solución**:
```bash
# 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**:
```bash
# 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):
```python
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):
```yaml
LLM_BATCH_SIZE: 20
```
2. **Cache Q4** (menos VRAM, ligeramente más lento):
```yaml
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:
```python
# 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).