MEMORIA TÉCNICA

Registro de la Propiedad Intelectual de la Comunidad de Madrid

Obra: WHeat-Jobs | Fenotipo

Copyright © 2026 Ricardo M. Trigo Calonge
versión 1.0

1. Identificación de la obra y declaración de autoría

Autor de la obra software
Ricardo Manuel Trigo Calonge
Doctor en Derecho · Licenciado con Grado en Ciencias Químicas · Máster en Fisioterapia, Fisiología y Psicología del Deporte
Madrid, España · 2026

La presente memoria técnica describe la estructura, arquitectura, organización funcional, persistencia, lógica algorítmica y subsistemas principales de la obra software WHeat-Jobs | Fenotipo, a efectos de identificación técnica de la obra y de exposición ordenada de sus elementos originales de diseño e implementación.

Se hace constar que el Dr. Ricardo Manuel Trigo Calonge es autor del diseño funcional, de la arquitectura de software, de la organización lógica del sistema, del modelo algorítmico, del diseño de persistencia y del código fuente incorporado a la presente obra. La eventual utilización de herramientas de asistencia a la programación no altera la autoría sobre la concepción, selección, validación e integración final del resultado.

2. Ficha técnica y entorno de ejecución

Naturaleza de la obra: Aplicación web de gestión biométrica, nutricional y clínica, con motor de evaluación funcional heurística auto-referenciada y asistencia mediante inteligencia artificial
Arquitectura de software: Patrón Modelo-Vista-Controlador (MVC) con separación entre presentación, servicios, persistencia técnica, cálculo y evaluación heurística
Lenguaje y framework: C# v13.0 | .NET 10.0 | ASP.NET Core MVC | Visual Studio 2026
Gestión de datos: SQL Server y Entity Framework Core (Code-First)
Autenticación y acceso: Microsoft AspNetCore Identity con tipado fuerte de modelos y validación estructurada
Motores de cálculo: Motor hardcodeado, motor declarativo y modo comparativo dual con resolución en tiempo de ejecución
Unidad lógica de entrada: HealthDashDataBundle como Colección de Datos Funcionales de perfil, biometría diaria y analítica clínica
Entorno de despliegue: Servidor web con soporte .NET Runtime y almacenamiento interno de ficheros funcionales de trabajo
Canal de importación masiva: Subida web de ficheros comprimidos mediante ZIP fragmentado por bloques (chunked upload), con recomposición secuencial en servidor, extracción controlada y persistencia interna del export.xml
Importación analítica clínica: Lectura automática de informes de laboratorio en PDF mediante IA nativa (AnthropicClient con documento base64 directo, sin OCR previo): extracción de hasta 60 biomarcadores con conversión automática de unidades por laboratorio, revisión interactiva con semáforo de rangos de referencia y persistencia en AnaliticaClinica

Esquema de la arquitectura lógica del sistema WHeat-Jobs | Fenotipo

Diagrama de Arquitectura

Diagrama 1

3. Descripción funcional y algorítmica

3.1. Descripción funcional general

WHeat-Jobs | Fenotipo es una plataforma web orientada a la integración, estructuración y explotación funcional de datos biométricos diarios, analíticas clínicas y variables personales de contexto, con la finalidad de generar indicadores compuestos, seguir su evolución temporal y asistir en la interpretación funcional del estado del usuario.

El sistema integra tres fuentes de datos primarias: PerfilPersonal, BiodataDiaria y AnaliticaClinica. A partir de ellas construye una unidad lógica de entrada denominada Colección de Datos Funcionales o HealthDashDataBundle, que constituye la base inmediata del cálculo técnico.

La aplicación no se limita al almacenamiento o visualización de información, sino que incorpora una arquitectura propia de formación de contexto fisiológico, cálculo técnico, persistencia histórica, reconstrucción temporal y evaluación heurística orientada a objetivo.

Junto a la gestión manual y estructurada de datos fisiológicos y clínicos, el sistema incorpora un subsistema de importación masiva destinado a la ingestión de exportaciones externas de datos de salud generadas por plataformas de terceros y dispositivos personales. En su estado actual, dicho subsistema admite la subida de ficheros comprimidos ZIP que contienen un export.xml de gran tamaño, evitando la carga directa monolítica del XML mediante una estrategia de subida fragmentada por bloques, recomposición en servidor, extracción controlada y persistencia interna.

Esta arquitectura desacopla la fase de transporte del fichero desde el equipo del usuario y la fase de lectura funcional del XML ya persistido. En consecuencia, el sistema no opera sobre rutas locales del cliente, sino sobre una copia interna validada y almacenada en servidor, reutilizable para lecturas posteriores, generación de archivos reducidos por semana y trazabilidad técnica.

La formación de la Colección de Datos Funcionales sigue reglas temporales determinadas. La analítica clínica utilizada es la última analítica disponible con fecha igual o anterior a la fecha de cálculo. La biometría diaria, por su parte, se selecciona dentro de una ventana temporal configurable de días naturales, aplicada de forma homogénea al cálculo actual y al cálculo histórico reconstruido.

En la configuración operativa vigente, la ventana biométrica queda expresada, con carácter general, como VentanaBiométrica = [ FechaCalculo − 6 días , FechaCalculo ], siendo ambas fechas inclusivas. En consecuencia, el número máximo teórico de registros biométricos integrados por cálculo es de 7 registros diarios, salvo ausencia material de datos en alguna de las fechas comprendidas.

El núcleo funcional actual se articula mediante tres índices: Score SNC, Score Metabólico y Score Bioenergético. Dichos scores se obtienen a partir del HealthDashDataBundle, incorporando reglas de interpretación, umbrales fisiológicos y, cuando procede, un factor corrector de fiabilidad analítica en función de la antigüedad de la analítica utilizada.

Junto a dichos scores compuestos, la aplicación incorpora además una línea de análisis paralela centrada en la evolución temporal de variables directas de BiodataDiaria y un subsistema específico de análisis energético-metabólico diario basado en EnergyDailyBalance. Estas capas no sustituyen al scoring, sino que lo complementan al permitir examinar tendencias recientes, balance energético, suficiencia proteica y disponibilidad energética funcional desde una perspectiva diaria y longitudinal.

El cálculo técnico se articula mediante una fachada de motor de score capaz de resolver dinámicamente distintas implementaciones. El sistema soporta un motor hardcodeado de referencia operativa, un motor declarativo alineado funcional y matemáticamente con el motor hardcodeado, y un modo de comparación dual destinado al contraste técnico entre ambos.

A diferencia de una implementación declarativa meramente efímera o dependiente únicamente del estado dinámico del sistema, la arquitectura actual incorpora persistencia histórica específica del motor declarativo. En cada ejecución declarativa se registra no solo el resultado numérico obtenido, sino también el estado declarativo efectivo aplicado en ese momento, incluyendo serialización estructurada, metadatos de versión y huella criptográfica de integridad.

La persistencia del estado declarativo efectivo permite reconstruir con precisión el modelo utilizado en cualquier cálculo histórico, facilitando auditoría técnica, comparación longitudinal entre configuraciones y verificación de consistencia lógica entre versiones.

3.2. Fundamentación científico-técnica del motor hardcoded

El motor hardcoded de WHeat-Jobs | Fenotipo no se configura como un sistema de diagnóstico médico autónomo, sino como un modelo funcional de estratificación fisiológica. Su estructura combina, de una parte, umbrales apoyados en referencias clínicas o biomédicas consolidadas y, de otra, tramos heurísticos internos orientados a monitorización funcional, detección de desviaciones y generación prudencial de recomendaciones.

En el dominio metabólico, la base científica es comparativamente más estable. La glucosa basal y la hemoglobina glicosilada (HbA1c) se apoyan en estándares contemporáneos de clasificación de normoglucemia, prediabetes y diabetes, que el sistema reutiliza en forma simplificada para producir scoring funcional. De igual modo, triglicéridos y colesterol HDL se incorporan como variables de contexto cardiometabólico.

La variable pasos / NEAT no constituye un criterio diagnóstico clínico formal, pero sí un marcador funcional razonable del volumen de movimiento habitual. La evidencia y las recomendaciones internacionales de actividad física respaldan la relevancia del movimiento cotidiano y del descenso del sedentarismo como ejes de salud general.

En el dominio SNC, el modelo utiliza HRV, sueño, frecuencia cardiaca en reposo, tensión arterial y magnesio sérico. La literatura apoya que la HRV disminuye con la edad y que una mayor variabilidad suele asociarse a mejor flexibilidad autonómica y mejor capacidad de recuperación; no obstante, los puntos de corte concretos empleados por el motor no equivalen a umbrales diagnósticos universales.

En materia de sueño, el motor combina el tiempo total de sueño con el sueño profundo y, cuando existe dato disponible, con otras variables de arquitectura del sueño. Las guías de referencia recomiendan entre 7 y 9 horas en adultos de 18 a 64 años, y entre 7 y 8 horas en mayores de 65. A partir de esa edad la relación entre duración del sueño y resultados en salud muestra una curva en U: tanto el déficit como el exceso se asocian a mayor mortalidad, y la arquitectura del sueño cambia fisiológicamente con la edad (menor proporción de sueño profundo, mayor fragmentación). Los umbrales del motor reflejan estas matizaciones, sin pretender equivalencia con criterios diagnósticos.

La frecuencia cardiaca en reposo se incorpora porque una frecuencia más elevada se ha asociado, a nivel poblacional, con mayor mortalidad total y cardiovascular. En cuanto a la presión arterial, el sistema utiliza una traducción prudente de la literatura y de las guías contemporáneas.

El bloque de magnesio sérico posee respaldo biomédico, aunque con menor uniformidad absoluta en torno al concepto de nivel óptimo. La literatura reciente sobre estandarización de rangos de referencia propone prestar especial atención a niveles séricos en torno a 0,85 mmol/L.

