Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.fhiron.cl/llms.txt

Use this file to discover all available pages before exploring further.

TL;DR. Pedirle a un LLM que valide un recurso FHIR contra CL Core es como pedirle a un compilador que sea probabilístico: si la respuesta cambia entre dos ejecuciones idénticas, no es validación. Fhiron MCP separa ese contrato. El LLM compone, el motor valida. La verdad de si un Patient cumple CorePacienteCl la dicta HAPI FHIR 8.8.0 cargado con hl7.fhir.cl.clcore 1.9.3, no el modelo. El resultado es una OperationOutcome reproducible, en español, auditable bajo Ley 21.719.

1. El problema: alucinación FHIR no se ve a primera vista

Un LLM moderno produce JSON FHIR sintácticamente impecable. Si le pides un Patient chileno con identificador RUT, te entrega algo así:
{
  "resourceType": "Patient",
  "meta": {
    "profile": ["https://hl7chile.cl/fhir/ig/clcore/StructureDefinition/CorePacienteCl"]
  },
  "identifier": [
    {
      "type": {
        "coding": [
          {
            "system": "https://hl7chile.cl/fhir/ig/clcore/CodeSystem/CSTipoIdentificador",
            "code": "RUN"
          }
        ]
      },
      "system": "https://hl7chile.cl/fhir/ig/clcore/NamingSystem/RUN",
      "value": "12.345.678-5"
    }
  ]
}
A primera vista parece correcto. En realidad falla en cuatro lugares distintos:
  1. El code del tipo de identificador en CL Core 1.9.3 no es "RUN". Es "01" (CSTipoIdentificador). El LLM eligió el string que parecía más natural.
  2. El system del NamingSystem oficial chileno para RUT no es https://hl7chile.cl/fhir/ig/clcore/NamingSystem/RUN. Es https://www.minsal.cl/sid/run. La URL inventada se ve plausible.
  3. El value "12.345.678-5" no cumple el formato esperado por el slice de identificador chileno (sin puntos, dígito verificador en mayúscula cuando es K).
  4. El dv (dígito verificador) "5" puede o no ser correcto para 12345678 — el LLM no calculó el módulo 11, lo eligió.
Ninguno de estos errores es atrapado por una validación sintáctica de JSON. Tampoco por un schema FHIR R4 base. Solo aparecen cuando el recurso pasa por el motor que tiene cargados los perfiles, CodeSystems y NamingSystems oficiales de CL Core 1.9.3, más la lógica de RUT con verificación módulo 11. Esto es lo que llamamos alucinación FHIR: producción que parece correcta, pasa el ojo humano, pasa el linter sintáctico, y se cae en producción cuando el EHR destino la rechaza con un mensaje opaco en inglés.

2. Por qué validación probabilística no aplica acá

Un LLM es, por construcción, un sistema de muestreo sobre una distribución de probabilidad. Tres cosas lo descalifican como validador de un estándar clínico: No es reproducible. Aun con temperature: 0, dos despliegues con cuantización distinta, dos versiones del modelo, o dos providers detrás de la misma API pueden dar respuestas levemente distintas. Para validación esto es un dealbreaker: si llamo validar(recurso) dos veces seguidas y obtengo dos OperationOutcome distintas, no validé nada. Confunde lookalikes. Los LLMs son extraordinariamente buenos generando texto que se parece al que ya vieron. Para datos clínicos esto es exactamente lo que no quieres: el modelo produce mg/dL en una unidad UCUM cuando el estándar pide mg/dl, o invierte los slices de un identificador porque la mayoría de ejemplos de internet están en otro formato. El error está en la apariencia, no en la estructura. No tiene fuente de verdad. El estándar CL Core v1.9.3 es un paquete NPM con artefactos versionados. Existe una versión exacta de CorePacienteCl. El LLM no carga ese paquete; carga una mezcla difusa de las miles de versiones que vio durante entrenamiento. Pedirle que sea autoridad sobre un perfil específico es pedirle que recuerde algo que nunca tuvo. La conclusión no es que los LLMs sean malos. Es que validar un estándar es un trabajo determinístico, y la herramienta para trabajos determinísticos es código determinístico.

