OpenMed 临床实体提取到 FHIR:从本地 NER 到确定性 R4 Bundle 的落地指南
2026/9/19 20:49:09 网站建设 项目流程

OpenMed 临床实体提取到 FHIR:从本地 NER 到确定性 R4 Bundle 的落地指南

【免费下载链接】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 仓库中的技能文档 extract-clinical-entities-to-fhir 展开,讲解如何把合成或已脱敏文本中的临床实体,映射为确定性的 FHIR R4 资源并组装成 Bundle 提交给 FHIR 服务器。读完本文,你将掌握"抽取与编码分离"的管线设计、analyze_textto_bundle的完整调用链、以及避免发明术语编码、保证隐私安全的工程要点。

核心思想:把"抽取"和"临床编码"彻底分离

OpenMed 的定位是 100% 本地运行的医疗 AI(临床 NER 与 HIPAA PII 脱敏),而 FHIR 导出是这条链路的"最后一公里"。技能文档开篇即给出原则:

Separate extraction from clinical coding. OpenMed finds spans and supplies the mechanical FHIR builders; the application decides which resource type and status are clinically appropriate.

也就是说:

  • 抽取(extraction):由 OpenMed 完成——找出文本中的实体 span,并给出置信度、标签、偏移量;
  • 编码(clinical coding):由应用层负责——决定每个 span 映射成哪种 FHIR 资源类型(Condition / MedicationStatement / Observation……)、赋予什么状态(active / confirmed / final……)、使用哪个术语编码(只能来自用户批准的映射或术语服务)。

OpenMed 提供的是一套"纯机械"的 FHIR 装配工具,从源码看,bundle.py 的模块注释明确写道:"The assembler is purely mechanical: it never synthesises resources (a Patient removed by de-identification stays absent) and does not validate profiles."——它从不凭空捏造资源,被脱敏移除的 Patient 不会复活,也不做 profile 校验(校验是独立的可选环节)。

六步操作流程

技能文档给出了标准流程,共六步:

  1. 保证数据安全:输入必须是合成文本,或在可信边界内先完成脱敏,再进行抽取;
  2. 运行分析模型:调用openmed.analyze_text,使用任务匹配的临床模型;
  3. 过滤预测结果:按 label 和置信度过滤,并把偏移量(offsets)保留在 PHI 安全的审计记录中;
  4. 映射资源类型:把每个被接受的 span 映射到正确的 FHIR 资源类型;
  5. 添加术语编码:只能来自用户批准的映射或术语服务,绝不发明编码
  6. 组装与校验:用to_bundle组装资源,并针对目标 profile 进行校验。

其中第 2 步是整个管线的入口。从源码看,analyze_text 是 OpenMed 顶层公开 API,默认模型为disease_detection_superclinical,关键参数包括:

  • model_name:注册表键、完整的 Hugging Face 模型 id 或本地模型路径;
  • confidence_threshold:实体最低置信度(None表示保留全部),技能示例中使用0.5
  • aggregation_strategy:Hugging Face 聚合策略,默认"simple",设为None可拿原始 token 输出;
  • output_format"dict"(默认)、"json""html""csv"
  • assert_context:默认关闭;开启后会给每个实体附加确定性的否定、不确定性、体验者(experiencer)与时间性标签,写入metadata["clinical_context"],这与技能文档"保留否定、时间性、体验者上下文"的要求直接相关;
  • include_confidence/group_entities/cache_results等格式化相关开关。

可运行的合成示例

技能文档要求先安装模型运行时:

python -m pip install "openmed[hf]"

随后即可运行完整示例(技能文档原文,保持可复制性):