En el dominio bioenergético, hemoglobina, TSH y ferritina constituyen el núcleo base. La hemoglobina se emplea como marcador indirecto de capacidad de transporte de oxígeno; la TSH, como proxy de regulación tiroidea; y la ferritina, como indicador de reservas de hierro.

La modulación bioenergética por sueño profundo, HRV y frecuencia cardiaca en reposo responde a una hipótesis fisiológica plausible: el estado de recuperación autonómica y del sueño condiciona la expresión funcional del sistema bioenergético.

Conclusión metodológica: el motor hardcoded de WHeat-Jobs | Fenotipo es un sistema mixto: combina tramos clínicamente anclados con tramos funcionales heurísticos. La convergencia posterior del motor declarativo acredita la reproducción fiel de esa lógica, pero no transforma automáticamente todos los umbrales en estándares clínicos oficiales.
3.3. Evaluación heurística orientada a objetivo
Precisión terminológica del sistema: el HealthDashDataBundle constituye la Colección de Datos Funcionales de entrada; los resultados numéricos de cada motor constituyen los scores técnicos; y el snapshot funcional representa la síntesis resultante de la consideración conjunta de los motores, su comparación técnica, la interpretación heurística, las recomendaciones y el prediagnóstico funcional.

Sobre el resultado técnico de cálculo, el sistema aplica una segunda capa de evaluación heurística destinada a determinar la dirección del estado, la magnitud del cambio, la alineación con el objetivo y el comportamiento de la pauta.

Esta lógica permite emitir decisiones operativas sobre la pauta, distinguiendo entre mantenimiento, ajuste, intensificación o sustitución, sin confundir la mera convergencia con el cumplimiento efectivo del objetivo.

El sistema dispone de persistencia técnica del cálculo y de persistencia heurística del resultado operativo actual. La primera se registra en HealthScoreHistory; la segunda, en HealthEvolutionSnapshot.

El cálculo histórico puede visualizarse como score técnico persistido o como reconstrucción histórica recalculada sobre datos fuente, manteniéndose el score técnico persistido como referencia principal de trazabilidad.

3.4. Especificación práctica del algoritmo
Especificación práctica del algoritmo

El algoritmo opera en ciclos de captura de contexto, importación, agregación, cálculo, evaluación heurística y persistencia.

FASE 0
Captura externa e importación masiva
Selección por el usuario de un fichero export.zip, subida web fragmentada por bloques, recomposición secuencial del ZIP en servidor, extracción controlada del export.xml y persistencia interna del XML resultante como fuente estable de lectura.
FASE 0 BIS
Reducción semanal, agregación diaria y normalización Biodata
A partir del export.xml ya persistido, el sistema genera un XML reducido por semana natural, reconstruye por fecha la información fisiológica relevante mediante lectura estructural en streaming y transforma el resultado en registros normalizados compatibles con BiodataDiaria.
FASE A
Contexto
Obtención de PerfilPersonal, selección de la ventana temporal configurada de BiodataDiaria y determinación de la AnaliticaClinica más próxima anterior o igual a la fecha de cálculo.
FASE B
Cálculo técnico
Cálculo de Score SNC, Score Metabólico y Score Bioenergético mediante motor técnico seleccionable.
FASE C
Recomendación inmediata
Generación de recomendaciones basadas en el estado funcional actual y en el cruce técnico entre scores y biomarcadores.
FASE D
Evaluación evolutiva y tendencias
Contraste entre histórico, pauta activa y objetivo para determinar dirección, alineación y decisión operativa, incorporando análisis longitudinal de variables directas de BiodataDiaria.
FASE E
Persistencia y trazabilidad
Persistencia del cálculo técnico en HealthScoreHistory, del resultado heurístico actual en HealthEvolutionSnapshot y del balance energético diario en EnergyDailyBalance.
ECUACIÓN CONCEPTUAL DE DESVIACIÓN FUNCIONAL
ΔEstado = |Objetivo - Real|
Expresión conceptual del diferencial entre el estado objetivo y el estado observado. Su traducción operativa se realiza mediante scores, histórico técnico y evaluación heurística orientada a objetivo.

En suma, el sistema analiza el estado actual del usuario, la dirección de su evolución y el comportamiento de la pauta, a fin de determinar si la intervención aplicada aproxima efectiva y progresivamente al objetivo funcional previsto.

3.5. Arquitectura de variables del algoritmo
Arquitectura de variables del algoritmo

El sistema distingue entre variables de latencia aguda y crónica. Aunque la base de datos admite un conjunto amplio de parámetros biométricos, clínicos y de perfil, el motor heurístico opera en su estado actual sobre un núcleo progresivamente auditado de variables esenciales, susceptible de ampliación o recalibración.

LATENCIA AGUDA Vectores dinámicos
  • TemperaturaCorporal: registro térmico basal.
  • HRV: equilibrio del sistema nervioso autónomo.
  • FrecuenciaCardiacaReposo: pulso basal y recuperación.
  • SueñoProfundoMinutos: reparación física.
  • SueñoRemMinutos: recuperación neurocognitiva.
  • Peso: masa corporal total.
  • PasosDiarios: movimiento NEAT.
  • LitrosAgua: hidratación registrada.
  • CaloriasConsumidas: ingesta energética diaria.
LATENCIA CRÓNICA Vectores estructurales
  • GlucosaAyunas: azúcar basal.
  • HemoglobinaGlicosilada: media glucémica de largo plazo.
  • Trigliceridos: grasas circulantes.
  • ColesterolHDL: fracción protectora.
  • ColesterolLDL: fracción aterogénica.
  • ProteinaCReactiva: inflamación de bajo grado.
  • Ferritina: almacén de hierro.
  • TSH: regulación tiroidea.
  • VitaminaD3: soporte inmunometabólico.
  • GGT / ALT: marcadores hepáticos.
  • Creatinina / FiltradoGlomerular: eficiencia renal.
  • CPK: daño muscular.
  • MagnesioSerico: regulador neuromuscular y metabólico.

4. Arquitectura de datos y lógica de negocio

El sistema se apoya en una arquitectura relacional y de servicios orientada a separar con claridad los datos fuente, la formación de la Colección de Datos Funcionales de entrada, el cálculo técnico, la interpretación heurística y la persistencia del resultado.

Datos base: PerfilPersonal

Define el marco basal y estratégico del usuario. Dispone de CRUD completo.

  • Biometría basal: edad, sexo, altura y composición.
  • Contexto: profesión, clima, entorno y hábitos.
  • Dirección: objetivo principal del usuario.
Datos base: BiodataDiaria

Registra la evolución cotidiana del usuario. Dispone de CRUD completo.

  • Actividad: ejercicio, pasos, carga diaria.
  • Recuperación: HRV, sueño, fatiga, estrés.
  • Homeostasis: pulso, temperatura, SpO2, hidratación.
Datos base: AnaliticaClinica

Aporta validación bioquímica estructural. Dispone de CRUD completo.

  • Metabolismo: glucosa, HbA1c, lípidos.
  • Inflamación y reservas: ferritina, PCR, vitaminas.
  • Función orgánica: renal, hepática, endocrina.
Servicios funcionales principales
  • HealthDashDataService: construcción de la Colección de Datos Funcionales a partir de perfil, biometría diaria y analítica clínica.
  • HealthImportController: orquestación MVC del proceso de importación, lectura semanal, generación de XML reducido y agregación posterior.
  • HealthImportPathService: resolución centralizada de rutas internas para export.xml, XML reducidos semanales y directorios temporales.
  • HealthScoreService: fachada central del cálculo técnico de scores.
  • HardcodedHealthScoreEngine: implementación operativa estable de referencia.
  • DeclarativeHealthScoreEngine: implementación basada en configuración dinámica de bloques, inputs y reglas.
  • HealthScoreModeResolver: resolución del motor activo en tiempo de ejecución.
  • HealthRecommendationService: recomendación inmediata basada en estado actual.
  • HealthScoreHistoryService: persistencia del score técnico del cálculo.
  • DeclarativeHistoryService: persistencia histórica específica del motor declarativo.
  • HealthScoreBackfillService: reconstrucción retrospectiva del histórico técnico.
  • BioDataEvolutionService / BioDataTrendAnalysisService: análisis de tendencias sobre variables persistidas en BiodataDiaria.
  • EnergyDailyBalanceService: cálculo y persistencia del balance energético diario, macronutrientes, proteína/kg, alcohol, gasto total y clasificación oficial del estado energético.
  • EnergyDailyBalanceQueryService: construcción de modelos de consulta y visualización del análisis metabólico diario.
  • HealthTrendService: análisis y proyección de tendencia sobre histórico persistido.
  • HealthEvolutionService: evaluación evolutiva orientada a objetivo.
  • HealthEvolutionOrchestrator: coordinación del flujo heurístico completo.
  • PlanAdjustmentService: decisión sobre mantenimiento, ajuste, intensificación o sustitución de la pauta.
  • AnaliticaInterpretService: interpretación clínica asistida por IA de una AnaliticaClinica ya persistida, generando un informe HTML contextualizado.
  • AnaliticaPdfImportService: extracción automática de biomarcadores desde PDF de laboratorio mediante AnthropicClient con documento nativo base64; aplica reglas de conversión de escala (×10ⁿ, mg/dL↔mmol/L, pmol/L↔ng/dL) y retorna el resultado para revisión antes de persistir.
  • OllamaCompletionService / ClaudeCompletionService: motores de completado IA para Ollama Cloud y Claude API respectivamente.
  • AiBackgroundWorker: servicio hospedado de procesamiento asíncrono de tareas IA mediante cola de canal interno (Channel<T>).
  • IBackgroundTaskQueue / IAiResultStore / IAiTimingService: infraestructura de encadenamiento, almacenamiento de resultados y estimación de tiempos de respuesta IA.
  • AiProviderSettings: gestión en tiempo de ejecución del proveedor IA activo (global y por administrador), con consola de administración dedicada.
  • HistoriaPacienteService: generación del informe de revisión histórica parcial para el período seleccionado: compila perfil, evolución de scores SNC/MET/BIOE, balance energético por período, macronutrientes medios, analítica clínica con verificación de ~55 parámetros contra rangos de referencia (BuildParametros) y apéndice de registros diarios brutos; construye las variables de contexto para el prompt IA (HistoriaPacientePromptVars).
  • HistoriaPacientePromptSeeder: seeder que persiste en base de datos la plantilla versionada HISTORIA_PACIENTE con sus bloques de contenido (IaPromptBlock), incluyendo las reglas de interpretación clínica estáticas y los marcadores [FUERA DE RANGO] para guiar al modelo.
