EHR 用药 RAG:判断“当前用药”先看 FHIR 时间字段

在 EHR 问答里,“患者现在还在吃某药吗”不适合直接按普通文档 RAG 做。病历 note 里常见“长期口服”“需监测 INR”这类句子,但它可能来自很久以前的记录。只做切片、embedding、相似度召回,再交给 LLM 总结,旧药很容易被写进“当前用药”。

一个构造样例:

MedicationStatement.status = completed
MedicationStatement.effectivePeriod.end = 2024-02-11
note = 患者长期口服华法林,需监测 INR

医生在 2024-08-20 问:

> 截至 2024-08-20,患者现在还在用抗凝药吗?

note 和问题很相关,但结构化字段已经说明这条记录在 2024-02-11 结束。这里要判断的不是“文本像不像”,而是“截至 as_of 是否仍有效”。

图1:一条 MedicationStatement 被拆成 resourceType、status、effectivePeriod、encounter、source 五个标签图1:一条 MedicationStatement 被拆成 resourceType、status、effectivePeriod、encounter、source 五个标签

资源语义不要入库后丢掉

FHIR 里的用药相关资源不能混成一段“药物文本”。至少要保留 resourceTypestatus、时间字段、encounter、来源,以及院内状态映射。

资源含义判断重点
MedicationRequest 医嘱或处方请求 statusintentauthoredOn、有效期、停嘱/续方链
MedicationStatement 患者自述或临床记录 effectiveDateTime / effectivePeriodstatus
MedicationAdministration 给药执行事实 performed[x]、住院 encounter,不能外推出院后仍用
MedicationDispense 药房发药事实 whenHandedOverdaysSupply、退药或取消记录

一个常见坑是跨资源套用 statusMedicationAdministration.completed 多数表示一次给药完成;MedicationDispense.completed 多指发药流程完成;MedicationStatement.completed 通常不应进入当前用药池。不同医院对状态、停嘱、续方的编码可能不一样,生产前需要先做本地字典校准。

图2:FHIR 结构化过滤 → 当前/历史/否定/无法判定四个池 → 向量召回 → LLM 证据摘要 → 医生确认图2:FHIR 结构化过滤 → 当前/历史/否定/无法判定四个池 → 向量召回 → LLM 证据摘要 → 医生确认

先分桶,再做向量召回

对问题先解析出患者、截止日期、药物范围:

{
  "query": "current_anticoagulant_use",
  "patient": "Patient/P001",
  "as_of": "2024-08-20",
  "drug_class": "anticoagulant"
}

结构化层先把记录放入四类:当前候选、历史候选、否定证据、无法判定。最小规则可以这样写:

{
  "MedicationStatement": {
    "current": "status = active AND effective_start <= as_of AND (effective_end IS NULL OR effective_end >= as_of)",
    "history": "status IN (completed, stopped) OR effective_end < as_of",
    "negated": "status = not-taken",
    "unknown": "effective_start IS NULL AND effective_end IS NULL"
  },
  "MedicationDispense": {
    "coverage_end": "whenHandedOver + daysSupply - local_returned_days",
    "current": "coverage_end >= as_of"
  }
}

索引表可以先摊平关键字段:

SELECT resource_id, resource_type, status, effective_start, effective_end
FROM ehr_med_index
WHERE patient_id = 'P001'
  AND drug_class = 'anticoagulant'
  AND status <> 'entered-in-error';

entered-in-error 直接排除并记录原因;effective_end < as_of 进入历史池;缺少时间字段进入无法判定池。向量召回只在候选池内做,用来处理“华法林 / warfarin / DOAC / 抗凝药”等表达差异,而不是替代时间判断。

写规则前,我一般会先查公开研究里的字段设计和评测口径,例如 FHIR medication resourcesEHR medication reconciliationclinical timeline extraction。这一步可以用 超能文献 做中文检索和 PDF 阅读,整理论文里的时间字段、冲突标注和审计方法;它只是研究资料入口,不读取患者 EHR,也不替代临床判断。

输出证据,不只输出结论

LLM 更适合做证据摘要和冲突解释,不应改写字段含义。输出结构可以是:

{
  "answer_class": "history",
  "as_of": "2024-08-20",
  "evidence": [
    {
      "resource_id": "MedicationStatement/ms-778",
      "drug": "warfarin",
      "status": "completed",
      "effective_end": "2024-02-11",
      "bucket": "history"
    }
  ],
  "conflict": "note contains long-term use, but structured end date is before as_of"
}

评测集也要覆盖容易误导模型的样本:停药、换药、重复处方、出院带药、患者自述与处方冲突、住院给药但出院未带药。指标不要只看回答相似度,至少记录 current/history 分类准确率、旧药误报率、证据字段引用完整率、as_of 覆盖率、无法判定样本拒答率。

回到开头的华法林记录:note 像当前用药,但 status=completedeffectivePeriod.end=2024-02-11 指向历史用药。先把几十条审计样本跑通,导出四个证据池和过滤理由,再让 RAG 回答“截至某日是否仍在使用”,通常比直接堆向量库更可靠。

作者:超能文献

posted @ 2026-05-19 22:00  JaxGo  阅读(11)  评论(0)    收藏  举报