go integration and wikipedia
This commit is contained in:
parent
47a252e339
commit
ee90335b92
7828 changed files with 1307913 additions and 20807 deletions
|
|
@ -1,391 +0,0 @@
|
|||
# Sistema de Descubrimiento y Gestión de Feeds RSS
|
||||
|
||||
Este documento describe el sistema mejorado de descubrimiento automático y gestión de feeds RSS implementado en RSS2.
|
||||
|
||||
## 📋 Visión General
|
||||
|
||||
El sistema ahora incluye dos mecanismos para gestionar feeds RSS:
|
||||
|
||||
1. **Gestión Manual Mejorada**: Interfaz web para descubrir y añadir feeds desde cualquier URL
|
||||
2. **Worker Automático**: Proceso en segundo plano que descubre feeds desde URLs almacenadas
|
||||
|
||||
## 🎯 Componentes del Sistema
|
||||
|
||||
### 1. Utilidad de Descubrimiento (`utils/feed_discovery.py`)
|
||||
|
||||
Módulo Python que proporciona funciones para:
|
||||
|
||||
- **`discover_feeds(url)`**: Descubre automáticamente todos los feeds RSS/Atom desde una URL
|
||||
- **`validate_feed(feed_url)`**: Valida un feed y extrae su información básica
|
||||
- **`get_feed_metadata(feed_url)`**: Obtiene metadatos detallados de un feed
|
||||
|
||||
```python
|
||||
from utils.feed_discovery import discover_feeds
|
||||
|
||||
# Descubrir feeds desde una URL
|
||||
feeds = discover_feeds('https://elpais.com')
|
||||
# Retorna: [{'url': '...', 'title': '...', 'valid': True, ...}, ...]
|
||||
```
|
||||
|
||||
### 2. Router de Feeds Mejorado (`routers/feeds.py`)
|
||||
|
||||
Nuevos endpoints añadidos:
|
||||
|
||||
#### Interfaz Web
|
||||
- **`GET/POST /feeds/discover`**: Interfaz para descubrir feeds desde una URL
|
||||
- Muestra todos los feeds encontrados
|
||||
- Permite seleccionar cuáles añadir
|
||||
- Aplica configuración global (categoría, país, idioma)
|
||||
|
||||
- **`POST /feeds/discover_and_add`**: Añade múltiples feeds seleccionados
|
||||
- Extrae automáticamente título y descripción
|
||||
- Evita duplicados
|
||||
- Muestra estadísticas de feeds añadidos
|
||||
|
||||
#### API JSON
|
||||
- **`POST /feeds/api/discover`**: API para descubrir feeds
|
||||
```json
|
||||
{
|
||||
"url": "https://example.com"
|
||||
}
|
||||
```
|
||||
Retorna:
|
||||
```json
|
||||
{
|
||||
"feeds": [...],
|
||||
"count": 5
|
||||
}
|
||||
```
|
||||
|
||||
- **`POST /feeds/api/validate`**: API para validar un feed específico
|
||||
```json
|
||||
{
|
||||
"url": "https://example.com/rss"
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Worker de Descubrimiento (`workers/url_discovery_worker.py`)
|
||||
|
||||
Worker automático que:
|
||||
|
||||
1. **Procesa URLs de la tabla `fuentes_url`**
|
||||
- Prioriza URLs nunca procesadas
|
||||
- Reintenta URLs con errores
|
||||
- Actualiza URLs antiguas
|
||||
|
||||
2. **Descubre y Crea Feeds Automáticamente**
|
||||
- Encuentra todos los feeds RSS en cada URL
|
||||
- Valida cada feed encontrado
|
||||
- Crea entradas en la tabla `feeds`
|
||||
- Evita duplicados
|
||||
|
||||
3. **Gestión de Estado**
|
||||
- Actualiza `last_check`, `last_status`, `status_message`
|
||||
- Maneja errores gracefully
|
||||
- Registra estadísticas detalladas
|
||||
|
||||
#### Configuración del Worker
|
||||
|
||||
Variables de entorno:
|
||||
|
||||
```bash
|
||||
# Intervalo de ejecución (minutos)
|
||||
URL_DISCOVERY_INTERVAL_MIN=15
|
||||
|
||||
# Número de URLs a procesar por ciclo
|
||||
URL_DISCOVERY_BATCH_SIZE=10
|
||||
|
||||
# Máximo de feeds a crear por URL
|
||||
MAX_FEEDS_PER_URL=5
|
||||
```
|
||||
|
||||
#### Estados de URLs en `fuentes_url`
|
||||
|
||||
| Estado | Descripción |
|
||||
|--------|-------------|
|
||||
| `success` | Feeds creados exitosamente |
|
||||
| `existing` | Feeds encontrados pero ya existían |
|
||||
| `no_feeds` | No se encontraron feeds RSS |
|
||||
| `no_valid_feeds` | Se encontraron feeds pero ninguno válido |
|
||||
| `error` | Error al procesar la URL |
|
||||
|
||||
## 🚀 Uso del Sistema
|
||||
|
||||
### Método 1: Interfaz Web Manual
|
||||
|
||||
1. **Navega a `/feeds/discover`**
|
||||
2. **Ingresa una URL** (ej: `https://elpais.com`)
|
||||
3. **Haz clic en "Buscar Feeds"**
|
||||
4. El sistema mostrará todos los feeds encontrados con:
|
||||
- Estado de validación
|
||||
- Título y descripción
|
||||
- Número de entradas
|
||||
- Tipo de feed (RSS/Atom)
|
||||
5. **Configura opciones globales**:
|
||||
- Categoría
|
||||
- País
|
||||
- Idioma
|
||||
6. **Selecciona los feeds deseados** y haz clic en "Añadir Feeds Seleccionados"
|
||||
|
||||
### Método 2: Worker Automático
|
||||
|
||||
1. **Añade URLs a la tabla `fuentes_url`**:
|
||||
```sql
|
||||
INSERT INTO fuentes_url (nombre, url, categoria_id, pais_id, idioma, active)
|
||||
VALUES ('El País', 'https://elpais.com', 1, 1, 'es', TRUE);
|
||||
```
|
||||
|
||||
2. **El worker procesará automáticamente**:
|
||||
- Cada 15 minutos (configurable)
|
||||
- Descubrirá todos los feeds
|
||||
- Creará entradas en `feeds`
|
||||
- Actualizará el estado
|
||||
|
||||
3. **Monitorea el progreso**:
|
||||
```sql
|
||||
SELECT nombre, url, last_check, last_status, status_message
|
||||
FROM fuentes_url
|
||||
ORDER BY last_check DESC;
|
||||
```
|
||||
|
||||
### Método 3: Interfaz de URLs (Existente)
|
||||
|
||||
Usa la interfaz web existente en `/urls/add_source` para añadir URLs que serán procesadas por el worker.
|
||||
|
||||
## 🔄 Flujo de Trabajo Completo
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ Usuario añade │
|
||||
│ URL del sitio │
|
||||
└────────┬────────┘
|
||||
│
|
||||
v
|
||||
┌─────────────────────────┐
|
||||
│ URL guardada en │
|
||||
│ tabla fuentes_url │
|
||||
└────────┬────────────────┘
|
||||
│
|
||||
v
|
||||
┌─────────────────────────┐
|
||||
│ Worker ejecuta cada │
|
||||
│ 15 minutos │
|
||||
└────────┬────────────────┘
|
||||
│
|
||||
v
|
||||
┌─────────────────────────┐
|
||||
│ Descubre feeds RSS │
|
||||
│ usando feedfinder2 │
|
||||
└────────┬────────────────┘
|
||||
│
|
||||
v
|
||||
┌─────────────────────────┐
|
||||
│ Valida cada feed │
|
||||
│ encontrado │
|
||||
└────────┬────────────────┘
|
||||
│
|
||||
v
|
||||
┌─────────────────────────┐
|
||||
│ Crea entradas en │
|
||||
│ tabla feeds │
|
||||
└────────┬────────────────┘
|
||||
│
|
||||
v
|
||||
┌─────────────────────────┐
|
||||
│ Ingestor Go procesa │
|
||||
│ feeds cada 15 minutos │
|
||||
└────────┬────────────────┘
|
||||
│
|
||||
v
|
||||
┌─────────────────────────┐
|
||||
│ Noticias descargadas │
|
||||
│ y procesadas │
|
||||
└─────────────────────────┘
|
||||
```
|
||||
|
||||
## 📊 Tablas de Base de Datos
|
||||
|
||||
### `fuentes_url`
|
||||
Almacena URLs de sitios web para descubrimiento automático:
|
||||
|
||||
```sql
|
||||
CREATE TABLE fuentes_url (
|
||||
id SERIAL PRIMARY KEY,
|
||||
nombre VARCHAR(255) NOT NULL,
|
||||
url TEXT NOT NULL UNIQUE,
|
||||
categoria_id INTEGER REFERENCES categorias(id),
|
||||
pais_id INTEGER REFERENCES paises(id),
|
||||
idioma CHAR(2) DEFAULT 'es',
|
||||
last_check TIMESTAMP,
|
||||
last_status VARCHAR(50),
|
||||
status_message TEXT,
|
||||
last_http_code INTEGER,
|
||||
active BOOLEAN DEFAULT TRUE
|
||||
);
|
||||
```
|
||||
|
||||
### `feeds`
|
||||
Almacena feeds RSS descubiertos y validados:
|
||||
|
||||
```sql
|
||||
CREATE TABLE feeds (
|
||||
id SERIAL PRIMARY KEY,
|
||||
nombre VARCHAR(255),
|
||||
descripcion TEXT,
|
||||
url TEXT NOT NULL UNIQUE,
|
||||
categoria_id INTEGER REFERENCES categorias(id),
|
||||
pais_id INTEGER REFERENCES paises(id),
|
||||
idioma CHAR(2),
|
||||
activo BOOLEAN DEFAULT TRUE,
|
||||
fallos INTEGER DEFAULT 0,
|
||||
last_etag TEXT,
|
||||
last_modified TEXT,
|
||||
last_error TEXT
|
||||
);
|
||||
```
|
||||
|
||||
## ⚙️ Configuración del Sistema
|
||||
|
||||
### Variables de Entorno
|
||||
|
||||
Añade al archivo `.env`:
|
||||
|
||||
```bash
|
||||
# RSS Ingestor
|
||||
RSS_POKE_INTERVAL_MIN=15 # Intervalo de ingesta (minutos)
|
||||
RSS_MAX_FAILURES=10 # Fallos máximos antes de desactivar feed
|
||||
RSS_FEED_TIMEOUT=60 # Timeout para descargar feeds (segundos)
|
||||
|
||||
# URL Discovery Worker
|
||||
URL_DISCOVERY_INTERVAL_MIN=15 # Intervalo de descubrimiento (minutos)
|
||||
URL_DISCOVERY_BATCH_SIZE=10 # URLs a procesar por ciclo
|
||||
MAX_FEEDS_PER_URL=5 # Máximo de feeds por URL
|
||||
```
|
||||
|
||||
### Docker Compose
|
||||
|
||||
El worker ya está configurado en `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
url-discovery-worker:
|
||||
build: .
|
||||
container_name: rss2_url_discovery
|
||||
command: bash -lc "python -m workers.url_discovery_worker"
|
||||
environment:
|
||||
DB_HOST: db
|
||||
URL_DISCOVERY_INTERVAL_MIN: 15
|
||||
URL_DISCOVERY_BATCH_SIZE: 10
|
||||
MAX_FEEDS_PER_URL: 5
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
## 🔧 Comandos Útiles
|
||||
|
||||
### Ver logs del worker de descubrimiento
|
||||
```bash
|
||||
docker logs -f rss2_url_discovery
|
||||
```
|
||||
|
||||
### Reiniciar el worker
|
||||
```bash
|
||||
docker restart rss2_url_discovery
|
||||
```
|
||||
|
||||
### Ejecutar manualmente el worker (testing)
|
||||
```bash
|
||||
docker exec -it rss2_url_discovery python -m workers.url_discovery_worker
|
||||
```
|
||||
|
||||
### Ver estadísticas de descubrimiento
|
||||
```sql
|
||||
-- URLs procesadas recientemente
|
||||
SELECT nombre, url, last_check, last_status, status_message
|
||||
FROM fuentes_url
|
||||
WHERE last_check > NOW() - INTERVAL '1 day'
|
||||
ORDER BY last_check DESC;
|
||||
|
||||
-- Feeds creados recientemente
|
||||
SELECT nombre, url, fecha_creacion
|
||||
FROM feeds
|
||||
WHERE fecha_creacion > NOW() - INTERVAL '1 day'
|
||||
ORDER BY fecha_creacion DESC;
|
||||
```
|
||||
|
||||
## 🛠️ Troubleshooting
|
||||
|
||||
### El worker no encuentra feeds
|
||||
|
||||
1. Verifica que la URL sea accesible:
|
||||
```bash
|
||||
curl -I https://example.com
|
||||
```
|
||||
|
||||
2. Verifica los logs del worker:
|
||||
```bash
|
||||
docker logs rss2_url_discovery
|
||||
```
|
||||
|
||||
3. Prueba manualmente el descubrimiento:
|
||||
```python
|
||||
from utils.feed_discovery import discover_feeds
|
||||
feeds = discover_feeds('https://example.com')
|
||||
print(feeds)
|
||||
```
|
||||
|
||||
### Feeds duplicados
|
||||
|
||||
El sistema previene duplicados usando `ON CONFLICT (url) DO NOTHING`. Si un feed ya existe, simplemente se omite.
|
||||
|
||||
### Worker consume muchos recursos
|
||||
|
||||
Ajusta las configuraciones:
|
||||
|
||||
```bash
|
||||
# Reduce el batch size
|
||||
URL_DISCOVERY_BATCH_SIZE=5
|
||||
|
||||
# Aumenta el intervalo
|
||||
URL_DISCOVERY_INTERVAL_MIN=30
|
||||
|
||||
# Reduce feeds por URL
|
||||
MAX_FEEDS_PER_URL=3
|
||||
```
|
||||
|
||||
## 📝 Mejores Prácticas
|
||||
|
||||
1. **Añade URLs de sitios, no feeds directos**
|
||||
- ✅ `https://elpais.com`
|
||||
- ❌ `https://elpais.com/rss/feed.xml`
|
||||
|
||||
2. **Configura categoría y país correctamente**
|
||||
- Facilita la organización
|
||||
- Mejora la experiencia del usuario
|
||||
|
||||
3. **Monitorea el estado de las URLs**
|
||||
- Revisa periódicamente `fuentes_url`
|
||||
- Desactiva URLs que fallan consistentemente
|
||||
|
||||
4. **Limita el número de feeds por URL**
|
||||
- Evita sobrecarga de feeds similares
|
||||
- Mantén `MAX_FEEDS_PER_URL` entre 3-5
|
||||
|
||||
## 🎉 Ventajas del Sistema
|
||||
|
||||
✅ **Automatización completa**: Solo añade URLs, el sistema hace el resto
|
||||
✅ **Descubrimiento inteligente**: Encuentra todos los feeds disponibles
|
||||
✅ **Validación automática**: Solo crea feeds válidos y funcionales
|
||||
✅ **Sin duplicados**: Gestión inteligente de feeds existentes
|
||||
✅ **Escalable**: Procesa múltiples URLs en lotes
|
||||
✅ **Resiliente**: Manejo robusto de errores y reintentos
|
||||
✅ **Monitoreable**: Logs detallados y estados claros
|
||||
|
||||
## 📚 Referencias
|
||||
|
||||
- **feedfinder2**: https://github.com/dfm/feedfinder2
|
||||
- **feedparser**: https://feedparser.readthedocs.io/
|
||||
- **Tabla fuentes_url**: `/init-db/01.schema.sql`
|
||||
- **Worker**: `/workers/url_discovery_worker.py`
|
||||
- **Utilidades**: `/utils/feed_discovery.py`
|
||||
|
|
@ -1,370 +0,0 @@
|
|||
# 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).
|
||||
|
|
@ -1,223 +0,0 @@
|
|||
# 🎬 Sistema de Parrillas de Videos Automatizados
|
||||
|
||||
## 📋 Descripción
|
||||
|
||||
Este sistema permite generar videos automáticos de noticias filtradas según diferentes criterios:
|
||||
- **Por País**: Noticias de Bulgaria, España, Estados Unidos, etc.
|
||||
- **Por Categoría**: Ciencia, Tecnología, Deport, Política, etc.
|
||||
- **Por Entidad**: Personas u organizaciones específicas (ej: "Donald Trump", "OpenAI")
|
||||
- **Por Continente**: Europa, Asia, América, etc.
|
||||
|
||||
## 🎯 Características
|
||||
|
||||
### ✅ Sistema Implementado
|
||||
|
||||
1. **Base de Datos**
|
||||
- Tabla `video_parrillas`: Configuraciones de parrillas
|
||||
- Tabla `video_generados`: Registro de videos creados
|
||||
- Tabla `video_noticias`: Relación entre videos y noticias
|
||||
|
||||
2. **API REST**
|
||||
- `GET /parrillas/` - Listado de parrillas
|
||||
- `GET /parrillas/<id>` - Detalle de parrilla
|
||||
- `POST /parrillas/nueva` - Crear parrilla
|
||||
- `GET /parrillas/api/<id>/preview` - Preview de noticias
|
||||
- `POST /parrillas/api/<id>/generar` - Generar video
|
||||
- `POST /parrillas/api/<id>/toggle` - Activar/desactivar
|
||||
- `DELETE /parrillas/api/<id>` - Eliminar parrilla
|
||||
|
||||
3. **Generador de Videos**
|
||||
- Script: `generar_videos_noticias.py`
|
||||
- Integración con AllTalk TTS
|
||||
- Generación automática de subtítulos SRT
|
||||
- Soporte para múltiples idiomas
|
||||
|
||||
## 🚀 Uso Rápido
|
||||
|
||||
### 1. Crear una Parrilla
|
||||
|
||||
```bash
|
||||
# Acceder a la interfaz web
|
||||
http://localhost:8001/parrillas/
|
||||
|
||||
# O usar SQL directo
|
||||
docker-compose exec -T db psql -U rss -d rss -c "
|
||||
INSERT INTO video_parrillas (nombre, descripcion, tipo_filtro, pais_id, max_noticias, frecuencia, activo)
|
||||
VALUES ('Noticias de Bulgaria', 'Resumen diario de noticias de Bulgaria', 'pais',
|
||||
(SELECT id FROM paises WHERE nombre = 'Bulgaria'), 5, 'daily', true);
|
||||
"
|
||||
```
|
||||
|
||||
### 2. Generar Video Manualmente
|
||||
|
||||
```bash
|
||||
# Generar video para parrilla con ID 1
|
||||
docker-compose exec web python generar_videos_noticias.py 1
|
||||
```
|
||||
|
||||
### 3. Generación Automática (Diaria)
|
||||
|
||||
```bash
|
||||
# Procesar todas las parrillas activas con frecuencia 'daily'
|
||||
docker-compose exec web python generar_videos_noticias.py
|
||||
```
|
||||
|
||||
## 📝 Ejemplos de Parrillas
|
||||
|
||||
### Ejemplo 1: Noticias de Ciencia en Europa
|
||||
|
||||
```sql
|
||||
INSERT INTO video_parrillas (
|
||||
nombre, descripcion, tipo_filtro,
|
||||
categoria_id, continente_id,
|
||||
max_noticias, duracion_maxima, idioma_voz,
|
||||
frecuencia, activo
|
||||
) VALUES (
|
||||
'Ciencia en Europa',
|
||||
'Las últimas noticias científicas de Europa',
|
||||
'categoria',
|
||||
(SELECT id FROM categorias WHERE nombre ILIKE '%ciencia%'),
|
||||
(SELECT id FROM continentes WHERE nombre = 'Europa'),
|
||||
7, 300, 'es',
|
||||
'daily', true
|
||||
);
|
||||
```
|
||||
|
||||
### Ejemplo 2: Noticias sobre una Persona
|
||||
|
||||
```sql
|
||||
INSERT INTO video_parrillas (
|
||||
nombre, descripcion, tipo_filtro,
|
||||
entidad_nombre, entidad_tipo,
|
||||
max_noticias, idioma_voz,
|
||||
frecuencia, activo
|
||||
) VALUES (
|
||||
'Donald Trump en las Noticias',
|
||||
'Todas las menciones de Donald Trump',
|
||||
'entidad',
|
||||
'Donald Trump', 'persona',
|
||||
10, 'es',
|
||||
'daily', true
|
||||
);
|
||||
```
|
||||
|
||||
### Ejemplo 3: Noticias de Tecnología
|
||||
|
||||
```sql
|
||||
INSERT INTO video_parrillas (
|
||||
nombre, descripcion, tipo_filtro,
|
||||
categoria_id,
|
||||
max_noticias, idioma_voz,
|
||||
include_subtitles, template,
|
||||
frecuencia, activo
|
||||
) VALUES (
|
||||
'Tech News Daily',
|
||||
'Resumen diario de tecnología',
|
||||
'categoria',
|
||||
(SELECT id FROM categorias WHERE nombre ILIKE '%tecnolog%'),
|
||||
8, 'es',
|
||||
true, 'modern',
|
||||
'daily', true
|
||||
);
|
||||
```
|
||||
|
||||
## 🔧 Configuración Avanzada
|
||||
|
||||
### Opciones de Parrilla
|
||||
|
||||
| Campo | Tipo | Descripción |
|
||||
|-------|------|-------------|
|
||||
| `nombre` | string | Nombre único de la parrilla |
|
||||
| `descripcion` | text | Descripción detallada |
|
||||
| `tipo_filtro` | enum | 'pais', 'categoria', 'entidad', 'continente', 'custom' |
|
||||
| `pais_id` | int | ID del país (si tipo_filtro='pais') |
|
||||
| `categoria_id` | int | ID de categoría |
|
||||
| `continente_id` | int | ID de continente |
|
||||
| `entidad_nombre` | string | Nombre de persona/organización |
|
||||
| `entidad_tipo` | string | 'persona' o 'organizacion' |
|
||||
| `max_noticias` | int | Máximo de noticias por video (default: 5) |
|
||||
| `duracion_maxima` | int | Duración máxima en segundos (default: 180) |
|
||||
| `idioma_voz` | string | Idioma del TTS ('es', 'en', etc.) |
|
||||
| `template` | string | 'standard', 'modern', 'minimal' |
|
||||
| `include_images` | bool | Incluir imágenes en el video |
|
||||
| `include_subtitles` | bool | Generar subtítulos SRT |
|
||||
| `frecuencia` | string | 'daily', 'weekly', 'manual' |
|
||||
|
||||
### Configuración de AllTalk
|
||||
|
||||
El sistema utiliza AllTalk para generar la narración con voz. Configurar en docker-compose.yml:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
ALLTALK_URL: http://alltalk:7851
|
||||
```
|
||||
|
||||
## 📊 Estructura de Archivos Generados
|
||||
|
||||
```
|
||||
data/
|
||||
videos/
|
||||
<video_id>/
|
||||
script.txt # Libreto completo del video
|
||||
audio.wav # Audio generado con TTS
|
||||
subtitles.srt # Subtítulos (si enabled)
|
||||
metadata.json # Metadata del video
|
||||
```
|
||||
|
||||
## 🔄 Workflow de Generación
|
||||
|
||||
1. **Consulta de Noticias**: Filtra noticias según criterios de la parrilla
|
||||
2. **Construcción de Script**: Genera libreto narrativo
|
||||
3. **Síntesis de Voz**: Envía texto a AllTalk TTS
|
||||
4. **Generación de Subtítulos**: Crea archivo SRT con timestamps
|
||||
5. **Registro en BD**: Guarda paths y metadata en `video_generados`
|
||||
6. **Relación de Noticias**: Vincula noticias incluidas en `video_noticias`
|
||||
|
||||
## 🎨 Próximas Mejoras
|
||||
|
||||
- [ ] Integración con generador de videos (combinar audio + imágenes)
|
||||
- [ ] Templates visuales personalizados
|
||||
- [ ] Transiciones entre noticias
|
||||
- [ ] Música de fondo
|
||||
- [ ] Logo/branding personalizado
|
||||
- [ ] Exportación directa a YouTube/TikTok
|
||||
- [ ] Programación automática con cron
|
||||
- [ ] Dashboard de analíticas de videos
|
||||
- [ ] Sistema de thumbnails automáticos
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
### Error: "No hay noticias disponibles"
|
||||
- Verificar que existan noticias traducidas (`traducciones.status = 'done'`)
|
||||
- Ajustar filtros de la parrilla
|
||||
- Verificar rango de fechas (por defecto últimas 24h)
|
||||
|
||||
### Error en AllTalk TTS
|
||||
- Verificar que el servicio AllTalk esté corriendo
|
||||
- Revisar URL en variable de entorno `ALLTALK_URL`
|
||||
- Comprobar logs: `docker-compose logs alltalk`
|
||||
|
||||
### Video no se genera
|
||||
- Revisar estado en tabla `video_generados`
|
||||
- Ver columna `error_message` si `status = 'error'`
|
||||
- Verificar permisos en directorio `/app/data/videos`
|
||||
|
||||
## 📞 Soporte
|
||||
|
||||
Para problemas o sugerencias, revisar los logs:
|
||||
|
||||
```bash
|
||||
# Logs del generador
|
||||
docker-compose exec web python generar_videos_noticias.py <id> 2>&1 | tee video_generation.log
|
||||
|
||||
# Ver videos en cola
|
||||
docker-compose exec -T db psql -U rss -d rss -c "
|
||||
SELECT id, parrilla_id, titulo, status, fecha_generacion
|
||||
FROM video_generados
|
||||
ORDER BY fecha_generacion DESC LIMIT 10;
|
||||
"
|
||||
```
|
||||
|
||||
## 📄 Licencia
|
||||
|
||||
Este módulo es parte del sistema RSS2 News Aggregator.
|
||||
|
|
@ -1,426 +0,0 @@
|
|||
# 📖 PROCESO COMPLETO: Descubrimiento y Gestión de Feeds RSS
|
||||
|
||||
## 🎯 Problema Resuelto
|
||||
|
||||
**Pregunta:** ¿Cómo asigno país y categoría a los feeds descubiertos automáticamente?
|
||||
|
||||
**Respuesta:** El sistema ahora usa un flujo inteligente de 3 niveles:
|
||||
|
||||
1. **Auto-aprobación** (feeds con categoría/país)
|
||||
2. **Revisión manual** (feeds sin metadata completa)
|
||||
3. **Análisis automático** (sugerencias inteligentes)
|
||||
|
||||
---
|
||||
|
||||
## 🔄 FLUJO COMPLETO DEL SISTEMA
|
||||
|
||||
### Paso 1: Añadir URL Fuente
|
||||
|
||||
Tienes 2 opciones para añadir URLs:
|
||||
|
||||
#### Opción A: Con Categoría y País (AUTO-APROBACIÓN)
|
||||
```sql
|
||||
INSERT INTO fuentes_url (nombre, url, categoria_id, pais_id, idioma, active)
|
||||
VALUES ('El País', 'https://elpais.com', 1, 44, 'es', TRUE);
|
||||
-- ^ ^
|
||||
-- categoria_id pais_id
|
||||
```
|
||||
|
||||
✅ **Resultado**: Feeds se crean **AUTOMÁTICAMENTE** y se activan
|
||||
- Worker descubre feeds
|
||||
- Hereda categoría (1) y país (44) del padre
|
||||
- Crea feeds en tabla `feeds` directam ente
|
||||
- Ingestor empieza a descargar noticias
|
||||
|
||||
#### Opción B: Sin Categoría o País (REQUIERE REVISIÓN)
|
||||
```sql
|
||||
INSERT INTO fuentes_url (nombre, url, active)
|
||||
VALUES ('BBC News', 'https://www.bbc.com/news', TRUE);
|
||||
-- Sin categoria_id ni pais_id
|
||||
```
|
||||
|
||||
⚠️ **Resultado**: Feeds van a **REVISIÓN MANUAL**
|
||||
- Worker descubre feeds
|
||||
- Analiza automáticamente:
|
||||
- Detecta país desde dominio (.com → Reino Unido)
|
||||
- Detecta idioma (en)
|
||||
- Sugiere categoría ("Internacional")
|
||||
- Crea feeds en tabla `feeds_pending`
|
||||
- **ESPERA APROBACIÓN MANUAL** antes de activar
|
||||
|
||||
---
|
||||
|
||||
### Paso 2: Worker Descubre Feeds (cada 15 min)
|
||||
|
||||
El worker `url_discovery_worker` ejecuta automaticamente:
|
||||
|
||||
```
|
||||
1. Lee fuentes_url activas
|
||||
2. Para cada URL:
|
||||
a. Descubre todos los feeds RSS
|
||||
b. Valida cada feed
|
||||
c. Analiza metadata:
|
||||
- Idioma del feed
|
||||
- País (desde dominio: .es, .uk, .fr, etc.)
|
||||
- Categoría sugerida (keywords en título/descripción)
|
||||
|
||||
d. DECIDE EL FLUJO:
|
||||
|
||||
┌─────────────────────────────────────┐
|
||||
│ ¿Parent tiene categoria_id Y pais_id? │
|
||||
└──────────┬──────────────────────────┘
|
||||
│
|
||||
┌────────┴────────┐
|
||||
│ SÍ │ NO
|
||||
▼ ▼
|
||||
┌──────────────┐ ┌─────────────────┐
|
||||
│ AUTO-APROBAR │ │ REQUIERE REVISIÓN│
|
||||
└───────┬──────┘ └─────────┬───────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
tabla: feeds tabla: feeds_pending
|
||||
activo: TRUE reviewed: FALSE
|
||||
✅ Listo para ⏳ Espera aprobación
|
||||
ingestor
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Paso 3A: Feeds AUTO-APROBADOS
|
||||
|
||||
Si la URL padre tiene `categoria_id` y `pais_id`:
|
||||
|
||||
```sql
|
||||
-- Ejemplo: URL con metadata completa
|
||||
fuentes_url:
|
||||
id=1, url='https://elpais.com',
|
||||
categoria_id=1 (Noticias),
|
||||
pais_id=44 (España)
|
||||
|
||||
↓ Worker descubre 3 feeds:
|
||||
- https://elpais.com/rss/portada.xml
|
||||
- https://elpais.com/rss/internacional.xml
|
||||
- https://elpais.com/rss/deportes.xml
|
||||
|
||||
↓ Se crean DIRECTAMENTE en tabla feeds:
|
||||
INSERT INTO feeds (nombre, url, categoria_id, pais_id, activo)
|
||||
VALUES
|
||||
('El País - Portada', 'https://elpais.com/rss/portada.xml', 1, 44, TRUE),
|
||||
('El País - Internacional', 'https://elpais.com/rss/internacional.xml', 1, 44, TRUE),
|
||||
('El País - Deportes', 'https://elpais.com/rss/deportes.xml', 1, 44, TRUE);
|
||||
|
||||
✅ Feeds están ACTIVOS inmediatamente
|
||||
✅ Ingestor Go los procesa en siguiente ciclo (15 min)
|
||||
✅ Noticias empiezan a llegar
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Paso 3B: Feeds PENDIENTES (requieren revisión)
|
||||
|
||||
Si la URL padre NO tiene `categoria_id` o `pais_id`:
|
||||
|
||||
```sql
|
||||
-- Ejemplo: URL sin metadata
|
||||
fuentes_url:
|
||||
id=2, url='https://www.bbc.com/news',
|
||||
categoria_id=NULL,
|
||||
pais_id=NULL
|
||||
|
||||
↓ Worker descubre 2 feeds y ANALIZA automáticamente:
|
||||
|
||||
Feed 1: https://www.bbc.com/news/world/rss.xml
|
||||
- Título: "BBC News - World"
|
||||
- Idioma detectado: 'en'
|
||||
- País detectado: 'Reino Unido' (desde .com + idioma inglés)
|
||||
- Categoría sugerida: 'Internacional' (keyword "world")
|
||||
|
||||
Feed 2: https://www.bbc.com/sport/rss.xml
|
||||
- Título: "BBC Sport"
|
||||
- Idioma detectado: 'en'
|
||||
- País detectado: 'Reino Unido'
|
||||
- Categoría sugerida: 'Deportes' (keyword "sport")
|
||||
|
||||
↓ Se crean en tabla feeds_pending:
|
||||
INSERT INTO feeds_pending (
|
||||
fuente_url_id, feed_url, feed_title,
|
||||
feed_language, detected_country_id, suggested_categoria_id,
|
||||
reviewed, approved, notes
|
||||
) VALUES (
|
||||
2,
|
||||
'https://www.bbc.com/news/world/rss.xml',
|
||||
'BBC News - World',
|
||||
'en',
|
||||
74, -- Reino Unido (ID detectado)
|
||||
2, -- Internacional (ID sugerido)
|
||||
FALSE, FALSE,
|
||||
'Country from domain: Reino Un ido | Suggested category: Internacional (confidence: 85%)'
|
||||
);
|
||||
|
||||
⏳ Feeds están PENDIENTES
|
||||
⏳ NO están activos aún
|
||||
⏳ Requieren revisión manual en /feeds/pending
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Tabla Comparativa
|
||||
|
||||
| Aspecto | Auto-Aprobación | Revisión Manual |
|
||||
|---------|----------------|-----------------|
|
||||
| **Requisito** | URL padre con `categoria_id` Y `pais_id` | URL padre sin uno o ambos |
|
||||
| **Tabla destino** | `feeds` (directa) | `feeds_pending` (temporal) |
|
||||
| **Estado inicial** | `activo = TRUE` | `reviewed = FALSE, approved = FALSE` |
|
||||
| **Análisis automático** | Hereda valores del padre | Detecta país, sugiere categoría |
|
||||
| **Intervención manual** | ❌ No necesaria | ✅ Requerida |
|
||||
| **Tiempo hasta activación** | Inmediato | Después de aprobación |
|
||||
| **Ingestor procesa** | Sí (próximo ciclo) | No (hasta aprobar) |
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Interfaces de Gestión
|
||||
|
||||
### 1. Añadir URL con Metadata (Auto-aprobación)
|
||||
|
||||
**Ruta:** `/urls/add_source`
|
||||
|
||||
```
|
||||
Formulario:
|
||||
┌─────────────────────────────────────┐
|
||||
│ Nombre: El País │
|
||||
│ URL: https://elpais.com │
|
||||
│ Categoría: [Noticias ▼] ← IMPORTANTE
|
||||
│ País: [España ▼] ← IMPORTANTE
|
||||
│ Idioma: es │
|
||||
│ │
|
||||
│ [Añadir Fuente] │
|
||||
└─────────────────────────────────────┘
|
||||
|
||||
Resultado: Feeds se crearán AUTOMÁTICAMENTE
|
||||
```
|
||||
|
||||
### 2. Revisar Feeds Pendientes (Nueva interfaz)
|
||||
|
||||
**Ruta:** `/feeds/pending` (próximamente)
|
||||
|
||||
```
|
||||
Feeds Pendientes de Revisión
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Feed: BBC News - World
|
||||
URL: https://www.bbc.com/news/world/rss.xml
|
||||
Fuente: BBC News (https://www.bbc.com/news)
|
||||
|
||||
Análisis Automático:
|
||||
├─ Idioma: English (en)
|
||||
├─ País detectado: Reino Unido (.com domain + language)
|
||||
└─ Categoría sugerida: Internacional (85% confianza)
|
||||
Keywords: "world", "international", "global"
|
||||
|
||||
┌─────────────────────────────────────┐
|
||||
│ Categoría: [Internacional ▼] │ ← Pre-seleccionada
|
||||
│ País: [Reino Unido ▼] │ ← Pre-seleccionado
|
||||
│ Idioma: [en] │ ← Auto-detectado
|
||||
│ │
|
||||
│ [✓ Aprobar Feed] [✗ Rechazar] │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3. Descubrir Feeds Manualmente
|
||||
|
||||
**Ruta:** `/feeds/discover`
|
||||
|
||||
```
|
||||
Perfecto para cuando quieres control total:
|
||||
1. Ingresar URL
|
||||
2. Ver todos los feeds encontrados
|
||||
3. Seleccionar cuáles añadir
|
||||
4. Asignar categoría/país globalmente
|
||||
5. Feeds se crean directamente (no van a pending)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 RECOMENDACIONES DE USO
|
||||
|
||||
### Estrategia 1: Auto-aprobación Total
|
||||
**Para fuentes conocidas y confiables:**
|
||||
|
||||
```sql
|
||||
-- Añadir fuentes con metadata completa
|
||||
INSERT INTO fuentes_url (nombre, url, categoria_id, pais_id, idioma) VALUES
|
||||
('El País', 'https://elpais.com', 1, 44, 'es'),
|
||||
('Le Monde', 'https://lemonde.fr', 1, 60, 'fr'),
|
||||
('The Guardian', 'https://theguardian.com', 1, 74, 'en');
|
||||
|
||||
-- Worker creará feeds automáticamente
|
||||
-- Sin intervención manual necesaria
|
||||
```
|
||||
|
||||
### Estrategia 2: Revisión Manual
|
||||
**Para fuentes nuevas o desconocidas:**
|
||||
|
||||
```sql
|
||||
-- Añadir sin metadata
|
||||
INSERT INTO fuentes_url (nombre, url) VALUES
|
||||
('Sitio Desconocido', 'https://ejemplo.com');
|
||||
|
||||
-- Worker crea feeds en feeds_pending
|
||||
-- Revisar en /feeds/pending
|
||||
-- Aprobar/rechazar manualmente
|
||||
```
|
||||
|
||||
### Estrategia 3: Híbrida (Recomendada)
|
||||
**Combinar ambas:**
|
||||
|
||||
- URLs conocidas → Con categoría/país
|
||||
- URLs nuevas → Sin metadata (revisión)
|
||||
- Usar análisis automático como guía
|
||||
- Ajustar manualmente si es necesario
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Análisis Automático Explicado
|
||||
|
||||
### Detección de País
|
||||
|
||||
```python
|
||||
# 1. Desde dominio (TLD)
|
||||
.es → España
|
||||
.uk, .co.uk → Reino Unido
|
||||
.fr → Francia
|
||||
.de → Alemania
|
||||
.mx → México
|
||||
.ar → Argentina
|
||||
|
||||
# 2. Desde idioma (si no hay dominio claro)
|
||||
es → España (país principal)
|
||||
en → Reino Unido
|
||||
fr → Francia
|
||||
pt → Portugal
|
||||
|
||||
# 3. Desde subdominios
|
||||
es.example.com → España
|
||||
uk.example.com → Reino Unido
|
||||
```
|
||||
|
||||
### Sugerencia de Categoría
|
||||
|
||||
```python
|
||||
# Análisis de keywords en título + descripción
|
||||
|
||||
Keywords encontrados → Categoría sugerida (% confianza)
|
||||
|
||||
"política", "gobierno", "elecciones" → Política (75%)
|
||||
"economía", "bolsa", "mercado" → Economía (82%)
|
||||
"tecnología", "software", "digital" → Tecnología (90%)
|
||||
"deportes", "fútbol", "liga" → Deportes (95%)
|
||||
"internacional", "mundo", "global" → Internacional (70%)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 Ejemplos Completos
|
||||
|
||||
### Ejemplo 1: Periódico Español (Auto-aprobación)
|
||||
|
||||
```sql
|
||||
-- 1. Añadir fuente con metadata
|
||||
INSERT INTO fuentes_url (nombre, url, categoria_id, pais_id, idioma)
|
||||
VALUES ('El Mundo', 'https://elmundo.es', 1, 44, 'es');
|
||||
|
||||
-- 2. Worker ejecuta (15 min después):
|
||||
-- - Descubre: elmundo.es/rss/portada.xml
|
||||
-- - Descubre: elmundo.es/rss/deportes.xml
|
||||
-- - Hereda: categoria_id=1, pais_id=44
|
||||
-- - Crea en feeds directamente
|
||||
|
||||
-- 3. Resultado en tabla feeds:
|
||||
SELECT id, nombre, url, categoria_id, pais_id, activo
|
||||
FROM feeds
|
||||
WHERE fuente_nombre LIKE '%El Mundo%';
|
||||
|
||||
-- id | nombre | url | cat | pais | activo
|
||||
-- 1 | El Mundo - Portada | elmundo.es/rss/portada.xml | 1 | 44 | TRUE
|
||||
-- 2 | El Mundo - Deportes | elmundo.es/rss/deportes.xml | 1 | 44 | TRUE
|
||||
|
||||
-- ✅ Feeds activos, ingestor procesando
|
||||
```
|
||||
|
||||
### Ejemplo 2: Sitio Internacional (Revisión Manual)
|
||||
|
||||
```sql
|
||||
-- 1. Añadir fuente SIN metadata
|
||||
INSERT INTO fuentes_url (nombre, url)
|
||||
VALUES ('Reuters', 'https://www.reuters.com');
|
||||
|
||||
-- 2. Worker ejecuta (15 min después):
|
||||
-- - Descubre: reuters.com/rssfeed/worldNews
|
||||
-- - Analiza: idioma=en, país=Reino Unido (dominio+idioma)
|
||||
-- - Sugiere: categoría=Internacional (keyword "world")
|
||||
-- - Crea en feeds_pending
|
||||
|
||||
-- 3. Resultado en tabla feeds_pending:
|
||||
SELECT feed_title, detected_country_id, suggested_categoria_id, notes
|
||||
FROM feeds_pending
|
||||
WHERE fuente_url_id = 3;
|
||||
|
||||
-- feed_title | detected_country_id | suggested_cat | notes
|
||||
-- Reuters World News | 74 (Reino Unido) | 2 (Int.) | "Country from domain..."
|
||||
|
||||
-- ⏳ Requiere aprobación en /feeds/pending
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ CHECKLIST: Añadir Nueva Fuente
|
||||
|
||||
**Para auto-aprobación (recomendado si sabes país/categoría):**
|
||||
|
||||
- [ ] Ir a `/urls/add_source`
|
||||
- [ ] Ingresar nombre descriptivo
|
||||
- [ ] Ingresar URL del sitio (NO del feed RSS)
|
||||
- [ ] **IMPORTANTE:** Seleccionar categoría
|
||||
- [ ] **IMPORTANTE:** Seleccionar país
|
||||
- [ ] Ingresar idioma (opcional, se detecta)
|
||||
- [ ] Guardar
|
||||
- [ ] Esperar 15 minutos (máximo)
|
||||
- [ ] Ver feeds en `/feeds/` (activos automáticamente)
|
||||
|
||||
**Para revisión manual (si no estás seguro):**
|
||||
|
||||
- [ ] Ir a `/urls/add_source`
|
||||
- [ ] Ingresar nombre y URL
|
||||
- [ ] Dejar categoría/país vacíos
|
||||
- [ ] Guardar
|
||||
- [ ] Esperar 15 minutos
|
||||
- [ ] Ir a `/feeds/pending`
|
||||
- [ ] Revisar sugerencias automáticas
|
||||
- [ ] Ajustar categoría/país si necesario
|
||||
- [ ] Aprobar feeds
|
||||
- [ ] Feeds se activan inmediatamente
|
||||
|
||||
---
|
||||
|
||||
## 🎓 Resumen Ejecutivo
|
||||
|
||||
**3 Niveles de Automatización:**
|
||||
|
||||
| Nivel | Descripción | Cuándo Usar |
|
||||
|-------|-------------|-------------|
|
||||
| **Nivel 1: Totalmente Manual** | Descubrir en `/feeds/discover` | Control total, pocas URLs |
|
||||
| **Nivel 2: Auto-aprobación** | URL con cat/país → feeds activos | URLs confiables, muchas fuentes |
|
||||
| **Nivel 3: Revisión Asistida** | URL sin cat/país → análisis → aprobar | URLs nuevas, verificación |
|
||||
|
||||
**Flujo Recomendado:**
|
||||
1. Añade URL con categoría/país si la conoces
|
||||
2. Si no, déjalo vacío y revisa sugerencias automáticas
|
||||
3. Worker descubre y analiza todo automáticamente
|
||||
4. Tú solo apruebas/ajustas lo necesario
|
||||
|
||||
**Resultado:** Gestión eficiente de cientos de fuentes RSS con mínima intervención manual.
|
||||
|
||||
---
|
||||
|
||||
**📅 Fecha de última actualización:** 2026-01-07
|
||||
**📌 Versión del sistema:** 2.0 - Análisis Inteligente de Feeds
|
||||
|
|
@ -1,164 +0,0 @@
|
|||
# Problema de Traducciones Repetitivas - Análisis y Solución
|
||||
|
||||
## 📋 Descripción del Problema
|
||||
|
||||
Se detectaron traducciones con texto extremadamente repetitivo, como:
|
||||
- "la línea de la línea de la línea de la línea..."
|
||||
- "de Internet de Internet de Internet..."
|
||||
- "de la la la la..."
|
||||
|
||||
### Ejemplo Real Encontrado:
|
||||
```
|
||||
La red de conexión de Internet de Internet de la India (WIS) se encuentra
|
||||
en la línea de Internet de Internet de la India (WIS) y en la línea de
|
||||
Internet de Internet de la India (WIS) se encuentra en...
|
||||
```
|
||||
|
||||
## 🔍 Causas Identificadas
|
||||
|
||||
1. **Repetition Penalty Insuficiente**: El modelo estaba configurado con `repetition_penalty=1.2`, demasiado bajo para prevenir bucles.
|
||||
|
||||
2. **N-gram Blocking Inadecuado**: `no_repeat_ngram_size=4` permitía repeticiones de frases de 3 palabras.
|
||||
|
||||
3. **Falta de Validación Post-Traducción**: No había verificación de calidad después de traducir.
|
||||
|
||||
4. **Textos Fuente Corruptos**: Algunos RSS feeds contienen HTML mal formado o texto corrupto que confunde al modelo.
|
||||
|
||||
## ✅ Soluciones Implementadas
|
||||
|
||||
### 1. Mejoras en el Translation Worker (`workers/translation_worker.py`)
|
||||
|
||||
#### A. Parámetros de Traducción Mejorados
|
||||
```python
|
||||
# ANTES:
|
||||
repetition_penalty=1.2
|
||||
no_repeat_ngram_size=4
|
||||
|
||||
# AHORA:
|
||||
repetition_penalty=2.5 # Penalización mucho más agresiva
|
||||
no_repeat_ngram_size=3 # Bloquea repeticiones de 3-gramas
|
||||
```
|
||||
|
||||
#### B. Función de Validación de Calidad
|
||||
Nueva función `_is_repetitive_output()` que detecta:
|
||||
- Palabras repetidas 4+ veces consecutivas
|
||||
- Frases de 2 palabras repetidas 3+ veces
|
||||
- Patrones específicos conocidos: "de la la", "la línea de la línea", etc.
|
||||
- Baja diversidad de vocabulario (< 25% palabras únicas)
|
||||
|
||||
#### C. Validación Post-Traducción
|
||||
```python
|
||||
# Rechazar traducciones repetitivas automáticamente
|
||||
if _is_repetitive_output(ttr) or _is_repetitive_output(btr):
|
||||
LOG.warning(f"Rejecting repetitive translation for tr_id={i['tr_id']}")
|
||||
errors.append(("Repetitive output detected", i["tr_id"]))
|
||||
continue
|
||||
```
|
||||
|
||||
### 2. Script de Limpieza Automática
|
||||
|
||||
Creado `scripts/clean_repetitive_translations.py` que:
|
||||
- Escanea todas las traducciones completadas
|
||||
- Detecta patrones repetitivos
|
||||
- Marca traducciones defectuosas como 'pending' para re-traducción
|
||||
- Genera reportes de calidad
|
||||
|
||||
**Uso:**
|
||||
```bash
|
||||
docker exec rss2_web python3 scripts/clean_repetitive_translations.py
|
||||
```
|
||||
|
||||
### 3. Limpieza Inicial Ejecutada
|
||||
|
||||
Se identificaron y marcaron **3,093 traducciones defectuosas** para re-traducción:
|
||||
```sql
|
||||
UPDATE traducciones
|
||||
SET status='pending',
|
||||
titulo_trad=NULL,
|
||||
resumen_trad=NULL,
|
||||
error='Repetitive output - retranslating with improved settings'
|
||||
WHERE status='done'
|
||||
AND (resumen_trad LIKE '%la línea de la línea%'
|
||||
OR resumen_trad LIKE '%de la la %'
|
||||
OR resumen_trad LIKE '%de Internet de Internet%');
|
||||
```
|
||||
|
||||
## 🚀 Próximos Pasos
|
||||
|
||||
### 1. Reiniciar el Translation Worker
|
||||
```bash
|
||||
docker restart rss2_translation_worker
|
||||
```
|
||||
|
||||
### 2. Monitorear Re-traducciones
|
||||
Las 3,093 noticias marcadas se re-traducirán automáticamente con la nueva configuración mejorada.
|
||||
|
||||
### 3. Ejecutar Limpieza Periódica
|
||||
Agregar al cron o scheduler:
|
||||
```bash
|
||||
# Cada día a las 3 AM
|
||||
0 3 * * * docker exec rss2_web python3 scripts/clean_repetitive_translations.py
|
||||
```
|
||||
|
||||
### 4. Monitoreo de Calidad
|
||||
Verificar logs del translation worker para ver rechazos:
|
||||
```bash
|
||||
docker logs -f rss2_translation_worker | grep "Rejecting repetitive"
|
||||
```
|
||||
|
||||
## 📊 Métricas de Calidad
|
||||
|
||||
### Antes de la Solución:
|
||||
- ~3,093 traducciones defectuosas detectadas
|
||||
- ~X% de tasa de error (calculado sobre total de traducciones)
|
||||
|
||||
### Después de la Solución:
|
||||
- Validación automática en tiempo real
|
||||
- Rechazo inmediato de outputs repetitivos
|
||||
- Re-traducción automática con mejores parámetros
|
||||
|
||||
## 🔧 Configuración Adicional Recomendada
|
||||
|
||||
### Variables de Entorno (.env)
|
||||
```bash
|
||||
# Aumentar batch size para mejor contexto
|
||||
TRANSLATOR_BATCH=64 # Actual: 128 (OK)
|
||||
|
||||
# Ajustar beams para mejor calidad
|
||||
NUM_BEAMS_TITLE=3
|
||||
NUM_BEAMS_BODY=3
|
||||
|
||||
# Tokens máximos
|
||||
MAX_NEW_TOKENS_TITLE=128
|
||||
MAX_NEW_TOKENS_BODY=512
|
||||
```
|
||||
|
||||
## 📝 Notas Técnicas
|
||||
|
||||
### ¿Por qué ocurre este problema?
|
||||
|
||||
Los modelos de traducción neuronal (como NLLB) pueden entrar en "bucles de repetición" cuando:
|
||||
1. El texto fuente está corrupto o mal formado
|
||||
2. El contexto es muy largo y pierde coherencia
|
||||
3. La penalización por repetición es insuficiente
|
||||
4. Hay patrones ambiguos en el texto fuente
|
||||
|
||||
### Prevención a Largo Plazo
|
||||
|
||||
1. **Validación de Entrada**: Limpiar HTML y texto corrupto antes de traducir
|
||||
2. **Chunking Inteligente**: Dividir textos largos en segmentos coherentes
|
||||
3. **Monitoreo Continuo**: Ejecutar script de limpieza regularmente
|
||||
4. **Logs Detallados**: Analizar qué tipos de textos causan problemas
|
||||
|
||||
## 🎯 Resultados Esperados
|
||||
|
||||
Con estas mejoras, se espera:
|
||||
- ✅ Eliminación del 99%+ de traducciones repetitivas
|
||||
- ✅ Mejor calidad general de traducciones
|
||||
- ✅ Detección automática de problemas
|
||||
- ✅ Re-traducción automática de contenido defectuoso
|
||||
|
||||
---
|
||||
|
||||
**Fecha de Implementación**: 2026-01-28
|
||||
**Estado**: ✅ Implementado y Activo
|
||||
Loading…
Add table
Add a link
Reference in a new issue