4.1. Subsistema de análisis de tendencias de BiodataDiaria

Junto al cálculo técnico de scores y a la evaluación heurística global, el sistema incorpora un subsistema específico de análisis de tendencias sobre la entidad BiodataDiaria, destinado a observar la dirección, estabilidad y magnitud del cambio de variables fisiológicas cotidianas.

Este subsistema opera sobre una ventana temporal seleccionable de registros diarios y permite obtener una lectura sintética de la evolución reciente de variables como peso, cintura, frecuencia cardiaca en reposo, HRV, sueño, distancia recorrida, pasos y energía activa.

Desde el punto de vista arquitectónico, este módulo constituye una capa analítica intermedia entre la persistencia diaria de biometría y la evaluación heurística global.

4.2. Subsistema de análisis metabólico diario y dinámica energética funcional

El sistema incorpora un subsistema específico de análisis energético-metabólico diario, destinado a interpretar la relación entre ingesta, gasto energético, distribución nutricional, proteína relativa al peso corporal, actividad física y balance energético final.

La fuente técnica principal de este subsistema es la entidad EnergyDailyBalance, generada a partir de BiodataDiaria y recalculada mediante servicios internos. Dicha entidad consolida la energía ingerida, el gasto basal ajustado, el gasto activo ajustado, el gasto total, el balance energético, los macronutrientes, el alcohol ingerido, la proteína por kilogramo de peso corporal y el estado energético resultante.

La clasificación oficial del estado energético se centraliza en EnergyDailyBalanceService.ResolveEnergyStatus, evitando que las vistas, prompts o módulos auxiliares reclasifiquen el balance mediante umbrales paralelos.

  • DeficitSevero: balance ≤ -900 kcal.
  • DeficitModerado: balance ≤ -450 kcal.
  • DeficitLigero: balance ≤ -150 kcal.
  • Equilibrado: balance < 150 kcal.
  • SuperavitModerado: balance ≤ 500 kcal.
  • SuperavitAlto: balance > 500 kcal.

Sobre esta base se articula el módulo de Análisis metabólico diario, que ofrece una lectura funcional del día evaluado: ingesta total, gasto estimado, balance calórico, distribución de hidratos, proteínas, grasas y alcohol, suficiencia proteica relativa y compatibilidad del patrón observado con preservación de masa magra o pérdida progresiva de grasa.

De forma complementaria, el módulo de Dinámica metabólica representa visualmente la evolución funcional del día mediante una curva por fases, integrando balance energético acumulado e índice relativo de disponibilidad energética. Este índice no constituye una magnitud física directa, sino una señal sintética orientativa que combina balance energético, proteína/kg, hidratos, grasas y alcohol.

Desde el punto de vista arquitectónico, ambos módulos forman una capa analítica específica dentro del sistema, conectada con BiodataDiaria y EnergyDailyBalance, pero separada del motor principal de scores.

Formación de la Colección de Datos Funcionales y regla temporal de cálculo

La unidad efectiva de cálculo del sistema es el HealthDashDataBundle, integrado por: PerfilPersonal, una colección acotada de BiodataDiaria y una AnaliticaClinica seleccionada por proximidad temporal anterior o igual a la fecha de cálculo.

La selección de biometría diaria se realiza mediante una ventana temporal natural de N días, siendo N un parámetro configurable del sistema. Cuando N = 7, equivale a [ FechaCalculo − 6 , FechaCalculo ].

La regla temporal aplicada queda reflejada en histórico a través de los campos FechaBioInicio, FechaBioFin, NumeroRegistrosBioUsados y ReglaAplicada.

Arquitectura de importación masiva y persistencia interna del XML

El sistema incorpora una arquitectura específica para la ingestión de exportaciones XML de gran tamaño, basada en la subida de un fichero ZIP fragmentado en bloques.

Una vez reconstruido el ZIP, el sistema realiza una extracción controlada del contenido, localiza el export.xml válido y lo copia a la ubicación interna definitiva del usuario.

Subsistema de importación estructurada y generación automática de BiodataDiaria

La arquitectura actual incorpora un subsistema específico de ingestión documental, reducción estructural, agregación funcional y transformación normalizada de datos externos en registros persistentes de BiodataDiaria.

Una vez persistido el export.xml, la aplicación genera archivos reducidos por semana natural, construidos mediante lectura secuencial en streaming y filtrado selectivo de nodos relevantes.

Sobre dichos XML reducidos semanales actúa un motor de agregación diaria diseñado para reconstruir, por fecha, la información fisiológica relevante contenida en el documento.

Esta cadena captura documental → persistencia interna → reducción semanal → agregación diaria → normalización → persistencia en BiodataDiaria constituye un componente técnico propio del sistema.

Validación y tipado: los modelos emplean DataAnnotations, precisión decimal y restricciones de persistencia mediante Entity Framework Core y SQL Server.

4.3. Calibración del factor de corrección de ingesta calórica (OllamaIntakeCorrectionFactor)

La IA estima la ingesta calórica a partir de los alimentos declarados por el usuario, aplicando el sistema Atwater general (4/4/9/7 kcal por gramo de hidratos/proteína/grasa/alcohol). El resultado se desvía del balance de masa real por varias causas mezcladas — imprecisión de la IA al interpretar la declaración, imprecisión del propio Atwater general (no distingue fibra, matriz alimentaria ni eficacia de absorción real del individuo) y hábitos de registro del usuario — que el sistema no puede aislar entre sí. El sistema incorpora un mecanismo de calibración empírica que calcula un factor multiplicador (OllamaIntakeCorrectionFactor) individual que absorbe el conjunto de esas desviaciones, no solo el error de la IA.

Principio físico del balance energético

La calibración se fundamenta en la identidad del balance energético:

IngestaReal = GastoTotal + (ΔPeso × 7700 kcal/kg)

Donde ΔPeso es la variación de peso corporal en el período analizado (negativo si hubo pérdida, positivo si hubo ganancia) y 7700 kcal/kg es el equivalente energético aproximado de un kilogramo de tejido adiposo. Esta igualdad permite deducir cuántas kilocalorías reales ingirió el usuario a partir de su gasto medido y la evolución de su peso, sin depender en absoluto de la estimación de la IA.

El factor de corrección se obtiene entonces como:

Factor = IngestaReal / SumaOllamaRawKcal

Si el factor resultante es mayor que 1, la ingesta real supera a la calculada por Atwater sobre lo declarado; si es menor que 1, es inferior. El factor es individual y mezcla todas las causas de desviación (IA, Atwater general, absorción real) sin distinguir cuál pesa más en cada caso. Aplicarlo a las estimaciones futuras produce una ingesta corregida más cercana a la realidad fisiológica del usuario en conjunto, no una corrección aislada del error de la IA.

Limitación conocida: la equivalencia de 7 700 kcal/kg solo es razonablemente válida a largo plazo (meses). A corto plazo, la mayor parte de la variación de peso refleja cambios en agua corporal, glucógeno muscular y contenido intestinal, no tejido adiposo real. Por eso el sistema exige un mínimo de 30 días para considerar el factor «Bueno» y 60 días para «Excelente», y prefiere la regresión OLS al endpoint cuando R² es suficiente: ambas medidas reducen el peso del ruido diario, pero no lo eliminan. El factor debe interpretarse como una estimación orientativa de la tendencia del usuario, no como una verdad fisiológica exacta.
Dos variantes del factor: Endpoint y Regresión OLS

El servicio EnergyCalibrationRegressionService calcula dos estimaciones independientes del ΔPeso:

  • ΔPeso Endpoint: diferencia simple entre el último peso registrado en el período y el primero. Es directo pero sensible a fluctuaciones puntuales (retención de líquidos, hora del pesaje, etc.).
  • ΔPeso Regresión (OLS): se ajusta una recta de mínimos cuadrados ordinarios sobre toda la serie de pesos del período. La pendiente de esa recta (kg/día) multiplicada por el número de días proporciona un ΔPeso suavizado, menos afectado por el ruido diario y más representativo de la tendencia real del tejido corporal.

A cada variante de ΔPeso le corresponde un factor independiente (FactorEndpoint y FactorRegresion).

Factor recomendado y criterio de ponderación

El factor final que se propone al usuario (FactorRecomendado) pondera las dos variantes en función del coeficiente de determinación R² de la regresión del peso:

Si R² ≥ 0,30 → FactorRecomendado = 0,70 × FactorRegresión + 0,30 × FactorEndpoint
Si R² < 0,30 → FactorRecomendado = FactorEndpoint

Un R² bajo indica alta variabilidad en el peso (ruido fisiológico, mediciones irregulares), por lo que la regresión no es fiable y se usa únicamente el endpoint. Con R² suficiente, la regresión recibe mayor peso (70%) por ser más robusta frente al ruido de corto plazo.

El factor resultante se limita al rango [0,80 – 2,50] como salvaguarda ante datos atípicos o períodos de registro incompleto.

Evaluación de la calidad de los datos

El servicio clasifica automáticamente la fiabilidad de la calibración en cuatro niveles:

