Passei 240 recursos FHIR sintéticos pelo validate_resource() do HealthChain 0.16.1. Eram 60 válidos e 180 com um defeito conhecido cada. Ele reprovou 80 dos 180: todos os 80 com defeito de forma, que são campo obrigatório ausente, data em dd/mm/aaaa, campo que não existe e gender fora do conjunto. Dos 100 com defeito de conteúdo, reprovou nenhum. Um CPF com dígito verificador errado passou como válido. Nos 60 recursos corretos, 0 falsos positivos.
Código, dados e saída gravada: demos/11-healthchain-validacao-fhir no nursia-research-lab.
Como rodei
- HealthChain 0.16.1 (
pip install healthchain==0.16.1), Apache-2.0, Python 3.11, CPU. Por baixo ele usa ofhir.resources8.1.0. As duas versões estão fixadas norequirements.txt. - Gerei 240 recursos FHIR R4B com semente fixa: Patient, Observation de frequência cardíaca (LOINC 8867-4) e Condition (CID-10).
- 60 são válidos e servem de controle. Os outros 180 têm um defeito cada, 20 por categoria, em 9 categorias. O defeito e a categoria são escolha minha. Os 20 casos de referência quebrada são Bundles (um Patient e uma Observation), o resto são recursos soltos.
- Cada recurso passou por
validate_resource(), que devolve um relatório comvalide a lista de problemas. Contei os reprovados (valid == False).
O CPF dos pacientes passa no dígito verificador, mas é sorteado e não pertence a ninguém. Nomes saem de uma lista curta inventada.
O que deu
| Defeito colocado | Tipo | Reprovados |
|---|---|---|
| Campo obrigatório ausente | forma | 20 de 20 |
| Data em dd/mm/aaaa | forma | 20 de 20 |
| Campo que não existe no recurso | forma | 20 de 20 |
Valor fora do conjunto obrigatório (gender, status) |
forma | 20 de 20 |
| Código inventado sob o sistema LOINC | conteúdo | 0 de 20 |
| Unidade que não é UCUM válida | conteúdo | 0 de 20 |
| Referência pra um Patient que não está no Bundle | conteúdo | 0 de 20 |
| CPF com dígito verificador errado | conteúdo | 0 de 20 |
Valor e dataAbsentReason juntos (invariante obs-6) |
conteúdo | 0 de 20 |
| Controle: recurso válido | – | 0 de 60 |
Forma: 80 de 80. Conteúdo: 0 de 100. Chamo de forma o que o modelo Pydantic ou o vínculo de code primitivo recusa, e de conteúdo o resto. Como o rótulo segue o que o módulo diz checar, o resultado confirma o docstring e não descobre nada novo. Tempo médio de cerca de 0,2 ms por recurso: a rodada gravada deu 0,17 ms, e entre rodadas e máquinas vi de 0,11 a 0,25 ms. O tempo muda a cada rodada, o resto é determinístico.
O que ele pega
Dois tipos de erro: o que o modelo Pydantic do fhir.resources recusa (campo ausente, data mal escrita, campo desconhecido) e o que o HealthChain confere em seguida, que são os valores de campos code com conjunto obrigatório. Um gender escrito “feminino” cai no segundo caso. Monto a saída a partir de expression, code e diagnostics do relatório:
Patient.gender (code-invalid)
Value 'feminino' is not in the required value set. Allowed values: male, female, other, unknown
A mensagem completa está em results/exemplo.txt. Cada problema vem com o caminho do campo (Patient.gender, Observation.status) e um código no vocabulário do OperationOutcome do FHIR. O relatório se converte em OperationOutcome com to_operation_outcome(). Na minha leitura, um agente que monta recurso FHIR consegue usar o caminho do campo pra corrigir o que errou. Não testei com agente.
O que ele deixa passar
Os cinco defeitos de conteúdo voltam como valid=True, com a lista de problemas vazia. Exemplo com dado brasileiro:
from healthchain.fhir import validate_resource
paciente = {
"resourceType": "Patient",
"identifier": [{
"system": "http://www.saude.gov.br/fhir/r4/NamingSystem/cpf",
"value": "44797837682", # dígito verificador errado
}],
}
rel = validate_resource(paciente)
print(rel.valid, rel.issues) # True []
Esse é um dos CPFs com dígito errado da demo, e o validador não olhou o valor do identificador nos 20 casos. O mesmo vale pro código LOINC inventado, pra unidade fora do UCUM e pra referência a um Patient que não existe no Bundle. A invariante obs-6 (uma Observation com valor e motivo de ausência de dado) também passou nas 20.
O docstring de healthchain/fhir/validation.py lista o que não é checado: vínculos (bindings) de CodeableConcept e Coding, invariantes FHIRPath, conformidade com perfil e integridade de referência. Pra validação completa, o próprio módulo manda usar o $validate de um servidor FHIR. Três das cinco categorias de conteúdo caem nesses itens: o LOINC inventado, a referência quebrada e a obs-6. O CPF com dígito errado e a unidade fora do UCUM não estão na lista, são extensão minha. A demo mede esse recorte: 80 de 180 neste conjunto.
O que eu faria
- Use o
validate_resource()como porteiro de forma, na saída de um agente que gera FHIR. Ele é rápido e dá o caminho do erro. - Não trate
valid=Truecomo “o FHIR está certo”. Pra conteúdo, passe o recurso por um validador de verdade, como o validador oficial da HL7 ou o$validatedo seu servidor, e use o perfil que o seu caso exige. Eu não rodei nenhum dos dois nesta demo. - Pra CPF, valide o dígito fora do FHIR. São poucas linhas, e não tratam sequência repetida (11111111111) nem CPF com máscara:
def digito_cpf(base):
soma = sum(d * p for d, p in zip(base, range(len(base) + 1, 1, -1)))
resto = (soma * 10) % 11
return 0 if resto == 10 else resto
def cpf_confere(cpf):
d = [int(c) for c in cpf]
return len(d) == 11 and digito_cpf(d[:9]) == d[9] and digito_cpf(d[:10]) == d[10]
Limites
- O gabarito é meu. Escolhi as 9 categorias e fiz o defeito de cada uma. Com outras categorias a proporção seria outra. O 80 de 180 descreve este conjunto, e não a taxa de erro de FHIR real.
- Não comparei com o validador oficial da HL7. A demo diz o que o
validate_resource()deixa passar, não o que outro validador pegaria. - Perfil brasileiro não entra. Usei o sistema de identificador de CPF do Ministério da Saúde só pra montar o dado.
- Testei só
validate_resource(), em recurso solto e em Bundle montado à mão. Não testeiload_bundle()nem o resto do SDK (gateway, MCP, sandbox). - 240 recursos sintéticos, uma semente, FHIR R4B, três tipos de recurso e Bundle. O 0 de 60 falsos positivos vale pro controle que eu mesmo gerei.
Fonte e como reproduzir
Clone https://github.com/rogeriorrodrigues/nursia-research-lab e entre em demos/11-healthchain-validacao-fhir. Daí são três comandos:
pip install -r requirements.txt
python3 demo.py
python3 -m pytest tests -q
- Pasta no repositório: demos/11-healthchain-validacao-fhir. Saída gravada em
results/. - HealthChain: github.com/healthchainai/HealthChain, versão 0.16.1, Apache-2.0.
- Módulo de validação:
healthchain/fhir/validation.pydentro do pacote 0.16.1 (PyPI).
NursIA é pesquisa do PPGINFOS/UFSC, com bolsa FAPESC.
In English
HealthChain 0.16.1 validate_resource() rejected 80 of 180 defective synthetic FHIR resources: all 80 structural defects and none of the 100 content defects (invented LOINC code, non-UCUM unit, broken reference, CPF with a bad check digit, invariant obs-6). No false positives on 60 valid resources. The module’s docstring already lists most of these as unchecked; the demo measures the gap.

