文章 Nicky Zhu · 7 小时 前 5m read

为 FHIR 患者快照代理添加向量搜索和嵌入式 Python 功能

在我的第一篇文章中,我介绍了 FHIR 患者快照代理的基础版本:这是一个基于 Streamlit 和 Python 的应用程序,它从 InterSystems IRIS for Health 中检索 FHIR 资源,并利用大型语言模型(LLM)生成简明扼要的患者摘要。

本文作为后续文章,将介绍我是如何通过添加两项针对 InterSystems 的功能来扩展该项目的:

  • 源上下文向量搜索
  • 用于 IRIS 兼容审查的嵌入式 Python 组件

目标是让该项目作为临床摘要原型更具实用性,同时保持设计足够精简,以便于理解和复现。

项目回顾

FHIR 患者快照代理(FHIR Patient Snapshot Agent)是一款基于人工智能的 FHIR 患者数据摘要工具。

该应用程序可检索所选患者的结构化资源:

  • 患者
  • 病情
  • 用药请求
  • 过敏与不耐受
  • 观察
  • 就诊
  • 护理计划

Python 层将这些资源整合为一个紧凑的患者背景。用户随后可以生成以下任一内容:

  • 确定性摘要,或
  • 由大型语言模型(LLM)生成的针对特定角色的摘要

该 LLM 集成通过兼容 OpenAI 的聊天补全 API 使用 Nebius Token Factory。演示中使用的模型为:

meta-llama/Llama-3.3-70B-Instruct
 

该项目为开源项目:

在线演示:

为何要添加向量搜索?

该应用的第一个版本已经显示了生成的患者快照以及所使用的源FHIR资源。

然而,即便是简短的患者病历,也可能包含许多规范化的部分:

  • 当前病情
  • 用药情况
  • 过敏史
  • 观察结果
  • 就诊记录
  • 护理计划
  • 缺失信息
  • 来源与资源参考

对于审稿人而言,快速提出以下问题会很有帮助:

medications allergies labs
 

或者:

encounters care plans
 

该应用现已在 Streamlit 界面中新增了“源上下文向量搜索”部分。生成快照后,用户可对标准化后的患者上下文进行搜索,并查看最相关的段落。

向量搜索的实现方式

在此原型中,我对归一化的患者快照段落采用了轻量级的本地向量化方法。

具体实现如下:

  1. 将标准化后的患者上下文划分为命名片段。
  2. 将每个片段转换为令牌频率向量。
  3. 将用户的查询转换为相同的向量表示形式。
  4. 使用余弦相似度对片段进行排序。
  5. 在用户界面中显示相关性最高的源上下文部分。

该实现刻意设计得简洁且透明:

app/vector_search.py
 

Streamlit 用户界面调用此模块并显示排序后的匹配结果:

app/web_ui.py
 

这并非旨在取代生产环境中的嵌入模型,而是为了清晰地演示向量搜索的工作流程:

  • 对源数据进行归一化
  • 将临床上下文部分向量化
  • 比较查询向量与上下文向量
  • 返回可追溯的源文本段落

在生产环境中,可以将这一概念迁移到 IRIS Vector Search 中,利用基于 FHIR 生成的临床上下文的持久化向量实现。

嵌入式 Python 组件

我还添加了兼容 IRIS 的 ObjectScript 类,其中包含嵌入式 Python 方法,位于以下路径下:

src/FHIR/Snapshot/
 

该代码库包含:

src/FHIR/Snapshot/EmbeddedPythonDemo.cls src/FHIR/Snapshot/EmbeddedVectorSearch.cls
 

这些类演示了如何在 IRIS 类方法中实现 Python 逻辑。

嵌入式 Python 示例包括:

  • 提取数值观测值
  • 将文本分词为简单的向量表示
  • 计算余弦相似度

示例方法:

ClassMethod NormalizeObservationValue(value As %String) As %String [ Language = python ] ClassMethod Vectorize(text As %String) As %String [ Language = python ] ClassMethod CosineSimilarity(leftVectorJson As %String, rightVectorJson As %String) As %String [ Language = python ]
 

包含这些组件是为了使该项目能够作为面向InterSystems的应用程序进行评审,而不仅仅是一个外部Python应用程序。

IPM / ZPM 元数据

该项目还包含一个名为 module.xml 的文件,其中包含包元数据和文件复制条目。

虽然主应用程序的运行环境仍是 Python 和 Streamlit,但包元数据使得在 InterSystems 生态系统背景下更容易审查该代码库。

相关文件:

module.xml
 

观察可视化

我还在 Streamlit 用户界面中添加了简单的观察数据可视化功能。

该应用从标准化患者上下文中提取FHIR观察数据的数值,并按观察类别分组显示图表,例如:

  • 生命体征
  • 实验室
  • 问诊/社会史

这支持了 InterSystems 社区关于为医生提供自定义可视化功能的构想:

目标并非创建一个完整的临床仪表盘,而是展示如何将FHIR观察资源转换为紧凑的可视化摘要,并将其与生成的文本快照并列呈现。

在线演示模式

该在线演示托管在 Render 上:

由于公开托管的应用程序无法访问本地 localhost 上的 IRIS FHIR 服务器,因此在线演示使用了从本地 IRIS for Health FHIR 服务器配置中捕获的“患者 1”上下文。

完整的实时 FHIR 工作流仍在本机 InterSystems IRIS for Health 上运行。

对于本地开发,流程如下:

InterSystems IRIS for Health FHIR Server -> Python FHIR client -> normalizer -> deterministic or LLM summary -> Streamlit UI
 

安全框架

该应用仅用于摘要生成。

它不具备以下功能:

  • 诊断
  • 推荐治疗方案
  • 替代临床判断

每份生成的摘要均包含明确的安全说明及来源资源的可追溯性。用户应参照所列的FHIR资源,对所有生成的内容进行核对。

开发过程中发现的错误报告

在审查 InterSystems Vector Search 演示材料时,我在 LlamaIndex 演示中针对本地 HuggingFace 嵌入的备用路径中发现并报告了一个可重现的问题:

该问题与 llama_demo.ipynb 中的本地嵌入路径有关:其中 LangchainEmbedding 被直接使用而未被导入,且即使选择了本地 all-MiniLM-L6-v2 路径,嵌入维度仍被设置为 OpenAI 嵌入的默认值。

我的收获

这一扩展使该项目更像是一个 InterSystems 应用程序,而非简单的 LLM 封装。

最有用的新增功能包括:

  • 将 IRIS for Health 用作 FHIR 源系统
  • 保持源资源的可追溯性
  • 添加针对特定角色的 LLM 摘要
  • 添加源数据上下文向量搜索
  • 添加嵌入式Python构建产物
  • 添加观察结果可视化
  • 提供在线演示

虽然结果仍是一个小型原型,但现在已能展示更完整的工作流:

FHIR data -> normalized clinical context -> visual review -> vector search -> LLM summary
 

链接

GitHub 仓库:

https://github.com/vinogrannia/fhir-patient-snapshot-agent

在线演示:

YouTube 演示:

首篇文章: