Agentic-Smart-Health

mcp
Security Audit
Warn
Health Warn
  • License — License: Apache-2.0
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Low visibility — Only 5 GitHub stars
Code Warn
  • fs module — File system access in .github/workflows/ai-code-review.yml
Permissions Pass
  • Permissions — No dangerous permissions requested

No AI report is available for this listing yet.

SUMMARY

Multi-agent system that unifies heterogeneous dental data (CBCT, STL, clinical reports, photos) into a patient Digital Twin built on Gaussian Splatting, with per-region clinical attributes and longitudinal series.

README.md

Agentic Smart Health

Sistema multiagente para la integración, análisis y representación de datos clínicos dentales heterogéneos sobre un Digital Twin del paciente, basado en Gaussian Splatting con atributos clínicos por punto/zona y soporte de series temporales.

tests

Proyecto open source · Licencia Apache 2.0 · Python ≥ 3.13


Contexto del proyecto

El sector dental maneja datos altamente heterogéneos: escáneres CBCT (DICOM), archivos STL de escaneos intraorales, informes clínicos en PDF e imágenes 2D. Esta información vive fragmentada en silos por proveedor y por clínica, lo que impide un seguimiento longitudinal real del paciente y compromete su soberanía sobre los propios datos de salud.

Agentic Smart Health aborda este problema mediante una arquitectura multiagente que organiza, integra y analiza de forma autónoma datos dentales heterogéneos, proyectándolos sobre un gemelo digital del paciente. El proceso es reversible: el sistema puede regenerar ficheros STL e imágenes directamente desde el Digital Twin.


Cómo encaja todo (vista rápida)

Varios agentes (trabajadores con una única responsabilidad) traducen ficheros
clínicos heterogéneos (DICOM, STL, PDF, foto) a un documento común —el
TwinSnapshot de core-schemas—, lo enriquecen y lo
materializan para que un visor lo muestre; un orquestador
(agent-orchestrator) reparte el trabajo. El
«modelo» (LLM) no es una capa central: es el cerebro que razona dentro de un
agente concreto (hoy solo research-agent), y no todos lo necesitan.

📐 Mapa completo de las 6 capas y el recorrido del dato (pensado para quien
llega nuevo): docs/architecture/multi-agent-pipeline.md §0.

Estado actual — MVP cerrado (semana 8)

La ingesta, la fusión, la segmentación y los cuatro canales de exportación están
construidos y probados
, y el recorrido completo entrada → twin → fichero tiene prueba de
integración.

📋 El inventario honesto del cierre —qué está medido, qué no está resuelto y en qué
orden atacarlo— está en docs/cierre-mvp.md. Lo que sigue es lo
que funciona; lo que no, está allí con su medida.

Contrato de datoscore-schemas (Pydantic v2, esquema 1.6.0). El
TwinSnapshot es el documento común: gaussian_field_ref (campo 3DGS),
surface_ref (malla), image_refs (fotos, lista), regional (observaciones por
diente FDI) y provenance por valor (trazabilidad raw→contrato).

Agentes de ingestapackages/ingestion-agents: 4 modalidades, una por
soporte, deterministas y fail-loud (nunca lanzan una excepción; devuelven estado +
confianza y dejan la basura en cuarentena):

Agente Entrada Produce
mesh-agent STL / OBJ (escáner intraoral) superficie + normales
cbct-agent DICOM (CBCT) volumen → campo de gaussianas
report-agent PDF / TXT (informe clínico) pH por diente FDI (reglas o LLM)
image-agent JPG / PNG / HEIC (foto) píxeles RGB sin EXIF

Diseño transversal: Provenance por valor, ArtifactStore direccionado por
contenido (SHA-256), gate de human-in-the-loop por umbral de confianza (0,7) y
anonimización (EXIF fuera, seudonimización HMAC — ver
docs/architecture/anonymization-strategy.md).

Orquestadoragent-orchestrator dispara los agentes en paralelo, ensambla el
TwinSnapshot, aplica el gate HITL y respeta el presupuesto de <60 s.

Reconstrucción 3DGS (en notebooks, ver más abajo): malla real → Blender
(vistas con pose exacta, sin COLMAP) → gsplat → campo de gaussianas evaluable en
vistas retenidas, servido en un visor web (dental-3dgs-viewer,
repo aparte) con dos casos reales — Teeth3DS+ (con color por armónicos) y Bite2Text
(color de esmalte/encía muestreado de las fotos con el image-agent).

Lo que el contrato promete de todos los agentes por igual —los tres caminos
(OK/MISSING/FAILED), no lanzar nunca, emitir Provenance, ser reproducible y
no copiar dato clínico a la cuarentena— lo verifica una suite de conformidad
(test_conformidad.py)
parametrizada sobre los cuatro agentes. Un agente nuevo entra en esa lista y queda
sometido a las nueve reglas sin escribir un test.