Nivel Criterio
Excelente≥ 60 días con ingesta y R² ≥ 0,50
Buena≥ 30 días con ingesta y R² ≥ 0,30
Aceptable≥ 20 días con ingesta
Insuficiente< 15 días con ingesta o sin datos de peso

Con menos de 15 días de ingesta declarada el sistema devuelve calidad «Insuficiente» y no aplica el factor. Se recomienda un mínimo de 30 días para un factor fiable.

4.4. Automatización de Ingesta (IntakeAutomationService)

Módulo de siembra sintética que genera una semana completa de registros de ingesta plausibles a partir del historial fisiológico del usuario, sin intervención de IA generativa. El algoritmo es puramente estadístico:

Algoritmo de generación
  1. Se requieren al menos 30 días previos con ingesta registrada en el período de referencia. Sin ese mínimo el servicio rechaza la operación.
  2. Para cada macro (ProteinasIngeridasGr, HidratosIngeridosGr, GrasasIngeridasGr, AlcoholIngeridoGr) se calculan la media (μ) y la desviación típica (σ) por día de semana (lunes…domingo) sobre el período de referencia elegido. Si un día de semana no tiene historial, se usa la estadística global del período.
  3. La σ efectiva se limita: σ_ef = min(σ_hist, μ × 0,25). Así la variación máxima garantizada es el ±25 % de la media, evitando valores absurdos cuando el historial es ruidoso.
  4. Cada valor se muestrea como μ + N(0,1) × σ_ef (método Box-Muller) y se recorta al intervalo [0, μ + 2,5·σ_ef].
  5. Las calorías se calculan por coherencia a posteriori: Kcal = Prot×4 + HC×4 + Grasa×9 + Alcohol×7. No se reescalan los macros — la coherencia calórica es informativa, no forzada.
  6. Solo se siembran los días que no tienen ya ingesta en BiodataDiaria. Los días con datos previos se muestran como "ya existe" en la previsualización y no se modifican.
Limitación y advertencia de uso

La automatización introduce inexactitud estadística en los registros de ingesta. Los valores generados son plausibles con el patrón histórico del usuario pero no son reales. Afectan al balance energético, a la calibración del modelo y a los informes evolutivos. Se recomienda utilizar esta función únicamente cuando no hay datos reales disponibles y la brecha en el historial impide el funcionamiento de otros módulos. Es mejor que no tener nada, pero nunca equivale a la declaración real de ingesta.

Aviso de alérgenos en la estimación nutricional

El módulo de ingesta (tanto en modo IA pura como en el modo híbrido OFf+IA descrito en el apartado 8.14) incorpora una capa adicional de detección de alérgenos declarados en el perfil, desacoplada del cálculo de macros para no comprometer su fiabilidad numérica. Combina un diccionario determinista de los 14 alérgenos de declaración obligatoria de la UE con una capa complementaria de IA, cuyo resultado se filtra por el mismo diccionario antes de fusionarse. Descripción técnica completa en el apartado 8.23.

4.5. Asistente IA conversacional (AiAssistantController)

Panel de chat contextual accesible desde cualquier página de la aplicación mediante un botón flotante. No requiere tablas propias ni migraciones: el historial de conversación se mantiene íntegramente en el cliente (array JS) y se serializa en texto dentro del user prompt en cada petición.

Arquitectura y contexto
  • Reutiliza la interfaz IAiCompletionService existente, compatible con Claude API y Ollama Cloud, sin ningún cambio en los motores subyacentes.
  • El system prompt se construye dinámicamente en cada petición con: (a) manual de funcionalidades de Fenotipo, (b) perfil personal del usuario (PerfilesPersonales) y (c) los últimos 30 días de BiodataDiaria en formato tabular compacto, incluyendo pasos, sueño, FC, HRV, SpO₂, peso, energía basal, energía activa, gasto total, ingesta calórica, balance y macros (P/HC/G).
  • El historial se limita a 10 turnos (20 entradas) para controlar el consumo de tokens.
  • Las respuestas se renderizan con marked.js. El contenedor del chat lleva class="tex2jax_ignore" para que MathJax (cargado globalmente) no procese los símbolos matemáticos de las respuestas del modelo.
  • El endpoint POST /AiAssistant/Chat está protegido con [Authorize] y token anti-CSRF.

4.6. Módulo de detección y medición de adaptación metabólica

La adaptación metabólica (adaptive thermogenesis) designa la reducción del gasto energético real por encima de la explicable por cambios de masa corporal, como respuesta homeostática del organismo a la restricción calórica prolongada. El sistema incorpora un módulo propio que cuantifica este fenómeno de forma automática y longitudinal.

Principio de cálculo

El módulo compara dos estimaciones del TDEE (Gasto Energético Total Diario) para el período analizado:

  • TDEE teórico: media del campo EnergyDailyBalance.EnergiaGastoTotalKcal, calculado por EnergyDailyBalanceService como suma directa de Basal Importado y Activa Importada de HealthKit, cada una multiplicada por su factor de calibración personal (UserEnergyCalibration).
  • TDEE real estimado: derivado por conservación de energía:
    TDEE_real = (ΣIngestaCorregida − ΔPeso_kg × 7 700) / n_días
    donde ΣIngestaCorregida aplica el factor OllamaIntakeCorrectionFactor solo sobre macros (el alcohol usa fórmula determinista, sin corrección).

La variación de peso se obtiene mediante regresión OLS sobre la serie histórica de peso si R² ≥ 0,25 (criterio de señal suficiente); en caso contrario se usa la diferencia entre endpoints, reduciendo el sesgo de retención hídrica.

Adaptación = TDEE_teórico − TDEE_real. Un valor positivo indica que el cuerpo gasta menos calorías de las que el modelo predice: es la medida operativa de la adaptación.

Tanto el panel principal como el histórico marcan en ámbar las ventanas con R² < 0,50: la tendencia de peso de esa ventana concreta está dominada por ruido no atribuible a cambio real de masa grasa/magra — típicamente retención hídrica por rachas de alcohol de varios días seguidos (que la mediana adaptativa no absorbe tan bien como un pico aislado de un solo día), sal, ciclo hormonal o variabilidad de la báscula. La cifra de Adaptación de esas ventanas no es incorrecta por diseño — sigue calculándose igual — pero debe leerse con cautela: parte del residuo puede reflejar esa fluctuación ajena a la grasa, no una reacción metabólica real.

Niveles de adaptación y alertas
Nivel internoUmbral (% del TDEE teórico)Etiqueta en pantallaImplicación clínica
SinAdaptacion< 0 %Muy adaptableGasta más de lo teórico, sin freno; muy buena señal.
SinAdaptacion0–10 %Sin adaptaciónGasto coherente con el modelo teórico; esperable en cualquier déficit prolongado.
Leve10–15 %LeveAjuste inicial; vigilar proteína y NEAT.
Moderada15–25 %ModeradaConsiderar reactivación metabólica.
Severa> 25 %SeveraAdaptación significativa; reevaluar pauta.

El umbral se aplica sobre el % de adaptación respecto al TDEE teórico (no kcal absolutas), para que sea comparable entre personas con gasto energético muy distinto; un balance negativo (TDEE real por debajo del teórico) se etiqueta además como "Muy adaptable" cuando el signo es favorable. Alertas automáticas: reactivación metabólica cuando la adaptación residual ≥ 15 % del TDEE teórico y período ≥ 20 días con datos; proteína insuficiente cuando cobertura proteica media < 80 % del objetivo.

Serie histórica y persistencia

El módulo calcula una serie histórica de 12 ventanas de 28 días, desplazadas 7 días entre sí, que permite visualizar la evolución de la adaptación a lo largo de los últimos meses. Los snapshots se persisten en MetabolicAdaptationSnapshots, tanto bajo demanda del usuario (recálculo manual de un día concreto) como de forma automática mediante MetabolicSnapshotWorker, servicio en segundo plano que cada noche a las 02:00 recalcula y guarda dos ventanas por usuario: la de D-1 (clave de cálculo el día actual) y la de D-2 (clave de cálculo el día anterior, sobrescribiendo su snapshot). Esta doble pasada compensa que los datos de un día pueden seguir completándose o corrigiéndose durante la mañana o tarde del día siguiente, después de que su primer snapshot ya se hubiera guardado. Los snapshots se representan visualmente con Chart.js (barras de adaptación y línea TDEE teórico vs real).

Causas documentadas en el modal de información del módulo: regulación hormonal (leptina, T3, cortisol, ghrelina), supresión del SNA, colapso del NEAT, aumento de eficiencia muscular (Rosenbaum: 20-25 % a −10 % de peso) y rol de la proteína (TEF, preservación de masa magra, efecto NEAT). Evidencia de reactivación metabólica prolongada: estudio MATADOR (Byrne 2017 — reducción de adaptación del 50 % con descansos de 2 semanas).

5. Persistencia funcional del sistema

HealthScoreHistory

Snapshot técnico persistido del cálculo funcional.

  • FechaCalculo: fecha efectiva del cálculo.
  • FechaAnaliticaUsada: analítica clínica tomada como referencia temporal.
  • FechaBioInicio / FechaBioFin: ventana real de biometría diaria utilizada.
  • NumeroRegistrosBioUsados: número real de registros diarios incluidos.
  • ScoreSNC / ScoreMetabolico / ScoreBioenergetico: resultado técnico del cálculo.
  • ReglaAplicada: regla temporal y técnica declarada para el cálculo.
  • EdadEnFechaCalculo: edad computada para la fecha evaluada.
  • FactorFiabilidadAnalitica: corrector aplicado por antigüedad de la analítica.
HealthEvolutionSnapshot

Persistencia del resultado heurístico completo de la evaluación operativa vigente.

  • Objetivo y dirección.
  • Alineación de pauta.
  • Score ponderado objetivo.
  • Decisión de pauta.
  • Justificación y recomendación ajustada.
  • Origen de pauta y versión asociada.
UserActivePlan / UserActivePlanVersion

Persistencia real de la pauta activa y de sus versiones.

Permite trazabilidad histórica y vinculación del snapshot heurístico con una versión concreta de pauta.

EnergyDailyBalance

Persistencia diaria del balance energético y nutricional calculado por el sistema.

  • Ingesta: energía total ingerida y macronutrientes.
  • Gasto: energía basal ajustada, energía activa ajustada y gasto total.
  • Balance: diferencia entre ingesta y gasto total.
  • Proteína/kg: indicador funcional de suficiencia proteica.
  • Alcohol: integración energética y moduladora del día.
  • EstadoEnergetico: clasificación oficial centralizada del balance diario.
  • ReglaAplicada: versión del modelo de gasto energético utilizado.
HealthScoreMasterParameters y modelo declarativo

Catálogo maestro técnico y estructura declarativa para la configuración progresiva del motor de cálculo.

Comprende parámetros scoreables, bloques, inputs, modelos y futuras reglas o modificadores.

Persistencia interna del import XML

El sistema conserva internamente una copia funcional del export.xml del usuario como fuente estable de lectura.

  • Origen: ZIP externo subido por bloques y recombinado en servidor.
  • Destino: ruta interna persistente por usuario.
  • Función: lectura completa, generación de reducidos semanales y reutilización funcional posterior.
MetabolicAdaptationSnapshot

Snapshot de adaptación metabólica calculada bajo demanda del usuario.

  • FechaDesde / FechaHasta: ventana temporal analizada (14–90 días).
  • TdeePredichoMediaKcal: media de EnergyDailyBalance.EnergiaGastoTotalKcal.
  • TdeeRealEstimadoKcal: TDEE real derivado por balance de masa.
  • AdaptacionKcal / AdaptacionPorcentaje: gap TDEE teórico − real.
  • Nivel: SinAdaptacion / Leve / Moderada / Severa.
  • DeltaPesoKg / R2Peso: cambio de peso y bondad de ajuste OLS.
  • CoberturaProteicaMedia: ratio de suficiencia proteica en el período.
HealthScoreDeclarativeHistory

Persistencia técnica específica del motor declarativo.

  • Scores persistidos: SNC, Metabólico y Bioenergético.
  • Contexto temporal: fecha de cálculo, analítica usada y ventana biométrica aplicada.
  • Modelo persistido: serialización JSON completa del estado declarativo efectivo.
  • Integridad: hash SHA-256 del modelo declarativo utilizado.
  • Trazabilidad: versión del modelo, origen de configuración y existencia de parámetros de usuario.
Regla operativa vigente: el sistema distingue entre score técnico persistido, reconstrucción histórica recalculada, evaluación heurística vigente y balance energético diario persistido. Cada uno de estos planos cumple una función distinta dentro de la trazabilidad funcional del sistema.

7. Instrucciones y logística funcional

Pautas de suministro de datos, uso operativo y explotación funcional del sistema.

01Entrada: Perfil Personal

Datos básicos de marco: biometría basal, contexto personal, hábitos y objetivo funcional.

02Seguimiento diario

Registro manual o importación estructurada de biometría diaria y datos históricos de salud.

03Lectura semanal reducida

Generación de reducidos semanales, agregación diaria y transformación a BiodataDiaria.

04Verificación: Analítica Clínica

Registro analítico para auditoría de recomendaciones, trazabilidad temporal y confirmación bioquímica. Los valores pueden introducirse manualmente o mediante importación automática desde PDF: el informe de laboratorio se envía directamente a la IA, que extrae y convierte hasta 60 biomarcadores; el usuario revisa el resultado con semáforo de rangos y confirma antes de persistir.

8.22. Por qué el factor de actividad se deja neutro — metodología canónica de calibración

(El Índice de Eficiencia Locomotora — IEL, EnergiaActivaKcal del wearable frente a un modelo mecánico de la marcha — sigue siendo puramente diagnóstico en Adaptación Metabólica: una deriva decreciente sostenida es una tercera línea de evidencia de adaptación metabólica activa, junto a la brecha TDEE_teórico/TDEE_real y la regresión OLS del peso. Nunca se usa para fijar ActiveEnergyCorrectionFactor, por el mismo motivo que se explica a continuación.)

Tres intentos sucesivos de derivar el factor de Activa por vía indirecta — los tres descartados

El factor corrector de energía activa (ActiveEnergyCorrectionFactor) pasó por tres métodos de derivación indirecta, todos descartados por el mismo motivo de fondo:

  • Factor bibliográfico fijo (0,75), anclado en un meta-análisis (npj Digital Medicine 2025, 56 estudios) que estima una sobreestimación del Apple Watch del 20-30%.
  • KeytelHrService (Keytel et al. 2005, estimación de energía activa por frecuencia cardíaca media diaria) — en su momento pareció confirmar el 0,75 (0,762 sobre 26 días válidos), pero compartía el mismo punto ciego que el bibliográfico: ambos comparan contra una referencia idealizada (tapiz rodante/mecánica uniforme) que no contempla el NEAT concurrente real, así que su convergencia mutua no era una confirmación independiente. Tenía además dispersión día a día muy alta (StdDev 0,46 sobre media 0,76 — CV ≈60%) y solo 26 de 94 días producían un valor válido. Borrado del código, no aparcado.
  • WalkingMetCalibrationService (tramos de marcha reales del propio usuario, detectados por contigüidad en DistanceWalkingRunning, contra predicción MET/mecánica con el peso real de cada día) — corrigió problemas reales de los dos métodos anteriores (usaba datos propios del usuario, no bibliografía ni FC, excluía sueño y velocidades biomecánicamente imposibles, deduplicaba Watch+iPhone), pero seguía siendo, en el fondo, la misma clase de validación: un modelo mecánico/físico indirecto usado para decidir el factor de Activa. Tras usarlo en la práctica, no resultó una base fiable para calibrar — el juicio final fue que ningún modelo indirecto (bibliografía, FC, o MET por tramos) sustituye la validación contra el único dato objetivo: el peso real. Borrado del código, no aparcado.

Conclusión de fondo, válida para los tres métodos: no existe una dirección universal de sesgo del sensor — depende del dispositivo, la versión de firmware y la zancada de cada persona — y ningún modelo que compare contra una referencia idealizada o mecánica puede sustituir una validación contra el dato real. Por eso ActiveEnergyCorrectionFactor se deja en 1,00 por defecto y solo se toca con evidencia directa (comparativa contra calorimetría indirecta real) — nunca a partir de un modelo indirecto, por sofisticado que sea.

La circularidad de calibrar los tres factores a la vez

El factor de ingesta (OllamaIntakeCorrectionFactor) se deriva por regresión MCO de IngestaReal = GastoTotal + Δpeso × 7700, donde GastoTotal ya incluye Basal y Activa con sus factores vigentes aplicados. Esto significa que cambiar el factor Basal o Activa desplaza mecánicamente el factor de ingesta recomendado, sin que eso implique ninguna evidencia nueva sobre la ingesta en sí — se comprobó en vivo: subir Activa de 0 a 1,00 elevó automáticamente la ingesta recomendada de 1,20 a más de 1,70, por el simple efecto aritmético de sumar más gasto al mismo lado de la ecuación.

Con tres factores libres y una sola ecuación de balance (masa/energía), el sistema está indeterminado — cualquier combinación que cuadre el balance es matemáticamente válida, pero solo una es correcta. La única forma de romper la indeterminación sin introducir más supuestos no verificados es fijar dos de los tres factores en un valor de referencia y calibrar solo el tercero contra un dato verdaderamente independiente: el peso real medido.

El basal de Apple no coincide con Mifflin ni con Katch-McArdle — caso de estudio individual

Todo lo que sigue en este apartado es un caso concreto de un único usuario (el fundador), usado como ejemplo ilustrativo del método — NO son cifras universales de Fenotipo ni valores por defecto para ningún otro usuario. Cada persona debe repetir esta misma comparación con sus propios datos antes de tocar su factor Basal; no hay ninguna razón para esperar el mismo porcentaje de desviación, ni siquiera la misma dirección, en otro dispositivo o fisiología.

Antes de fijar Basal=1,00 como referencia neutra hay que descartar que el basal que entrega HealthKit esté, en términos absolutos, inflado (o deflactado) para ese usuario — si lo está, cualquier factor de ingesta calibrado contra un basal sesgado absorbe ese error sistemáticamente, sin que sea un problema de la ingesta. En el caso del fundador, se contrastó sobre 52 días con datos completos:

  • Katch-McArdle (370 + 21,6 × masa magra, con BiodataDiaria.GrasaCorporalImpedanciaPct real medido por bioimpedancia, no un porcentaje poblacional supuesto): media 1892,6 kcal en este caso.
  • Mifflin-St Jeor (edad, altura, peso, sin composición corporal): media 1825,0 kcal en este caso.
  • Basal de Apple (HealthKit): media 2351,7 kcal en este caso.

Para este usuario, las dos fórmulas independientes — una poblacional, otra individual vía composición corporal real — coincidieron entre sí (~4% de diferencia) y ambas quedaron muy por debajo del basal de Apple (~24-29%). Esto ya no es solo "un algoritmo distinto pero igual de válido" (conclusión de una sesión anterior, que solo había comprobado que el basal de Apple respondía correctamente en pendiente al peso — Basal~Peso+Distancia, R² alto): en nivel absoluto, para este usuario concreto, el basal de Apple estaba inflado. Aplicar un factor Basal ≈0,80 (en vez de 1,00) redujo el factor de ingesta recomendado por la MCO de 1,74 a 1,44 sobre la misma ventana — confirma que la ingesta estaba compensando, en parte, la inflación del basal en este caso. Afinando después contra el valor medio de Mifflin/Katch-McArdle de este usuario (~1880 kcal) se convergió en Basal=0,77 para él — el mismo número, obtenido por dos vías independientes (plausibilidad absoluta contra Mifflin/Katch-McArdle, y ensayo-error contra su peso real), lo que refuerza la confianza en ese valor para ese usuario. El valor por defecto de BasalEnergyImportedCorrectionFactor para usuarios nuevos sigue siendo 1,00 (ver herramienta más abajo para que cada usuario derive el suyo).

