refactor: reorganizar estructura de archivos en raiz
Antes la raiz tenia 20+ archivos sueltos. Ahora organizado en:
docs/ 10 archivos .md de documentacion tecnica
scripts/ 3 scripts utilitarios (credentials, migrate, verify)
config/ entity_config.json (aliases y blacklist NER)
data/ feeds.csv (feeds precargados)
Eliminados restos de Docker que ya no aplican:
.dockerignore, .env.example, .env.secure.example, nginx.conf (raiz)
Makefile: eliminados targets docker-build, añadidos install/rebuild/check/poc
Referencias actualizadas en:
deploy/debian/install.sh entity_config.json -> config/entity_config.json
deploy/debian/build.sh entity_config.json -> config/entity_config.json
README.md links a docs/ y data/ actualizados,
arbol de estructura del repo reescrito
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ec839b5b54
commit
b3bf3d7a7f
23 changed files with 59 additions and 335 deletions
54
docs/DEPLOY.md
Normal file
54
docs/DEPLOY.md
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# Deployment Guide
|
||||
|
||||
This guide describes how to deploy the application to a new server.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* **Linux Server** (Ubuntu 22.04+ recommended)
|
||||
* **NVIDIA GPU**: Required for translation, embeddings, and NER services.
|
||||
* **NVIDIA Container Toolkit**: Must be installed to allow Docker to access the GPU.
|
||||
* **Docker** & **Docker Compose**: Latest versions.
|
||||
* **Git**: To clone the repository.
|
||||
* **External Service**: An instance of [AllTalk](https://github.com/erew123/alltalk_tts) running externally or on the host (port 7851 by default).
|
||||
|
||||
## Deployment Steps
|
||||
|
||||
1. **Clone the Repository**
|
||||
```bash
|
||||
git clone <your-repo-url>
|
||||
cd <your-repo-name>
|
||||
```
|
||||
|
||||
2. **Configure Environment Variables**
|
||||
Copy the example configuration file:
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
Edit `.env` and set secure passwords and configuration:
|
||||
```bash
|
||||
nano .env
|
||||
```
|
||||
* Change `POSTGRES_PASSWORD` and `DB_PASS` to a strong unique password.
|
||||
* Change `SECRET_KEY` to a long random string.
|
||||
* Verify `ALLTALK_URL` points to your AllTalk instance (default assumes host machine access).
|
||||
|
||||
3. **Start the Services**
|
||||
Run the following command to build and start the application:
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
4. **Database Initialization**
|
||||
The database will automatically initialize on the first run using the scripts in `init-db/`. This may take a few minutes. Check logs with:
|
||||
```bash
|
||||
docker compose logs -f db
|
||||
```
|
||||
|
||||
5. **Verify Deployment**
|
||||
Access the application at `http://<your-server-ip>:8001`.
|
||||
|
||||
## Important Notes
|
||||
|
||||
* **Models**: The application mounts `./models` and `./hf_cache` to persist AI models. On the first run, it will attempt to download necessary models (NLLB, BERT, etc.), which requires significant bandwidth and time.
|
||||
* **Data Persistence**: Database data is stored in `./pgdata` (mapped in docker-compose). Ensure this directory is backed up.
|
||||
* **Security**: Ensure port 5432 (Postgres) and 6379 (Redis) are firewall-protected and not exposed to the public internet unless intended (Docker maps them to the host network).
|
||||
280
docs/DEPLOY_DEBIAN.md
Normal file
280
docs/DEPLOY_DEBIAN.md
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
# COCONEWS · Despliegue en Debian (sin Docker)
|
||||
|
||||
Guía completa para instalar y operar COCONEWS en un servidor Debian 12 (Bookworm) o Ubuntu 22.04+, sin Docker ni contenedores.
|
||||
|
||||
---
|
||||
|
||||
## Requisitos de hardware
|
||||
|
||||
| Modo | CPU | RAM | Disco |
|
||||
|------|-----|-----|-------|
|
||||
| **Mínimo (solo CPU)** | 4 cores | 8 GB | 40 GB |
|
||||
| **Recomendado** | 8 cores | 16 GB | 80 GB |
|
||||
| **Con GPU** | 8 cores + NVIDIA | 16 GB | 80 GB |
|
||||
|
||||
> Los modelos de IA (NLLB-200 + MiniLM + spaCy) ocupan ~5 GB en disco una vez descargados.
|
||||
|
||||
---
|
||||
|
||||
## Servicios que se instalan en el servidor
|
||||
|
||||
| Servicio | Tecnología | Gestionado por |
|
||||
|----------|-----------|----------------|
|
||||
| Base de datos | PostgreSQL 16 | apt + systemd |
|
||||
| Caché | Redis 7 | apt + systemd |
|
||||
| Búsqueda vectorial | Qdrant (binario) | systemd |
|
||||
| API REST | Go (backend) | systemd |
|
||||
| Ingestor RSS | Go | systemd |
|
||||
| Scraper / Discovery / Wiki / Topics / Related / Qdrant-worker | Go | systemd |
|
||||
| Traducción (NLLB-200) | Python + CTranslate2 | systemd |
|
||||
| Embeddings | Python + Sentence-Transformers | systemd |
|
||||
| NER | Python + spaCy | systemd |
|
||||
| Clustering / Categorización | Python | systemd |
|
||||
| Frontend | React (estático compilado) | nginx |
|
||||
| Proxy / Web | nginx | apt + systemd |
|
||||
|
||||
---
|
||||
|
||||
## Instalación paso a paso
|
||||
|
||||
### 1. Clonar el repositorio
|
||||
|
||||
```bash
|
||||
git clone https://gitea.laenre.net/pietre/rss2.git /opt/src/rss2
|
||||
cd /opt/src/rss2
|
||||
git checkout coconews
|
||||
```
|
||||
|
||||
### 2. Configurar variables de entorno
|
||||
|
||||
```bash
|
||||
cp deploy/debian/env.example /opt/rss2/.env
|
||||
nano /opt/rss2/.env
|
||||
```
|
||||
|
||||
Valores que **debes cambiar obligatoriamente**:
|
||||
|
||||
```env
|
||||
POSTGRES_PASSWORD=contraseña_segura_postgres
|
||||
DB_PASS=contraseña_segura_postgres
|
||||
REDIS_PASSWORD=contraseña_segura_redis
|
||||
SECRET_KEY=cadena_aleatoria_minimo_32_caracteres
|
||||
```
|
||||
|
||||
Genera claves seguras con:
|
||||
```bash
|
||||
openssl rand -hex 32
|
||||
```
|
||||
|
||||
### 3. Descargar y convertir el modelo de traducción (NLLB-200)
|
||||
|
||||
Este paso se hace **una sola vez** y puede tardar 10-30 minutos dependiendo de la conexión.
|
||||
|
||||
```bash
|
||||
# Instalar dependencias Python primero (si aun no se hizo)
|
||||
python3 -m venv /opt/rss2/venv
|
||||
/opt/rss2/venv/bin/pip install ctranslate2 transformers sentencepiece
|
||||
|
||||
# Convertir modelo NLLB-200 a formato CTranslate2 (tarda 10-30 min)
|
||||
/opt/rss2/venv/bin/python - <<'EOF'
|
||||
from ctranslate2.converters import OpusMTConverter
|
||||
converter = OpusMTConverter("facebook/nllb-200-distilled-600M")
|
||||
converter.convert("/opt/rss2/models/nllb-ct2", quantization="int8", force=True)
|
||||
print("Modelo convertido OK en /opt/rss2/models/nllb-ct2")
|
||||
EOF
|
||||
```
|
||||
|
||||
> El modelo ocupa ~600 MB convertido. Si la descarga de HuggingFace falla, exporta
|
||||
> `HF_ENDPOINT=https://huggingface.co` o usa un mirror.
|
||||
|
||||
### 4. Ejecutar el instalador
|
||||
|
||||
```bash
|
||||
sudo bash /opt/src/rss2/deploy/debian/install.sh
|
||||
```
|
||||
|
||||
El script hace automáticamente:
|
||||
- Instala PostgreSQL, Redis, nginx, Go, Node.js via apt
|
||||
- Descarga el binario de Qdrant
|
||||
- Crea el usuario `rss2` del sistema
|
||||
- Crea la base de datos y ejecuta las migraciones
|
||||
- Compila los 8 binarios Go
|
||||
- Compila el frontend React
|
||||
- Instala y habilita los 16 servicios systemd
|
||||
|
||||
---
|
||||
|
||||
## Verificar que funciona
|
||||
|
||||
```bash
|
||||
# Estado de todos los servicios
|
||||
systemctl status rss2-backend rss2-ingestor rss2-translator rss2-embeddings
|
||||
|
||||
# Ver logs en tiempo real
|
||||
journalctl -u rss2-backend -f
|
||||
journalctl -u rss2-translator -f
|
||||
|
||||
# Comprobar que el API responde
|
||||
curl http://localhost:8080/api/stats
|
||||
|
||||
# Acceder al frontend
|
||||
# http://IP_DEL_SERVIDOR:8001
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Gestión de servicios
|
||||
|
||||
### Iniciar / parar / reiniciar
|
||||
|
||||
```bash
|
||||
# Un servicio concreto
|
||||
systemctl start rss2-backend
|
||||
systemctl stop rss2-translator
|
||||
systemctl restart rss2-embeddings
|
||||
|
||||
# Todos los workers de una vez
|
||||
systemctl restart rss2-backend rss2-ingestor rss2-scraper rss2-discovery \
|
||||
rss2-wiki rss2-topics rss2-related rss2-qdrant-worker \
|
||||
rss2-langdetect rss2-translation-scheduler rss2-translator \
|
||||
rss2-embeddings rss2-ner rss2-cluster rss2-categorizer
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
|
||||
```bash
|
||||
journalctl -u rss2-backend -f # API Go
|
||||
journalctl -u rss2-translator -f # Traductor
|
||||
journalctl -u rss2-embeddings -f # Embeddings
|
||||
journalctl -u rss2-ner -f # NER entidades
|
||||
journalctl -u rss2-ingestor -f # Ingestor RSS
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Actualizar el código
|
||||
|
||||
Cuando hay nuevos cambios en el repositorio:
|
||||
|
||||
```bash
|
||||
cd /opt/src/rss2
|
||||
git pull
|
||||
sudo bash deploy/debian/build.sh
|
||||
```
|
||||
|
||||
El script `build.sh` recompila los binarios Go, el frontend y sincroniza los workers Python, y reinicia los servicios automáticamente.
|
||||
|
||||
---
|
||||
|
||||
## Estructura de directorios en el servidor
|
||||
|
||||
```
|
||||
/opt/rss2/
|
||||
├── .env # Variables de entorno (permisos 600)
|
||||
├── bin/ # Binarios Go compilados
|
||||
│ ├── server # API REST
|
||||
│ ├── ingestor # Ingestor RSS
|
||||
│ ├── scraper
|
||||
│ ├── discovery
|
||||
│ ├── wiki_worker
|
||||
│ ├── topics
|
||||
│ ├── related
|
||||
│ └── qdrant_worker
|
||||
├── src/
|
||||
│ └── workers/ # Workers Python
|
||||
├── venv/ # Virtualenv Python (ML)
|
||||
├── models/
|
||||
│ └── nllb-ct2/ # Modelo traduccion CTranslate2
|
||||
├── hf_cache/ # Cache HuggingFace (embeddings, NER)
|
||||
├── frontend/
|
||||
│ └── dist/ # Frontend React compilado (servido por nginx)
|
||||
├── data/
|
||||
│ ├── wiki_images/ # Imagenes Wikipedia descargadas
|
||||
│ └── qdrant_storage/ # Datos vectoriales Qdrant
|
||||
└── qdrant/
|
||||
└── qdrant # Binario Qdrant
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Requisitos de red / firewall
|
||||
|
||||
Solo exponer al exterior el puerto **8001** (nginx). El resto deben ser internos:
|
||||
|
||||
```bash
|
||||
# Con ufw:
|
||||
ufw allow 8001/tcp # COCONEWS web
|
||||
ufw deny 5432/tcp # PostgreSQL - solo localhost
|
||||
ufw deny 6379/tcp # Redis - solo localhost
|
||||
ufw deny 6333/tcp # Qdrant - solo localhost
|
||||
ufw deny 8080/tcp # API Go - solo localhost (nginx hace proxy)
|
||||
ufw enable
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Backup de datos
|
||||
|
||||
```bash
|
||||
# PostgreSQL
|
||||
sudo -u postgres pg_dump rss > /opt/rss2/backups/rss_$(date +%Y%m%d).sql
|
||||
|
||||
# Datos Qdrant
|
||||
systemctl stop rss2-qdrant
|
||||
tar -czf /opt/rss2/backups/qdrant_$(date +%Y%m%d).tar.gz /opt/rss2/data/qdrant_storage
|
||||
systemctl start rss2-qdrant
|
||||
|
||||
# Imagenes Wikipedia (opcional, se pueden re-descargar)
|
||||
tar -czf /opt/rss2/backups/wiki_images_$(date +%Y%m%d).tar.gz /opt/rss2/data/wiki_images
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Solución de problemas frecuentes
|
||||
|
||||
### El traductor no arranca
|
||||
|
||||
```bash
|
||||
journalctl -u rss2-translator -n 50
|
||||
# Si dice "model not found": el modelo NLLB-200 no está convertido
|
||||
# Ejecutar el paso 3 de la instalación
|
||||
```
|
||||
|
||||
### PostgreSQL rechaza la conexión
|
||||
|
||||
```bash
|
||||
# Verificar que el .env tiene DB_HOST=127.0.0.1 (no "db")
|
||||
grep DB_HOST /opt/rss2/.env
|
||||
|
||||
# Verificar que el usuario existe
|
||||
sudo -u postgres psql -c "\du"
|
||||
```
|
||||
|
||||
### nginx devuelve 502 Bad Gateway
|
||||
|
||||
```bash
|
||||
# El backend Go no está corriendo
|
||||
systemctl status rss2-backend
|
||||
journalctl -u rss2-backend -n 30
|
||||
```
|
||||
|
||||
### Memoria insuficiente para los modelos Python
|
||||
|
||||
Con 8 GB RAM el translator + embeddings + NER pueden coincidir. Si el servidor tiene poca RAM, deshabilitar el translator-gpu y bajar el batch:
|
||||
|
||||
```bash
|
||||
# En /opt/rss2/.env
|
||||
TRANSLATOR_BATCH=8
|
||||
EMB_BATCH=32
|
||||
NER_BATCH=16
|
||||
systemctl restart rss2-translator rss2-embeddings rss2-ner
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Primer inicio de sesión
|
||||
|
||||
1. Abrir `http://IP:8001` en el navegador
|
||||
2. Al no haber usuarios, el sistema te redirige al registro
|
||||
3. El primer usuario registrado se convierte en **administrador**
|
||||
4. Ir a Configuración → Feeds → Importar el `feeds.csv` del repositorio para empezar con fuentes precargadas
|
||||
75
docs/FUNCIONES_DE_ARCHIVOS.md
Normal file
75
docs/FUNCIONES_DE_ARCHIVOS.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# Descripción de Archivos y Funciones del Proyecto RSS2
|
||||
|
||||
Este documento detalla la estructura del proyecto y la función de sus archivos principales.
|
||||
|
||||
## 🐳 Infraestructura y Despliegue
|
||||
|
||||
| Archivo / Directorio | Descripción |
|
||||
|----------------------|-------------|
|
||||
| `docker-compose.yml` | **Orquestador principal**. Define todos los servicios (db, web, workers, redis, qdrant, etc.), redes, volúmenes de persistencia y configuración de recursos. |
|
||||
| `Dockerfile` | Definición de la imagen base para la aplicación web y la mayoría de los workers en Python. |
|
||||
| `Dockerfile.llm_worker` | Imagen específica para el worker de LLM, incluye dependencias de CUDA y PyTorch para ExLlamaV2. |
|
||||
| `Dockerfile.url_worker` | Imagen optimizada para el worker de descubrimiento y procesamiento de URLs. |
|
||||
| `nginx.conf` | Configuración del servidor web Nginx que actúa como proxy inverso y servidos de archivos estáticos. |
|
||||
| `.env` | Variables de entorno con credenciales y configuración sensible (NO compartir). |
|
||||
| `gunicorn_config.py` | Configuración del servidor de aplicaciones WSGI Gunicorn para producción. |
|
||||
|
||||
## 🧠 Núcleo de la Aplicación (Python)
|
||||
|
||||
| Archivo | Descripción |
|
||||
|---------|-------------|
|
||||
| `app.py` | **Punto de entrada**. Inicializa la aplicación Flask, registra blueprints (rutas) y configura extensiones. |
|
||||
| `config.py` | Carga y valida la configuración desde variables de entorno. Define constantes globales. |
|
||||
| `db.py` | Gestión de la conexión a la base de datos PostgreSQL (pool de conexiones). |
|
||||
| `cache.py` | Capa de abstracción para Redis. Maneja caché de respuestas y estados transitorios. |
|
||||
| `scheduler.py` | Planificador de tareas periódicas (cron jobs internos) para mantenimiento y disparadores. |
|
||||
| `requirements.txt` | Lista de dependencias de Python necesarias para el proyecto. |
|
||||
|
||||
## 👷 Workers (Procesamiento en Segundo Plano)
|
||||
|
||||
Ubicados en `workers/`:
|
||||
|
||||
| Archivo | Función |
|
||||
|---------|---------|
|
||||
| `llm_categorizer_worker.py` | Categoriza noticias usando un LLM local (Mistral/ExLlamaV2). Asigna etiquetas temáticas. |
|
||||
| `url_worker_daemon.py` | Procesa y valida URLs extraídas, gestionando la cola de descargas. |
|
||||
| `url_discovery_worker.py` | Busca nuevos feeds RSS a partir de las URLs base. |
|
||||
| `translation_worker.py` | Traduce contenido usando modelos NLLB (No Language Left Behind). |
|
||||
| `embeddings_worker.py` | Genera vectores semánticos para búsqueda y clustering. |
|
||||
| `cluster_worker.py` | Agrupa noticias similares en "historias" o eventos. |
|
||||
| `ner_worker.py` | Extracción de Entidades Nombradas (personas, organizaciones, lugares). |
|
||||
| `topics_worker.py` | Identifica y extrae tópicos principales de los textos. |
|
||||
| `qdrant_worker.py` | Sincroniza los vectores generados con la base de datos vectorial Qdrant. |
|
||||
|
||||
## 🌐 API y Rutas
|
||||
|
||||
Ubicados en `routers/`:
|
||||
|
||||
| Archivo | Descripción |
|
||||
|---------|-------------|
|
||||
| `api.py` | Endpoints generales de la API REST. |
|
||||
| `feeds.py` | Gestión de fuentes RSS (CRUD). |
|
||||
| `news.py` | Endpoints para listar y filtrar noticias. |
|
||||
| `dashboard.py` | Rutas para el panel de administración y estadísticas. |
|
||||
| `auth.py` | Manejo de autenticación y autorización. |
|
||||
| `search.py` | Endpoints para búsqueda semántica y tradicional. |
|
||||
|
||||
## 🛠️ Herramientas y Scripts
|
||||
|
||||
| Archivo | Descripción |
|
||||
|---------|-------------|
|
||||
| `scripts/download_llm_model.sh` | Script para descargar modelos LLM cuantizados desde HuggingFace de forma segura. |
|
||||
| `verify_security.sh` | Auditoría de seguridad automatizada (verifica permisos, configuración TLS, etc.). |
|
||||
| `generate_secure_credentials.sh` | Genera contraseñas seguras y configura el entorno inicial. |
|
||||
| `rss-ingestor-go/` | Ingestor de RSS de alto rendimiento escrito en Go. |
|
||||
|
||||
## 📂 Directorios de Datos
|
||||
|
||||
| Directorio | Contenido |
|
||||
|------------|-----------|
|
||||
| `models/` | Almacenamiento persistente de modelos de IA (LLMs, Embeddings, Traducción). |
|
||||
| `pgdata/` | Persistencia de la base de datos PostgreSQL. |
|
||||
| `qdrant_storage/` | Persistencia de la base de datos vectorial Qdrant. |
|
||||
| `hf_cache/` | Caché de HuggingFace para modelos y tokenizers. |
|
||||
| `templates/` | Plantillas HTML (Jinja2) para la interfaz web. |
|
||||
| `static/` | Archivos CSS, JS e imágenes públicas. |
|
||||
401
docs/IMPLEMENTACION_LLM_RESUMEN.md
Normal file
401
docs/IMPLEMENTACION_LLM_RESUMEN.md
Normal file
|
|
@ -0,0 +1,401 @@
|
|||
# 📊 Resumen de Implementación - Sistema LLM Categorizer
|
||||
|
||||
**Fecha**: 2026-01-20
|
||||
**Estado**: ✅ Completado
|
||||
|
||||
---
|
||||
|
||||
## ✅ Tareas Completadas
|
||||
|
||||
### 1. Revisión y Levantamiento de la Aplicación
|
||||
|
||||
- ✓ Aplicación RSS2 levantada exitosamente
|
||||
- ✓ Todos los 22 contenedores funcionando correctamente
|
||||
- ✓ Web accesible en http://localhost:8001 (HTTP 200)
|
||||
- ✓ Base de datos operativa con **853,118 noticias**
|
||||
- ✓ **7,666 feeds** registrados (**1,695 activos**)
|
||||
|
||||
### 2. Implementación del Sistema LLM Categorizer
|
||||
|
||||
Se ha creado un sistema completo de categorización automática que:
|
||||
|
||||
- Toma **10 noticias** del feed simultáneamente
|
||||
- Las envía a un **LLM local** (ExLlamaV2)
|
||||
- El LLM **discrimina/categoriza** cada noticia automáticamente
|
||||
- Actualiza la base de datos con las categorías asignadas
|
||||
|
||||
#### Archivos Creados:
|
||||
|
||||
```
|
||||
/home/x/rss2/
|
||||
├── workers/
|
||||
│ └── llm_categorizer_worker.py ✓ Worker principal (440 líneas)
|
||||
├── Dockerfile.llm_worker ✓ Dockerfile con CUDA + ExLlamaV2
|
||||
├── docker-compose.yml ✓ Actualizado con servicio LLM
|
||||
├── scripts/
|
||||
│ ├── download_llm_model.sh ✓ Script de descarga de modelos
|
||||
│ └── test_llm_categorizer.py ✓ Script de prueba
|
||||
├── docs/
|
||||
│ └── LLM_CATEGORIZER.md ✓ Documentación completa
|
||||
├── QUICKSTART_LLM.md ✓ Guía rápida
|
||||
└── README.md ✓ Actualizado
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🤖 Características del Sistema
|
||||
|
||||
### Modelo Recomendado
|
||||
|
||||
**Mistral-7B-Instruct-v0.2 (GPTQ 4-bit)**
|
||||
- Optimizado para RTX 3060 12GB
|
||||
- Tamaño: ~4.5 GB
|
||||
- VRAM: ~6-7 GB
|
||||
- Rendimiento: 120-300 noticias/hora
|
||||
|
||||
### Alternativas Disponibles
|
||||
|
||||
1. Mistral-7B-Instruct-v0.2 (EXL2 4.0bpw) - Más rápido
|
||||
2. OpenHermes-2.5-Mistral-7B (GPTQ) - Mejor generalista
|
||||
3. Neural-Chat-7B (GPTQ) - Bueno para español
|
||||
|
||||
### Categorías Predefinidas
|
||||
|
||||
El sistema clasifica en **15 categorías**:
|
||||
|
||||
- Política
|
||||
- Economía
|
||||
- Tecnología
|
||||
- Ciencia
|
||||
- Salud
|
||||
- Deportes
|
||||
- Entretenimiento
|
||||
- Internacional
|
||||
- Nacional
|
||||
- Sociedad
|
||||
- Cultura
|
||||
- Medio Ambiente
|
||||
- Educación
|
||||
- Seguridad
|
||||
- Otros
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Configuración Técnica
|
||||
|
||||
### Servicio Docker
|
||||
|
||||
```yaml
|
||||
llm-categorizer:
|
||||
container: rss2_llm_categorizer
|
||||
GPU: NVIDIA (1 GPU asignada)
|
||||
Memoria: 10GB límite
|
||||
Modelo: ExLlamaV2
|
||||
Backend: CUDA 12.1
|
||||
```
|
||||
|
||||
### Variables de Entorno
|
||||
|
||||
| Variable | Valor | Descripción |
|
||||
|----------|-------|-------------|
|
||||
| `LLM_BATCH_SIZE` | 10 | Noticias por lote |
|
||||
| `LLM_SLEEP_IDLE` | 30s | Espera entre lotes |
|
||||
| `LLM_MAX_SEQ_LEN` | 4096 | Longitud máxima de contexto |
|
||||
| `LLM_CACHE_MODE` | FP16 | Modo de caché (FP16/Q4) |
|
||||
| `LLM_GPU_SPLIT` | auto | Distribución de GPU |
|
||||
|
||||
### Base de Datos
|
||||
|
||||
Se añadieron automáticamente 4 columnas nuevas a `noticias`:
|
||||
|
||||
| Columna | Tipo | Descripción |
|
||||
|---------|------|-------------|
|
||||
| `llm_categoria` | VARCHAR(100) | Categoría asignada |
|
||||
| `llm_confianza` | FLOAT | Nivel de confianza (0.0-1.0) |
|
||||
| `llm_processed` | BOOLEAN | Si fue procesada |
|
||||
| `llm_processed_at` | TIMESTAMP | Fecha de procesamiento |
|
||||
|
||||
---
|
||||
|
||||
## 📈 Rendimiento Estimado
|
||||
|
||||
### Con RTX 3060 12GB
|
||||
|
||||
- **VRAM utilizada**: ~6-7 GB
|
||||
- **Tiempo por noticia**: 2-5 segundos
|
||||
- **Throughput**: 120-300 noticias/hora
|
||||
- **Precisión esperada**: 85-90%
|
||||
|
||||
### Procesamiento Total
|
||||
|
||||
Con **853,118 noticias** en la BD:
|
||||
|
||||
- **Tiempo estimado**: 47-118 horas (2-5 días continuos)
|
||||
- **Modo 24/7**: El worker procesa automáticamente
|
||||
- **Control**: Puedes detener/reiniciar en cualquier momento
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Próximos Pasos
|
||||
|
||||
### 1. Descargar el Modelo (OBLIGATORIO)
|
||||
|
||||
```bash
|
||||
cd /home/x/rss2
|
||||
./scripts/download_llm_model.sh
|
||||
```
|
||||
|
||||
Selecciona **opción 1** (Mistral-7B-Instruct GPTQ)
|
||||
|
||||
⏱️ Tiempo: 10-30 minutos
|
||||
💾 Espacio: 4.5 GB
|
||||
|
||||
### 2. Probar el Sistema (Recomendado)
|
||||
|
||||
```bash
|
||||
# Instalar dependencias
|
||||
pip3 install exllamav2 torch
|
||||
|
||||
# Ejecutar prueba
|
||||
python3 scripts/test_llm_categorizer.py
|
||||
```
|
||||
|
||||
Esto prueba el modelo ANTES de levantar Docker.
|
||||
|
||||
### 3. Levantar el Servicio
|
||||
|
||||
```bash
|
||||
# Construir y levantar
|
||||
docker compose up -d --build llm-categorizer
|
||||
|
||||
# Ver logs
|
||||
docker compose logs -f llm-categorizer
|
||||
```
|
||||
|
||||
**Primera carga**: 2-5 minutos cargando modelo en GPU
|
||||
|
||||
### 4. Monitorear
|
||||
|
||||
```bash
|
||||
# Ver estado
|
||||
docker compose ps llm-categorizer
|
||||
|
||||
# Ver categorías asignadas
|
||||
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;"
|
||||
|
||||
# Ver progreso
|
||||
docker exec -it rss2_db psql -U rss -d rss -c \
|
||||
"SELECT COUNT(*) as procesadas,
|
||||
(COUNT(*)::float / 853118 * 100)::numeric(5,2) as porcentaje
|
||||
FROM noticias WHERE llm_processed = TRUE;"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 Documentación
|
||||
|
||||
### Guías Disponibles
|
||||
|
||||
1. **QUICKSTART_LLM.md** - Guía rápida de inicio
|
||||
2. **docs/LLM_CATEGORIZER.md** - Documentación completa
|
||||
3. **README.md** - Visión general actualizada
|
||||
|
||||
### Comandos Útiles
|
||||
|
||||
```bash
|
||||
# Ver logs en vivo
|
||||
docker compose logs -f llm-categorizer
|
||||
|
||||
# Reiniciar servicio
|
||||
docker compose restart llm-categorizer
|
||||
|
||||
# Detener servicio
|
||||
docker compose stop llm-categorizer
|
||||
|
||||
# Ver uso de GPU
|
||||
nvidia-smi
|
||||
|
||||
# Ver todas las tablas
|
||||
docker exec -it rss2_db psql -U rss -d rss -c "\dt"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Consultas SQL Útiles
|
||||
|
||||
### Distribución de categorías
|
||||
|
||||
```sql
|
||||
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;
|
||||
```
|
||||
|
||||
### Progreso de procesamiento
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
COUNT(CASE WHEN llm_processed = TRUE THEN 1 END) as procesadas,
|
||||
COUNT(CASE WHEN llm_processed = FALSE THEN 1 END) as pendientes,
|
||||
(COUNT(CASE WHEN llm_processed = TRUE THEN 1 END)::float / COUNT(*) * 100)::numeric(5,2) as porcentaje
|
||||
FROM noticias;
|
||||
```
|
||||
|
||||
### Noticias por categoría (últimas)
|
||||
|
||||
```sql
|
||||
SELECT titulo, llm_categoria, llm_confianza, fecha
|
||||
FROM noticias
|
||||
WHERE llm_categoria = 'Tecnología'
|
||||
AND llm_processed = TRUE
|
||||
ORDER BY fecha DESC
|
||||
LIMIT 10;
|
||||
```
|
||||
|
||||
### Resetear para reprocesar
|
||||
|
||||
```sql
|
||||
-- Resetear últimas 100 noticias
|
||||
UPDATE noticias
|
||||
SET llm_processed = FALSE
|
||||
WHERE id IN (
|
||||
SELECT id FROM noticias
|
||||
WHERE llm_processed = TRUE
|
||||
ORDER BY fecha DESC
|
||||
LIMIT 100
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Troubleshooting
|
||||
|
||||
### Problema: Out of Memory
|
||||
|
||||
**Solución**: Reducir batch size y usar cache Q4
|
||||
|
||||
```yaml
|
||||
# En docker-compose.yml
|
||||
environment:
|
||||
LLM_BATCH_SIZE: 5
|
||||
LLM_CACHE_MODE: Q4
|
||||
```
|
||||
|
||||
### Problema: Modelo no encontrado
|
||||
|
||||
**Solución**: Verificar descarga
|
||||
|
||||
```bash
|
||||
ls -la /home/x/rss2/models/llm/
|
||||
# Debe contener: config.json, model.safetensors, etc.
|
||||
```
|
||||
|
||||
### Problema: No procesa noticias
|
||||
|
||||
**Solución**: Verificar si hay noticias pendientes
|
||||
|
||||
```bash
|
||||
docker exec -it rss2_db psql -U rss -d rss -c \
|
||||
"SELECT COUNT(*) FROM noticias WHERE llm_processed = FALSE;"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Ventajas del Sistema
|
||||
|
||||
✅ **100% Local**: Sin envío de datos a APIs externas
|
||||
✅ **Alta Precisión**: LLM entiende contexto, no solo keywords
|
||||
✅ **Automático**: Procesamiento continuo en background
|
||||
✅ **Escalable**: Procesa 10 noticias por lote eficientemente
|
||||
✅ **Integrado**: Worker nativo del ecosistema RSS2
|
||||
✅ **Optimizado**: Específico para RTX 3060 12GB
|
||||
✅ **Extensible**: Fácil añadir nuevas categorías
|
||||
✅ **Monitoreable**: Logs detallados y métricas en BD
|
||||
|
||||
---
|
||||
|
||||
## 📊 Estado de Feeds
|
||||
|
||||
### Estadísticas Actuales
|
||||
|
||||
- **Total de feeds**: 7,666
|
||||
- **Feeds activos**: 1,695 (22%)
|
||||
- **Total de noticias**: 853,118
|
||||
- **Noticias sin categorizar (LLM)**: 853,118 (100%)
|
||||
|
||||
### Recomendación
|
||||
|
||||
Considera **reevaluar los feeds inactivos**:
|
||||
|
||||
```sql
|
||||
-- Ver feeds inactivos con errores
|
||||
SELECT nombre, url, fallos, last_error
|
||||
FROM feeds
|
||||
WHERE activo = FALSE
|
||||
ORDER BY fallos DESC
|
||||
LIMIT 20;
|
||||
|
||||
-- Reactivar feeds con pocos fallos
|
||||
UPDATE feeds
|
||||
SET activo = TRUE, fallos = 0
|
||||
WHERE activo = FALSE AND fallos < 5;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔮 Mejoras Futuras Sugeridas
|
||||
|
||||
1. **Subcategorías automáticas** - Categorización más granular
|
||||
2. **Resúmenes por categoría** - Generar resúmenes diarios
|
||||
3. **Trending topics** - Detectar temas de moda por categoría
|
||||
4. **Alertas personalizadas** - Notificar por categorías de interés
|
||||
5. **Fine-tuning del modelo** - Entrenar con feedback de usuario
|
||||
6. **API REST** - Endpoint para categorización bajo demanda
|
||||
7. **Dashboard web** - Visualización de categorías en tiempo real
|
||||
|
||||
---
|
||||
|
||||
## 📞 Soporte
|
||||
|
||||
### Logs
|
||||
|
||||
```bash
|
||||
docker compose logs llm-categorizer
|
||||
```
|
||||
|
||||
### GPU
|
||||
|
||||
```bash
|
||||
nvidia-smi
|
||||
watch -n 1 nvidia-smi # Monitoreo en vivo
|
||||
```
|
||||
|
||||
### Base de Datos
|
||||
|
||||
```bash
|
||||
docker exec -it rss2_db psql -U rss -d rss
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✨ Conclusión
|
||||
|
||||
El sistema LLM Categorizer está **completamente implementado y listo para usar**.
|
||||
|
||||
Solo necesitas:
|
||||
1. ✅ Descargar el modelo (~15 min)
|
||||
2. ✅ Levantar el servicio (1 comando)
|
||||
3. ✅ Monitorear el progreso
|
||||
|
||||
**Resultado**: Categorización automática e inteligente de todas las noticias del sistema.
|
||||
|
||||
---
|
||||
|
||||
**Implementado por**: Antigravity AI
|
||||
**Fecha**: 2026-01-20
|
||||
**Versión**: 1.0
|
||||
**Estado**: ✅ Producción
|
||||
145
docs/NEWSPAPER_STYLE_GUIDE.md
Normal file
145
docs/NEWSPAPER_STYLE_GUIDE.md
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
# Diseño Periodístico Clásico - El Observador
|
||||
|
||||
## 🎨 Transformación Visual Completa
|
||||
|
||||
La aplicación ha sido completamente rediseñada con un **estilo periodístico clásico** inspirado en los mejores periódicos del mundo:
|
||||
- **The New York Times** (estructura y jerarquía)
|
||||
- **El País** (elementos visuales españoles)
|
||||
- **The Guardian** (claridad tipográfica)
|
||||
|
||||
## ✨ Características Implementadas
|
||||
|
||||
### 1. Tipografía Periodística
|
||||
- **Titulares**: Old Standard TT & Playfair Display (serif clásico)
|
||||
- **Cuerpo**: Merriweather (serif legible para lectura larga)
|
||||
- **UI/Navegación**: Lato (sans-serif limpio y moderno)
|
||||
|
||||
### 2. Paleta de Colores Clásica
|
||||
- **Tinta Negra** (#1a1a1a): Texto principal con peso y autoridad
|
||||
- **Papel Blanco** (#ffffff): Fondo limpio y profesional
|
||||
- **Papel Crema** (#f9f7f4): Fondo general con calidez
|
||||
- **Rojo Acento** (#c1121f): Color institucional para destacados
|
||||
- **Azul Enlaces** (#326891): Enlaces legibles y tradicionales
|
||||
|
||||
### 3. Elementos de Diseño Periodístico
|
||||
- ✅ Cabecera con nombre en mayúsculas y bordes dobles
|
||||
- ✅ Fecha y hora actualizadas en tiempo real (Estilo Madrid)
|
||||
- ✅ Navegación sticky negra con bordes rojos
|
||||
- ✅ Tarjetas de noticias con bordes sutiles
|
||||
- ✅ Tipografía jerárquica (títulos grandes, meta pequeña)
|
||||
- ✅ Hover effects suaves y profesionales
|
||||
- ✅ Badges de categoría en rojo institucional
|
||||
- ✅ Layout responsive adaptado a móviles
|
||||
|
||||
### 4. Página de Artículos
|
||||
- 📰 Breadcrumbs para navegación
|
||||
- 📰 Título prominente con tipografía serif
|
||||
- 📰 Metadata periodística (fuente, fecha, país, categoría)
|
||||
- 📰 Resumen destacado con borde rojo
|
||||
- 📰 Sidebar con artículos relacionados
|
||||
- 📰 Modo lectura inmersivo
|
||||
- 📰 Botones de compartir, PDF y favoritos
|
||||
|
||||
### 5. Funcionalidades
|
||||
- **Modo Lectura**: Aumenta fuente y elimina distracciones
|
||||
- **Modo Oscuro**: Tema completamente adaptado
|
||||
- **Responsive**: Diseño adaptado para móvil, tablet y desktop
|
||||
- **Animaciones**: Transiciones suaves y profesionales
|
||||
- **Accesibilidad**: Contraste adecuado y jerarquía clara
|
||||
|
||||
## 📱 Responsive Design
|
||||
|
||||
- **Desktop (>968px)**: Layout completo con sidebar
|
||||
- **Tablet (768px-968px)**: Grid adaptado en 2 columnas
|
||||
- **Mobile (<768px)**: Una columna, menú de hamburguesa
|
||||
|
||||
## 🌓 Modo Oscuro
|
||||
|
||||
Paleta invertida manteniendo la estética:
|
||||
- Fondo oscuro (#0f0f0f)
|
||||
- Texto claro (#e0e0e0)
|
||||
- Acento rojo más brillante (#ff4444)
|
||||
- Bordes sutiles (#333)
|
||||
|
||||
## 🎯 Mejoras de UX
|
||||
|
||||
1. **Historial de Lectura**: Artículos leídos aparecen con opacidad reducida
|
||||
2. **Favoritos Persistentes**: Sistema de guardado con estrellas
|
||||
3. **Búsqueda Avanzada**: Filtros por categoría, país, fecha
|
||||
4. **Búsqueda Semántica IA**: Toggle para búsqueda inteligente
|
||||
5. **Notificaciones**: Sistema de alertas para nuevas noticias
|
||||
6. **Paginación Clara**: Navegación entre páginas de noticias
|
||||
|
||||
## 📂 Archivos Modificados
|
||||
|
||||
- `static/style.css` - CSS completamente reescrito (1300+ líneas)
|
||||
- `templates/base.html` - Actualizado con nuevas fuentes y nombre
|
||||
- `templates/noticia_classic.html` - Template de detalle mejorado
|
||||
- `templates/_noticias_list.html` - Cards de noticias (sin cambios necesarios)
|
||||
|
||||
## 🚀 Activación
|
||||
|
||||
Los cambios están **activos automáticamente** después de reiniciar nginx:
|
||||
|
||||
```bash
|
||||
docker compose restart nginx
|
||||
```
|
||||
|
||||
## 🎨 Paleta de Colores Completa
|
||||
|
||||
```css
|
||||
--ink-black: #1a1a1a /* Texto principal */
|
||||
--newspaper-gray: #333333 /* Texto secundario */
|
||||
--paper-white: #ffffff /* Fondo de tarjetas */
|
||||
--paper-cream: #f9f7f4 /* Fondo general */
|
||||
--border-gray: #d1d1d1 /* Bordes */
|
||||
--accent-red: #c1121f /* Acento principal */
|
||||
--accent-red-dark: #9a0e1a /* Acento hover */
|
||||
--link-blue: #326891 /* Enlaces */
|
||||
--text-gray: #4a4a4a /* Meta información */
|
||||
--light-gray: #f0f0f0 /* Fondos sutiles */
|
||||
```
|
||||
|
||||
## 💡 Inspiración de Diseño
|
||||
|
||||
El diseño sigue los principios de los mejores periódicos:
|
||||
|
||||
1. **Jerarquía Clara**: Los títulos dominan visualmente
|
||||
2. **Espacios en Blanco**: El contenido respira
|
||||
3. **Legibilidad**: Fuentes serif para lectura larga
|
||||
4. **Profesionalismo**: Colores sobrios y clásicos
|
||||
5. **Credibilidad**: Diseño serio y confiable
|
||||
|
||||
## 🔄 Migración desde Diseño Anterior
|
||||
|
||||
El diseño anterior era moderno con:
|
||||
- Glassmorphism
|
||||
- Gradientes animados
|
||||
- Colores vibrantes (púrpura, rosa, azul)
|
||||
- Fuentes sans-serif (Poppins, Roboto)
|
||||
|
||||
El nuevo diseño es periodístico con:
|
||||
- Fondos sólidos
|
||||
- Colores clásicos (blanco, negro, rojo)
|
||||
- Tipografía serif tradicional
|
||||
- Bordes definidos
|
||||
|
||||
## ✅ Testing Recomendado
|
||||
|
||||
1. ✓ Verificar que el header muestra "EL OBSERVADOR"
|
||||
2. ✓ Comprobar que las fuentes serif se carguen correctamente
|
||||
3. ✓ Probar el modo oscuro (botón luna/sol)
|
||||
4. ✓ Verificar responsive en móvil
|
||||
5. ✓ Probar funcionalidades (favoritos, búsqueda, filtros)
|
||||
6. ✓ Verificar que las imágenes de noticias se vean bien
|
||||
7. ✓ Comprobar paginación
|
||||
8. ✓ Probar modo lectura en artículos
|
||||
|
||||
## 👨💻 Créditos
|
||||
|
||||
Diseño creado siguiendo las mejores prácticas de diseño periodístico digital, manteniendo toda la funcionalidad existente del agregador de noticias RSS.
|
||||
|
||||
---
|
||||
|
||||
**Fecha de Actualización**: Enero 2026
|
||||
**Versión**: 2.0 - Diseño Periodístico Clásico
|
||||
257
docs/QDRANT_SETUP.md
Normal file
257
docs/QDRANT_SETUP.md
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
# ✅ Sistema Qdrant - Búsquedas Semánticas
|
||||
|
||||
## 🎯 Arquitectura Actual
|
||||
|
||||
El sistema vectoriza **directamente** las noticias traducidas y proporciona búsqueda semántica en tiempo real.
|
||||
|
||||
```
|
||||
Noticias Originales (RSS)
|
||||
↓
|
||||
Traducción (translator workers)
|
||||
↓
|
||||
PostgreSQL (tabla 'traducciones')
|
||||
↓
|
||||
Qdrant Worker (vectorización directa)
|
||||
↓
|
||||
Qdrant (búsquedas semánticas)
|
||||
↓
|
||||
API de Búsqueda (utils/qdrant_search.py)
|
||||
↓
|
||||
Buscador General + Monitor de Conflictos
|
||||
```
|
||||
|
||||
## ✅ Servicios y Componentes
|
||||
|
||||
| Componente | Puerto/Ubicación | Descripción |
|
||||
|----------|--------|-------------|
|
||||
| **Qdrant** | 6333 | Base de datos vectorial |
|
||||
| **Qdrant Worker** | - | Vectorización continua |
|
||||
| **Búsqueda Semántica** | `utils/qdrant_search.py` | API de búsqueda vectorial |
|
||||
| **Buscador General** | `routers/search.py` | Búsqueda con fallback a PostgreSQL |
|
||||
| **Monitor de Conflictos** | `routers/conflicts.py` | Búsqueda por keywords con vectores |
|
||||
|
||||
### Configuración del Worker
|
||||
|
||||
- **Origen**: Tabla `traducciones`
|
||||
- **Modelo**: `paraphrase-multilingual-MiniLM-L12-v2`
|
||||
- **Dimensiones**: 384
|
||||
- **Dispositivo**: CPU (GPU disponible)
|
||||
- **Velocidad**: ~100+ noticias/minuto
|
||||
- **Total Vectorizado**: ~507,000 noticias
|
||||
|
||||
## 🚀 Integración Completa
|
||||
|
||||
### Buscador General (`/api/search`)
|
||||
|
||||
La búsqueda ahora usa **Qdrant primero** para mayor velocidad y precisión semántica:
|
||||
|
||||
1. **Búsqueda Semántica** (por defecto): Usa vectores de Qdrant
|
||||
2. **Fallback PostgreSQL**: Si falla o no hay resultados
|
||||
3. **Enriquecimiento**: Combina datos de ambas fuentes
|
||||
|
||||
**Ventajas:**
|
||||
- ✅ Búsquedas 10-100x más rápidas (sin escaneo de 500k filas)
|
||||
- ✅ Comprende sinónimos y contexto ("protestas" encuentra "manifestaciones")
|
||||
- ✅ Multilingüe automático
|
||||
- ✅ Sin dependencia de palabras exactas
|
||||
|
||||
**Parámetros:**
|
||||
- `q`: Texto de búsqueda
|
||||
- `limit`: Máximo de resultados (default: 10, max: 50)
|
||||
- `semantic`: `true/false` (default: `true`)
|
||||
|
||||
### Monitor de Conflictos (`/conflicts/<id>`)
|
||||
|
||||
Ahora usa búsqueda semántica por keywords:
|
||||
|
||||
**Antes (ILIKE con PostgreSQL):**
|
||||
- ❌ "irán protestas" requería coincidencia exacta de toda la frase
|
||||
- ❌ Lento con 500k noticias
|
||||
- ❌ No encontraba variaciones ("manifestación", "protesta")
|
||||
|
||||
**Ahora (Qdrant):**
|
||||
- ✅ "irán protestas" busca por ambas palabras independientemente
|
||||
- ✅ Rápido (búsqueda vectorial)
|
||||
- ✅ Encuentra contenido semánticamente similar
|
||||
|
||||
## 🔧 Comandos Útiles
|
||||
|
||||
### Ver Logs
|
||||
```bash
|
||||
docker-compose logs -f qdrant-worker
|
||||
docker-compose logs -f qdrant
|
||||
docker-compose logs -f rss2_web # Para ver logs de búsqueda
|
||||
```
|
||||
|
||||
### Estadísticas
|
||||
```bash
|
||||
docker exec -it rss2_web python scripts/migrate_to_qdrant.py --stats
|
||||
```
|
||||
|
||||
### Vectorizar Pendientes (Manual)
|
||||
```bash
|
||||
docker exec -it rss2_web python scripts/migrate_to_qdrant.py --vectorize --batch-size 200
|
||||
```
|
||||
|
||||
### Reset Completo (⚠️ Destructivo)
|
||||
```bash
|
||||
docker exec -it rss2_web python scripts/migrate_to_qdrant.py --reset
|
||||
```
|
||||
|
||||
### Probar Búsqueda Semántica
|
||||
```bash
|
||||
# Búsqueda semántica
|
||||
curl "http://localhost:8001/api/search?q=protestas+en+iran&semantic=true"
|
||||
|
||||
# Búsqueda tradicional (fallback)
|
||||
curl "http://localhost:8001/api/search?q=protestas+en+iran&semantic=false"
|
||||
```
|
||||
|
||||
## 📊 Verificar Estado
|
||||
|
||||
### Base de Datos
|
||||
```sql
|
||||
-- Progreso de vectorización
|
||||
SELECT
|
||||
COUNT(*) as total,
|
||||
COUNT(*) FILTER (WHERE vectorized = TRUE) as vectorizadas,
|
||||
COUNT(*) FILTER (WHERE vectorized = FALSE) as pendientes
|
||||
FROM traducciones
|
||||
WHERE lang_to = 'es' AND status = 'done';
|
||||
```
|
||||
|
||||
### Qdrant API
|
||||
```bash
|
||||
# Estado de la colección
|
||||
curl http://localhost:6333/collections/news_vectors
|
||||
|
||||
# Health check
|
||||
curl http://localhost:6333/healthz
|
||||
|
||||
# Conteo de puntos
|
||||
curl http://localhost:6333/collections/news_vectors | jq '.result.points_count'
|
||||
```
|
||||
|
||||
## 🔍 Variables de Entorno
|
||||
|
||||
```bash
|
||||
# .env
|
||||
QDRANT_HOST=qdrant
|
||||
QDRANT_PORT=6333
|
||||
QDRANT_COLLECTION_NAME=news_vectors
|
||||
QDRANT_BATCH_SIZE=100
|
||||
QDRANT_SLEEP_IDLE=30
|
||||
EMB_MODEL=sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
|
||||
```
|
||||
|
||||
## 📁 Archivos Relevantes
|
||||
|
||||
| Archivo | Función |
|
||||
|---------|---------|
|
||||
| `workers/qdrant_worker.py` | Worker de vectorización continua |
|
||||
| `utils/qdrant_search.py` | **NUEVO**: API de búsqueda semántica |
|
||||
| `routers/search.py` | **ACTUALIZADO**: Buscador con Qdrant |
|
||||
| `routers/conflicts.py` | **ACTUALIZADO**: Monitor de conflictos con Qdrant |
|
||||
| `scripts/migrate_to_qdrant.py` | Migración/estadísticas |
|
||||
| `docker-compose.yml` | Configuración de servicios + timezone sync |
|
||||
|
||||
## ⏰ Sincronización de Hora
|
||||
|
||||
**Problema Resuelto:** Todos los contenedores Docker ahora tienen la hora sincronizada (TZ=Europe/Madrid).
|
||||
|
||||
**Cambios:**
|
||||
- ✅ Variable `TZ=Europe/Madrid` en todos los servicios
|
||||
- ✅ Volúmenes `/etc/timezone` y `/etc/localtime` en servicios clave
|
||||
- ✅ Logs consistentes entre todos los workers
|
||||
|
||||
## 🚀 Despliegue en Nuevas Máquinas
|
||||
|
||||
### Requisitos Previos
|
||||
1. Docker y Docker Compose instalados
|
||||
2. Al menos 8GB RAM (recomendado 16GB)
|
||||
3. GPU NVIDIA (opcional, para workers de traducción)
|
||||
|
||||
### Pasos de Instalación
|
||||
|
||||
```bash
|
||||
# 1. Clonar repositorio
|
||||
git clone <repo-url>
|
||||
cd rss2
|
||||
|
||||
# 2. Configurar variables de entorno
|
||||
cp .env.example .env
|
||||
# Editar .env con tus credenciales
|
||||
|
||||
# 3. Iniciar servicios
|
||||
docker-compose up -d
|
||||
|
||||
# 4. Verificar que Qdrant está funcionando
|
||||
curl http://localhost:6333/healthz
|
||||
|
||||
# 5. Monitorear vectorización
|
||||
docker-compose logs -f qdrant-worker
|
||||
```
|
||||
|
||||
### Migración de Datos Existentes
|
||||
|
||||
Si ya tienes noticias traducidas sin vectorizar:
|
||||
|
||||
```bash
|
||||
# Ver estadísticas
|
||||
docker exec -it rss2_web python scripts/migrate_to_qdrant.py --stats
|
||||
|
||||
# Vectorizar todas las pendientes (puede tardar horas con 500k noticias)
|
||||
# El worker lo hace automáticamente, pero puedes forzarlo:
|
||||
docker-compose restart qdrant-worker
|
||||
```
|
||||
|
||||
## 🔍 Troubleshooting
|
||||
|
||||
### La búsqueda no usa Qdrant
|
||||
```bash
|
||||
# Verificar que Qdrant está corriendo
|
||||
docker ps | grep qdrant
|
||||
|
||||
# Ver logs del worker
|
||||
docker-compose logs qdrant-worker
|
||||
|
||||
# Verificar conexión
|
||||
curl http://localhost:6333/collections/news_vectors
|
||||
```
|
||||
|
||||
### Búsqueda lenta aún
|
||||
```bash
|
||||
# Verificar cuántas noticias están vectorizadas
|
||||
docker exec -it rss2_web python scripts/migrate_to_qdrant.py --stats
|
||||
|
||||
# Si hay muchas pendientes, el worker las procesará automáticamente
|
||||
# Para acelerar, aumenta el batch size en docker-compose.yml:
|
||||
# QDRANT_BATCH_SIZE=200
|
||||
```
|
||||
|
||||
### Error "No module named 'qdrant_client'"
|
||||
```bash
|
||||
# Reconstruir imagen web
|
||||
docker-compose build rss2_web
|
||||
docker-compose restart rss2_web
|
||||
```
|
||||
|
||||
## 📈 Rendimiento
|
||||
|
||||
**Antes (PostgreSQL solo):**
|
||||
- Búsqueda simple: 2-5 segundos (500k filas)
|
||||
- Búsqueda compleja: 10-30 segundos
|
||||
- Monitor de conflictos: 5-15 segundos
|
||||
|
||||
**Ahora (Qdrant + PostgreSQL):**
|
||||
- Búsqueda semántica: 50-200ms
|
||||
- Enriquecimiento PostgreSQL: +50ms
|
||||
- Monitor de conflictos: 100-300ms
|
||||
- **Mejora: 10-100x más rápido** ⚡
|
||||
|
||||
## 🎯 Próximos Pasos
|
||||
|
||||
- [ ] Implementar filtros avanzados (fecha, país, categoría)
|
||||
- [ ] Cachear resultados frecuentes en Redis
|
||||
- [ ] Agregar búsqueda híbrida (combinar PostgreSQL FTS + Qdrant)
|
||||
- [ ] Dashboard de métricas de búsqueda
|
||||
269
docs/QUICKSTART_LLM.md
Normal file
269
docs/QUICKSTART_LLM.md
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
# 🚀 Guía Rápida: Sistema LLM Categorizer
|
||||
|
||||
## ✅ Estado Actual
|
||||
|
||||
- ✓ Aplicación RSS2 levantada y funcionando correctamente
|
||||
- ✓ Todos los contenedores están operativos
|
||||
- ✓ Web accesible en http://localhost:8001
|
||||
- ✓ Nuevo sistema LLM Categorizer creado y configurado
|
||||
|
||||
## 📋 Próximos Pasos
|
||||
|
||||
### 1. Descargar el Modelo LLM (REQUERIDO)
|
||||
|
||||
```bash
|
||||
cd /home/x/rss2
|
||||
./scripts/download_llm_model.sh
|
||||
```
|
||||
|
||||
**Selecciona la opción 1** (Mistral-7B-Instruct-v0.2 GPTQ) - Recomendado para RTX 3060 12GB
|
||||
|
||||
⏱️ **Tiempo estimado**: 10-30 minutos según tu conexión
|
||||
💾 **Espacio necesario**: ~4.5 GB
|
||||
|
||||
### 2. Probar el Sistema (OPCIONAL pero recomendado)
|
||||
|
||||
```bash
|
||||
# Instalar dependencias para prueba local
|
||||
pip3 install exllamav2 torch
|
||||
|
||||
# Ejecutar prueba
|
||||
python3 scripts/test_llm_categorizer.py
|
||||
```
|
||||
|
||||
Esto te permite verificar que el modelo funciona ANTES de levantar el contenedor.
|
||||
|
||||
### 3. Levantar el Servicio LLM
|
||||
|
||||
```bash
|
||||
# Construir y levantar el contenedor
|
||||
docker compose up -d --build llm-categorizer
|
||||
|
||||
# Monitorear los logs
|
||||
docker compose logs -f llm-categorizer
|
||||
```
|
||||
|
||||
**Primera ejecución**: El contenedor tardará 2-5 minutos en cargar el modelo en GPU.
|
||||
|
||||
### 4. Verificar Funcionamiento
|
||||
|
||||
```bash
|
||||
# Ver estado
|
||||
docker compose ps llm-categorizer
|
||||
|
||||
# Ver últimas categorizaciones
|
||||
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;"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Configuración
|
||||
|
||||
### Archivos Creados
|
||||
|
||||
```
|
||||
/home/x/rss2/
|
||||
├── workers/
|
||||
│ └── llm_categorizer_worker.py # Worker principal
|
||||
├── Dockerfile.llm_worker # Dockerfile específico
|
||||
├── scripts/
|
||||
│ ├── download_llm_model.sh # Descarga del modelo
|
||||
│ └── test_llm_categorizer.py # Script de prueba
|
||||
├── docs/
|
||||
│ └── LLM_CATEGORIZER.md # Documentación completa
|
||||
└── docker-compose.yml # Actualizado con servicio llm-categorizer
|
||||
```
|
||||
|
||||
### Servicio en docker-compose.yml
|
||||
|
||||
```yaml
|
||||
llm-categorizer:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.llm_worker
|
||||
environment:
|
||||
LLM_BATCH_SIZE: 10 # Noticias por lote
|
||||
LLM_SLEEP_IDLE: 30 # Segundos entre lotes
|
||||
LLM_MODEL_PATH: /app/models/llm
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 10G
|
||||
reservations:
|
||||
devices:
|
||||
- driver: nvidia
|
||||
count: 1
|
||||
capabilities: [ gpu ]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Cómo Funciona
|
||||
|
||||
1. **Recopilación**: El worker consulta la BD y obtiene 10 noticias sin categorizar
|
||||
2. **Procesamiento**: Envía cada noticia al LLM local (Mistral-7B)
|
||||
3. **Categorización**: El LLM determina la categoría más apropiada
|
||||
4. **Actualización**: Guarda la categoría y confianza en la BD
|
||||
5. **Loop**: Repite el proceso continuamente
|
||||
|
||||
### Categorías Disponibles
|
||||
|
||||
- Política
|
||||
- Economía
|
||||
- Tecnología
|
||||
- Ciencia
|
||||
- Salud
|
||||
- Deportes
|
||||
- Entretenimiento
|
||||
- Internacional
|
||||
- Nacional
|
||||
- Sociedad
|
||||
- Cultura
|
||||
- Medio Ambiente
|
||||
- Educación
|
||||
- Seguridad
|
||||
- Otros
|
||||
|
||||
---
|
||||
|
||||
## 📊 Rendimiento Esperado
|
||||
|
||||
### Con RTX 3060 12GB + Mistral-7B GPTQ
|
||||
|
||||
- **VRAM utilizada**: ~6-7 GB
|
||||
- **Tiempo por noticia**: 2-5 segundos
|
||||
- **Throughput**: ~120-300 noticias/hora
|
||||
- **Precisión**: ~85-90% (depende del contenido)
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Consultas SQL Útiles
|
||||
|
||||
### Ver distribución de categorías
|
||||
|
||||
```sql
|
||||
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
|
||||
|
||||
```sql
|
||||
SELECT titulo, llm_categoria, llm_confianza, fecha
|
||||
FROM noticias
|
||||
WHERE llm_categoria = 'Tecnología'
|
||||
AND llm_processed = TRUE
|
||||
ORDER BY fecha DESC
|
||||
LIMIT 20;
|
||||
```
|
||||
|
||||
### Resetear procesamiento (para reprocesar)
|
||||
|
||||
```sql
|
||||
-- Resetear últimas 100 noticias
|
||||
UPDATE noticias
|
||||
SET llm_processed = FALSE
|
||||
WHERE id IN (
|
||||
SELECT id FROM noticias
|
||||
ORDER BY fecha DESC
|
||||
LIMIT 100
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Troubleshooting Rápido
|
||||
|
||||
### ❌ "Out of memory"
|
||||
```yaml
|
||||
# Reducir batch size en docker-compose.yml
|
||||
LLM_BATCH_SIZE: 5
|
||||
LLM_CACHE_MODE: Q4
|
||||
```
|
||||
|
||||
### ❌ "Model not found"
|
||||
```bash
|
||||
# Verificar descarga
|
||||
ls -la models/llm/
|
||||
|
||||
# Re-descargar si necesario
|
||||
./scripts/download_llm_model.sh
|
||||
```
|
||||
|
||||
### ❌ No procesa noticias
|
||||
```bash
|
||||
# Verificar cuántas faltan
|
||||
docker exec -it rss2_db psql -U rss -d rss -c \
|
||||
"SELECT COUNT(*) FROM noticias WHERE llm_processed = FALSE;"
|
||||
|
||||
# Resetear algunas para probar
|
||||
docker exec -it rss2_db psql -U rss -d rss -c \
|
||||
"UPDATE noticias SET llm_processed = FALSE LIMIT 20;"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 Documentación Completa
|
||||
|
||||
Para más detalles, consulta:
|
||||
|
||||
```bash
|
||||
cat docs/LLM_CATEGORIZER.md
|
||||
```
|
||||
|
||||
O abre: `/home/x/rss2/docs/LLM_CATEGORIZER.md`
|
||||
|
||||
---
|
||||
|
||||
## 🎓 Comandos Útiles
|
||||
|
||||
```bash
|
||||
# Ver todos los servicios
|
||||
docker compose ps
|
||||
|
||||
# Reiniciar solo el LLM
|
||||
docker compose restart llm-categorizer
|
||||
|
||||
# Ver uso de GPU
|
||||
nvidia-smi
|
||||
|
||||
# Ver logs + seguir
|
||||
docker compose logs -f llm-categorizer
|
||||
|
||||
# Detener el LLM
|
||||
docker compose stop llm-categorizer
|
||||
|
||||
# Eliminar completamente (rebuild desde cero)
|
||||
docker compose down llm-categorizer
|
||||
docker compose up -d --build llm-categorizer
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚡ Optimizaciones Futuras
|
||||
|
||||
Si quieres mejorar el rendimiento:
|
||||
|
||||
1. **Usar EXL2 en lugar de GPTQ** (más rápido en ExLlamaV2)
|
||||
2. **Aumentar batch size** si sobra VRAM
|
||||
3. **Fine-tune el modelo** con tus propias categorizaciones
|
||||
4. **Usar vLLM** para servidor de inferencia más eficiente
|
||||
|
||||
---
|
||||
|
||||
## 🤝 Soporte
|
||||
|
||||
Si encuentras problemas:
|
||||
|
||||
1. Revisa logs: `docker compose logs llm-categorizer`
|
||||
2. Consulta documentación: `docs/LLM_CATEGORIZER.md`
|
||||
3. Verifica GPU: `nvidia-smi`
|
||||
|
||||
---
|
||||
|
||||
**¡Listo!** El sistema está completamente configurado. Solo falta descargar el modelo y levantarlo. 🚀
|
||||
201
docs/SECURITY_AUDIT.md
Normal file
201
docs/SECURITY_AUDIT.md
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
# 🔒 Auditoría de Seguridad de Red - Resumen Ejecutivo
|
||||
|
||||
**Fecha**: 2026-01-12
|
||||
**Sistema**: RSS2 News Aggregator
|
||||
**Auditor**: Análisis Automatizado de Seguridad
|
||||
|
||||
---
|
||||
|
||||
## 📊 RESUMEN EJECUTIVO
|
||||
|
||||
Se han identificado **múltiples vulnerabilidades críticas** en la configuración de red de los contenedores Docker. El sistema actual expone servicios internos sin autenticación y utiliza credenciales débiles que comprometen severamente la seguridad de la aplicación.
|
||||
|
||||
**Nivel de Riesgo Global**: 🔴 **CRÍTICO**
|
||||
|
||||
---
|
||||
|
||||
## 🚨 VULNERABILIDADES CRÍTICAS (Prioridad 1)
|
||||
|
||||
### 1. Credenciales Comprometidas
|
||||
- **Severidad**: 🔴 CRÍTICA
|
||||
- **CVSS Score**: 9.8 (Critical)
|
||||
- **Descripción**:
|
||||
- PostgreSQL usa password `x` (1 carácter)
|
||||
- Flask SECRET_KEY es `secret` (valor por defecto conocido)
|
||||
- Grafana usa password `admin` (credencial por defecto)
|
||||
- **Impacto**:
|
||||
- Acceso completo a la base de datos
|
||||
- Posible firma de sesiones falsas
|
||||
- Compromiso total del sistema de autenticación
|
||||
- **Solución**: Generar credenciales aleatorias de 32+ caracteres
|
||||
|
||||
### 2. Exposición de Base de Datos Vectorial (Qdrant)
|
||||
- **Severidad**: 🔴 CRÍTICA
|
||||
- **CVSS Score**: 8.6 (High)
|
||||
- **Puertos Expuestos**: 6333, 6334
|
||||
- **Descripción**: Qdrant accesible públicamente sin autenticación
|
||||
- **Impacto**:
|
||||
- Lectura/modificación de vectores de noticias
|
||||
- Potencial exfiltración de datos
|
||||
- Manipulación de búsquedas semánticas
|
||||
- **Solución**: Eliminar exposición de puertos, usar solo red interna
|
||||
|
||||
### 3. Redis Sin Autenticación
|
||||
- **Severidad**: 🔴 ALTA
|
||||
- **CVSS Score**: 7.5 (High)
|
||||
- **Descripción**: Redis accesible sin password
|
||||
- **Impacto**:
|
||||
- Acceso no autorizado a caché
|
||||
- Posible inyección de datos maliciosos
|
||||
- DoS mediante flush de caché
|
||||
- **Solución**: Habilitar requirepass en Redis
|
||||
|
||||
### 4. Exposición de Prometheus y cAdvisor
|
||||
- **Severidad**: 🟠 ALTA
|
||||
- **CVSS Score**: 7.2 (High)
|
||||
- **Puertos Expuestos**: 9090 (Prometheus), 8081 (cAdvisor)
|
||||
- **Descripción**: Métricas del sistema accesibles públicamente
|
||||
- **Impacto**:
|
||||
- Información sensible sobre arquitectura
|
||||
- Vectores de ataque (uptime, recursos, vulnerabilidades)
|
||||
- Reconocimiento de servicios internos
|
||||
- **Solución**: Internalizar puertos, acceso solo via túnel SSH
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ VULNERABILIDADES DE RIESGO MEDIO (Prioridad 2)
|
||||
|
||||
### 5. Ausencia de Segmentación de Red
|
||||
- **Severidad**: 🟠 MEDIA
|
||||
- **Descripción**: Todos los servicios en una única red Docker
|
||||
- **Impacto**: Movimiento lateral fácil si un contenedor es comprometido
|
||||
- **Solución**: Implementar 3 redes segmentadas (frontend, backend, monitoring)
|
||||
|
||||
### 6. Sin Límites de Recursos
|
||||
- **Severidad**: 🟡 MEDIA-BAJA
|
||||
- **Descripción**: Contenedores sin límites de CPU/memoria
|
||||
- **Impacto**: Posible DoS por consumo excesivo de recursos
|
||||
- **Solución**: Establecer límites y reservas de recursos
|
||||
|
||||
### 7. Montaje de Volúmenes con Permisos Excesivos
|
||||
- **Severidad**: 🟡 BAJA
|
||||
- **Descripción**: Código fuente montado en read-write
|
||||
- **Impacto**: Modificación de código desde contenedor comprometido
|
||||
- **Solución**: Montar volúmenes críticos en modo read-only
|
||||
|
||||
---
|
||||
|
||||
## ✅ SOLUCIONES IMPLEMENTADAS
|
||||
|
||||
### Archivos Creados
|
||||
|
||||
1. **`docker-compose.secure.yml`**
|
||||
- Redes segmentadas (frontend, backend, monitoring)
|
||||
- Puertos internalizados
|
||||
- Autenticación en Redis
|
||||
- Límites de recursos en todos los servicios
|
||||
- Volúmenes read-only donde aplica
|
||||
|
||||
2. **`.env.secure.example`**
|
||||
- Template con instrucciones de seguridad
|
||||
- Placeholders para credenciales fuertes
|
||||
|
||||
3. **`generate_secure_credentials.sh`**
|
||||
- Script automatizado para generar credenciales
|
||||
- Genera passwords de 32 caracteres
|
||||
- Crea .env con configuración segura
|
||||
|
||||
4. **`SECURITY_GUIDE.md`**
|
||||
- Guía completa de migración
|
||||
- Troubleshooting
|
||||
- Best practices
|
||||
|
||||
5. **Código Python actualizado**
|
||||
- `config.py`: Soporte para REDIS_PASSWORD
|
||||
- `cache.py`: Autenticación en Redis
|
||||
|
||||
---
|
||||
|
||||
## 📈 MEJORAS DE SEGURIDAD
|
||||
|
||||
| Métrica | ANTES | DESPUÉS | Mejora |
|
||||
|---------|-------|---------|--------|
|
||||
| Puertos públicos | 7 | 1 | **-85%** |
|
||||
| Servicios con autenticación | 1/4 | 4/4 | **+300%** |
|
||||
| Redes aisladas | 1 | 3 | **+200%** |
|
||||
| Servicios con límites de recursos | 0% | 100% | **+100%** |
|
||||
| Fortaleza de passwords (bits) | ~4 bits | ~256 bits | **+6300%** |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 PLAN DE ACCIÓN RECOMENDADO
|
||||
|
||||
### Fase 1: INMEDIATO (Hoy)
|
||||
1. ✅ Revisar archivos generados
|
||||
2. ✅ Leer SECURITY_GUIDE.md
|
||||
3. ⏳ Ejecutar `./generate_secure_credentials.sh`
|
||||
4. ⏳ Guardar credenciales en gestor de passwords
|
||||
|
||||
### Fase 2: CORTO PLAZO (Esta semana)
|
||||
5. ⏳ Hacer backup completo de datos
|
||||
6. ⏳ Migrar a `docker-compose.secure.yml`
|
||||
7. ⏳ Validar funcionamiento en desarrollo
|
||||
8. ⏳ Configurar acceso SSH a Grafana
|
||||
|
||||
### Fase 3: MEDIO PLAZO (Este mes)
|
||||
9. ⏳ Implementar monitoreo de seguridad
|
||||
10. ⏳ Configurar backups automáticos encriptados
|
||||
11. ⏳ Implementar rate limiting en nginx
|
||||
12. ⏳ Configurar fail2ban
|
||||
|
||||
---
|
||||
|
||||
## 📋 CHECKLIST DE VALIDACIÓN
|
||||
|
||||
Antes de marcar como resuelto, verificar:
|
||||
|
||||
- [ ] Todas las credenciales cambiadas y guardadas
|
||||
- [ ] Solo puerto 8001 expuesto públicamente
|
||||
- [ ] Qdrant NO accesible desde internet
|
||||
- [ ] Prometheus NO accesible desde internet
|
||||
- [ ] cAdvisor NO accesible desde internet
|
||||
- [ ] Redis requiere autenticación
|
||||
- [ ] Grafana solo en localhost (127.0.0.1:3001)
|
||||
- [ ] Web app funciona correctamente
|
||||
- [ ] Workers se conectan a servicios
|
||||
- [ ] Búsqueda funciona
|
||||
- [ ] Backups configurados
|
||||
- [ ] Firewall del servidor activo
|
||||
|
||||
---
|
||||
|
||||
## 🔗 REFERENCIAS
|
||||
|
||||
- [Docker Security Best Practices](https://docs.docker.com/develop/security-best-practices/)
|
||||
- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
|
||||
- [CIS Docker Benchmark](https://www.cisecurity.org/benchmark/docker)
|
||||
- [NIST Cybersecurity Framework](https://www.nist.gov/cyberframework)
|
||||
|
||||
---
|
||||
|
||||
## 📞 CONTACTO Y SOPORTE
|
||||
|
||||
Para asistencia con la migración:
|
||||
- Revisar `SECURITY_GUIDE.md` (troubleshooting completo)
|
||||
- Verificar logs: `docker-compose logs -f`
|
||||
- Verificar conectividad de redes: `docker network inspect rss2_backend`
|
||||
|
||||
---
|
||||
|
||||
**Última actualización**: 2026-01-12 18:18 CET
|
||||
**Próxima revisión recomendada**: 2026-02-12 (mensual)
|
||||
|
||||
---
|
||||
|
||||
## 🏆 CONCLUSIÓN
|
||||
|
||||
La implementación de las soluciones propuestas reducirá el riesgo de seguridad de **CRÍTICO a BAJO**, cerrando todas las vulnerabilidades identificadas y estableciendo una base sólida de seguridad para la aplicación RSS2.
|
||||
|
||||
**Tiempo estimado de implementación**: 2-4 horas
|
||||
**Complejidad**: Media
|
||||
**ROI de seguridad**: Extremadamente Alto
|
||||
383
docs/SECURITY_GUIDE.md
Normal file
383
docs/SECURITY_GUIDE.md
Normal file
|
|
@ -0,0 +1,383 @@
|
|||
# 🔒 GUÍA DE SEGURIDAD Y MIGRACIÓN - RSS2 Application
|
||||
|
||||
## ⚠️ RESUMEN DE VULNERABILIDADES ENCONTRADAS
|
||||
|
||||
### CRÍTICAS (Arreglar INMEDIATAMENTE)
|
||||
|
||||
1. **Credenciales débiles en .env**
|
||||
- PostgreSQL password: `x`
|
||||
- Flask SECRET_KEY: `secret`
|
||||
- Grafana password: `admin`
|
||||
|
||||
2. **Servicios expuestos públicamente sin autenticación**
|
||||
- Qdrant (puertos 6333, 6334) - Base de datos vectorial
|
||||
- Prometheus (puerto 9090) - Métricas del sistema
|
||||
- cAdvisor (puerto 8081) - Estadísticas de contenedores
|
||||
|
||||
3. **Redis sin autenticación**
|
||||
- Accesible por todos los contenedores sin password
|
||||
|
||||
### ALTO RIESGO
|
||||
|
||||
4. **Ausencia de segmentación de red**
|
||||
- Todos los servicios en una única red Docker
|
||||
|
||||
5. **Ausencia de límites de recursos**
|
||||
- Contenedores sin límites de CPU/memoria (riesgo de DoS)
|
||||
|
||||
6. **Volúmenes con permisos excesivos**
|
||||
- Código fuente montado con permisos de escritura
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ SOLUCIONES IMPLEMENTADAS
|
||||
|
||||
### 1. Archivo `docker-compose.secure.yml`
|
||||
|
||||
**Mejoras de seguridad implementadas:**
|
||||
|
||||
#### 🔹 Redes Segmentadas
|
||||
```yaml
|
||||
networks:
|
||||
frontend: # Solo nginx y rss2_web
|
||||
backend: # BD, workers, redis, qdrant (interna)
|
||||
monitoring: # Prometheus, Grafana, cAdvisor (interna)
|
||||
```
|
||||
|
||||
#### 🔹 Puertos Internalizados
|
||||
- ❌ **Eliminados puertos públicos de:**
|
||||
- Qdrant (6333, 6334) → Solo acceso interno
|
||||
- Prometheus (9090) → Solo acceso interno
|
||||
- cAdvisor (8081) → Solo acceso interno
|
||||
- rss-web-go (8002) → Servicio comentado (duplicado)
|
||||
|
||||
- ✅ **Único puerto público:**
|
||||
- Nginx (8001) → Proxy reverso con seguridad
|
||||
|
||||
- ✅ **Puerto localhost únicamente:**
|
||||
- Grafana (127.0.0.1:3001) → Acceso solo local o via túnel SSH
|
||||
|
||||
#### 🔹 Autenticación en Redis
|
||||
```yaml
|
||||
redis:
|
||||
command: >
|
||||
redis-server
|
||||
--requirepass ${REDIS_PASSWORD}
|
||||
```
|
||||
|
||||
#### 🔹 Límites de Recursos
|
||||
Todos los contenedores tienen límites de CPU y memoria:
|
||||
```yaml
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 2G
|
||||
reservations:
|
||||
memory: 512M
|
||||
```
|
||||
|
||||
#### 🔹 Volúmenes Read-Only
|
||||
Código fuente montado en modo lectura donde sea posible:
|
||||
```yaml
|
||||
volumes:
|
||||
- ./app.py:/app/app.py:ro
|
||||
- ./routers:/app/routers:ro
|
||||
- ./templates:/app/templates:ro
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 PASOS DE MIGRACIÓN
|
||||
|
||||
### Opción A: Migración Gradual (RECOMENDADO para producción)
|
||||
|
||||
#### Paso 1: Generar Credenciales Seguras
|
||||
|
||||
```bash
|
||||
# 1. Generar password para PostgreSQL
|
||||
POSTGRES_PASSWORD=$(openssl rand -base64 32)
|
||||
echo "POSTGRES_PASSWORD=$POSTGRES_PASSWORD"
|
||||
|
||||
# 2. Generar password para Redis
|
||||
REDIS_PASSWORD=$(openssl rand -base64 32)
|
||||
echo "REDIS_PASSWORD=$REDIS_PASSWORD"
|
||||
|
||||
# 3. Generar SECRET_KEY para Flask
|
||||
SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
|
||||
echo "SECRET_KEY=$SECRET_KEY"
|
||||
|
||||
# 4. Generar password para Grafana
|
||||
GRAFANA_PASSWORD=$(openssl rand -base64 24)
|
||||
echo "GRAFANA_PASSWORD=$GRAFANA_PASSWORD"
|
||||
|
||||
# Guardar estos valores en un lugar seguro (gestor de passwords)
|
||||
```
|
||||
|
||||
#### Paso 2: Copiar y Configurar .env Seguro
|
||||
|
||||
```bash
|
||||
# Copiar el ejemplo seguro
|
||||
cp .env.secure.example .env
|
||||
|
||||
# Editar .env y pegar las contraseñas generadas
|
||||
nano .env # o usa tu editor preferido
|
||||
```
|
||||
|
||||
#### Paso 3: Backup de Datos
|
||||
|
||||
```bash
|
||||
# Backup de PostgreSQL
|
||||
docker exec rss2_db pg_dump -U rss rss > backup_$(date +%Y%m%d_%H%M%S).sql
|
||||
|
||||
# Backup de Qdrant
|
||||
tar -czf qdrant_backup_$(date +%Y%m%d_%H%M%S).tar.gz qdrant_storage/
|
||||
|
||||
# Backup de Redis (opcional)
|
||||
docker exec rss2_redis redis-cli --rdb /data/dump.rdb
|
||||
cp redis-data/dump.rdb redis_backup_$(date +%Y%m%d_%H%M%S).rdb
|
||||
```
|
||||
|
||||
#### Paso 4: Detener Servicios Actuales
|
||||
|
||||
```bash
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
#### Paso 5: Migrar a Configuración Segura
|
||||
|
||||
```bash
|
||||
# Renombrar archivo actual (backup)
|
||||
mv docker-compose.yml docker-compose.yml.insecure.bak
|
||||
|
||||
# Usar la versión segura
|
||||
cp docker-compose.secure.yml docker-compose.yml
|
||||
|
||||
# Verificar configuración
|
||||
docker-compose config
|
||||
```
|
||||
|
||||
#### Paso 6: Iniciar con Nueva Configuración
|
||||
|
||||
```bash
|
||||
# Iniciar servicios
|
||||
docker-compose up -d
|
||||
|
||||
# Verificar logs
|
||||
docker-compose logs -f
|
||||
|
||||
# Verificar que todos los contenedores están corriendo
|
||||
docker-compose ps
|
||||
```
|
||||
|
||||
#### Paso 7: Verificar Conectividad
|
||||
|
||||
```bash
|
||||
# Test web app
|
||||
curl http://localhost:8001
|
||||
|
||||
# Test Redis (desde dentro de un contenedor)
|
||||
docker exec rss2_web bash -c 'python3 -c "import redis; r = redis.Redis(host=\"redis\", port=6379, password=\"$REDIS_PASSWORD\"); print(r.ping())"'
|
||||
|
||||
# Verificar logs de workers
|
||||
docker-compose logs rss2_tasks_py | tail -20
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Opción B: Migración Directa (Para desarrollo/testing)
|
||||
|
||||
```bash
|
||||
# 1. Backup de datos (como en Opción A, Paso 3)
|
||||
|
||||
# 2. Detener todo
|
||||
docker-compose down -v # CUIDADO: -v elimina volúmenes
|
||||
|
||||
# 3. Generar credenciales y configurar .env
|
||||
cp .env.secure.example .env
|
||||
# Editar .env con credenciales generadas
|
||||
|
||||
# 4. Restaurar datos si es necesario
|
||||
# (Restaurar dump SQL, qdrant_storage, etc.)
|
||||
|
||||
# 5. Iniciar con configuración segura
|
||||
cp docker-compose.secure.yml docker-compose.yml
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 ACCESO A SERVICIOS PROTEGIDOS
|
||||
|
||||
### Grafana (Monitoring)
|
||||
|
||||
Ahora solo accesible en localhost. Para acceso remoto:
|
||||
|
||||
```bash
|
||||
# Opción 1: Túnel SSH (RECOMENDADO)
|
||||
ssh -L 3001:localhost:3001 usuario@servidor
|
||||
|
||||
# Luego acceder en tu navegador local:
|
||||
# http://localhost:3001
|
||||
# Usuario: admin
|
||||
# Password: El que configuraste en GRAFANA_PASSWORD
|
||||
```
|
||||
|
||||
### Qdrant (Base de Datos Vectorial)
|
||||
|
||||
Ya no es accesible públicamente. Para acceso de desarrollo:
|
||||
|
||||
```bash
|
||||
# Opción 1: Temporalmente exponer puerto (SOLO para debug)
|
||||
# Editar docker-compose.yml y descomentar:
|
||||
# ports:
|
||||
# - "127.0.0.1:6333:6333"
|
||||
|
||||
# Opción 2: Acceder desde dentro de la red Docker
|
||||
docker exec -it rss2_qdrant_worker bash
|
||||
curl http://qdrant:6333/collections
|
||||
```
|
||||
|
||||
### Prometheus (Métricas)
|
||||
|
||||
```bash
|
||||
# Acceso via túnel SSH
|
||||
ssh -L 9090:localhost:9090 usuario@servidor
|
||||
|
||||
# O exponer temporalmente en localhost:
|
||||
# En docker-compose.yml, prometheus service:
|
||||
# ports:
|
||||
# - "127.0.0.1:9090:9090"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 TESTING DE SEGURIDAD
|
||||
|
||||
### Verificar que puertos NO son accesibles públicamente:
|
||||
|
||||
```bash
|
||||
# Desde FUERA del servidor (desde tu máquina local)
|
||||
|
||||
# Estos NO deberían responder:
|
||||
curl http://servidor:6333 # Qdrant - debe fallar
|
||||
curl http://servidor:9090 # Prometheus - debe fallar
|
||||
curl http://servidor:8081 # cAdvisor - debe fallar
|
||||
|
||||
# Este SÍ debe responder:
|
||||
curl http://servidor:8001 # Nginx - debe funcionar
|
||||
```
|
||||
|
||||
### Verificar segmentación de redes:
|
||||
|
||||
```bash
|
||||
# Los contenedores NO deberían poder acceder a servicios fuera de su red
|
||||
|
||||
# Desde un worker backend, NO debe alcanzar nginx:
|
||||
docker exec rss2_cluster_py curl http://nginx # Debería fallar
|
||||
|
||||
# Desde monitoring, NO debe alcanzar db:
|
||||
docker exec rss2_prometheus curl http://db:5432 # Debería fallar
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 COMPARATIVA: ANTES vs DESPUÉS
|
||||
|
||||
| Aspecto | ANTES (Inseguro) | DESPUÉS (Seguro) |
|
||||
|---------|------------------|------------------|
|
||||
| **Puertos expuestos** | 7 puertos públicos | 1 puerto público + 1 localhost |
|
||||
| **Autenticación Redis** | ❌ Sin password | ✅ Autenticado |
|
||||
| **PostgreSQL Password** | `x` (débil) | 32+ caracteres aleatorios |
|
||||
| **Flask SECRET_KEY** | `secret` | 64 caracteres hex |
|
||||
| **Segmentación de red** | ❌ Una red única | ✅ 3 redes aisladas |
|
||||
| **Límites de recursos** | ❌ Sin límites | ✅ CPU y RAM limitados |
|
||||
| **Volúmenes** | Read-Write | Read-Only donde posible |
|
||||
| **Qdrant público** | ⚠️ Sí (puerto 6333) | ✅ Solo interno |
|
||||
| **Prometheus público** | ⚠️ Sí (puerto 9090) | ✅ Solo interno |
|
||||
|
||||
---
|
||||
|
||||
## 🚨 CHECKLIST FINAL DE SEGURIDAD
|
||||
|
||||
Antes de poner en producción, verifica:
|
||||
|
||||
- [ ] Todas las contraseñas generadas aleatoriamente (min 32 caracteres)
|
||||
- [ ] Archivo `.env` NO está en el repositorio (revisar .gitignore)
|
||||
- [ ] Solo puerto 8001 expuesto públicamente
|
||||
- [ ] Grafana accesible solo en localhost
|
||||
- [ ] Redis requiere autenticación
|
||||
- [ ] Todos los workers pueden conectarse a Redis
|
||||
- [ ] Todos los workers pueden conectarse a PostgreSQL
|
||||
- [ ] Web app funciona correctamente
|
||||
- [ ] Búsqueda semántica (Qdrant) funciona
|
||||
- [ ] Backups automáticos configurados
|
||||
- [ ] Monitoring (Grafana) accessible via SSH tunnel
|
||||
- [ ] Firewall del servidor configurado (solo permitir 8001, 22)
|
||||
|
||||
---
|
||||
|
||||
## 🔧 TROUBLESHOOTING
|
||||
|
||||
### Error: Redis authentication failed
|
||||
|
||||
```bash
|
||||
# Verificar que REDIS_PASSWORD está en .env
|
||||
grep REDIS_PASSWORD .env
|
||||
|
||||
# Verificar que los workers tienen la variable
|
||||
docker exec rss2_web env | grep REDIS
|
||||
|
||||
# Reiniciar servicios
|
||||
docker-compose restart
|
||||
```
|
||||
|
||||
### Error: No puedo acceder a Grafana desde mi máquina
|
||||
|
||||
```bash
|
||||
# Asegurarte de que el túnel SSH está activo
|
||||
ssh -L 3001:localhost:3001 usuario@servidor
|
||||
|
||||
# Verificar que Grafana está corriendo
|
||||
docker-compose ps | grep grafana
|
||||
|
||||
# Logs de Grafana
|
||||
docker-compose logs grafana
|
||||
```
|
||||
|
||||
### Workers no pueden conectarse a Qdrant
|
||||
|
||||
```bash
|
||||
# Verificar que Qdrant está en la red backend
|
||||
docker network inspect rss2_backend | grep qdrant
|
||||
|
||||
# Verificar logs de Qdrant
|
||||
docker-compose logs qdrant
|
||||
|
||||
# Test de conectividad desde un worker
|
||||
docker exec rss2_qdrant_worker curl http://qdrant:6333/collections
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 RECURSOS ADICIONALES
|
||||
|
||||
- [Docker Networks Security Best Practices](https://docs.docker.com/network/network-tutorial-standalone/)
|
||||
- [Redis Security](https://redis.io/docs/management/security/)
|
||||
- [PostgreSQL Security](https://www.postgresql.org/docs/current/security.html)
|
||||
- [OWASP Docker Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html)
|
||||
|
||||
---
|
||||
|
||||
## 📝 NOTAS IMPORTANTES
|
||||
|
||||
1. **Backup Regular**: Configura backups automáticos ANTES de migrar
|
||||
2. **Testing**: Prueba en un entorno de desarrollo primero
|
||||
3. **Downtime**: Planifica una ventana de mantenimiento
|
||||
4. **Monitoring**: Verifica que Grafana funciona después de migrar
|
||||
5. **Documentation**: Documenta las contraseñas en un gestor seguro
|
||||
|
||||
---
|
||||
|
||||
**Última actualización**: 2026-01-12
|
||||
**Autor**: Auditoría de Seguridad Automatizada
|
||||
202
docs/TRANSLATION_FIX_SUMMARY.md
Normal file
202
docs/TRANSLATION_FIX_SUMMARY.md
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
# 🎯 Resumen de Solución - Traducciones Repetitivas
|
||||
|
||||
## ✅ Problema Resuelto
|
||||
|
||||
### Estado Inicial
|
||||
- **3,093 traducciones defectuosas** detectadas con patrones repetitivos
|
||||
- Ejemplos: "la línea de la línea de la línea...", "de Internet de Internet..."
|
||||
|
||||
### Soluciones Implementadas
|
||||
|
||||
#### 1. ✅ Mejoras en Translation Worker
|
||||
**Archivo**: `workers/translation_worker.py`
|
||||
|
||||
**Cambios aplicados:**
|
||||
- ✅ `repetition_penalty`: 1.2 → **2.5** (penalización más agresiva)
|
||||
- ✅ `no_repeat_ngram_size`: 4 → **3** (bloqueo de 3-gramas)
|
||||
- ✅ Nueva función `_is_repetitive_output()` para validación post-traducción
|
||||
- ✅ Rechazo automático de outputs repetitivos
|
||||
|
||||
**Código clave añadido:**
|
||||
```python
|
||||
# Validación automática
|
||||
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
|
||||
**Archivo**: `scripts/clean_repetitive_translations.py`
|
||||
|
||||
**Funcionalidad:**
|
||||
- Escanea todas las traducciones completadas
|
||||
- Detecta patrones repetitivos mediante regex y análisis de diversidad
|
||||
- Marca traducciones defectuosas como 'pending' para re-traducción
|
||||
- Genera reportes detallados
|
||||
|
||||
**Uso:**
|
||||
```bash
|
||||
docker exec rss2_web python3 scripts/clean_repetitive_translations.py
|
||||
```
|
||||
|
||||
#### 3. ✅ Script de Monitoreo
|
||||
**Archivo**: `scripts/monitor_translation_quality.py`
|
||||
|
||||
**Funcionalidad:**
|
||||
- Estadísticas en tiempo real de traducciones
|
||||
- Detección de problemas de calidad
|
||||
- Modo watch para monitoreo continuo
|
||||
|
||||
**Uso:**
|
||||
```bash
|
||||
# Reporte único
|
||||
docker exec rss2_web python3 scripts/monitor_translation_quality.py --hours 24
|
||||
|
||||
# Monitoreo continuo
|
||||
docker exec rss2_web python3 scripts/monitor_translation_quality.py --watch
|
||||
```
|
||||
|
||||
#### 4. ✅ Limpieza de Base de Datos
|
||||
**Ejecutado:**
|
||||
```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%');
|
||||
```
|
||||
|
||||
**Resultado:** 3,093 traducciones marcadas para re-traducción
|
||||
|
||||
#### 5. ✅ Workers Reiniciados
|
||||
```bash
|
||||
docker restart rss2_translator_py rss2_translator_py2 rss2_translator_py3
|
||||
```
|
||||
|
||||
**Estado:** ✅ Todos los workers funcionando con nueva configuración
|
||||
|
||||
## 📊 Resultados Verificados
|
||||
|
||||
### Estado Actual de la Base de Datos
|
||||
```
|
||||
Total traducciones: 1,026,356
|
||||
├─ Completadas (done): 1,022,466
|
||||
├─ Pendientes: 3,713 (incluye las 3,093 marcadas)
|
||||
└─ Errores: 49
|
||||
```
|
||||
|
||||
### Verificación de Calidad (últimos 10 minutos)
|
||||
```
|
||||
Nuevas traducciones repetitivas: 0 ✅
|
||||
```
|
||||
|
||||
## 🔍 Detección de Patrones Repetitivos
|
||||
|
||||
La función `_is_repetitive_output()` detecta:
|
||||
|
||||
1. **Palabras repetidas 4+ veces consecutivas**
|
||||
- Regex: `(\b\w+\b)( \1){3,}`
|
||||
|
||||
2. **Frases de 2 palabras repetidas 3+ veces**
|
||||
- Regex: `(\b\w+ \w+\b)( \1){2,}`
|
||||
|
||||
3. **Patrones específicos conocidos:**
|
||||
- "de la la"
|
||||
- "la línea de la línea"
|
||||
- "de Internet de Internet"
|
||||
- "de la de la"
|
||||
- "en el en el"
|
||||
|
||||
4. **Baja diversidad de vocabulario**
|
||||
- Threshold: < 25% palabras únicas
|
||||
|
||||
## 🚀 Próximos Pasos
|
||||
|
||||
### Automático (Ya en marcha)
|
||||
- ✅ Re-traducción de 3,093 noticias con nueva configuración
|
||||
- ✅ Validación automática de nuevas traducciones
|
||||
- ✅ Rechazo inmediato de outputs repetitivos
|
||||
|
||||
### Manual (Recomendado)
|
||||
1. **Monitorear logs del translation worker:**
|
||||
```bash
|
||||
docker logs -f rss2_translator_py | grep -E "(Rejecting|WARNING|repetitive)"
|
||||
```
|
||||
|
||||
2. **Ejecutar limpieza periódica (semanal):**
|
||||
```bash
|
||||
docker exec rss2_web python3 scripts/clean_repetitive_translations.py
|
||||
```
|
||||
|
||||
3. **Revisar calidad mensualmente:**
|
||||
```bash
|
||||
docker exec rss2_web python3 scripts/monitor_translation_quality.py --hours 720
|
||||
```
|
||||
|
||||
## 📈 Métricas de Éxito
|
||||
|
||||
### Antes
|
||||
- ❌ 3,093 traducciones repetitivas detectadas
|
||||
- ❌ ~0.3% de tasa de error de calidad
|
||||
- ❌ Sin validación automática
|
||||
|
||||
### Después
|
||||
- ✅ 0 nuevas traducciones repetitivas (verificado)
|
||||
- ✅ Validación automática en tiempo real
|
||||
- ✅ Rechazo inmediato de outputs defectuosos
|
||||
- ✅ Re-traducción automática programada
|
||||
|
||||
## 🛠️ Archivos Modificados/Creados
|
||||
|
||||
### Modificados
|
||||
1. `workers/translation_worker.py` - Mejoras en parámetros y validación
|
||||
|
||||
### Creados
|
||||
1. `scripts/clean_repetitive_translations.py` - Limpieza automática
|
||||
2. `scripts/monitor_translation_quality.py` - Monitoreo de calidad
|
||||
3. `docs/TRANSLATION_QUALITY_FIX.md` - Documentación completa
|
||||
|
||||
## 🎓 Lecciones Aprendidas
|
||||
|
||||
### ¿Por qué ocurrió?
|
||||
1. **Repetition penalty insuficiente** (1.2 era muy bajo)
|
||||
2. **N-gram blocking inadecuado** (4-gramas permitían repeticiones de 3 palabras)
|
||||
3. **Sin validación post-traducción**
|
||||
4. **Textos fuente corruptos** de algunos RSS feeds
|
||||
|
||||
### Prevención a futuro
|
||||
1. ✅ Validación automática implementada
|
||||
2. ✅ Parámetros optimizados
|
||||
3. ✅ Scripts de monitoreo disponibles
|
||||
4. ✅ Documentación completa
|
||||
|
||||
## 📞 Soporte
|
||||
|
||||
Si detectas nuevas traducciones repetitivas:
|
||||
|
||||
1. **Verificar logs:**
|
||||
```bash
|
||||
docker logs rss2_translator_py | tail -100
|
||||
```
|
||||
|
||||
2. **Ejecutar limpieza:**
|
||||
```bash
|
||||
docker exec rss2_web python3 scripts/clean_repetitive_translations.py
|
||||
```
|
||||
|
||||
3. **Reiniciar workers si es necesario:**
|
||||
```bash
|
||||
docker restart rss2_translator_py rss2_translator_py2 rss2_translator_py3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Implementado por:** Antigravity AI
|
||||
**Fecha:** 2026-01-28
|
||||
**Estado:** ✅ Completado y Verificado
|
||||
**Impacto:** 3,093 traducciones mejoradas, 0% nuevos errores
|
||||
Loading…
Add table
Add a link
Reference in a new issue