Y frente al caso que sale bien de synthetic.py hay un catálogo de casos
límite
(edge_cases.py):
cabecera DICOM truncada, resonancia etiquetada como CBCT, espaciado cero o negativo,
NaN en la malla, PNG a medias, rutas con unicode, enlaces rotos. Cada caso declara
qué debe pasar y por qué, porque no todos deben fallar: un pH imposible se
descarta línea a línea y la ingesta sigue siendo válida. Encontró cuatro defectos
reales el día que se escribió.

Cobertura: la suite completa en verde, verificada en cada push y cada PR por
el workflow tests — el badge de arriba lo publica
esa ejecución. El CI falla si la cobertura de agentes y pipeline baja del 80 %,
que es el criterio de éxito del proyecto; el umbral vive en pyproject.toml, así
que uv run pytest --cov mide en local exactamente lo mismo. Aquí no se escribe
ningún número a mano: los recuentos manuales envejecen solos.

Lo que el CI no verifica, dicho antes de que haga falta preguntarlo. El runner no
tiene GPU. Eso no deja partes del pipeline sin probar: ningún módulo de
packages/ ni de apps/ importa torch al importarse
, a propósito — los modelos
entran por los Protocol Segmenter y Registrar, así que lo que se ejecuta en
producción es numpy y se prueba entero. La única excepción es
gaussian_engine.ajuste, que
importa torch dentro de la función que ajusta elipsoides y con un mensaje que dice
qué extra falta: así el paquete se instala y se prueba sin CUDA. Lo que queda fuera son
los siete scripts de investigación que sí lo necesitan
(entrenar_3dgs.py,
refina_3dgs.py,
segmentar_fdi.py,
entrena_diente_cbct.py,
entrena_gs_escaner.py,
composicion_cbct_ios.py,
ablacion_recetas.py) y los notebooks: se ejecutan a
mano en una máquina con GPU y su producto es una medida, no un servicio. Cuando un
número de esta página sale de ahí, la sección lo dice y enlaza el script que lo produjo.

Fusión y segmentaciónfusion-agents (registro geométrico + anclaje semántico
al FDI, ADR 004) y analysis-agents (segmentation-agent: region_id por gaussiana
y el mapa FDI → confianza que consume la fusión semántica). El registro
CBCT↔intraoral está medido sobre un paciente real
(scripts/registro_ios_cbct.py): 0,452 mm sobre la
población solapada, con la etapa gruesa que el ADR dejaba pendiente ya implementada.

Exportación reversibleexport-agents, y todos miden lo que producen
releyéndolo
en vez de prometerlo:

Agente Materializa Error medido
export-agent surface_refSTL binario 3,8·10⁻⁶ mm de desviación máxima sobre un escaneo real de Teeth3DS+ (110.804 vértices, arcada de 86 mm) en 0,07 s — la que impone el float32 del formato, cuatro órdenes de magnitud bajo el presupuesto de 0,1 mm del brief
field-export-agent gaussian_field_refPLY binario 0,0 mm exactos sobre el CBCT de un paciente real (498.407 primitivas, 27,9 MB en 0,06 s): las posiciones van en double para que la verificación mida bugs de formato y no el redondeo
render-export-agent gaussian_field_refPNG multivista PSNR 102 dB · SSIM 0,99999999 en el ciclo twin → PLY → render, reproducible byte a byte
composite-mesh-export-agent escáner + CBCT → arcada imprimible + un STL por diente 0,372 mm (p95) · sesgo −0,02 mm — y este número no es reversibilidad: mide el reconstructor de raíces contra la corona escaneada, que es la única banda donde hay dos medidas del mismo tejido. Por eso este canal queda fuera de la comprobación de reversibilidad

El STL sale en el sistema del escáner o en el del twin; el PLY, centrado o en mm reales
del CBCT. Y un snapshot parcial lo declara en hitl_reasons y dentro del propio
fichero
.

⚠️ La distinción que separa las tres primeras filas de la última. Los canales
reversibles re-materializan lo que entró: su desviación responde «¿sale lo que metí?» y el
presupuesto de 0,1 mm del brief va sobre eso. El último escribe geometría que no entró
—la raíz, que ninguna otra medida cubre— y su número responde a otra pregunta con la misma
unidad. Medirlas en el mismo cajón ponía el recorrido entero en rojo por 0,37 mm de una
superficie que nadie había medido antes, mientras los canales que sí prometen
reversibilidad daban 0,000000 mm.