Metodología canónica de calibración (prueba y error contra peso real)

Es el procedimiento que debe seguir el usuario en el panel de Calibración, en este orden exacto:

  1. Fijar los tres factores en 1:1:1 (neutro) y guardar.
  2. Recalcular histórico para propagar el neutro a todos los registros pasados.
  3. Calibrar solo la ingesta con la calculadora MCO (≥30 días) y aplicar el factor recomendado.
  4. Recalcular histórico de nuevo para propagar el nuevo factor de ingesta.
  5. Ejecutar la Validación: peso calculado vs. peso real (ver más abajo) sobre un período que incluya días no usados en la calibración anterior, y comprobar si el Δ peso implícito coincide razonablemente con el Δ peso real.
  6. Si no coincide, antes de volver a tocar la ingesta (que solo desplazaría el error, no lo eliminaría), comprobar si el basal es plausible en términos absolutos frente a Mifflin/Katch-McArdle como se describe arriba; solo si hay evidencia de inflación/deflación ajustar Basal y repetir desde el paso 2.

Historial de validación fuera de muestra del fundador según se afinó su basal (de nuevo, cifras de un único caso, no una expectativa general): con Basal=1,00 el residuo era 18,4% (0,70 kg); con Basal=0,80/Ingesta=1,44 sobre 57 días, 0,57 kg (~15%); convergiendo finalmente en Basal=0,77 (el valor que igualaba su basal medio con su propia referencia Mifflin/Katch-McArdle) el residuo bajó a 0,33 kg sobre 90 días — una ventana más larga y exigente que las anteriores, lo que hace la mejora más significativa que una simple reducción proporcional. No se considera necesariamente su óptimo absoluto, pero el error residual ya es pequeño frente al ruido esperable (hidratación puntual, imprecisión Atwater/IA residual).

Herramienta en la app: EnergyWeightValidationService

Antes esta comprobación (paso 5) exigía scripts SQL manuales fuera de la aplicación. La tarjeta "Validación: peso calculado vs. peso real" del panel de Calibración la automatiza: suma EnergyDailyBalances.BalanceEnergeticoKcal exactamente en la misma ventana que tiene peso registrado en BiodataDiaria (primer y último día con peso > 0 dentro del período elegido, no el período completo solicitado si hay huecos en los extremos), la divide entre 7700 kcal/kg para obtener el Δ peso implícito, y lo compara contra el Δ peso real (último peso − primer peso). Muestra la diferencia en kg y en % relativo, y avisa explícitamente si el período solapa con el usado para calibrar la ingesta — en ese caso la coincidencia es en parte esperable (tautológica), no una predicción genuina; para una validación honesta hay que usar un período distinto o más amplio que el de calibración.

8.23. Sistema híbrido de detección de alérgenos en estimación nutricional
Motivación

El perfil personal recoge alergias/intolerancias alimentarias declaradas (PerfilPersonal.AlergiasAlimentarias, texto libre). Se detectó que este dato no llegaba a ninguna de las consultas de IA de la aplicación (asistente conversacional, resúmenes de biodata, historia clínica, análisis de ingesta), pese a ser directamente relevante en contexto nutricional.

Descarte de la primera aproximación

Un primer intento incrustó el aviso directamente en el prompt de cálculo de macros (NUTRITION_INTAKE_ANALYSIS). El prompt de cálculo es un DSL minimalista tipo configuración (@rules / @task / @output) ajustado por prueba y error para producir de forma fiable el JSON de macros; cualquier texto añadido — incluso un placeholder condicional vacío en ausencia de alergias — desestabilizó el resultado numérico (gramos a 0, kcal incorrecta en alimentos ya resueltos). Se revirtió y se descartó modificar ese prompt para cualquier finalidad ajena al cálculo de macros.

Arquitectura final: dos capas desacopladas del cálculo

El aviso se resuelve en un paso posterior e independiente, una vez el JSON de macros ya está calculado y validado, de modo que un fallo en la detección de alérgenos nunca puede alterar el cálculo nutricional:

  • Capa determinista (AllergenKeywordMatcher): diccionario de palabras clave que cubre los 14 alérgenos de declaración obligatoria del Reglamento (UE) 1169/2011, con normalización de texto (sin tildes, minúsculas) y detección explícita de frases de negación ("sin gluten", "libre de lactosa", "deslactosado"...) para evitar falsos positivos por coincidencia de subcadena (p. ej. "sin gluten" contiene literalmente "gluten"). Es la única garantía de determinismo del sistema: mismo alimento, mismo resultado, siempre.
  • Capa complementaria por IA: llamada independiente que compara nombres de alimentos contra las alergias declaradas, con conocimiento semántico más amplio que el diccionario, pero sin garantía de determinismo — se observó empíricamente que el mismo alimento y la misma alergia producían respuestas distintas entre llamadas sucesivas (mismo modelo, temperature=0), y que la regla de negación del propio prompt no siempre se respetaba. Por ello, el resultado de esta capa se filtra por el mismo diccionario de negación antes de fusionarse — el diccionario actúa como veto final sobre falsos positivos de la IA, no solo como fuente independiente.

Ambas capas se aplican de forma idéntica en los dos motores de cálculo de ingesta (IA pura y modo híbrido OFf+IA), mediante la clase compartida AllergenKeywordMatcher, invocada tanto desde NutritionAiService como desde NutritionOpenFoodFactsService — evitando que un alimento resuelto directamente por la etiqueta real de OpenFoodFacts quede fuera de la verificación.

Criterio de producto

El sistema solo emite aviso cuando existe certeza razonable a partir del propio nombre del alimento (p. ej. pan, pasta o cerveza para gluten). Categorías genuinamente ambiguas — p. ej. jamón cocido, donde la presencia de gluten depende del aditivo del fabricante concreto y no del tipo de alimento — se excluyen deliberadamente del diccionario, para no generar una falsa sensación de certeza ni de responsabilidad del sistema sobre una valoración que corresponde al etiquetado del producto real.

8.24. Oxigenación nocturna: SDS/SAP como cribado de apnea del sueño
Motivación

El sistema ya importa SpO₂ nocturna de Apple Watch a través del export.xml de HealthKit, pero hasta ahora solo se aprovechaba como agregado diario único (BiodataDiaria.SaturacionOxigeno). Combinado con datos ya presentes en el perfil y en Biodata (IMC, edad, tensión arterial, alcohol), esa señal permite un cribado orientativo de riesgo de apnea del sueño sin necesidad de ningún dispositivo o dato adicional.

Arquitectura de dos índices desacoplados

El panel Oxigenación nocturna (SleepOxygenationService) calcula deliberadamente dos índices distintos, para no confundir lo que el dispositivo observa con la probabilidad clínica que se infiere a partir de ello:

  • SDS (Índice de Desaturación del Sueño, 0-100): puramente objetivo. Mismo patrón de baseline personal que RecoveryService (media/desviación típica sobre una ventana rolling, z-score acotado a ±3σ), pero con la dirección invertida respecto a Recuperación: aquí un valor más alto es peor (más desaturación que el patrón habitual del propio usuario), no mejor. Cada noche se delimita entre las 20:00 y las 11:00 (hora de Madrid) para separar SpO₂ de sueño de SpO₂ diurna, exige un mínimo de 5 lecturas esa noche para fiarse de su media (el Apple Watch muestrea SpO₂ de forma episódica, no continua) y un mínimo de 7 noches previas para fiarse del baseline.
  • SAP (Probabilidad de Apnea del Sueño, 0-100): mitad SDS, mitad puntuación de un cribado STOP-BANG simplificado de 7 factores (IMC>30, edad>65, ronquidos, apnea observada por terceros, hipertensión, somnolencia diurna, alcohol registrado ese día), sin pesos diferenciados por factor — igual que el cuestionario STOP-BANG real, que tampoco los tiene. Los factores sin dato disponible ese día se excluyen del cálculo y se renormaliza sobre los restantes, en vez de contarlos como ausentes/negativos; con menos de 4 de los 7 disponibles, el resultado se marca explícitamente como cribado incompleto.
Fuente de datos: sin tabla de series intradía

A diferencia del resto de señales del sistema, la SpO₂ intradía no se persiste en base de datos. Se lee bajo demanda directamente del export.xml de HealthKit del usuario mediante el servicio de series temporales ya existente (IHealthKitTimeSeriesService), el mismo que alimenta el módulo de Evolución Temporal (sección 8.18). Dado que ese fichero puede pesar varios cientos de MB, el recálculo completo solo se dispara cuando su fecha de última escritura (HealthImportPathService.GetUserExportLastWriteUtc) es más reciente que el último cálculo persistido para ese usuario — en caso contrario se sirve directamente lo ya calculado, sin releer el fichero.

Criterio de producto

SAP se presenta siempre como cribado orientativo, nunca como diagnóstico: la interfaz distingue explícitamente "esto observa tu dispositivo" (SDS) de "esto es una probabilidad de cribado, consulta con un profesional" (SAP), con ayuda visible junto a ambas siglas — no solo en un texto explicativo aparte — dado que la base de usuarios del sistema es predominantemente hispanohablante y las siglas se definen en inglés en la literatura clínica de referencia (STOP-BANG).

8.25. Generador de Informes: exploración dinámica sin persistir diseño ni resultado
Motivación

