ASM2-Client es el sistema cliente desarrollado por In2AI para la gestión documental, monitorización y analítica del sistema RAG (Retrieval-Augmented Generation).
El proyecto se compone de varios módulos integrados:
- Backend FastAPI: API principal para chat, conectores, autenticación, indexado y métricas.
- Dashboard SPA: Frontend React/TanStack Router servido por Caddy y conectado al backend vía
/api. - Servicios auxiliares: TimescaleDB para métricas, historial de chat y credenciales, Qdrant para vectores, Logto para autenticación y, opcionalmente, Ollama para modelos locales.
Este sistema permite:
- Chat RAG sobre documentos de Google Drive y Dropbox, con filtrado de resultados por los permisos reales de cada usuario.
- Generación de documentos (
pdf,markdown,txt) como herramienta del agente, descargables desde el chat. - Indexado compartido gestionado por administradores, con progreso visible para managers y administradores.
- Protección ante borrados masivos durante el indexado, con alertas y notificaciones.
- Extraer y visualizar métricas de uso (modelos, tokens, latencia, actividad de usuarios).
- Almacenamiento eficiente de series temporales.
- Gestión de usuarios y autenticación segura con roles (
admin,manager,user). - Extracción automática de tópicos de documentos.
| Documento | Contenido |
|---|---|
| DROPBOX_CONNECTOR.md | Modelo de permisos del conector de Dropbox y creación de la app |
| LOGTO_SETUP.md | Configuración de Logto para la SPA y el API de FastAPI |
| SISTEMA_ALERTAS_INDEXADO.md | Guard de borrados masivos, alertas y notificaciones |
| DOKPLOY_HOME_SERVER.md | Despliegue del stack completo en Dokploy |
| GOOGLE_DRIVE_MIGRATION_STATUS.md | Informe de la migración de Google Drive desde la app Streamlit |
| benchmark/README.md | Benchmark RAG: flujo, variables y métricas |
| frontend/README.md | Desarrollo y despliegue del dashboard |
El sistema se despliega mediante contenedores Docker orquestados:
| Servicio | Descripción |
|---|---|
backend |
API FastAPI para chat, métricas, indexado y conectores |
dashboard |
SPA React servida por Caddy, que además hace de proxy /api/* hacia backend:8001 |
qdrant |
Base vectorial para búsqueda híbrida |
timescaledb |
Instancia PostgreSQL + TimescaleDB compartida: datos de la aplicación y base independiente de Logto |
timescaledb-init |
Contenedor efímero para inicialización de esquemas |
logto |
Proveedor de autenticación local opcional |
ollama |
Servidor de modelos locales opcional (--local-model) |
- Docker y Docker Compose (recomendado para despliegue).
- Python 3.10–3.13 y uv (para desarrollo local del backend;
backend/pyproject.tomlfijarequires-python = ">=3.10,<3.14").
- Python 3.13 y uv (para desarrollo local del backend;
backend/pyproject.tomlfijarequires-python = ">=3.13,<3.14").
- Node.js 24 y pnpm 11 (para desarrollo local del dashboard; la imagen de build usa
node:24-alpineypackage.jsonfijapackageManager: pnpm@11.11.0).
El proyecto requiere un archivo .env en la raíz. Copia el archivo de ejemplo y configura según tus necesidades:
cp .env.example .env| Variable | Descripción | Ejemplo |
|---|---|---|
OPENAI_API_KEY |
Clave de API para los modelos de OpenAI | sk-... |
TOGETHER_API_KEY |
Clave de API de Together.ai (solo si se cambia el LLM evaluador del benchmark a together) |
tgp_v1_... |
CLIENT_SECRET |
JSON del cliente OAuth de Google (mismo contenido que secrets/client_secret.json; una sola línea en .env) |
{"web":{...}} o {"installed":{...}} |
GOOGLE_CLIENT_SECRET_FILE |
Ruta opcional al fichero JSON del cliente OAuth cuando se monta en Docker. Por defecto secrets/client_secret.json |
/app/secrets/client_secret.json |
GDRIVE_ROOTS |
IDs de las carpetas raíz de Google Drive que se indexarán (separados por comas) | folder_id_1,folder_id_2 |
GDRIVE_EXCLUDE |
IDs de las carpetas de Google Drive que se excluirán de la indexación (separados por comas) | folder_id_3,folder_id_4 |
DROPBOX_APP_KEY |
App key de la app de Dropbox (user-scoped, sin team scopes) | abc123def456ghi |
DROPBOX_APP_SECRET |
App secret de la misma app de Dropbox | jkl789mno012pqr |
DROPBOX_ROOTS |
Rutas de las carpetas a indexar, relativas a la raíz del espacio de equipo (separadas por comas). Vacío no indexa nada; / indexa todo el espacio de equipo |
Seguridad,Shared/Wiki |
DROPBOX_EXCLUDE |
Rutas de las carpetas de Dropbox que se excluirán de la indexación (separadas por comas) | Seguridad/Drafts |
HF_TOKEN |
Token de Hugging Face opcional usado solo en tiempo de build del backend para acelerar la descarga de modelos (evita el rate limit anónimo). No se usa en runtime. | hf_... |
| Variable | Descripción | Default |
|---|---|---|
PG_HOST |
Host de TimescaleDB. run.sh lo fuerza a timescaledb en --local y --remote; solo se usa fuera de Docker |
timescaledb |
PG_PORT |
Puerto PostgreSQL. run.sh lo fuerza a 5432 en --local y --remote |
5432 |
PG_USER |
Usuario de base de datos | postgres |
PG_PASSWORD |
Contraseña de PostgreSQL usada por backend e init SQL | (vacío) |
PG_DB |
Nombre de la base de datos | tsdb |
| Variable | Descripción | Default |
|---|---|---|
QDRANT_HOST |
Host del servicio Qdrant, resuelto dentro de la red Docker | qdrant |
QDRANT_META_PATH |
Ruta del manifiesto del índice dentro del contenedor (volumen backend-data) |
/app/data/qdrant_meta |
| Variable | Descripción |
|---|---|
LOGTO_APP_ID |
ID de la aplicación SPA en Logto |
LOGTO_ENDPOINT |
Endpoint público de Logto tal como lo alcanza el navegador; se compila en el bundle de la SPA y es el issuer OIDC |
LOGTO_INTERNAL_ENDPOINT |
Opcional: el mismo Logto tal como lo alcanza el contenedor del backend (discovery, JWKS y Management API). Los overrides local y dokploy ya lo fijan en http://logto:3001 |
LOGTO_API_RESOURCE |
Audience del API compartido entre la SPA y la validación estricta en FastAPI |
LOGTO_ADMIN_ENDPOINT |
Endpoint del panel de administración de Logto |
LOGTO_POSTGRES_PASSWORD |
Contraseña del rol logto dentro de la instancia PostgreSQL compartida |
LOGTO_MANAGEMENT_APP_ID |
Client ID opcional de la app M2M para la Management API |
LOGTO_MANAGEMENT_APP_SECRET |
Client secret opcional de la app M2M para la Management API |
LOGTO_MANAGEMENT_API_RESOURCE |
Resource opcional de la Management API de Logto (default https://default.logto.app/api) |
El backend intenta resolver los roles (
admin,manager,user) contra la Management API de Logto. Sin credenciales M2M, conserva los claimsroles/roledel JWT cuando están presentes; si tampoco existen, las rutas protegidas por rol quedan inaccesibles.
| Variable | Descripción | Default |
|---|---|---|
OPENAI_MODEL |
Modelo de OpenAI usado para el chat y para el juez de relevancia de chunks | gpt-4o-mini |
OPENAI_REASONING_EFFORT |
Esfuerzo de razonamiento; solo lo usan los modelos razonadores (gpt-5*, o1/o3/o4) y solo para la respuesta del chat. Con cualquier valor distinto de none, esa llamada pasa a la Responses API |
none |
USE_LOCAL_MODEL |
Usar el servicio ollama en lugar de OpenAI para el chat |
false |
OLLAMA_MODEL |
Modelo del registro oficial de Ollama. Tiene prioridad sobre LOCAL_HF_MODEL |
(vacío) |
LOCAL_HF_MODEL |
Alternativa: repositorio GGUF de Hugging Face | (vacío) |
LOCAL_HF_MODEL_QUANT |
Cuantización del repositorio GGUF anterior | (vacío) |
USE_LOCAL_EMB |
Usar embeddings locales (sentence-transformers) en lugar de los de OpenAI. También es argumento de build del backend, que precarga el modelo en la imagen |
false |
LOCAL_EMB_REPO |
Repositorio del modelo de embeddings local | (vacío) |
HOST_PORT |
Puerto del host donde se publica ollama cuando se usa --local-model |
11434 |
Tracing opcional de las llamadas LLM y del grafo de LangGraph. Si las tres variables están vacías, el backend arranca con el tracing desactivado y no envía datos a Langfuse.
| Variable | Descripción | Ejemplo |
|---|---|---|
LANGFUSE_PUBLIC_KEY |
Clave pública del proyecto (Langfuse UI → Settings → API Keys) | pk-lf-... |
LANGFUSE_SECRET_KEY |
Clave secreta del proyecto | sk-lf-... |
LANGFUSE_BASE_URL |
Host de Langfuse (EU: https://cloud.langfuse.com, US: https://us.cloud.langfuse.com) |
| Variable | Descripción | Default |
|---|---|---|
CORS_ALLOW_ORIGINS |
Orígenes CORS permitidos por el backend FastAPI (lista separada por comas) | http://localhost:3000,http://localhost:3001,http://localhost:5173 |
| Variable | Descripción | Default |
|---|---|---|
PREV_CHUNKS |
Chunks anteriores que se añaden a cada chunk recuperado | 1 |
NEXT_CHUNKS |
Chunks posteriores que se añaden a cada chunk recuperado | 2 |
LONG_CONTEXT |
Habilitar el pipeline de contexto largo sobre los ficheros relevantes | false |
LONG_CONTEXT_IMGS |
Analizar también las imágenes de los ficheros en contexto largo | false |
LONG_CONTEXT_BEFORE_FILTER |
Analizar los ficheros relevantes antes de filtrar chunks. Mejor razonamiento, peor rendimiento | false |
| Variable | Descripción | Default |
|---|---|---|
CALCULATE_TOPICS |
Habilitar extracción de tópicos (True/False) |
False |
TOPIC_MIN_SIZE |
Mínimo de chunks para extraer tópicos | 20000 |
TOPIC_RESOLUTION |
Resolución de detección (menor = más grueso) | 0.0125 |
TOPIC_MIN_CONTRIB |
Fracción mínima de representación del tópico | 0.3 |
MIN_COMMUNITY_DOCS |
Mínimo de documentos que debe abarcar un tópico | 2 |
MIN_COMMUNITY_SIZE |
Mínimo de chunks para que un tópico sea válido | 300 |
MAX_TOPICS |
Máximo de tópicos; si se detectan más, se conservan los más representativos | 300 |
Para el desarrollo del frontend fuera de Docker, usa .env.local en la raíz (el envDir configurado en Vite) con la URL del backend y el endpoint público de Logto.
frontend/vite.config.ts lee los nombres sin prefijo VITE_ (LOGTO_ENDPOINT, LOGTO_APP_ID, LOGTO_API_RESOURCE, BACKEND_URL) y los inyecta en el bundle como import.meta.env.VITE_*. Por eso los archivos Docker Compose pasan LOGTO_* como build args del dashboard.
Nota: Para Google Drive, el backend puede leer el JSON del cliente desde la variable de entorno
CLIENT_SECRETen.env, o usar el archivosecrets/client_secret.jsonmontado en el contenedor medianteGOOGLE_CLIENT_SECRET_FILE.Nota: Dropbox usa una app user-scoped: cada usuario conecta su propia cuenta y no hace falta ser administrador del equipo de Dropbox. No marques ningún team scope en la App Console, o Dropbox exigirá un administrador al autorizar. El funcionamiento del conector y los pasos para crear la app se documentan en DROPBOX_CONNECTOR.md.
| Archivo | Descripción |
|---|---|
docker-compose.yml |
Stack base (backend, dashboard, qdrant) |
docker-compose.timescaledb.yml |
Override con TimescaleDB local (timescaledb, timescaledb-init), aplicado siempre en --local y --remote |
docker-compose.local.yml |
Override para Logto local (logto) publicado en localhost |
docker-compose.gpu.yml |
Override para habilitar GPU NVIDIA en backend |
docker-compose.gpu-amd.yml |
Override para habilitar GPU AMD (ROCm) en backend, usando Dockerfile.rocm |
docker-compose.qdrant-nvidia.yml |
Override para Qdrant con GPU NVIDIA |
docker-compose.qdrant-amd.yml |
Override para Qdrant con GPU AMD (ROCm) |
docker-compose.ollama.yml |
Servicio ollama para modelos locales (CPU) |
docker-compose.ollama-nvidia.yml |
Override de ollama con GPU NVIDIA |
docker-compose.ollama-amd.yml |
Override de ollama con GPU AMD (ROCm) |
docker-compose.bench.yml |
Stack autocontenido para benchmark: reemplaza el web server del backend por el evaluador (benchmark.py) |
docker-compose.dokploy.yml |
Stack de producción para Dokploy, con GPU NVIDIA y red dokploy-network. Ver DOKPLOY_HOME_SERVER.md |
Con ./run.sh up (--local o --remote):
| Servicio | Publicación |
|---|---|
dashboard |
3001 en todas las interfaces → http://localhost:3001 |
backend |
127.0.0.1:8001 (solo el host; el tráfico normal entra por Caddy en /api) |
timescaledb |
127.0.0.1:5432 (para inspeccionar la base desde el host) |
logto |
3011 (endpoint) y 3002 (consola de administración), solo en --local |
qdrant |
No publicado: solo accesible desde la red Docker |
ollama |
${HOST_PORT:-11434}, solo con --local-model |
Levanta backend, SPA, Qdrant, TimescaleDB y Logto local con un solo comando:
./run.sh upEsto iniciará:
- Dashboard: http://localhost:3001
- Logto: http://localhost:3011
- Consola de Logto: http://localhost:3002
Si Logto ya está desplegado fuera de Docker, ejecuta backend, dashboard, qdrant y TimescaleDB local, sin logto:
./run.sh up --remoteTimescaleDB no se toma del despliegue remoto: este modo levanta su propio contenedor
timescaledb igual que --local, con los datos en el volumen timescaledb-data.
Requisitos previos:
-
Actualiza tu archivo
.envcon las URLs y credenciales remotas de Logto. Las variablesPG_*describen la base local:PG_HOSTyPG_PORTlos sobrescribe el override atimescaledb:5432, mientras quePG_USER,PG_PASSWORDyPG_DBcrean la base local.PG_USER=postgres PG_PASSWORD=tu_contraseña PG_DB=tsdb LOGTO_ENDPOINT=https://tu-logto-remoto LOGTO_APP_ID=tu_spa_app_id LOGTO_API_RESOURCE=https://tu-api-resource
Esto iniciará:
- Dashboard: http://localhost:3001
El servicio backend puede utilizar GPU para acelerar el procesamiento local. Sin argumentos, --gpu mantiene la compatibilidad anterior y selecciona NVIDIA.
./run.sh up --gpu
# Equivalente explícito:
./run.sh up --gpu nvidia
# Con servicios remotos:
./run.sh up --remote --gpu nvidiaRequisitos NVIDIA:
- Drivers NVIDIA instalados
- nvidia-container-toolkit
./run.sh up --gpu amd
# AMD tanto en backend como en Qdrant:
./run.sh up --gpu amd --qdrant amdEl backend AMD utiliza por defecto la imagen validada rocm/pytorch:rocm7.2_ubuntu24.04_py3.13_pytorch_release_2.10.0. Se puede cambiar con ROCM_PYTORCH_IMAGE si el modelo de GPU requiere otra versión compatible.
Requisitos AMD:
- Linux y una GPU incluida en la matriz de compatibilidad ROCm
- Drivers AMD ROCm compatibles instalados
- Dispositivos
/dev/kfdy/dev/driaccesibles - Usuario en los grupos
videoyrender
run.sh detecta automáticamente los GID de esos grupos. Si se invoca Docker Compose directamente, expórtalos antes:
export VIDEO_GID="$(getent group video | cut -d: -f3)"
export RENDER_GID="$(getent group render | cut -d: -f3)"La GPU del backend acelera principalmente el reranker y los embeddings locales (
USE_LOCAL_EMB=true). Las llamadas a modelos OpenAI se ejecutan de forma remota.
Qdrant soporta aceleración GPU para indexación vectorial. Por defecto, se usa la versión CPU (qdrant/qdrant:v1.16.2). Puedes habilitar GPU utilizando los archivos de override correspondientes:
./run.sh up --qdrant nvidia
# Combinando con GPU del backend:
./run.sh up --gpu --qdrant nvidiaRequisitos NVIDIA:
- Drivers NVIDIA instalados
- nvidia-container-toolkit
./run.sh up --qdrant amdRequisitos AMD:
- Drivers AMD ROCm instalados
- Dispositivos
/dev/kfdy/dev/driaccesibles - Usuario en los grupos
videoyrender
El archivo base docker-compose.ollama.yml contiene la configuración compartida. Selecciona el acelerador con el argumento opcional de --local-model:
Configura preferentemente un modelo del registro oficial de Ollama en .env:
USE_LOCAL_MODEL=true
OLLAMA_MODEL=qwen3.5:27b
LOCAL_HF_MODEL=
LOCAL_HF_MODEL_QUANT=OLLAMA_MODEL tiene prioridad. LOCAL_HF_MODEL y LOCAL_HF_MODEL_QUANT se mantienen como alternativa para repositorios GGUF alojados en Hugging Face. El backend se conecta al servicio en http://ollama:11434/v1 mediante la API compatible con OpenAI.
# CPU
./run.sh up --local-model cpu
# GPU NVIDIA
./run.sh up --local-model nvidia
# GPU AMD con ROCm
./run.sh up --local-model amd
# Ollama y Qdrant sobre AMD
./run.sh up --local-model amd --qdrant amdLos overrides específicos son docker-compose.ollama-nvidia.yml y docker-compose.ollama-amd.yml. Si no se indica un acelerador, --local-model usa CPU.
Para AMD se requieren ROCm y acceso a /dev/kfd y /dev/dri. Para NVIDIA se requieren los drivers y nvidia-container-toolkit.
cd backend
# Crear el entorno del proyecto e instalar dependencias
uv sync
# Ejecutar el backend
uv run uvicorn server:app --host 0.0.0.0 --port 8001Las pruebas del backend viven en backend/tests/ e importan como src.*, así que se ejecutan desde backend/:
cd backend
pytest
pytestno está declarado enbackend/pyproject.toml; instálalo en el entorno antes de ejecutar las pruebas.
cd frontend
# Instalar dependencias
pnpm install
# Iniciar servidor de desarrollo
pnpm devEl dashboard estará disponible en http://localhost:3001.
El frontend usa Vite+ (vp) a través de los scripts de package.json:
pnpm check # formato, lint y comprobación de tipos
pnpm test # pruebas unitarias (Vitest)
pnpm test:e2e # pruebas end-to-end (Playwright)
pnpm build # build de producción en dist/El modo --bench levanta un stack autocontenido (backend, dashboard, qdrant, sin TimescaleDB ni Logto) sustituyendo el servidor web del backend por el script de evaluación benchmark.py, que mide la calidad del pipeline RAG con métricas de RAGAS 0.4.3 (context_precision, context_recall, answer_relevancy, faithfulness) además de los tiempos de cada evaluación (consulta RAG + cálculo de métricas) y de cada lote.
El funcionamiento del benchmark (flujo de ejecución, variables parametrizables, métricas y ficheros de salida) se documenta en detalle en el README del benchmark.
Nota: La versión de RAGAS (
0.4.3) está fijada enbackend/uv.lock(specifierragas>=0.4.3). El benchmark depende de la APIragas.metrics.collectionsde esa versión.
./run.sh up --benchRequisitos previos:
-
OPENAI_API_KEYen.env. Es obligatoria en cualquier caso: el LLM evaluador por defecto esgpt-4o-minide OpenAI (EVAL_LLM_PROVIDER = "openai"enbackend/benchmark.py) y los embeddings deanswer_relevancyusan siempretext-embedding-3-smallde OpenAI. -
TOGETHER_API_KEYen.envsolo si cambiasEVAL_LLM_PROVIDERa"together", que usameta-llama/Llama-3.3-70B-Instruct-Turbo. -
Un dataset de preguntas/respuestas en
benchmark/data/(por defectodataset_asm2.csv). -
Una base PostgreSQL alcanzable.
benchmark.pyconstruye el pool al importarse (get_pg_pool()usaminconn=1, que abre la conexión de inmediato), perodocker-compose.bench.ymlno define el serviciotimescaledbyrun.sh --benchno aplica el override. ApuntaPG_HOST/PG_PORTa una base accesible, o levanta el stack a mano añadiendo el override:docker compose -f docker-compose.bench.yml -f docker-compose.timescaledb.yml up --build
Resultados: se escriben en benchmark/results/ (montado como volumen), entre otros:
rag_evaluation_results_<fuentes>_attempt_<n>.csv— resultados de métricas por pregunta.query_timings_<fuentes>_attempt_<n>.csv— tiempo total por pregunta (consulta + métricas).batch_timings_<fuentes>_attempt_<n>.csv— tiempo total por lote.rag_evaluation_summary_<fuentes>.csv— resumen de métricas y tiempos por ejecución.
Por defecto realiza 1 ejecución de evaluación (NUM_EVALUATIONS).
ASM2-client/
├── backend/ # Backend FastAPI, conectores, grafo LangGraph y pruebas
│ ├── server.py # Endpoints HTTP y tareas periódicas
│ ├── benchmark.py # Evaluador RAG usado por --bench
│ ├── graph/ # Grafo LangGraph: nodos, estado y herramientas del agente
│ ├── src/ # Conectores, indexado, métricas, generación y config
│ └── tests/ # Pruebas de backend
├── frontend/ # SPA React/TanStack Router servida por Caddy
├── sql/ # Inicialización de TimescaleDB y arranque de Logto
├── secrets/ # Credenciales y ficheros sensibles
├── ollama/ # Imagen del servidor de modelos locales
├── img/ # Imágenes y assets
├── benchmark/ # Datasets QA de entrada, generación y resultados del benchmark
├── docker-compose*.yml # Stack base y overrides (ver tabla más arriba)
├── .env.example # Plantilla de variables de entorno
└── run.sh # Wrapper de modos de ejecución Docker
Este proyecto ha sido financiado por el Instituto Galego de Promoción Económica (IGAPE) y la Xunta de Galicia en el marco del Plan de Recuperación, Transformación y Resiliencia, financiado por la Unión Europea – NextGenerationEU, dentro del procedimiento IG408M (“Ayudas para el desarrollo tecnológico y la innovación mediante el uso de la Inteligencia Artificial – IA360”).
