2026山东大学软件学院创新实训——IntelliHealth(七)
文章目录
前言
IntelliHealth 药物禁忌分析 多说明书检索 + 段落打分 的技术说明。
1.原有方案
Neo4j 未命中时,系统会走 OpenFDA 冷启动链路:
拉 FDA 说明书 → 取英文相互作用 → LLM 写中文摘要 → 写入图库
早期实现非常直接:
// 旧逻辑
.queryParam("limit", 1)
return interactions.get(0);
这带来两个问题:
① 只看了 1 份说明书
OpenFDA 搜索 drug_interactions:aspirin+AND+ibuprofen 可能命中 数千份 label,但 limit=1 只返回排序第一的 一份。
这份药的主药可能是 etodolac,只是在 drug_interactions 大段文字里顺带提到 aspirin,并不是 aspirin ↔ ibuprofen 的专述。
② 偏题也硬返回
尽管没有在说明 药物A 和 药物B 的校验,LLM 只能基于偏题英文写摘要,结果变成「可能增加不良反应,不建议合用」这类笼统话,远不如人工清洗数据(如 布洛芬干扰阿司匹林抗血小板作用…)准确。
解决方案 :
我们需要先从多份说明书中找对段,再交给 LLM。
2. OpenFDA接口
请求示例:
GET https://api.fda.gov/drug/label.json?search=drug_interactions:aspirin+AND+ibuprofen&limit=10
响应结构:
{
"meta": { "results": { "total": 4264 } },
"results": [
{
"openfda": { "generic_name": ["..."] },
"drug_interactions": "很长的一段英文,或 [\"段落1\", \"段落2\"]"
}
]
}
要点:
| 概念 | 说明 |
|---|---|
limit |
返回 几份药品说明书,不是几个字段 |
results[i] |
某一份独立 label |
drug_interactions |
该 label 下与药物相互作用相关的 长英文,可能是一个字符串,也可能是字符串数组 |
total: 4264 |
同时含 aspirin 与 ibuprofen 关键词的 label 很多,只取 1 条极易偏题 |
我们真正需要的,是从 drug_interactions 里截出 同时讨论 drugA 与 drugB 的那一小段,而不是整篇或第一节。
3. 新方案
图库里没有这对药的记录时,才走 OpenFDA 冷启动。
系统会先拉 排名前 10 的说明书(limit=10),对每份的 drug_interactions 解析、切分,必要时用句窗口合并,再对每个候选段 scoreInteractionSegment 打分;
先在单份 label 里选最好的一段,再在 10 份之间取 全局最高分。
低于 100 分(没有一段同时提到两种药)→ 返回 null,不写库;
≥100 分 → 规则分类 → LLM 中文摘要 → 写 Neo4j。
热门药对已 manual 入库的,仍 优先读 Neo4j,不经过本链路。
4. 实现细节
实现位于 DrugInteractionService.queryOpenFda 及若干提取 helper。
4.1 拉取 10 份说明书
private static final int OPENFDA_SEARCH_LIMIT = 10;
选取为10的原因: 在全量 4000+ 条里不可能全查;10 是在 准确度与 API 耗时 之间的折中。排名靠前的 label 更可能同时包含两种药名,比 limit=1 稳得多。若 10 条仍无合格段落,返回 null,宁可查无结果,也不返回偏题段落。
4.2 兼容字段形态
OpenFDA 的 drug_interactions 可能是:
- 单个
String(一整篇) List<String>(多段)
统一转成 List<String> 文本块,避免类型强转失败或漏数据。
4.3 切分长文
FDA 说明书常见两种结构:
- 双换行 \n\n 分段
- 句号 + 大写开头(如 Aspirin When…)分小节
先尝试空行切分,再尝试 FDA_SECTION_SPLIT 正则,把一整篇 interactions拆成多个 候选段落。
4.4 句窗口合并
有时两种药名分散在 相邻几句 里,单句切分拿不到同时含 A、B 的段。
算法实现:
滑动合并连续 1~4 句,仅保留 同时出现 drugA 与 drugB 的窗口,再参与打分。
4.5 单条 label 内选最优段
对切分出的所有段落(及句窗口)调用 scoreInteractionSegment,取 该 label 内最高分 的一段作为候选。
4.6 10 条 label 全局择优
遍历 10 份 results,每份一个候选段,再取 全局最高分 作为最终英文原文,供后续分类与摘要使用。
5. 打分机制
5.1 药名命中(核心)
使用整词匹配(\baspirin\b,大小写不敏感):
| 条件 | 得分 |
|---|---|
| 同时提到 drugA 和 drugB | +100 |
| 只提到其中一个 | +20 |
| 两个都没提到 | 0(直接淘汰) |
100 分是可靠段落的门槛(MIN_ACCEPTABLE_SCORE = 100)。
只提到一种药,说明可能是etodolac 说明书里顺带提 aspirin,不能通过。
5.2 风险与机制关键词(附加分)
在药名得分之上累加,用于 10 条 label 之间的 tie-break(谁更像真正的相互作用描述):
| 英文关键词 | 附加分 |
|---|---|
| not recommended / contraindicated / avoid / do not | +15 |
| antiplatelet / cardiovascular / bleeding / interaction | +10 |
| concomitant / administered with | +5 |
例子 : aspirin + ibuprofen 合格段常含 antiplatelet、not generally recommended,会在 100 分基础上再加分,更容易在多条 label 中胜出。
5.3 合格标准
if (best == null || bestScore < MIN_ACCEPTABLE_SCORE) {
return null; // 前 10 条均无可靠段落
}
| bestScore | 含义 | 行为 |
|---|---|---|
| ≥ 100 | 段落同时含两种药,可信 | 进入分类 + LLM 摘要 + 写 Neo4j |
| 20~99 | 只提到一种药或勉强相关 | 丢弃 |
| 0 | 不相关 | 丢弃 |
6. 分类与摘要
提取到合格英文段落后,仍有两步:
- 规则分类
classifyByRules:从英文中识别 contraindicated / avoid / dose_adjustment 等(OpenFDA的监管用语) - 规则定不了 → LLM 从枚举中选 type
- LLM 写
edge_summary:Prompt 中带入已确定的 type,要求摘要与风险等级一致
这样 英文提取 与 风险类型 分工明确。
总结
OpenFDA 链路是长尾药对的补充,不是替代人工清洗。
之所以对这个进行优化,是因为我们数据是根据目前最新的数据集清洗的,并不能保证OpenFDA后续不会更新数据集,保证软件禁忌分析功能持续可用。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)