3. Arquitectura: separar composición de verificación

Fhiron MCP implementa una frontera explícita de tool-use:
┌──────────────┐         ┌──────────────────┐         ┌─────────────────┐
│              │  tool   │                  │  HTTP   │                 │
│     LLM      ├────────>│   MCP Connector  ├────────>│   Motor Fhiron  │
│  (compone)   │  call   │   (intermedia)   │         │  (HAPI 8.8.0 +  │
│              │<────────│                  │<────────│   CL Core 1.9.3)│
└──────────────┘ result  └──────────────────┘         └─────────────────┘
El LLM nunca evalúa si un recurso cumple un perfil. Cuando necesita esa respuesta, llama a fhiron_validate_fhir_resource con el JSON que produjo. El connector reenvía el recurso al motor, el motor responde con una OperationOutcome, y el connector se la entrega al modelo como tool_result. El modelo entonces tiene tres caminos:
  1. El motor dijo OK — continuar la conversación.
  2. El motor reportó issues — leer las OperationOutcome.issue, identificar el expression que falló, corregir el recurso, llamar de nuevo.
  3. El motor no pudo procesar el input (JSON malformado, recurso truncado) — reformular.
Esta separación tiene una propiedad clave: el modelo nunca tiene la última palabra sobre validación. Puede componer mil veces, pero el veredicto siempre lo entrega un sistema que carga literalmente los artefactos publicados de CL Core 1.9.3.

4. Las herramientas que cruzan la frontera

@fhiron/mcp-connector 0.4.5 expone siete tools al LLM. Todas tienen idempotentHint: true y la mayoría readOnlyHint: true:
ToolQué haceDeterminístico
fhiron_validate_fhir_resourceValida contra CL Core v1.9.3, devuelve OperationOutcome en españolSí — motor HAPI
fhiron_fhir_lint_localLinter offline para reglas estructurales rápidasSí — reglas estáticas
fhiron_fhir_lint_runVerifica RUT con módulo 11 chilenoSí — algoritmo determinístico
fhiron_fhir_apply_quickfixAplica un fix sugerido por el linter al recursoSí — transformación pura
fhiron_get_exampleDevuelve un ejemplo válido del perfil pedido (precargado)Sí — payload constante
fhiron_search_terminologyBusca en CodeSystems chilenos cargados localmenteSí — lookup
fhiron_explain_codeExplica un código de error de validación en españolSí — diccionario
Cada tool es una función pura del input. El motor detrás (/api/validate, linter local, catálogos de terminología) corre el mismo código sin importar quién llame. Mismo input → mismo output. Es el contrato que un LLM no puede ofrecer.

5. Garantías que se ganan al hacer este corte

Determinismo. Si auditas dos llamadas idénticas a fhiron_validate_fhir_resource con el mismo JSON, las dos OperationOutcome son byte-a-byte iguales (modulo timestamps que se anotan fuera del payload validado). Idempotencia. Reintentar una validación es seguro. Esto importa cuando el agente compone, falla, corrige y reintenta múltiples veces dentro de una misma sesión. Audit trail real. Cada validación produce un registro en validations_log con: tenant, recurso enviado (hash), perfil aplicado, OperationOutcome, timestamp. La conversación del LLM es opcional como contexto humano; la evidencia de cumplimiento es el registro del motor. Sin alucinación de perfiles. El motor solo conoce los StructureDefinitions, CodeSystems, ValueSets y NamingSystems publicados oficialmente: hl7.fhir.cl.clcore 1.9.3, hl7.fhir.r4.core, hl7.terminology.r4, más los estándares internacionales con URI canónica documentada. Si el recurso referencia un perfil que no existe, el motor lo rechaza explícitamente. Si CL Core no cubre un caso de uso, eso aparece como MISSING en el audit de cobertura, no se inventa un perfil propio que parezca oficial. Errores en español. El motor traduce cada OperationOutcome.issue al español con referencia al perfil exacto que falló. El LLM no necesita “interpretar” el error: lo lee y lo aplica.

6. Comparación uno-a-uno