El contenedor .uos y su visor — el entregable, y lo que el resto alimenta. Un caso
clínico real cierra en 419 entradas · 397 cortes DICOM · conformidad UOS-Core + UOS-Vol ·
0 errores y 0 avisos · 18 vistas
, con la serie CBCT byte a byte y hash por corte
—hay tests que lo comprueban quitando un corte, colando uno de más y alterando uno—. El
visor de referencia (repo aparte) lo abre en el
navegador por rangos y sin subir nada: malla, capa clínica por pieza, vistas guardadas
y las capas del campo gaussiano. Qué lleva dentro y a quién le sirve:
docs/spec/uos-format-spec-v0.2.tex.

Todavía no: color per-píxel y pathology-agent. Sobre el color, la pregunta se ha
estrechado y conviene el matiz: la señal está medida —un umbral sobre a* separa
diente de encía con 3,4–4,3 σ en las cuatro fotos de arcada que el contenedor ya lleva
(docs/research/frontera-encia-desde-foto.md)—
y lo que falta es la pose de cámara, que hoy va a projection: null con el campo ya
definido en el esquema. Sigue pendiente del ADR de motor de render de dónde sale el
color
de un campo de densidad: un CBCT no mide color y el PLY no se lo inventa. El paquete 3dgs-engine es hoy un
placeholder: la reconstrucción vive en los notebooks + gsplat.


Arquitectura del monorepo

