rag-service/docs/OPERATIVA.md

192 lines
16 KiB
Markdown

# Operativa del servicio RAG
**Modulo:** RAG
**Ultima actualizacion:** 2026-09-24
**Version:** 1.7
---
Este documento registra los hechos operativos del servicio RAG: la configuracion vigente en EasyPanel y la salida por etapas del OCR. No contiene secretos: las credenciales reales viven unicamente en EasyPanel.
## Estado verificado (2026-09-24)
- Preflight R5 de solo lectura (2026-09-23): el contenedor RAG usa la base `db_rag` con el rol `usr_rag`. `rag_schema_migrations` contiene `001_knowledge_lifecycle.sql`, `002_ocr_review.sql` y `003_ocr_recovery_audit.sql`, todos con checksums coincidentes con el commit publicado; la unica migracion pendiente es `004_ocr_quality_diagnostics.sql`.
- Despliegue R5 verificado (2026-09-23): RAG y OCR exponen `version=0.2.1` y `revision=0654ce5`; sus etiquetas OCI coinciden. Digests: RAG `sha256:9a22730406c09c6b773891d8953f451e6263bf9fb0033188f4479f0b93ec8bbf`, OCR `sha256:6725991d3893a4f16ae6c75f28e9dd59b051bdbc344b28d79f1292224ddceda1`. `004_ocr_quality_diagnostics.sql` esta aplicada; OCR esta `live/ready`, con cola vacia, worker/sweeper operativos y almacenamiento disponible; RAG confirma PostgreSQL, Qdrant y reconciliador sanos.
- Despliegue vigente verificado (2026-09-24): RAG y OCR exponen `version=0.2.1` y `revision=bfb2d48`; sus etiquetas OCI coinciden, las migraciones `001-004` conservan sus checksums y PostgreSQL, Qdrant, reconciliador, OCR, cola y almacenamiento estan sanos.
- Candidata R5 historica: FacturaTech v8 `f4fd1502-1d4d-454f-bbfb-4c3b6820ee97` completo 25/25 paginas OCR y su revision visual, pero el 2026-09-24 fue rechazada por decision del usuario sin aprobarse, indexarse ni activarse. La version activa `3fc78163-9cfb-4979-985c-1520a63327b0` sigue intacta.
- Limpieza pendiente: la candidata sintetica blank `6011a4eb-3e42-4ea4-a7da-a3e930cc60ed` permanece `review_required` y la sintetica valida `c4646668-30e4-409e-800d-23a8ee465a42` permanece `purging`. Los dos defectos que bloqueaban rechazo y purga estan corregidos y validados localmente; no reintentar la limpieza productiva hasta que el usuario despliegue la correccion.
- Diagnostico de precision (2026-09-24): los PNG productivos verificados de las paginas 8, 15 y 16 muestran los ceros correctos, mientras `ocr-result.json` ya contiene las tres confusiones `o/0` con confianza alta y la candidata las conserva sin cambios. Se descartan renderizado, reconstruccion y RAG como origen. El OCR queda aceptado para esta fase como servicio best-effort autonomo; la mejora de precision, la consola humana y un posible revisor visual por IA quedan diferidos. Esta aceptacion no aprueba ni activa v8.
- `KNOWLEDGE_LIFECYCLE_ENFORCED=true`: el ciclo de vida del conocimiento esta activo; no queda pendiente ninguna activacion.
- `OCR_INGEST_ENABLED=true`: la ingesta OCR esta habilitada en el RAG desplegado.
- `OCR_SERVICE_URL`: configurada hacia el servicio privado `ocr-service` de la red interna.
- `OCR_INTERNAL_TOKEN`: secreto compartido configurado en EasyPanel (RAG y servicio OCR); su valor nunca se documenta.
- Volumen duradero del RAG en `/data/ingestions`: conserva originales y artefactos de revision.
- Volumen transitorio del OCR en `/data/jobs`: cola SQLite y trabajos en curso.
- La respuesta publica sin token de una ruta OCR protegida es `401`, lo que confirma que el proceso desplegado tiene OCR habilitado. Con OCR deshabilitado, la misma ruta responderia `404` antes de autenticar.
- El estado mostrado en EasyPanel debe contrastarse con su entorno persistido y con el entorno del contenedor en ejecucion; anteriormente la UI mostro valores distintos por variables duplicadas y desfasadas.
- `GET /health` del RAG confirma PostgreSQL, Qdrant y reconciliador sanos. OCR responde internamente `live=ok` y `ready=true`, con cola vacia, worker/sweeper operativos y version/revision `0.2.1`/`bfb2d48`.
- La migracion `003_ocr_recovery_audit.sql` esta aplicada y su tabla de auditoria existe.
- Las rutas autenticadas de revision y recuperacion devuelven errores estructurados y seguros para una candidata inexistente: `404`, `OCR_CANDIDATE_NOT_FOUND` y accion `verify_version_id`.
- La candidata OCR heredada de FacturaTech v4 fue cerrada como `failed` el 2026-09-20 mediante recuperacion administrativa auditada tras confirmar `OCR_ARTIFACT_UNAVAILABLE`. No fue indexada ni activada; la fuente conserva su version activa y no tiene candidatas OCR bloqueantes.
- Las imagenes en ejecucion tienen ambas etiquetas OCI `org.opencontainers.image.revision=bfb2d48`: EasyPanel recibe `BUILD_REVISION` durante el build. La identidad se verifica mediante las etiquetas y el health; los digests registrados de entregas anteriores son evidencia historica, no la identidad de la revision vigente.
## Version de despliegue
- La version correcta de cada despliegue es la declarada en el fichero raiz `VERSION` del commit que se publica (para la entrega actual, `0.2.1`). No reutilizar numeros escritos en entradas historicas de este documento.
- Ambas imagenes publican `org.opencontainers.image.version` y `org.opencontainers.image.revision`; las respuestas de salud muestran `version` y `revision`.
- EasyPanel debe construir ambas imagenes con la version de `VERSION` y con `BUILD_REVISION=<commit desplegado>`.
- Por decision del usuario, RAG y OCR se despliegan juntos con `OCR_INGEST_ENABLED=true`; no se desactiva preventivamente ni se introduce una ventana sin ingestas por protocolo.
- `OCR_INGEST_ENABLED=false` y el despliegue por etapas se reservan para responder a un fallo real cuando sus sintomas y la evidencia indiquen que aislar un servicio ayudara al diagnostico o a la recuperacion.
## Lista rapida en EasyPanel
1. Rotar las credenciales expuestas (ver seccion siguiente); la rotacion sigue pendiente.
2. Comprobar que las variables coinciden con la tabla de configuracion actual.
3. Mantener `OCR_INGEST_ENABLED=true`; cambiarlo a `false` solo ante un fallo real que justifique aislar el flujo OCR.
4. Pulsar `Deploy` en EasyPanel despues de cada cambio principal.
5. Verificar `GET /health` del RAG y las rutas internas de salud del OCR tras cada deploy.
6. Registrar el digest de cada imagen y confirmar que `version` y `revision` coinciden con la entrega desplegada.
## Accion de seguridad urgente: rotar credenciales
Credenciales reales quedaron expuestas en una conversacion de chat. Hay que rotarlas:
- La credencial de OpenRouter (variable `EMBEDDING_API_KEY`; y `ANSWER_API_KEY` si tiene valor propio).
- La contrasena y la URL de PostgreSQL (variable `POSTGRES_URL`).
- El token administrativo del ciclo de vida (variable `LIFECYCLE_ADMIN_TOKEN`).
Como hacerlo:
- Generar valores nuevos y actualizarlos unicamente en EasyPanel.
- Este documento no afirma que la rotacion ya se haya realizado.
- Nunca pegar valores de credenciales en chat, documentacion, historial ni ficheros versionados.
## Como se despliega
- Tras cada cambio principal (variables de entorno o codigo nuevo subido por Git), el usuario pulsa el boton `Deploy` de EasyPanel.
- No existe ninguna accion automatizada documentada sobre EasyPanel: ni scripts, ni comandos de panel, ni webhooks. El deploy siempre lo dispara una persona desde el panel.
- Verificacion minima tras cada deploy: `GET /health` del RAG.
## Acceso remoto a VPS2
- Entorno: `VPS2 PRODUCCION`.
- Host SSH: `162.19.152.15`, puerto `22`, usuario `debian`.
- Metodo actual: conexion SSH directa con password mediante `SSH_ASKPASS` temporal; nunca reenviar la contrasena por entrada estandar ni mostrarla en la salida.
- Fuente canonica del procedimiento y de la credencial vigente: `Empresa/Servidores/VPS2/Vps2_despliegue_apps/instrucciones_montado_y_despliegue_apps_vps2_easypanel.md`.
- Verificacion de acceso: 2026-09-23, conexion directa correcta como `debian` al host `vps-4863ac0d`.
- Antes de cualquier accion remota, indicar expresamente `VPS2 PRODUCCION`; las comprobaciones que no contacten ese host se etiquetan `LOCAL`.
- La contrasena no se copia en este repositorio, historial ni chat. Si cambia, actualizarla solo en la fuente canonica.
## Configuracion actual (valores no secretos)
Valores vigentes confirmados en produccion (2026-09-17):
| Variable | Valor actual |
|---|---|
| `NODE_ENV` | `production` |
| `PORT` | `80` |
| `QDRANT_URL` | `http://qdrant:6333` |
| `QDRANT_API_KEY` | vacia (sin valor) |
| `QDRANT_COLLECTION` | `rag_chunks` |
| `EMBEDDING_PROVIDER` | `openrouter` |
| `EMBEDDING_MODEL` | `qwen/qwen3-embedding-8b` |
| `EMBEDDING_BASE_URL` | `https://openrouter.ai/api/v1` |
| `ANSWER_PROVIDER` | `openrouter` |
| `ANSWER_MODEL` | `openai/gpt-4.1-mini` |
| `ANSWER_BASE_URL` | `https://openrouter.ai/api/v1` |
| `POSTGRES_SSL` | `false` |
| `KNOWLEDGE_LIFECYCLE_ENFORCED` | `true` |
| `INGEST_WRITES_ENABLED` | `true` |
| `OCR_INGEST_ENABLED` | `true` |
| `OCR_SERVICE_URL` | configurada hacia el servicio privado `ocr-service` |
Notas de comportamiento (derivadas del codigo, sin secretos):
- Si `ANSWER_API_KEY` no se define, el codigo reutiliza `EMBEDDING_API_KEY`.
- Si `POSTGRES_URL` no se define, el codigo prueba `DATABASE_URL`.
- Si `OCR_INGEST_ENABLED` no existe, el codigo tambien la toma como `false`; por eso debe existir explicitamente con valor `true` en el entorno persistido de EasyPanel.
- Un cambio en `KNOWLEDGE_LIFECYCLE_ENFORCED`, `INGEST_WRITES_ENABLED` o `OCR_INGEST_ENABLED` se hace siempre en EasyPanel y se registra en este documento.
## Variables con secretos
Estas variables contienen credenciales reales. Aqui solo se registran sus nombres:
| Variable | Estado |
|---|---|
| `EMBEDDING_API_KEY` | Configurada en EasyPanel; nunca copiarla en documentacion. |
| `ANSWER_API_KEY` | Configurada en EasyPanel; nunca copiarla en documentacion. |
| `POSTGRES_URL` | Configurada en EasyPanel; nunca copiarla en documentacion. |
| `LIFECYCLE_ADMIN_TOKEN` | Configurada en EasyPanel; nunca copiarla en documentacion. |
| `OCR_INTERNAL_TOKEN` | Configurada en EasyPanel, compartida con el servicio OCR y enviada como `Authorization: Bearer`; nunca copiarla en documentacion. |
## Salida por etapas del OCR
El OCR es un servicio privado e independiente. El RAG solo lo llama si `OCR_INGEST_ENABLED` esta activo; con el flag apagado, la ingesta sigue el flujo nativo actual, sin cambios. Hoy el flag esta en `true` en el RAG desplegado.
### Servicio OCR privado (referencia de configuracion)
- Contenedor `ocr-service`, imagen Python CPU con PaddleOCR `3.4.0` y PaddlePaddle `3.2.2`.
- Una sola replica y un solo worker; limites del servicio: maximo 3 CPU y 5 GiB de RAM.
- Modelos descargados durante el build, nunca en el arranque.
- Solo red interna del proyecto (`easypanel-ia_servicios`), sin dominio publico.
- `OCR_SERVICE_URL` apunta al servicio OCR interno por el puerto `8000`; el valor por defecto del codigo es `http://ocr-service:8000`, valido para un servicio llamado `ocr-service` en la red interna.
- Limites fijos (no configurables por variables): cola de 3 trabajos, concurrencia 1, render a 200 DPI, maximo 50 MiB por PDF y 100 paginas por trabajo.
- Cada trabajo dispone de un lease y un limite total de 15 minutos. Solo se recupera una vez; la segunda interrupcion o el timeout son terminales.
- La readiness es falsa hasta que modelo, worker, sweeper y almacenamiento estan operativos; expone conteos por estado, recuperaciones y ultima limpieza sin incluir secretos. El healthcheck de arranque concede 90 segundos.
### Volumenes
| Volumen | Ruta | Papel |
|---|---|---|
| Duradero del RAG | `/data/ingestions` | Originales y artefactos de revision conservados. |
| Transitorio del OCR | `/data/jobs` | Cola SQLite, PDFs y resultados en curso. |
### Limpieza transitoria del OCR
- Las filas transitorias, el PDF y los resultados del OCR se eliminan tras la transferencia duradera al RAG, cuando el resultado es aceptado.
- Si esa transferencia no llega, un sweeper ejecutado al arrancar y cada 15 minutos expira los trabajos a las 24 horas, hace checkpoint WAL y vacuum incremental.
- Antes de admitir el PDF y antes de publicar cada PNG, OCR limita su uso al menor entre 10 % del filesystem y 2 GiB, reservando libre el mayor entre 10 % y 2 GiB. La presion falla cerrada como `OCR_STORAGE_PRESSURE`.
- La limpieza transitoria nunca toca los artefactos duraderos del RAG ni el corpus activo.
- RAG incorpora desde la revision `bfb2d48` un scheduler durable basado en `next_attempt_at`, con backoff de 2 a 15 segundos y un unico temporizador. El reconciliador de `300000` ms permanece activo como fallback global.
### Verificacion posterior al despliegue (cuando cambian ambos servicios)
1. Publicar el codigo en Git `main` y desplegar juntos OCR y RAG con la version indicada en `VERSION` y `OCR_INGEST_ENABLED=true`.
2. Verificar el OCR internamente: `GET /health/live` y `GET /health/ready` deben responder con el servicio preparado.
3. Verificar `GET /health` del RAG: PostgreSQL, Qdrant y reconciliador deben estar correctos.
4. Comprobar una ruta OCR protegida sin token. Un `401 Lifecycle admin token is required` confirma que OCR esta habilitado y que la autenticacion administrativa permanece protegida; no es un error que requiera correccion.
5. Contrastar `OCR_INGEST_ENABLED=true` en el entorno persistido de EasyPanel y en el proceso del contenedor si la UI no coincide con el comportamiento efectivo.
6. Consultar con token administrativo una version inexistente en revision y recuperacion: ambas deben devolver un error estructurado `404 OCR_CANDIDATE_NOT_FOUND` sin modificar datos.
7. Confirmar que `rag_schema_migrations` contiene `003_ocr_recovery_audit.sql` y que existe `rag_ocr_recovery_audit`.
8. Revisar los digests y confirmar que las etiquetas OCI y las respuestas de salud muestran `0.2.1` y el commit desplegado, no `unknown`.
9. Tras completar estas comprobaciones, crear una candidata FacturaTech no activa. No aprobar, indexar ni activar hasta presentar la evidencia al usuario y recibir autorizacion explicita.
### Rollback de emergencia
- Ante un fallo cuyos sintomas indiquen que conviene aislar OCR: poner `OCR_INGEST_ENABLED=false`, desplegar el RAG y conservar la version activa actual del corpus. No aplicar este paso automaticamente a fallos no relacionados.
- El sistema es fail-closed: un fallo del OCR deja intacta la version activa anterior; no hay activacion parcial.
- Las paginas con OCR no se activan solas; pasan por el contrato de revision en estado `review_required`. En la fase autonoma actual esa revision puede ejecutarla el agente; una futura consola humana reutilizara el mismo contrato.
- Desactivar el OCR no borra el corpus activo ni exige reingesta.
- Si se revierte a `0.2.0`, la migracion aditiva `004_ocr_quality_diagnostics.sql` permanece aplicada. No se eliminan sus columnas ni su fila de historial; antes de reabrir trafico se verifica que RAG y OCR `0.2.0` arrancan y pasan health con ese esquema extendido.
### Limites opcionales del RAG (valores por defecto del codigo)
| Variable | Valor por defecto |
|---|---|
| `OCR_MAX_UPLOAD_BYTES` | `52428800` (50 MiB) |
| `OCR_MAX_PAGES` | `100` |
| `LIFECYCLE_RECONCILE_INTERVAL_MS` | `300000` |
| `LIFECYCLE_INDEXING_STALE_TIMEOUT_MS` | `1800000` |
| `QDRANT_LOGS_COLLECTION` | `rag_eval_logs` |
## Verificaciones habituales
- `GET /health` del RAG: estado general con PostgreSQL, Qdrant y reconciliador.
- `GET /health/live` y `GET /health/ready` del servicio OCR: solo accesibles desde la red interna del proyecto.
- Tras cambiar cualquier variable: pulsar `Deploy` y verificar `/health`.
## Reglas de uso de este documento
- Consultarlo antes de tareas con servicios externos, credenciales, despliegues o ejecuciones recurrentes.
- Nunca escribir secretos aqui: solo nombres de variables y donde viven (EasyPanel).
- Si un hecho operativo deja de ser valido, actualizarlo o marcarlo como obsoleto en la misma intervencion en que se descubra.