1. Introducción y Arquitectura del Sistema
Scider es una solución avanzada diseñada para optimizar la lectura y el análisis de artículos de investigación. Este documento detalla la arquitectura técnica para la fase Beta, integrando capacidades de inteligencia artificial y visualización de datos compleja.
El sistema emplea una arquitectura desacoplada basada en el modelo Cliente-Servidor (B/S). A continuación se describe el flujo de datos principle:
- Capa de Presentación: Aplicación SPA desarrollada en Vue 3 que gestiona la interactividad y randerizado de documentos.
- Capa de Orquestación: Nginx actúa como proxy inverso y servidor de activos estáticos.
- Capa de Aplicación: API RESTful construida con FastAPI para lógica de negocio de alto rendimiento.
- Capa de Procesamiento Asíncrono: Celery gestiona tareas intensivas como la extracción de texto y generación de grafos mediante LLM.
- Capa de Persistencia: PostgreSQL para datos relacionales, Redis para caché y pgvector para búsquedas semánticas.
2. Ecosistema Tecnológico
| Categoría | Tecnología | Propósito |
|---|---|---|
| Frontend | Vue 3 + TypeScript | Framework reactivo y tipado estático. |
| Visualización | D3.js | Manipulación dinámica del grafo de conocimiento. |
| Renderizado PDF | pdf.js (Community Edition) | Gestión de capas de anotación y búsqueda. |
| Backend | Python 3.12 + FastAPI | Servicios asíncronos de alto throughput. |
| ORM / Migraciones | SQLAlchemy 2.0 / Alembic | Modelado de datos y control de versiones de BD. |
| Búsqueda Vectorial | pgvector (PostgreSQL) | Almacenamiento y recuperación de embeddings para RAG. |
3. Especificaciones del Frontend
3.1 Gestión del Grafo Dinámico (D3.js)
Se ha implementado un motor de simulación de fuerzas para representar las relaciones entre artículos científicos. A diferencia de soluciones estáticas, el sistema permite la edición en tiempo real de los nodos.
// Implementación lógica para la actualización del grafo
const sincronizarVisualizacion = (datosActualizados) => {
const v_nodes = contenedor.selectAll('.punto-interes')
.data(datosActualizados.nodes, n => n.uuid);
const v_edges = contenedor.selectAll('.enlace-relacion')
.data(datosActualizados.links, l => `${l.origen}-${l.destino}`);
// Eliminación de elementos obsoletos
v_nodes.exit().transition().duration(250).style('opacity', 0).remove();
v_edges.exit().remove();
// Inserción de nuevos elementos
const nuevosNodos = v_nodes.enter().append('circle')
.attr('class', 'punto-interes')
.call(comportamientoArrastre);
// Reinicio del motor físico
simulacionFisica.nodes(datosActualizados.nodes);
simulacionFisica.force('link').links(datosActualizados.links);
simulacionFisica.alphaTarget(0.2).restart();
};
3.2 Renderizado y Anotaciones en PDF
La visualización de documentos se basa en pdf.js, configurada para soporte de scroll continuo. Las anotaciones se almacenan como coordenadas normalizadas (porcentajes respecto al ancho/alto de la página) para garantizar la compatibilidad entre diferentes resoluciones de pantalla.
4. Arquitectura del Backend y Servicios de IA
4.1 Flujo de Generación Aumentada por Recuperación (RAG)
Para el sistema de preguntas y respuestas sobre artículos, se sigue este proceso:
- Segmentación: El PDF se divide en fragmentos de 500 tokens con un solapamiento de 50 tokens.
- Vectorización: Se generan embeddings mediante modelos de OpenAI o locales y se almacenan en
pgvector. - Recuperación: Al recibir una consulta, se realiza una búsqueda de similitud de coseno para extraer los 5 fragmentos más relevantes.
- Inferencia: Se construye un prompt contextual que incluye los fragmentos recuperados y la pregunta del usuario para que el LLM genere la respuesta.
4.2 Modelado de Datos (Beta)
Ejemplo de estructura para la gestión de anotaciones técnicas y configuración de modelos:
from sqlalchemy import Column, JSON, String, Text, ForeignKey
from sqlalchemy.dialects.postgresql import UUID
import uuid
class RegistroAnotacion(Base):
__tablename__ = "anotaciones_cientificas"
id_interno = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
propietario_id = Column(UUID(as_uuid=True), ForeignKey("usuario.id"))
documento_id = Column(UUID(as_uuid=True), ForeignKey("articulo.id"))
cuerpo_nota = Column(Text) # Soporta Markdown
metadatos_posicion = Column(JSON) # { "pagina": 1, "rect": [x1, y1, x2, y2] }
class CredencialIA(Base):
__tablename__ = "config_proveedor_llm"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
user_id = Column(UUID(as_uuid=True), ForeignKey("usuario.id"))
proveedor = Column(String(60)) # 'anthropic', 'openai', 'ollama'
clave_encriptada = Column(String(512))
endpoint_personalizado = Column(String(255), nullable=True)
5. Estrategia de Pruebas y Calidad
Se aplica una pirámide de pruebas automatizada para asegurar la estabilidad del sistema:
- Pruebas Unitarias: Vitest para componentes Vue y pytest para lógica de servicios en Python.
- Pruebas de Integración: Validación de flujos de API mediante
httpxy bases de datos de prueba efímeras. - Pruebas E2E: Playwright para simular el viaje del usuario desde la carga del PDF hasta la generación del grafo.
- Pruebas de Carga: Uso de Locust para validar la respuesta del sistema bajo una carga de 100 usuarios concurrentes en tareas de chat.
6. Despliegue y Seguridad
6.1 Orquestación con Docker
El despliegue se gestiona mediente contenedores, asegurando la paridad entre entornos de desarrollo y producción.
# Fragmento de configuración de servicios
services:
scider-db:
image: pgvector/pgvector:pg16
environment:
POSTGRES_DB: scider_prod
volumes:
- datos_sql:/var/lib/postgresql/data
scider-api:
build: ./servidor
env_file: .env.prod
command: gunicorn -k uvicorn.workers.UvicornWorker app.main:app
depends_on:
- scider-db
- scider-cache
6.2 Medidas de Seguridad
- Autenticación: Implementación de JWT con rotación de Refresh Tokens almacenados en cookies
HttpOnly. - Protección de Datos: Las claves de API de los usuarios se cifran en reposo utilizando el algoritmo AES-256 (Fernet).
- Rate Limiting: Restricción de peticiones por IP en puntos críticos como el login y la carga de documentos para prevenir abusos de recursos de IA.
7. Optimización de Rendimiento
Para manejar grandes volúmenes de datos, se han implementado las siguientes estrategias:
- Virtual Scrolling: Utilizado en la lista de notas y referencias bibliográficas para mantener el DOM ligero.
- Caché Semántica: Los resultados de consultas comunes al LLM se almacenan en Redis si el contexto del documento no ha cambiado.
- Procesamiento en Paralelo: La extracción de entidades de múltiples PDFs se distribuye en workers de Celery con prioridad configurable.