Investigar una hipótesis puntual (p. ej. "¿el consumo de alcohol de estos días explica esta subida de peso?") exigía hasta ahora cruzar manualmente BiodataDiaria y EnergyDailyBalance por SQL directo, fuera de la aplicación. El Generador de Informes traslada ese cruce al propio sistema: el usuario elige qué campos y qué ventana temporal comparar, sin necesidad de conocimientos de SQL ni de esperar a un desarrollo a medida cada vez que surge una pregunta nueva.

Catálogo cerrado por tema clínico, no por tabla técnica

El selector de campos agrupa ~40 variables en 6 temas pensados para el usuario final (Peso y composición corporal, Actividad física, Cardio y variabilidad, Sueño, Nutrición e ingesta, Energía y balance) en vez de exponer los nombres reales de las tablas de origen — BiodataDiaria y EnergyDailyBalance se unen por UsuarioId+Fecha (InformeDinamicoService) de forma transparente para el usuario. Los cuatro macronutrientes muestran gramos junto con su aportación calórica entre paréntesis (factor Atwater: 4/4/9/7 kcal por gramo de hidratos/proteína/grasa/alcohol), para no obligar a hacer esa cuenta aparte.

Sin persistencia — ni diseño ni resultado

Ni la selección de campos ni el informe resultante se guardan en base de datos: cada petición recalcula desde cero a partir de las tablas fuente. La única "persistencia" es la propia URL, ya que la ventana temporal, los campos elegidos y el criterio de orden viajan como querystring de una petición GET — de modo que un informe concreto es reproducible o compartible con solo reenviar el enlace, sin necesidad de una tabla de "informes guardados" ni de exponer los datos de un paciente en un almacén adicional. El orden por cualquier campo (no solo por fecha) se resuelve por reflexión sobre un único catálogo (InformeDinamicoCampos), evitando un switch manual por cada una de las ~40 variables.

Doble eje del gráfico por magnitud, no por tema

Al permitir combinar libremente campos de escalas muy distintas (pasos en miles, IMC en decenas, sodio en miligramos), un único eje Y haría que el campo de mayor magnitud aplanase visualmente a los demás. El gráfico calcula el valor máximo absoluto de cada serie ya seleccionada sobre los propios datos del usuario y la asigna al eje izquierdo o derecho según supere o no un umbral fijo — una heurística basada en los datos reales, no en una clasificación temática rígida por campo.

Criterio de producto

La cabecera del informe (paciente, edad, sexo, altura, autor y momento de generación) está pensada para que el documento tenga sentido por sí solo si se imprime o exporta a PDF, sin depender del contexto de la aplicación. Para el traslado a un asistente de IA se optó deliberadamente por un botón que copia el informe en formato tabla al portapapeles, en vez de construir una tubería de IA nueva — la interpretación se apoya en el asistente conversacional ya existente en la aplicación, sin duplicar infraestructura.

8.26. Correlación Termogénesis-Recuperación: convergencia entre dos baselines personales
Motivación

El indicador de Adaptación metabólica (TDEE_teórico − TDEE_real) arrastra un sesgo estructural: el "teórico" depende de una fórmula (Mifflin-St Jeor corregido con factores de calibración) que nunca acierta el basal real de un individuo concreto al milímetro — cada persona difiere de la predicción poblacional por su propio genotipo/fenotipo. Ese sesgo es aproximadamente constante en el tiempo para un mismo usuario (el metabolismo basal es fisiológicamente estable, cambia despacio — el propio estudio de seguimiento de "The Biggest Loser" muestra una supresión gradual y sostenida, no picos), así que el valor absoluto de Adaptación en un momento dado no es del todo fiable, pero su tendencia sí lo es: la resta entre dos snapshots cancela el sesgo constante.

Dos señales independientes, mismo principio de baseline personal

CorrelacionTermogenesisService cruza el histórico ya persistido de MetabolicAdaptationSnapshot (ventanas de 28 días rodantes) con RecoveryDailyScore (diario, HRV/FC reposo/temperatura/sueño), promediando Recovery sobre la misma ventana temporal de cada snapshot de Adaptación — no son directamente comparables fila a fila por su distinta granularidad. Ambos sistemas comparten ya el mismo criterio de cálculo (contra el baseline histórico del propio paciente, no contra baremos poblacionales), lo que los hace comparables por construcción sin necesidad de normalizarlos de ninguna forma especial.

Convergencia como señal, no un score nuevo

No se calcula ningún índice compuesto ni coeficiente de correlación estadística: se marca ConvergenciaNegativa cuando, respecto a la ventana anterior, la Adaptación empeora (% sube) y el Recovery medio empeora (score baja) a la vez. La justificación fisiológica es directa, no solo estadística: la restricción calórica crónica es un estresor real, asociado en la literatura a caída de HRV y subida de FC reposo (contextos de RED-S/infra-alimentación sostenida), y la propia reducción de actividad simpática es uno de los mecanismos propuestos de la termogénesis adaptativa — si ambas señales empeoran a la vez, es la misma fisiología vista desde dos ángulos independientes, no una coincidencia de dos métricas sin relación mecanística entre sí.

Criterio de producto

No introduce ningún cálculo nuevo de Adaptación ni de Recovery — solo los alinea temporalmente y señala cuándo coinciden. Presentado explícitamente como señal de alerta para valorar con el profesional, nunca como diagnóstico, igual que el resto de cribados del sistema.

8.27. Conservación de la Flexibilidad Metabólica: modelo jerárquico de referencia
Motivación y distinción conceptual

La Adaptación Metabólica (apartado 8.20) mide si el organismo está ahorrando energía, comparando TDEE teórico y TDEE real. La Flexibilidad Metabólica es un concepto distinto: la capacidad de alternar con facilidad entre sustratos energéticos (grasa/glucosa) según la disponibilidad de energía. Fenotipo no mide el cambio de sustrato directamente —requeriría calorimetría indirecta con cociente respiratorio, fuera del alcance de un wearable de consumo—. En su lugar, FlexibilidadMetabolicaService presenta un conjunto de señales conductuales con relación documentada con la flexibilidad metabólica, mostradas por separado, sin combinarlas en un único número: ponderarlas en un score compuesto exigiría pesos que hoy no están validados con datos propios del usuario.

Modelo jerárquico de referencia: baseline propio antes que umbral poblacional

Fenotipo aplica de forma consistente un principio de diseño: siempre que existe suficiente información longitudinal, la comparación se realiza contra el comportamiento histórico del propio paciente, no contra referencias poblacionales. Las recomendaciones poblacionales (p. ej. OMS/ACSM) solo se contemplan para inicializar el modelo o cuando la evidencia individual es todavía insuficiente. En este módulo, el entrenamiento de fuerza y de cardio (BiodataDiaria.MinutosEjercicioGym / MinutosEjercicioExterior) se comparan contra la propia media del paciente en los 90 días anteriores a la ventana analizada, no contra una cifra externa. Si el paciente no acumula al menos 14 días con datos en ese periodo previo, el criterio correspondiente se omite del checklist en vez de sustituirse por un valor poblacional por defecto — el sistema prefiere no opinar a comparar contra una referencia que no es la del propio individuo.

La variabilidad de peso (dispersión de los pesajes alrededor de la tendencia, en % del peso corporal) seguía inicialmente el mismo principio solo a medias: se mostraba como cifra descriptiva, pero su etiqueta cualitativa (Baja/Normal/Elevada) usaba umbrales fijos en % de peso corporal en vez de la propia línea base del paciente — una omisión detectada y corregida para mantener la coherencia con fuerza y cardio. Ahora MetabolicAdaptationService se recalcula también sobre los 90 días anteriores a la ventana (mismo requisito de 14 días mínimos con datos) para obtener la variabilidad de peso residual habitual del paciente, y la etiqueta compara la ventana actual contra esa línea base propia (por debajo del 70% = Baja, 70-130% = Normal, por encima del 130% = Elevada) en vez de contra un umbral poblacional.

El déficit prolongado se juzga por adaptación medida, no por recuento de días

Un primer diseño de este módulo penalizaba cualquier racha de déficit energético superior a 14 días, asumiendo que el tiempo en déficit predice adaptación. El análisis del propio histórico de un usuario (más de 60 días consecutivos en déficit, con pérdida de peso sostenida, R² de la regresión de peso alto y, sin embargo, Nivel de Adaptación Metabólica en "Sin adaptación" durante casi toda la ventana) mostró que esa suposición no se sostiene siempre: dos pacientes con la misma racha de días en déficit pueden tener respuestas fisiológicas muy distintas. El criterio se corrigió para usar directamente el Nivel/AdaptacionResidualPorcentaje ya calculado por MetabolicAdaptationService sobre la misma ventana — el dato medido del propio paciente sustituye a la suposición basada en tiempo transcurrido.

Checklist sin pesos y explicación por reglas

Los criterios que sí se combinan lo hacen por recuento simple (enfoque tipo test Apgar): cada criterio cumplido suma un punto, sin ponderación relativa entre ellos, y el total es variable (normalmente entre 3 y 6, según haya o no línea base de entrenamiento disponible y haya o no un evento de readaptación evaluable). La interpretación en lenguaje natural que acompaña al checklist se genera mediante plantillas condicionales deterministas a partir de los mismos datos ya calculados — no interviene ningún modelo de lenguaje, lo que garantiza reproducibilidad exacta del texto mostrado para una misma combinación de datos.

Recuperación tras readaptación: verificar el efecto, no solo el evento

Detectar un Refeed o Diet break (apartado 8.28) no implica que haya funcionado. El criterio "Recuperación tras tu última readaptación" compara la adaptación medida (MetabolicAdaptationSnapshot.AdaptacionResidualKcal / TdeePredichoMediaKcal) en el snapshot más cercano ANTES del evento frente al más cercano DESPUÉS, exigiendo al menos 7 días de margen tras el fin del evento para que exista un snapshot que ya refleje el efecto. Solo se marca como éxito si la adaptación medida bajó realmente; no se asume que "hacer una readaptación ya basta". Se omite del checklist si no hay ningún evento con margen suficiente en el histórico disponible, en vez de forzar una comparación sin margen.