import json from openmed import analyze_text from openmed.clinical.exporters.fhir import to_bundle note = "Assessment: type 2 diabetes mellitus is stable on metformin." result = analyze_text( note, model_name="disease_detection_superclinical", confidence_threshold=0.5, ) resources = [{"resourceType": "Patient", "id": "synthetic-patient"}] for index, entity in enumerate(result.entities, start=1): if entity.label.upper() not in {"CONDITION", "DIAGNOSIS", "DISEASE"}: continue resources.append( { "resourceType": "Condition", "id": f"condition-{index}", "clinicalStatus": { "coding": [ { "system": ( "http://terminology.hl7.org/CodeSystem/" "condition-clinical" ), "code": "active", } ] }, "verificationStatus": { "coding": [ { "system": ( "http://terminology.hl7.org/CodeSystem/" "condition-ver-status" ), "code": "confirmed", } ] }, # A text-only CodeableConcept is preferable to an invented code. "code": {"text": entity.text}, "subject": {"reference": "Patient/synthetic-patient"}, } ) if len(resources) == 1: raise RuntimeError("No condition spans met the label and confidence rules") bundle = to_bundle(resources, doc_id="synthetic-note-001") print(json.dumps(bundle, indent=2))

这个示例有三个值得注意的工程细节:

  1. 状态编码使用 HL7 官方 CodeSystem URIcondition-clinicalcondition-ver-status分别承载clinicalStatus(active)与verificationStatus(confirmed);
  2. 没有可用编码时用纯文本CodeableConcept"code": {"text": entity.text}比发明一个不存在的编码安全得多;
  3. 空结果保护:如果没有 span 通过 label 与置信度过滤,直接抛出RuntimeError,避免向下游提交空 Bundle 造成误导。

to_bundle:确定性装配的底层原理

示例中的核心装配函数是to_bundle,其完整签名(见 bundle.py):

def to_bundle( resources: Sequence[Mapping[str, Any]], *, doc_id: str = "openmed-document", bundle_type: str = "transaction", profile_check: Callable[[Mapping[str, Any]], Any] | None = None, ) -> dict[str, Any]

参数语义:

  • resources:待包装的独立 FHIR 资源,按 Bundle 内顺序排列;每个资源必须含resourceType,带id的资源在 Bundle 内必须ResourceType/id唯一;
  • doc_id:源文档的稳定标识,与资源索引共同决定每个条目的确定性urn:uuidfullUrl,同一输入永远产生字节级一致的输出
  • bundle_type:Bundle 的type,默认"transaction";对transaction/batch类型,每个条目会附带request块(method: POSTurl: resourceType),服务器可直接据此创建资源;
  • profile_check:可选回调,收到完成 Bundle 的深拷贝,作为合规或策略门禁;回调返回值被忽略,但异常会向上传播——这正对应流程第 6 步"validate against the target profile"。

to_bundle内部做了三件确定性工作(见 bundle.py):

  • 生成稳定 fullUrl:每个资源通过deterministic_fullurl(doc_id, index)得到urn:uuid
  • 重写内部引用:把指向 Bundle 内已有资源的字面引用(如"Patient/synthetic-patient")改写为对应urn:uuid,杜绝悬空内部引用;引用不到的资源(如被脱敏移除的 Patient)保持原样不动(references.py 中的deterministic_fullurl);
  • 隐私净化:在导出前调用sanitize_india_health_identifiers,将 ABHA、UPI、ration-card 等印度健康标识符从 FHIRIdentifier和 PatientID 类字段中移除(见 privacy.py)。

to_bundle还会显式拒绝两类非法输入并抛ValueError:资源缺少resourceType,或两个资源共享相同resourceTypeid——重复 id 会静默破坏内部引用映射,因此被显式拦截。对应的测试见 tests/unit/clinical/test_fhir_bundle.py,覆盖了事务 Bundle 每个资源一个 entry、fullUrl 唯一、collection 类型无 request 块、batch 类型保留 request 块、缺 resourceType 抛错、重复 id 抛错等场景。

面向断言感知的导出器:to_condition 与 to_observation

技能文档强调"不要把否定、假设、时间性、体验者上下文丢在一边就断言资源为 active/confirmed"。仓库为此提供了断言感知(assertion-aware)的导出器,它们基于AssertedGroundedSpan(见 assertion_grounding.py)工作。

to_condition

to_condition 把断言感知的 grounded span 物化为 FHIR R4Condition

  • 体验者过滤:span 属于非患者主体(家人/其他人)时,to_condition直接返回None根本不会产生患者 Condition——这是结构性的安全边界;
  • 状态仅来自受审地图clinicalStatus/verificationStatus只来自受审的 status 映射,一个被否定或假设的 span 永远不可能被编码为active+confirmed
  • 无明确状态时省略:当状态不主张 active/inactive(如被否定或假设的发现)时,clinicalStatus字段被省略,符合 FHIR 不声称该状态的不变式;
  • 两个 HL7 系统 URI 常量CONDITION_CLINICAL_SYSTEMCONDITION_VER_STATUS_SYSTEM与技能示例中的字符串完全一致。

to_observation

to_observation 与之对称:

  • 被否定/假设的观察标记为cancelled而不是作为最终结果呈现(_observation_status中对 refuted/hypothetical 返回"cancelled",否则"final");
  • 数值用 FHIRvalueQuantity表达并保留可选的 UCUM 单位(system: http://unitsofmeasure.org),布尔用valueBoolean,其他值落为valueString
  • to_condition相同,非患者体验者的 span 返回None

其他导出器与统一门面

  • to_diagnostic_report:状态采用显式 allowlist(registeredpartialpreliminaryfinalamendedcorrectedappendedcancelledentered-in-errorunknown),缺失/空值用"unknown",非法状态 fail-closed 拒绝;字段名按 R4/R5 并集 allowlist 过滤,且绝不从conclusion推断conclusionCode
  • to_fhir:顶层门面,返回FHIRBundleFHIRExportSummary,是 grounded-span 导出的规范公共入口;
  • to_codeable_concept:受控的辅助型(assist-only)编码发射器,每个概念携带源证据偏移量,并附上显式的"非自主临床决策"免责声明扩展(MEDICAL_DEVICE_ASSIST_ONLY_DISCLAIMER)。

安全检查清单

技能文档给出了不可妥协的安全底线,全部保留如下:

  • 日志与追踪中禁止回显原始标识:原始标识符、源文本、可逆映射不得出现在日志、OperationOutcome.diagnostics或 trace 元数据中;
  • 患者身份服务与临床事实分离:患者身份服务必须与抽取出的临床事实分开维护;
  • 保留上下文再断言状态:在把资源断言为 active/confirmed 之前,必须保留否定(negation)、时间性(temporality)与体验者(experiencer)上下文;
  • 无可用编码时用纯文本CodeableConcept
  • 按接收方要求校验:Bundle 必须针对接收方的 FHIR 版本与 profile 要求进行校验;
  • 不捆绑受限术语表:受限术语(restricted terminologies)不要直接打进 Bundle,应使用用户自己的授权许可服务。

与此呼应,仓库内置的校验器 validate.py 提供了无依赖的 R4 结构校验:validate_bundle/validate_resource覆盖ConditionObservationMedicationStatementDiagnosticReportEncounter等 9 类BASE_R4_RESOURCE_TYPES,产出 FHIRPath 风格的定位与"值无关"(value-free)消息——校验报告不会回显临床内容或标识符,天然适配审计场景;另有 profile_check.py 的check_bundle负责实现指南包(implementation guide)级别的 profile 评估。

仓库配套实战示例

技能文档推荐阅读并运行仓库中的端到端演练 examples/first_five_minutes_redact_extract_fhir.py,它以"前五分钟上手"为目标,展示了一条离线友好的完整链路:

  1. 脱敏deidentify(note, method="mask", confidence_threshold=0.5, use_safety_sweep=True)对内置合成病历做掩码脱敏(示例内通过自定义 loader 避免首次运行下载模型);
  2. 抽取TextProcessor().extract_medical_entities(redacted_text)从脱敏文本中确定性抽取vital_signsdosages等实体;
  3. 组装:映射为最小 FHIR 资源集——Patient+Encounterstatus: finished)+ 每条生命体征一个Observationstatus: finalvalueString承载数值)+ 每个剂量一个MedicationStatementstatus: activemedicationCodeableConcept.textdosage[].text均用纯文本);
  4. 导出to_bundle(resources, doc_id="first-five-minutes-synthetic")生成确定性事务 Bundle。

该示例的完整调用链与技能文档六步流程一一对应,可作为把本指南落地的起点。示例中所有文本均为合成数据,标识符(DEMO-001demo.patient@example.test)均为占位符,可直接运行验证输出。

适用前提与限制

  • 本管线面向合成或已脱敏文本;若输入含真实 PHI,必须先在自己的可信边界内完成脱敏(OpenMed 的deidentify即为此设计),再进入抽取环节;
  • to_bundle是机械装配器,不校验 profile,也不代替你决定资源类型与状态——这两者属于应用层职责;
  • 术语编码只能来自用户批准的映射或术语服务,OpenMed 的 grounded 导出器提供的是带证据与免责声明的辅助编码(assist-only),需要人工复核后使用;
  • 若想在本机复现,请先python -m pip install "openmed[hf]"安装模型运行时,并确认网络可拉取所选模型(或配置本地模型路径)。

【免费下载链接】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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询