Mismo agente, misma tarea: producir un Patient chileno válido. Dos arquitecturas distintas.
DimensiónValidación pura por LLMFhiron MCP (LLM + motor)
Input idéntico → output idénticoNo garantizadoGarantizado
Cobertura de CL Core 1.9.3”Lo que recordó el modelo”11 perfiles oficiales + 28 SDs CL + 23 ValueSets + 20 CodeSystems
Validación de RUT módulo 11AproximadaAlgoritmo nativo
Mensajes de error en español”Lo que produzca el modelo”Generados por el motor con referencia al perfil
Reproducibilidad para auditoríaNoSí (registro validations_log)
Riesgo de inventar perfil/extensiónAltoBloqueado por construcción (regla canónico-only)
Persistencia de datos clínicosDepende del providerStateless: el gateway descarta el recurso tras validar
Costo marginal por validaciónTokens del LLMHTTP a motor local
LatenciaVariable según modeloPredecible (p95 < 3s objetivo)
Lo que se gana con la mediación no es solo correctness. Es la posibilidad de hacer afirmaciones verificables sobre el comportamiento del sistema.

7. El ángulo de cumplimiento: Ley 21.719

La Ley de Protección de Datos Personales chilena exige, entre otras cosas, demostrar trazabilidad sobre el tratamiento de datos sensibles. Para un sistema que valida recursos FHIR clínicos, esto se traduce en preguntas concretas:
  • ¿Se puede reproducir la validación que se hizo el 14 de junio a las 10:32 sobre el Patient X?
  • ¿Bajo qué versión exacta del estándar se evaluó?
  • ¿Quién (qué tenant, qué API key) ejecutó la validación?
  • ¿Dónde quedó persistido el dato clínico? (Idealmente: en ningún lado controlado por nosotros).
Fhiron MCP responde las cuatro:
  1. La OperationOutcome es función pura del input + versión del motor → reproducible.
  2. La versión del paquete CL Core (hl7.fhir.cl.clcore@1.9.3) y de HAPI (8.8.0) están fijas y trazadas.
  3. Cada llamada lleva tenant + API key, registradas en validations_log.
  4. El gateway es stateless: el FhironGatewayInterceptor retorna false, lo que indica a HAPI que no persista el recurso tras procesarlo.
Un agente puro LLM no puede contestar ninguna de las cuatro con la precisión que un auditor exige. Un agente con la mediación correcta sí.

8. Cuándo usar qué

La pregunta práctica para equipos de ingeniería: ¿dónde corto el LLM y dónde corto el motor? El LLM es muy bueno para:
  • Componer recursos FHIR a partir de descripciones en lenguaje natural (“crea un Patient de María González, RUT 12345678-5, vive en Ñuñoa”).
  • Interpretar errores de validación y proponer correcciones.
  • Orquestar conversaciones largas con múltiples idas y vueltas hasta llegar a un recurso válido.
  • Documentar lo que hizo en lenguaje humano.
El motor (vía MCP) es la única autoridad para:
  • Validar contra el perfil oficial.
  • Resolver terminologías a códigos chilenos válidos.
  • Calcular el dígito verificador de un RUT.
  • Aplicar quickFixes idempotentes.
  • Producir el registro auditable de qué pasó.
Esta es la frontera por la que pasa Fhiron MCP. El LLM compone hasta donde el motor diga “OK”. Ni un milímetro más.

9. Conclusión

Construir agentes que toquen datos clínicos en Chile no es un problema de prompt engineering. Es un problema de definir bien dónde termina la opinión del modelo y dónde empieza el estándar. Mientras esa frontera no exista en el código, el agente puede parecer útil en una demo y ser inauditable en producción. Fhiron MCP es el contrato que hace esa frontera explícita. El LLM compone. El motor valida. Y para los equipos que necesitan integrarse con HIS, EHR o RNS chilenos en condiciones de Ley 21.719, esa diferencia no es estética: es la condición para que el agente exista.

Recursos relacionados

@fhiron/mcp-connector

Paquete oficial en npm. Instalable en Claude Code, Cursor y Continue.

MCP — Overview

Cómo se conecta Fhiron MCP a tu IDE.

Bridge — Errores

Catálogo de errores de validación CL Core en español.

¿Qué es CL Core?

El estándar oficial chileno sobre el que valida Fhiron.