Recomendaciones no prescriptivas con prioridad cualitativa

El panel "Qué podrías valorar" genera una tarjeta por cada criterio del checklist no cumplido, con una prioridad cualitativa (Alto/Medio), no un score numérico. La prioridad se apoya en la evidencia ya citada en el apartado 8.20 (estudio MATADOR para déficit/readaptación, mTOR y preservación muscular para proteína, ambos "Alto") frente a los criterios autorreferenciales de fuerza/cardio ("Medio"), que son un patrón conductual propio sin el mismo respaldo bibliográfico directo. Se presenta explícitamente como contexto para valorar con el profesional, no como prescripción.

Criterio de producto

Los nombres de las puntuaciones son deliberadamente no diagnósticos ("Contexto favorable/poco favorable", no "Flexibilidad alta/baja"): el sistema no mide la flexibilidad metabólica de forma directa, mide la probabilidad de conservarla dados los hábitos observados. Se presenta como contexto para valorar con el profesional, igual que el resto de cribados del sistema.

8.28. Detección retrospectiva de eventos de readaptación (Refeed y Diet break)

En pantalla, estos eventos se presentan castellanizados como "Readaptación ligera (Refeed)" y "Readaptación prolongada (Diet break)" — término distinto y deliberadamente separado de "Reactivación metabólica corta/prolongada" (apartado 8.20, la recomendación educativa del modal): "Readaptación" nombra el evento detectado automáticamente; "Reactivación metabólica" sigue siendo la recomendación de cuándo conviene aplicarlo.

Motivación

Un refeed (reactivación corta) y un diet break (pausa estructural) son estrategias documentadas para mitigar la adaptación metabólica (MATADOR, Byrne et al. 2017), pero Fenotipo no pedía al paciente que declarase cuándo las aplicaba. RefeedDietBreakDetectionService los infiere retrospectivamente a partir del balance energético diario ya registrado (EnergyDailyBalance.BalanceEnergeticoKcal), sin necesidad de que el usuario marque nada expresamente.

Clasificación por bandas y rachas

Cada día se clasifica en Déficit / Mantenimiento / Superávit agresivo según si el balance diario se aleja más o menos de ±10% de su TDEE teórico. Un Refeed es una racha de 1-2 días en banda de mantenimiento, precedida de al menos 5 días consecutivos en déficit; un Diet break es una racha de 7-14 días, precedida de al menos 14 días consecutivos en déficit. Rachas de 3-6 días quedan deliberadamente sin clasificar: la literatura no sostiene un límite tan fino como para forzar una etiqueta. Solo se clasifica una racha ya concluida (con datos posteriores que confirman su fin) — una racha todavía en curso al final del histórico disponible podría seguir creciendo y cambiar de categoría, así que se deja sin clasificar hasta que termine.

Confirmación informativa, no excluyente

Tras cada evento se registra si el paciente volvió a déficit en los 1-2 días siguientes (Confirmado / SinConfirmar / NoVolvioADeficit). Esta confirmación es puramente informativa: no reclasifica el tipo de evento ya detectado, porque una reactivación deliberada tiene valor aunque no derive en una vuelta inmediata a déficit (podría marcar, por ejemplo, un cambio de fase consciente).

Advertencia explícita: readaptación impulsada por alcohol, no por hidratos

Un refeed real busca reponer glucógeno y leptina principalmente vía hidratos; el alcohol no cumple esa función y puede además perjudicar sueño/HRV y frenar la oxidación de grasa. Como el detector solo mira el balance calórico total, un día podría alcanzar la banda de mantenimiento por alcohol sin que haya habido un aumento real de hidratos. Para cada evento se compara el salto de balance real (déficit previo → ventana del evento) frente a cuánto de ese salto explican el alcohol y los hidratos por separado; se marca ImpulsadoPorAlcohol solo si el alcohol explica al menos la mitad del salto y los hidratos no explican también al menos la mitad — así un refeed real con hidratos (aunque incluya alcohol) no se marca, solo el que depende del alcohol para llegar a mantenimiento. Deliberadamente no se excluye el día ni se oculta el evento: se advierte de forma explícita en pantalla, para no decidir en silencio de la omisión algo que el profesional debe poder valorar.

Naturaleza retrospectiva y ejecución

El sistema no distingue si el patrón fue deliberado o casual — el efecto fisiológico medido sobre el balance energético es el mismo en ambos casos. La detección se ejecuta automáticamente cada noche para todos los usuarios dentro de MetabolicSnapshotWorker, reescaneando el histórico completo de balance energético del paciente (operación idempotente vía upsert sobre RefeedDietBreakEvents, con clave única UsuarioId+FechaInicio+Tipo); también puede forzarse manualmente desde la interfaz sin esperar al ciclo nocturno.

Criterio de producto

Los eventos detectados se muestran como contexto en Adaptación Metabólica y en Conservación de la Flexibilidad Metabólica (apartado 8.27). En Flexibilidad Metabólica, el evento concluido más reciente con margen suficiente sí alimenta un criterio puntuado del checklist ("Recuperación tras tu última readaptación"); en Adaptación Metabólica sigue siendo puramente informativo.

8.29. Inspector de asuntos internos IA — observabilidad de llamadas a Ollama/Claude
Motivación

Antes de este módulo, la lentitud o los fallos intermitentes de Ollama Cloud solo podían diagnosticarse por deducción indirecta (tiempos percibidos, capturas de pantalla, comparación manual entre entornos). IAiInspectorLogService registra cada llamada real a IA con datos objetivos, para diagnosticar con evidencia en vez de suposición.

Qué se registra

Cada intento de llamada (incluidos los reintentos) genera una línea con: proveedor (Ollama/Claude), origen (tipo de análisis: NUTRITION_INTAKE_ANALYSIS, HISTORIA_PACIENTE_IA, DAILY_METABOLIC_ANALYSIS, etc.), email del usuario que originó la llamada, modelo concreto usado, número de intento, resultado, tipo de error si lo hay, código HTTP, motivo de parada del modelo (DoneReason de Ollama o StopReason de Claude — mismo campo, dos providers), tokens de prompt y de respuesta (PromptEvalCount/EvalCount de Ollama, Usage.InputTokens/Usage.OutputTokens de Claude), duración y detalle textual libre.

Persistencia sin depender de acceso a directorios de sistema

El hosting de despliegue no da acceso a directorios principales del servidor. El log se persiste en AppData/IAInspectorLogs dentro del propio árbol de la aplicación (raíz de contenido, no wwwroot) — como app.UseStaticFiles() se invoca sin parámetros, solo sirve el wwwroot por defecto, así que esta carpeta queda automáticamente inaccesible por HTTP sin configuración adicional. Un fichero por día (ia-inspector-yyyy-MM-dd.log), con la fecha y hora calculadas siempre en huso horario de Madrid (no UTC crudo), para que el corte de "día" coincida con el que percibe el usuario. Si la escritura falla por cualquier motivo, cae a un fichero de respaldo (nunca se propaga el fallo a la llamada real de IA).

Corrección de enrutado Ollama/Claude en tareas en segundo plano

La construcción del instrumento reveló un fallo estructural preexistente: la fábrica de IAiCompletionService decide el proveedor según AiProviderSettings.ResolveProvider(isAdmin), y isAdmin se determinaba leyendo IHttpContextAccessor.HttpContext. En los análisis que se procesan en segundo plano mediante IServiceScopeFactory.CreateScope() (Intake, Historia Paciente, Informes, BioData Evolution, HealthDash y los cuatro análisis de EnergyManagerController), ese scope nuevo no tiene petición HTTP activa, así que isAdmin siempre resolvía false y la tarea usaba el proveedor global, ignorando el proveedor personal elegido por el administrador en el panel. Se corrigió con un puente scoped (IAiProviderContext): el controlador captura isAdmin con el HttpContext real antes de encolar, y lo fija en el scope de la tarea justo antes de resolver el servicio de IA; el mismo puente transporta también el origen del análisis y el email del usuario para el log del inspector.

Endurecimiento del criterio de éxito (Ollama)

El streaming de /api/generate puede cortarse a mitad sin llegar el chunk final GenerateDoneResponseStream — antes, si había algo de texto acumulado, se daba la respuesta por buena igualmente, con riesgo de devolver contenido incompleto sin ningún indicio. Ahora, si no llega ese chunk final, se trata como fallo (ErrorTipo = "SinDone") y se reintenta con el mismo backoff que un timeout, en vez de devolver una respuesta potencialmente truncada como si fuera correcta.

Panel de administración

Accesible solo para el rol Admin (IaInspectorLogController): listado de ficheros por día con el de hoy resaltado, vista tabular filtrable de cada línea (proveedor, origen, usuario, modelo, resultado, tokens, duración), y borrado de ficheros antiguos con confirmación mediante el modal genérico del sistema (_ModalConfirmarAccion, no el confirm() nativo del navegador).

9. Notas sobre el método heurístico de ensayo y ajuste progresivo

El término heurístico se emplea para designar un enfoque de decisión apoyado en reglas prácticas, comparación temporal y ajuste progresivo, sin pretensión de sustituir el juicio clínico.

El sistema observa resultados, detecta desviaciones, compara tendencias y reajusta recomendaciones en función del comportamiento real de las variables biométricas y clínicas.

12. Estado de protección y derechos de autor

Estado registral: Obra software en trámite de inscripción en el Registro de la Propiedad Intelectual.

Derechos de autor:
© 2026 Ricardo M. Trigo Calonge.
Todos los derechos reservados.

Queda prohibida la reproducción, distribución, comunicación pública o transformación, total o parcial, del código fuente, modelos estructurales, arquitectura software, algoritmos, lógica funcional y documentación técnica asociada, sin autorización expresa del titular.

Índice