尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

OpenMed 离线 FHIR Profile 校验指南:基于 WHO SMART Guidelines 的安全导出检查与脱敏审计

OpenMed 离线 FHIR Profile 校验指南:基于 WHO SMART Guidelines 的安全导出检查与脱敏审计 OpenMed 离线 FHIR Profile 校验指南基于 WHO SMART Guidelines 的安全导出检查与脱敏审计【免费下载链接】openmedLocal-first healthcare AI: clinical NER HIPAA PII de-identification that runs 100% on-device. 2,200 medical models, 21 languages, Apple MLX Python, no cloud, no patient data leaving your network. Apache-2.0项目地址: https://gitcode.com/GitHub_Trending/ope/openmed导读本文讲解 OpenMed 提供的离线 FHIR R4 Profile 一致性检查能力在把去标识化后的 FHIR Bundle 交给任何认识实施指南IG的 FHIR 服务器之前先用本地的 WHO SMART Guidelines 实施指南 npm 包对导出结果做一次不联网、不加载模型、不执行远程术语扩展的定向安全检查。读完本文你将掌握check_bundle()的完整用法、to_bundle(profile_check...)的导出闸门模式、基于original_bundle的脱敏影响审计方法以及检查器的约束子集边界与源码级实现原理。该能力面向的典型场景是OpenMed 将临床实体抽取结果导出为 FHIR R4 Bundle再经历脱敏处理最终投递到要求满足特定 SMART Guidelines Profile 的接收系统。在投递前做一次本地一致性把关可以提前发现脱敏把必填字段删空了这类会导致上游校验失败的问题。定位定向导出安全检查而非完整 FHIR 校验器OpenMed 的 Profile 检查器是一个轻量、可预测的 FHIR R4 Profile 校验子集其边界在 profile_check.py 的模块 docstring 中写得很明确从本地npm 包风格的 IG 快照目录读取StructureDefinition与ValueSet资源只校验资源在meta.profile中显式声明的 Profile不拉取网络包、不联系术语服务器、不执行 FHIRPath 不变量invariant也不试图替代完整的 FHIR 校验器。模块注释profile_check.py明确列出了支持的约束子集约束类型支持情况不满足时的行为最小/最大基数min/max cardinality✅ 支持error/requiredfixed[x]固定值✅ 支持error/value本地可枚举的绑定locally enumerable bindings✅ 支持按 binding strength 分级error/warning/information必填的identifier/categoryslice以 fixed 或 pattern 鉴别器描述✅ 支持error/requiredFHIRPath 不变量、远程术语展开、其他不支持约束❌ 不评估以information级别的not-supported问题返回绝不报错、绝不联网这套不支持就降级为信息的设计哲学贯穿整个检查器检查器永远只报告它确凿掌握的结论不会把没验证过当作代码非法。第一步获取并固定 WHO SMART Guidelines IG 包选择接收系统所用的 WHO SMART Guidelines 实施指南的已发布版本从其发布页或版本历史页复制对应package.tgz产物的链接。关键要求是把版本与校验和记录到部署配置中保证一致性检查的输入可复现reproducible。获取与解压 IG 包的完整命令如下来自原文档 fhir-smart-guidelines.mdexport SMART_IG_PACKAGE_URLhttps://example.org/path/to/package.tgz export SMART_IG_DIR./vendor/smart-ig mkdir -p $SMART_IG_DIR curl --fail --location $SMART_IG_PACKAGE_URL --output /tmp/smart-ig-package.tgz tar -xzf /tmp/smart-ig-package.tgz -C $SMART_IG_DIR test -d $SMART_IG_DIR/package关于 IG 包目录有几个重要的使用约定检查器接受包含package/的目录也接受package/目录本身源码中_load_package通过root / package if (root / package).is_dir() else root自动判断见 profile_check.py它只读取目录下递归扫描到的本地 JSON 资源rglob(*.json)OpenMed不打包、不分发WHO 实施指南需要使用者自行准备优先选择带生成式StructureDefinition.snapshot的包。如果只有 differential差异版本检查器只评估其中显式出现的约束不会去抓取或合并 Profile 的baseDefinition部分展开partial expansion或不可枚举non-enumerable的 ValueSet 内容会以 informational 报告而不会被当作代码无效的证据。_load_package会把无法解析的 JSON 文件记录为information/processing问题后跳过若目录中完全没有StructureDefinition会追加一条not-found信息并抛出FileNotFoundError当ig_dir不是目录时见 profile_check.py。仓库自带的合成测试 IG 包位于 tests/unit/clinical/fixtures/smart_profiles/package其 package.json 声明了type: fhir.ig、fhirVersions: [4.0.1]是观察 npm 包结构的最佳样例。第二步用 check_bundle() 检查一个 Bundlecheck_bundle()接收一个 FHIR R4 Bundle字典结构和 IG 目录路径返回一个 FHIR R4OperationOutcome。完全合规的 Bundle 得到标准的No issues detected.信息结果违规项使用 FHIRPath 风格表达式定位例如Bundle.entry[0].resource.name[0].family。from openmed.clinical.exporters.fhir import check_bundle, to_bundle bundle to_bundle(resources, doc_idencounter-123) outcome check_bundle(bundle, ./vendor/smart-ig) blocking [ issue for issue in outcome[issue] if issue[severity] in {fatal, error} ] if blocking: raise ValueError(FHIR export does not satisfy its declared profiles)返回值与 API 契约从 profile_check.py 的签名与 docstring 可以看到def check_bundle( bundle: Mapping[str, Any], ig_dir: str | PathLike[str], *, original_bundle: Mapping[str, Any] | None None, ) - dict[str, Any]:输入 Bundle永远不会被修改测试test_checker_is_pure_and_performs_no_network_access用socket.create_connection打桩验证了纯函数、零网络访问见 test_fhir_profile_check.py输入不是 mapping 形状抛TypeError不是resourceTypeBundle抛ValueErrorig_dir不存在抛FileNotFoundError返回的OperationOutcome.issue中severity严格限定为 FHIR R4issue-severity值集fatal/error/warning/informationcode严格限定为issue-type值集由共享的 operation_outcome.py 保证空问题列表时自动产出information/informational的No issues detected.见 operation_outcome.py。内部校验流水线检查器对每个Bundle.entry[i].resource依次执行源码见 profile_check.py校验entry与resource的结构完整性Bundle.entry必须是数组、资源必须带resourceType读取资源的meta.profile声明列表支持字符串或数组见_declared_profiles将声明中的 canonical URL 与本地包中的StructureDefinition.url匹配带|version时会剥离版本见_canonical若声明了本地包中没有的 Profile →information/not-found若 Profile 的type与资源类型不符 →error/structure对 Profile 的每个 elementsnapshot 优先、其次 differential做基数检查、fixed[x]检查、绑定检查并对sliceName元素做 slice 检查。路径解析与基数统计由无依赖的 _validation_primitives.py 完成它按父级分组统计每次出现的数量例如name在资源里出现两次就分别对每个name[0]、name[1]检查组内基数并支持[x]类型选择子value[x]会匹配valueString、valueQuantity等具体键。第三步to_bundle() 的 profile_check 回调闸门Bundle 导出器 bundle.py 提供了可选参数profile_check回调。回调收到已完成 Bundle 的深拷贝因此它可以收集 outcome 或抛出策略错误而不会改动正在导出的结果回调的返回值被忽略。outcomes [] def profile_gate(candidate): outcome check_bundle(candidate, ./vendor/smart-ig) outcomes.append(outcome) if any(issue[severity] error for issue in outcome[issue]): raise ValueError(FHIR profile check failed) bundle to_bundle( resources, doc_idencounter-123, profile_checkprofile_gate, )省略profile_check则保持原有的 Bundle 导出行为不变即该功能是**完全可选opt-in**的。源码层面bundle.py回调在 Bundle 组装完成含entry、request块后以copy.deepcopy(bundle)调用一次若传入不可调用对象抛TypeError(profile_check must be callable)回调内对副本的任意修改都不会影响导出的 Bundle测试test_bundle_profile_check_hook_is_opt_in_and_cannot_mutate_output验证了回调把副本type改成history后导出结果仍是transaction见 test_fhir_profile_check.py。顺带说明to_bundle的组装行为因为它与后续校验定位直接相关每个资源获得基于doc_id 资源索引种子的确定性urn:uuidfullUrl同输入产出字节级一致的输出Bundle 内部的字面引用ResourceType/id会被重写为目标的fullUrl避免悬空内部引用去标识化中被删除的资源如 Patient对应的外部引用保持原样transaction/batch类型下每个 entry 会携带request块method/url便于服务器直接处理。第四步审计脱敏对 Profile 一致性的影响脱敏可能把 Profile 要求的元素删空例如Patient.name.family是 1..1 必填。把脱敏前的 Bundle作为original_bundle传入检查器就能区分脱敏引入的违规与源导出本就存在的违规from openmed.clinical.exporters.fhir import check_bundle from openmed.interop.fhir_operations import de_identify_bundle original bundle deidentified de_identify_bundle(original, methodremove) outcome check_bundle( deidentified, ./vendor/smart-ig, original_bundleoriginal, )诊断信息使用如下两种前缀之一De-identification introduced profile violation对应的 Profile 约束在脱敏前通过、脱敏后失败——这是需要修复的信号Pre-existing profile violation同一个约束在两个 Bundle 中都失败——说明问题在源导出阶段就存在不应归咎于脱敏。de_identify_bundle是 fhir_operations.py 提供的入口它对每个entry.resource应用de_identify_resource同时保留 Bundle 的type、entry 顺序、fullUrl、request块与引用可选policy、method默认remove与deidentifier覆盖参数。源码中这个分类逻辑由_classify_deidentification实现profile_check.py它比较脱敏前后两个 Bundle 的违规身份指纹Profile URL entry 索引 元素表达式等构成的元组集合命中即判定为 pre-existing否则判定为 introduced。对应的测试覆盖test_fhir_profile_check.pytest_post_deid_audit_flags_exactly_the_removed_required_element删除family后精确报告Bundle.entry[0].resource.name[0].family为 introduced 违规test_pseudonymize_policy_variant_preserves_required_element用伪名化替换姓氏后检查通过——伪名化优于删除test_post_deid_audit_marks_unchanged_violations_as_preexisting源导出就缺少必填identifier时标记为 pre-existing。修复策略优先伪名/替代值而非保留原值如果删除操作把某个 Profile 必填元素清空了应调整该路径的脱敏策略优先采用策略允许的伪名pseudonym或替代值surrogate而不是保留原始值。原文档给出的例子非常典型一条必填的患者姓名可以以一致的伪名保持结构存在family字段仍然填充而原始标识符照常移除。修改策略后需要重跑脱敏后检查以及常规的隐私门禁privacy gates。隐私与解释边界诊断永不引用 PHI 值这是该检查器与普通校验器最关键的差异之一也是它能安全进入审计与可观测链路的原因诊断信息只标识约束种类如required、value、code-invalid与结构化表达式如Bundle.entry[0].resource.name[0].family诊断绝不引用被检查资源中的实际值或期望值。例如某字段绑定了required的 ValueSet 但代码不在其中报告只会说编码元素超出了本地可用的 required 绑定而不会打印那个违规代码本身测试test_outcome_never_quotes_checked_phi_values直接把姓氏和代码都改成Jane Roe然后用assert_redacted验证序列化后的 outcome 中不出现该字符串见 test_fhir_profile_check.py。因此OperationOutcome可以安全地路由经过常规的审计与可观测性通道并与其他导出元数据受同样的访问控制约束。另一个需要理解的语义是information级别的not-supported问题意味着本地检查器刻意没有评估该约束——它不代表通过也不代表失败。当接收程序需要完整的 FHIRPath 不变量、术语或 slicing 鉴别器覆盖时应使用接收方自己的完整校验器。源码中_unsupported_constraint_findingsprofile_check.py会把constraint、condition、maxLength、minValue/maxValue、非 slice 的pattern以及非value/pattern类型的 slice 鉴别器一律降级为information/not-supported。源码级原理检查器内部结构IG 包加载与 ValueSet 可枚举性判定_load_package递归扫描package目录下所有*.json按url的 canonical 值剥离|version建立两份索引StructureDefinition的 Profile 表与ValueSet表。ValueSet 的可枚举性判定逻辑_read_value_setprofile_check.py很细致优先读取expansion.contains逐层收集(system, code)abstract概念不计入可选代码offset/total/count参数用于判断展开是否完整——分页未到底或总数对不上都会被判定为不完整回退读取compose.include仅当 include 全部是直接的concept列表无filter、无嵌套valueSet时才算可枚举存在exclude直接视为不可枚举不可枚举或缺失的 ValueSet 一律不参与代码判定绑定检查降级为information/not-supported。这正是部分或不可枚举的 ValueSet 内容只作为信息报告不作为代码无效的证据的实现基础。绑定强度分级_check_bindingprofile_check.py按 FHIR 绑定强度分级报告binding.strength违规时 severityrequirederrorextensiblewarningpreferredinformationexample不评估直接忽略代码匹配支持code原语、Coding、CodeableConcept三种形状_extract_codes见 _validation_primitives.py负责从这三种形状中抽取(system, code)对无 system 的原始 code 仅在与值集代码匹配时才通过测试test_systemless_coding_does_not_match_a_system_specific_value_set专门守护了这一语义。Slice 检查identifier / category 子集_check_sliceprofile_check.py只支持基路径以identifier或category结尾的必填 slice鉴别器必须是value或pattern类型路径支持$this相对路径。判定流程为读取 slicing 元素上的discriminator声明解析出(相对路径, fixed|pattern, 期望值)约束列表对每个 occurrence 用_matches_slice匹配约束pattern做递归的部分匹配_pattern_matches校验 slice 的 min/max 基数并对命中的 slice 再递归检查其子元素约束。仓库的合成 Profile StructureDefinition-smart-anc-patient.json 展示了典型写法Patient.identifier使用value/system鉴别器定义anc-idslicemin1且system用fixedUri钉死同时Patient.name1..*与Patient.name.family1..1为必填——正是脱敏审计测试里被反复验证的约束。命令行与测试验证除 Python API 外仓库还提供了 CLI 入口openmed fhir validate支持--input、--version R4、--profile ips、--output、--json参数其行为在 tests/unit/cli/test_fhir_cli.py 中有端到端验证——对一个synthetic_ips_r4.json运行 IPS Profile 检查退出码 0输出valid: true且 outcome 的issue[0].severity为information。这为把 Profile 检查接入 CI/发布流水线提供了直接的命令行形态。单元测试 test_fhir_profile_check.py 覆盖了约 20 个场景除了前文提到的脱敏审计、PHI 不泄露、零网络访问、回调不可变之外还包括必填元素缺失的精确定位Bundle.entry[0].resource.name、错误的 fixed 码定位、必填 slice 缺失与子元素检查、部分展开 ValueSet 的非阻断性、抽象展开码不可选、仅按声明鉴别器路径匹配 slice、未知约束降级为 informational 等。边界与最佳实践小结定位牢记这是投递前的定向导出安全检查不是合规认证也不是临床决策工具——完整的 FHIR 校验请交给接收系统的完整校验器IG 输入要固定记录package.tgz的版本与校验和保证检查可复现优先 snapshotdifferential-only 的 Profile 只评估显式约束覆盖不完整脱敏审计常开始终传original_bundle让脱敏引入的违规与源导出遗留的违规一目了然删除改为伪名对 Profile 必填路径优先使用一致伪名/替代值而非删除修改策略后重跑检查与隐私门禁注意诊断纪律检查器诊断不含 PHI 值但任何下游二次加工也应遵守同样的只报结构、不报值约束理解 not-supported 语义看到information/not-supported不要当作失败它只是本地未评估必要时升级到完整校验器覆盖 FHIRPath 不变量、术语与 slicing 鉴别器。相关实现与测试路径profile_check.py、bundle.py、operation_outcome.py、fhir_operations.py、test_fhir_profile_check.py、合成 IG 样例 smart_profiles/package。关于 OperationOutcome 的脱敏安全报告设计可进一步参考 docs/fhir/operation-outcome.md。【免费下载链接】openmedLocal-first healthcare AI: clinical NER HIPAA PII de-identification that runs 100% on-device. 2,200 medical models, 21 languages, Apple MLX Python, no cloud, no patient data leaving your network. Apache-2.0项目地址: https://gitcode.com/GitHub_Trending/ope/openmed创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表