El repositorio está organizado como un **monorepo gestionado con [uv workspaces](https://docs.astral.sh/uv/concepts/workspaces/)**. El archivo pyproject.tomlraíz declara el workspace y agrupa automáticamente todos los miembros bajoapps/ypackages/`:

[tool.uv.workspace]
members = ["apps/*", "packages/*"]

Esto permite que cada aplicación y paquete tenga su propio pyproject.toml y ciclo de vida independiente, mientras comparten un único entorno virtual (.venv/) en la raíz y un lockfile común (uv.lock). Las dependencias internas se resuelven mediante referencias de workspace (workspace = true), sin pasar por PyPI.

agentic-smart-health/          ← workspace root
├── pyproject.toml             ← declaración del workspace uv
├── uv.lock                    ← lockfile unificado
├── Makefile                   ← comandos de desarrollo
├── apps/
│   ├── agent-orchestrator/    ← orquestador del sistema multiagente
│   ├── research-agent/        ← agente de investigación (RAG + literatura científica)
├── packages/
│   ├── core-schemas/          ← esquemas Pydantic compartidos (el contrato TwinSnapshot)
│   ├── ingestion-agents/      ← 4 agentes de ingesta (mesh · cbct · report · image)
│   ├── fusion-agents/         ← fusión geométrica y semántica sobre el twin
│   ├── analysis-agents/       ← segmentación anatómica: region_id (FDI) por gaussiana
│   ├── export-agents/         ← regeneración de malla, campo y render desde el twin, con el error medido
│   ├── gaussian-engine/       ← ajuste de elipsoides anisótropos a la densidad que midió el CBCT
│   ├── uos/                   ← contenedor Unified Oral Scene: el caso entero con sus relaciones declaradas
│   ├── tooth-aggregation/     ← agregación de etiquetas por punto a instancias de diente
│   └── 3dgs-engine/           ← placeholder (la reconstrucción 3DGS vive hoy en notebooks + gsplat)
├── data/
│   └── research-agent/        ← knowledge base del agente de investigación
├── schemas/                   ← JSON Schema publicado del manifiesto UOS, por versión (§12)
├── docs/                      ← documentación (ver nota más abajo)
├── notebooks/                 ← experimentación y exploración (01–07)
├── experiments/               ← bancos de prueba fuera del pipeline (CBCT→Blender→3DGS, capas por HU)
├── tests/                     ← suite de pruebas global
├── scripts/                   ← utilidades: render Blender, auditor de PRs, fetch de datasets
└── .github/
    └── workflows/             ← CI: agente de revisión de código (ai-code-reviewer)

Aplicaciones (apps/)

agent-orchestrator

Orquestador central del sistema multiagente. Coordina los agentes de cada fase del pipeline:

  • Ingesta(implementado): dispara los 4 agentes de ingestion-agents en paralelo sobre una adquisición (STL + CBCT + informe + N fotos), ensambla el TwinSnapshot y aplica el gate de revisión humana; presupuesto de <60 s.
  • Fusión(implementado): IngestionPipeline.fuse() encadena dos GeometricFusionAgent —registro escáner↔escáner y el ICP IOS↔CBCT, con su rms_error_mm y su estado de verificación— y el SemanticFusionAgent, que cuelga los hallazgos del informe de códigos FDI y marca el conflicto cuando informe y geometría discrepan.
  • Análisis 🟡 (la parte anatómica, sí; la clínica, no): el segmentation-agent corre dentro de fuse(), entre las dos etapas de fusión, y llena PipelineResult.analysis. ⚠️ Su calidad está medida y es el hueco principal del MVP: 11 de 14 piezas se descartan por anatomía (docs/research/segmentacion-fdi-escaner.md). El razonamiento clínico —pathology-agent— sigue planned, y va con revisión humana obligatoria por diseño.
  • Exportación(los cuatro canales): export-agents regenera desde el TwinSnapshot la malla en STL, el campo gaussiano en PLY (en el marco del twin o en mm reales del CBCT) y un render multivista en PNG por Beer-Lambert, cada uno con su error medido —desviación máxima y media para la geometría, PSNR/SSIM para la imagen—. Los dispara el orquestador con IngestionPipeline.exportar(result, destino), y el recorrido completo entrada → twin → fichero está probado de punta a punta en tests/test_e2e.py.

Depende de core-schemas e ingestion-agents (vía workspace) para garantizar contratos de datos compartidos con el resto del sistema.

Interoperabilidad con 3D Slicer y otras plataformas

Por formatos abiertos, no por un servidor. El pipeline materializa cada caso en STL, PLY
y PNG, más el JSON del propio TwinSnapshot, y todos ellos los lee Slicer de forma nativa.
Eso ya es interoperabilidad: no hay protocolo que negociar ni servicio que mantener vivo, y
el fichero sigue abriéndose dentro de diez años sin nosotros.

Hubo aquí un slicer-mcp-server y se ha retirado. Era un directorio con un server.py
de cero líneas descrito en este mismo README en presente —«expone una interfaz»,
«permite que los agentes interactúen»— y su desbloqueo dependía de que un tercero
confirmase formato y sentido de la llamada. Una pieza vacía que no podemos desbloquear
nosotros no es arquitectura: es una intención escrita en el sitio donde se documentan los
hechos.

Un servidor MCP tendría sentido para interacción viva y bidireccional — que un agente
conduzca la sesión de Slicer, no que lea un fichero. Nadie ha pedido eso todavía, y cuando
se pida se construye. Ver la issue #40, que ahora es una pregunta al partner y no un
componente de este repositorio.

research-agent

Agente de investigación autónomo que busca, ingerir y resume literatura científica sobre 3D Gaussian Splatting, el estándar DICOM y normativas clínicas. Construido con Python, Anthropic Claude / Ollama, Qdrant y embeddings locales.

Funcionalidades principales:

  • Búsqueda semántica de papers en Semantic Scholar y arXiv
  • Ingesta y indexación de documentos mediante RAG (Qdrant + fastembed)
  • Generación de reportes estructurados en Markdown
  • Soporte para ejecución local con Ollama (gratis, sin API key)

Modos de ejecución:

  • uv run python -m src.main — Claude con tool calling nativo (requiere API key)
  • uv run python -m src.main_local — Ollama local (gratis, 100% privado)

Corpus de partida. Los PDF de referencia no están en el repositorio: son
binarios de terceros y la licencia de buena parte de ellos no permite
redistribuirlos. Lo que se versiona es el inventario
(manifest.yaml: título, DOI o
arXiv ID, URL y licencia verificada en origen de cada documento). Para
materializarlos:

uv run python scripts/fetch_knowledge_base.py          # baja lo que falte
uv run python scripts/fetch_knowledge_base.py --check  # solo comprueba

Un par de editores (Wiley, AAAI) no sirven el PDF a un script: esos quedan como
descarga manual y el comando imprime el enlace. El agente funciona sin corpus —
search_references descubre literatura nueva—, pero read_directory e index
no encontrarán nada hasta que se ejecute.

Estructura:

  • src/main.py — Orquestador CLI con Claude
  • src/main_local.py — Variante local con Ollama
  • src/tools.py — Herramientas de sistema (sandbox de disco)
  • src/rag.py — Motor RAG (Qdrant + fastembed)
  • src/references.py — Descubrimiento de papers

No depende de core-schemas; mantiene sus propios modelos internos para RAG.

Nota: Este agente es un port de jeicob, adaptado para integrarse en el monorepo.


Paquetes compartidos (packages/)

core-schemas

Biblioteca de esquemas Pydantic v2 compartidos por todas las aplicaciones del workspace. Define los modelos de datos canónicos del sistema: el TwinSnapshot, la Provenance, las observaciones regionales por diente FDI y los contratos entre agentes. Actúa como fuente única de verdad de los tipos de datos del proyecto (esquema versionado, hoy 1.6.0).

ingestion-agents

Capa de ingesta del pipeline: 4 agentes (mesh · cbct · report · image), uno por modalidad/soporte, que traducen los ficheros crudos al contrato. Cada agente es determinista y fail-loud (nunca lanza; devuelve estado + confianza y aísla en cuarentena), adjunta Provenance por valor y guarda los artefactos pesados (mallas, volúmenes, píxeles) en un ArtifactStore direccionado por contenido (SHA-256). El image-agent descarta el EXIF por construcción (privacidad). Guía para añadir o modificar un agente: skill add-ingestion-agent; ficha completa en AGENTS.md.

export-agents

Capa de exportación (fase 6): la única familia que escribe ficheros de salida, igual que la ingesta es la única que lee ficheros de entrada. Cuatro canales, y todos miden lo que producen releyéndolo, no estimándolo: export-agentSTL desde surface_ref (desviación máxima y Chamfer), field-export-agentPLY desde gaussian_field_ref, render-export-agentPNG multivista con PSNR/SSIM del ciclo, y composite-mesh-export-agentarcada imprimible cerrada en sólido + un STL por diente, cuyo número mide otra cosa y por eso queda fuera de la comprobación de reversibilidad (ver «Estado actual»). Es de solo lectura sobre el gemelo: no muta el snapshot y su Protocol de almacén ni siquiera declara put.

Dos decisiones que se ven raras hasta que se leen: el PLY del campo no es un .ply de 3D Gaussian Splatting y el render no rasteriza splats. density es atenuación radiológica, no opacidad, y un CBCT no mide color — así que el fichero declara las propiedades que existen y el render compone por Beer-Lambert, que además es independiente del orden de las primitivas y por eso reproducible byte a byte. Fichas completas en AGENTS.md.

uos

El contenedor y su manifiesto — el entregable del proyecto. Un .uos es un ZIP sin comprimir (STORE, manifest.json primero) que lleva un caso dental entero con las relaciones entre sus partes declaradas: los ficheros nativos intactos y verificables por hash, los marcos de coordenadas y las registraciones que los unen, las vistas guardadas, la capa clínica colgada de códigos FDI, la procedencia encadenada y el estado PHI. Módulos por responsabilidad: contenedor · manifiesto · escena · volumen · vistas · clinico · derivados · procedencia · validador · esquema.

La regla que lo sostiene: lo medido y lo inferido no se mezclan. Todo lo que sale de un modelo vive solo bajo derived/ con regulatory.layer: 3 y su sidecar, y un .uos sin derived/ sigue siendo válido y completo. Esquema publicado en schemas/; qué lleva y a quién le sirve, en docs/spec/uos-format-spec-v0.2.tex.

fusion-agents

Capa de fusión (ADR 004): GeometricFusionAgent registra escáner↔escáner y CBCT↔intraoral por ICP —declarando siempre su rms_error_mm y si alguien lo ha verificado— y SemanticFusionAgent ancla los hallazgos del informe a códigos FDI, marcando el conflicto cuando informe y geometría discrepan. Incluye marco (el marco anatómico medido, no supuesto) y preparacion (qué dos nubes se registran, elegido por código y no a mano).

analysis-agents

Capa de análisis anatómico: el segmentation-agent produce region_id por gaussiana y el mapa FDI → confianza que consume la fusión semántica, y dental limpia las etiquetas por vértice del escáner con una prohibición explícita — no mover el margen gingival, que es una frontera clínica. ⚠️ Su calidad está medida y es el hueco principal del MVP: ver docs/research/segmentacion-fdi-escaner.md.

gaussian-engine

Ajuste del campo: de semillas isótropas del tamaño del vóxel a elipsoides medidos. Es el único paquete que toca torch, y lo importa dentro de la función, para que se instale y se pruebe sin CUDA.

tooth-aggregation

Agregación punto → diente (instancias + FDI) para el segmentation-agent. Escrito sin depender de torch a propósito: el forward del modelo es cosa de quien llama, así que la agregación se instala y se testea en el workspace normal.

3dgs-engine

Placeholder. Reservado para el motor de renderizado/procesamiento 3D Gaussian Splatting como paquete reutilizable. Hoy la reconstrucción 3DGS no vive aquí, sino en los notebooks (gsplat + Blender) y en el visor web. Se promoverá a paquete cuando la receta se estabilice y deje de ser experimental.


Notebooks — pruebas de concepto (spikes)

El directorio notebooks/ contiene spikes de validación técnica
(no el sistema final ni resultados clínicos): pruebas manuales que de-arriesgan las
decisiones de arquitectura antes de convertir cada eslabón en agente. Corren sobre
dos datasets reales: Teeth3DS+ (01–06, escáneres intraorales etiquetados,
CC-BY) y Bite2Text (07, escáner + fotos + informes, CC-BY-SA). Ambos gitignored.

Notebook Qué valida Dataset GPU
01 Malla → splatting clásico (VTK, baseline) → contrato · caracterización del dataset Teeth3DS+ No
02 Visor 3D interactivo de escritorio (VTK), sobre cualquier caso Teeth3DS+ No
03 Vistas sintéticas + poses de cámara (input del 3DGS, sin COLMAP) Teeth3DS+ No
04 3DGS moderno entrenado (gsplat) evaluado en vistas retenidas → contrato Teeth3DS+
05 Vistas sintéticas densas (528/caso) — rejilla más fina que 03 Teeth3DS+ No
06 3DGS denso con la receta de referencia (SSIM + densificación/poda, armónicos g2) Teeth3DS+
07 Escáner real → Blender (EEVEE) → 3DGS, con color de las fotos (image-agent) y pérdida SSIM · 1600 vistas · holdout 31,5 dB Bite2Text

Detalle, alcance y cómo ejecutarlos: notebooks/README.md.
El notebook 07 es el que integra los agentes de ingesta (mesh + report +
image) en el flujo de reconstrucción. No cubierto todavía: fusión multimodal
real (CBCT + STL + foto en un mismo twin), color per-píxel (registro foto↔malla)
y los agentes de análisis.

Revisión de código y CI (ai-code-reviewer)

Cada Pull Request pasa por un agente guardián de revisión estática ejecutado en GitHub Actions. No usa LLM: combina linters estándar con un auditor de arquitectura propio, y revisa únicamente los archivos Python que toca el PR (enfocado en el diff). Publica anotaciones inline sobre las líneas afectadas y un comentario-resumen en el PR.

Qué comprueba:

Chequeo Herramienta ¿Bloquea el merge?
Estilo y formato ruff No — informativo (anotaciones inline)
Tipos mypy No — informativo (anotaciones inline)
Arquitectura scripts/audit_pr.py — hace fallar el check
Coherencia documental scripts/docs_sync.py — hace fallar el check
Datos y licencias scripts/data_guard.py — hace fallar el check

Reglas de arquitectura (bloqueantes):

  • Pydantic v2 estricto en packages/core-schemas: prohíbe el shim pydantic.v1 y los idiomas de v1 (@validator, @root_validator, class Config, BaseSettings).
  • Sin dependencias cruzadas entre apps/: un app no puede importar el paquete de otro; el código compartido debe vivir en packages/ (p. ej. core-schemas).

Componentes:

  • .github/workflows/ai-code-review.yml — orquesta los chequeos, publica comentarios y decide el gate de merge.
  • scripts/audit_pr.py — auditor de arquitectura (AST, solo librería estándar).

Dos de esos chequeos bloquean el merge y el resto solo comenta, por un motivo
concreto: son los que producen daño que no se arregla con otro commit. La deriva
documental (docs_sync.py) se convierte en verdad publicada en cuanto se mergea, y
un dato ajeno (data_guard.py) entra en la historia de git y solo sale
reescribiéndola — que es lo que costó la issue 45.

Vigilancia de literatura (literature watch)

El único trabajo programado del repositorio: cada lunes,
scripts/watch_literature.py busca en arXiv lo
publicado esa semana, descarta lo que ya está en el manifiesto, lee la licencia del
OAI-PMH de arXiv
(no la supone) y abre una PR proponiendo las entradas nuevas.

Siete consultas en dos ámbitos, con puerta distinta cada uno: las cuatro
dentales (3DGS, segmentación CBCT, escaneo intraoral, gemelo digital) exigen un
término del dominio en título o resumen; las tres de estándares (DICOM, FHIR/HL7,
interoperabilidad en imagen médica) lo exigen en el título. La distinción está
medida, no supuesta: un artículo de interoperabilidad clínica casi nunca dice
«tooth», y uno que solo menciona DICOM de pasada no va sobre DICOM. El cupo de cada
PR se reparte por turnos entre consultas, para que las de mayor volumen no dejen la
propuesta sin un solo artículo dental.

Reparto de trabajo deliberado: la máquina hace lo repetitivo y verificable —qué hay
nuevo, bajo qué licencia—, y la persona que revisa la PR decide lo único que exige
criterio: si el artículo aporta algo al proyecto. El agente no mergea nunca.

Ningún PDF llega a escribirse: se descargan a memoria para calcular sha256 y
bytes, y se liberan ahí mismo. Lo que se propone commitear son diez líneas de
YAML por artículo. Los ficheros se materializan después, en local, con
uv run python scripts/fetch_knowledge_base.py.

Utilidades del repositorio (esta tabla la genera docs_sync.py):

Script Qué hace
scripts/ablacion_recetas.py Ablación de la receta de entrenamiento: qué aporta cada pieza.
scripts/altura_corona.py mide la altura de corona clínica sobre el escáner intraoral.
scripts/audit_pr.py Guardián de las reglas de arquitectura del monorepo.
scripts/blender_render_views.py Render multivista de una malla intraoral con Blender (headless).
scripts/caso_completo.py El pipeline entero sobre un caso clínico real, etapa por etapa.
scripts/composicion_cbct_ios.py Dientes segmentados en el CBCT + encía del IOS, en gaussianas.
scripts/data_guard.py Impide que datos ajenos entren al repositorio sin permiso.
scripts/desplazamiento_relativo.py ¿Se puede decir «esta pieza se desplazó X mm»? Referencia leave-one-out y umbral.
scripts/docs_sync.py Comprueba que la documentación no le mienta al código.
scripts/entrena_diente_cbct.py Segmentador de diente en CBCT, contra el listón del umbral.
scripts/entrena_gs_escaner.py 3DGS de verdad sobre la superficie del escaner.
scripts/entrenar_3dgs.py EXPERIMENTO con resultado NEGATIVO: 3DGS entrenado de una arcada.
scripts/eval_informes.py ¿Cuánto de lo que dice un informe acaba en el contrato?
scripts/fetch_knowledge_base.py Materializa la knowledge base del research-agent.
scripts/fetch_teeth3ds.sh Descarga reproducible de Teeth3DS+ desde el Google Drive oficial.
scripts/malla_mejorada.py El STL mejorado, sacado del contenedor y de nada más.
scripts/metricas.py las cuatro cifras del brief, MEDIDAS y no prometidas.
scripts/mide_segmentacion.py cuanto se puede DESCARTAR de la segmentacion FDI de un .uos.
scripts/prepara_toothfairy.py Descarga ToothFairy2 caso a caso y lo deja entrenable.
scripts/promedio_y_escala.py Dos preguntas de diseño sobre el registro por diente, medidas en vez de argumentadas.
scripts/refina_3dgs.py La fase que faltaba: el campo semilla optimizado como 3DGS.
scripts/registro_ios_cbct.py mide si el escáner intraoral y el CBCT se pueden alinear.
scripts/resolucion_modalidades.py Simula qué resolución alcanza cada modalidad dental.
scripts/segmentar_fdi.py etiqueta cada diente de una arcada con su código FDI.
scripts/seguimiento_histora.py cuánto se ha movido el margen gingival entre dos escaneos.
scripts/umbral_vs_verdad.py ¿Cuánto diente recupera un umbral, contra una verdad conocida?
scripts/verifica_contenedor.py que el .uos diga la verdad SOBRE SI MISMO.
scripts/watch_literature.py Vigila la literatura y propone entradas del manifiesto.

Las herramientas de desarrollo se instalan con uv sync --group dev (grupo dev: ruff, mypy). Ficha completa del agente en AGENTS.md.


Quickstart

Requisitos previos

  • Python ≥ 3.13
  • uv instalado en el sistema

Instalación

Clona el repositorio e instala todas las dependencias del workspace con un único comando:

git clone https://github.com/anfaia/agentic-smart-health.git
cd agentic-smart-health
make install

Esto ejecuta uv sync, que resuelve y bloquea todas las dependencias (internas y externas) y crea el entorno virtual en .venv/.

Comandos disponibles

Comando Ejecuta
make install uv sync
make hooks git config core.hooksPath .githooks
make test uv run pytest
make lint uv run ruff check
make docs uv run python scripts/docs_sync.py --write

make install activa además los hooks de git del repositorio
(git config core.hooksPath .githooks), y el de pre-commit hace dos cosas:

  • Detiene el commit si data_guard.py encuentra un dato ajeno en el stage
    (un PDF, una malla, un binario grande). Es el único sitio donde eso sale barato:
    una vez commiteado, sacarlo obliga a reescribir la historia.
  • Regenera los bloques generados de la documentación —tablas de variables,
    scripts, comandos y registro de agentes— y los añade al mismo commit, para
    que la documentación viaje siempre con el cambio que la afecta. Solo toca lo que
    hay entre marcas: la prosa nunca.

Si alguna vez estorba, git commit --no-verify se lo salta, y el CI seguirá
avisando en la PR.

Activar el entorno (opcional)

Si necesitas trabajar directamente en el entorno virtual:

source .venv/bin/activate

O bien, usa el prefijo uv run para ejecutar cualquier comando dentro del entorno sin activarlo:

uv run python -c "import core_schemas; print('workspace OK')"

Variables de entorno

Copia el archivo de ejemplo y configura las variables necesarias:

cp .env.example .env

.env.example documenta solo las variables que el código lee de verdad, con
quién las usa y qué pasa si no se definen. Esta tabla la genera
scripts/docs_sync.py leyendo el código, y el CI falla si
se desincroniza — por eso no se edita a mano. Un en la última columna significa
que la llamada no lleva valor por defecto (el módulo puede tener su propio respaldo):

Variable Se lee en Por defecto
ANTHROPIC_API_KEY apps/research-agent/src/main.py
ASH_PSEUDONYM_SALT packages/ingestion-agents/src/ingestion_agents/cbct_agent.py dev-salt-no-usar-en-produccion
OLLAMA_HOST apps/research-agent/src/main_local.py http://localhost:11434
QDRANT_PATH apps/research-agent/src/rag.py
RESEARCH_AGENT_LOCAL_MODEL apps/research-agent/src/main_local.py qwen2.5:7b
RESEARCH_AGENT_MODEL apps/research-agent/src/main.py claude-opus-4-8

Ninguna hace falta para ejecutar make test. La sal de seudónimo es la única que
es un secreto: sin ella el pipeline funciona, pero los seudónimos que emite no
sirven para datos de pacientes — y si cambia después, dejan de coincidir con los ya
emitidos.


Documentación

Nota: el directorio docs/ está reservado exclusivamente para documentación de investigación y arquitectura del proyecto. No contiene documentación de usuario ni tutoriales de uso del código.

  • docs/architecture/ — decisiones de diseño, diagramas de arquitectura y ADRs (Architecture Decision Records).
  • docs/research/ — referencias bibliográficas, notas de investigación sobre Gaussian Splatting, estándares DICOM/STL, interoperabilidad clínica y normativa aplicable (RGPD, HIPAA).

La documentación técnica orientada a desarrolladores y contribuidores se mantendrá en este README y en los pyproject.toml de cada componente.

Por dónde empezar a leer

Documento Responde a
docs/cierre-mvp.md qué está medido, qué no está resuelto y qué queda para después
docs/spec/uos-format-spec-v0.2.tex la especificación del formato: qué lleva un .uos, cómo se lee, cómo se amplía
docs/research/segmentacion-fdi-escaner.md por qué la segmentación FDI no está resuelta, con la medida
docs/research/frontera-encia-desde-foto.md dónde sí está la frontera diente-encía, y qué falta para usarla
docs/research/color-por-pieza-desde-foto.md el tono de cada corona, y cómo se descuenta la caída del flash sin invertirla
docs/research/segmentacion-diente-cbct.md hasta dónde llega un clasificador sobre el CBCT, y dónde deja de llegar

Hitos del proyecto

Semana Hito
2 Revisión de arquitectura multiagente y esquema de atributos clínicos del Digital Twin
4 Demo PoC: agentes de ingesta + primera versión del Digital Twin con datos sintéticos
6 Sistema integrado: agentes de fusión y exportación, regeneración STL desde el Digital Twin
8 MVP testado, validación preliminar con la organización partner, documentación técnica final 🟡

🟡 La semana 8 va a medias, y la mitad que falta es la que no depende del código. El MVP
está testado y la documentación técnica cerrada (docs/cierre-mvp.md);
lo que no ha ocurrido es la validación con la organización partner, que necesita que
alguien de fuera abra un .uos que no hayamos escrito nosotros.


Métricas de éxito

Las cuatro del brief, medidas con scripts/metricas.py y no
prometidas. Tres cumplen; la cuarta no, y se declara:

Compromiso Objetivo Medido
Latencia de ingesta de un conjunto completo (STL + CBCT + informe) < 60 s 12,7 s cumple
Fidelidad de la malla regenerada desde el Digital Twin < 0,1 mm 4,59 × 10⁻⁶ mm cumple
Cobertura de pruebas automatizadas > 80 % 95,1 % cumple
Fiabilidad de los agentes de ingesta > 95 % 93,8 % (N = 16) no cumple

⚠️ El fallo de fiabilidad es un informe escaneado sin capa de texto: el agente no
extrae nada y se declara FAILED, que es el comportamiento correcto. Con N = 16 casos
reales un solo fallo son 6,2 puntos. Se publica así en vez de subir el N con casos
sintéticos hasta que el porcentaje quede bien. Por separado, el mesh-agent sobre 120
mallas
de Teeth3DS+ da 100 %: las dos cifras van con su N al lado a propósito.


Licencia

Apache License 2.0


Becas de Verano ANFAIA 2026 · Julio – Agosto 2026

Reviews (0)

No results found