go integration and wikipedia

This commit is contained in:
jlimolina 2026-03-28 18:30:07 +01:00
parent 47a252e339
commit ee90335b92
7828 changed files with 1307913 additions and 20807 deletions

View file

@ -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`

View file

@ -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).

View file

@ -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.

View file

@ -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

View file

@ -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