PHAROS:4 个智能体,60 秒,漏检 1 个药物安全性信号就可能酿成灾难

Elasticsearch Agent Builder 黑客马拉松

pharos-blog.png

美国食品药品监督管理局 (FDA) 每年收到约 200 万份药物不良事件报告。根据法律要求,制药公司必须在收到严重不良事件报告后的 15 个日历日内发现安全性信号。但在实际工作中,药物警戒分析师需要手动审查分散在 FDA 不良事件报告系统 (FAERS)、EudraVigilance、电子健康记录 (EHR) 和社交媒体中的文档。检测往往需要数周至数月,每个信号还会占用分析师 40 多个小时。

响应迟缓的代价并不抽象。默克公司未能发现 Vioxx(万络)相关心脏安全性信号,最终支付了 48.5 亿美元和解金。单个漏检信号就可能引发 1 亿至 10 亿美元的罚款。但真正的代价是,患者仍在服用本应触发警示的药物,而相关风险却没有被及时发现。

我是 Prajwal Sutar,一名独立开发者。过去一年,我一直在将真实数据接入基于大型语言模型 (LLM) 的流水线,涉及数据摄取、异步编排和多智能体协调。我找不到任何现有工具,能够在同一条自动化流水线中整合信号检测、报告生成和升级处置。于是,我在 Elasticsearch Agent Builder Hackathon 期间构建了这样一套系统。

PHAROS的功能

PHAROS(Pharmacovigilance Autonomous Reasoning and Oversight System,药物警戒自主推理与监督系统)会从 FDA FAERS API 提取不良事件报告,运行 WHO 标准统计分析以发现安全性信号,生成实际监管文件(例如 MedWatch 3500A 表单、PSUR 章节和病例叙述),并将告警推送至 Slack、Jira 和电子邮件。

从数据摄取到告警发送,全程不到 60 秒

端到端来看,流程是这样的。系统收到 50 份针对虚构药物 CARDIVEX 的不良事件报告,这些报告都提到突发性视力丧失,并集中出现在日本、韩国和印度。随后,这些报告会被编入索引。不到一分钟,系统就检测到 CARDIVEX/视力丧失的比例报告比 (PRR) 为 18.94,识别出 JP/KR/IN 地理集群,生成 MedWatch 3500A 表单和 PSUR 章节,向 #safety-critical 发送 Slack 告警,创建 Jira P1 工单,并向安全负责人发送电子邮件。所有操作都会记录到 pharos-audit-log 中,因为在制药行业,没记录,就等于没发生。

四名代理负责此事,各自负责不同任务。

为什么是四个智能体,而不是一个

我将系统拆分开来,是因为这些任务差异足够大,单个智能体很难在所有任务上都表现出色。监控报告量激增是一种技能,计算统计比率是另一种技能,撰写监管文件又是另一种技能,而在凌晨 2 点决定该通知谁,则完全是另一类任务。每个智能体都有针对具体任务调优的系统提示,并匹配相应的温度参数:ANALYST 运行在 0.0,因为 PRR 数值不需要“创造性”发挥。SCRIBE 运行在 0.2,用于受控文本生成。SENTINEL 则为 0.1。

哨兵

SENTINEL 会监控 pharos-adverse-events 索引中的报告量激增情况。它使用 ES|QL 将过去 7 天的报告量与 90 天基线进行比较。如果某种药物的报告量达到基线的 3 倍,SENTINEL 就会触发 Elastic Workflows,启动 ANALYST。在 CARDIVEX 运行中,它捕捉到了 15 倍的激增。

分析师

ANALYST 是真正执行检测的环节。它完全在 ES|QL 中运行 WHO PRR 计算:STATS 用于计数, EVAL 用于计算比率,WHERE 用于针对药物-反应组合设置阈值。随后,它使用 BUCKET(report_date, 1 week) 执行时间分析,以捕捉每周集群;基于 geo.country_code 进行地理聚合;并结合 BM25 与密集向量混合搜索,查找相似的历史信号。严重程度按层级分类:PRR ≥ 5.0 且病例数 ≥ 5 的信号为 CRITICAL,PRR ≥ 2.0 且病例数 ≥ 3 的信号为 HIGH,任何高于 1.5 的信号都会进入 MONITORING。确认后的信号会写入 pharos-signals

文书智能体

SCRIBE 会接收已确认的信号,并生成三类文档:MedWatch 3500A、PSUR 第 VI 节和病例叙述。它会从不良事件索引中提取最多 100 份支持性病例报告,生成相应文档,并将这些文档索引到 pharos-regulatory-reports

通报智能体

HERALD 是执行层。CRITICAL 信号会触发 Slack 告警(采用 Block Kit 格式)、Jira P1 工单,以及发送给安全负责人和安全副总裁的电子邮件。HIGH 信号会触发 Slack、Jira P2 工单和发送给安全负责人的电子邮件。MONITORING 信号则会汇总到每周摘要中。如果 CRITICAL 信号在 2 小时升级超时后仍未确认,系统会再次提醒安全副总裁。

智能体之间的交接全部通过 Elastic Workflows 运行 — 总共九个工作流,涵盖智能体间协调、按 cron 计划执行的夜间 FAERS 数据摄取、Slack/Jira/电子邮件发送、审计日志记录,以及升级超时处理。

让统计计算留在 Elasticsearch 中

我有意选择将 PRR 计算留在 ES|QL 中完成,而不是把数据拉到 Python 中。一开始,我以为统计工作会需要 pandas。事实证明,我错了。

完整的 WHO PRR 公式、计数、比率计算、阈值和时间分桶,全部作为 ES|QL 查询运行。智能体调用 ES|QL 工具,对结果进行推理,然后写回结果 — 不需要 pandas,不需要外部计算,也没有数据传输瓶颈。统计计算会随集群扩展而扩展。

对于任意分析,ES|QL 不如 pandas 灵活。但对于 WHO 公式和每周 BUCKET 聚合,它可以干净利落地完成任务。去掉中间的 Python 层后,架构简化的程度超出了我的预期 — 智能体只需查询和推理,也少了一个可能出错的环节。

使其发挥作用的索引设计

PHAROS 在四个 Elasticsearch 无服务器索引上运行,其中主要的一个,pharos-adverse-events,是我花费最多设计时间的地方。

它配置了自定义 clinical_text_analyzer,通过 snowball 词干提取支持叙述文本搜索;配置基于 keyword tokenizer 的 drug_name_analyzer,用于药物名称精确匹配;配置 1,536 维 dense_vector 字段,用于叙述文本嵌入;配置 geo_point,用于地理集群;并配置 嵌套映射,用于反应数据。索引设计支持智能体所需的所有查询,包括模糊叙述文本搜索、精确药物查询、地理聚合和语义相似性。另外三个索引则更为直接:pharos-signals 用于存储检测到的信号、PRR 分数和分析师的推理链;pharos-regulatory-reports 用于保存生成的文档;pharos-audit-log 用于记录每个智能体操作的时间戳。

几乎让流水线崩溃的不起眼问题

让 LLM 稳定返回结构化 JSON,是我没有预料到的难题。

当您要求 LLM 返回 JSON 时,得到的可能是夹在三段解释文字中的 JSON,也可能是 Markdown 代码块里的 JSON,还可能先是一段对话式开场白,然后是 JSON,最后再附上一段贴心总结。智能体之间会相互传递结构化数据,因此每个响应都必须能够被顺利解析。如果 SCRIBE 无法可靠读取 ANALYST 的输出,那么无论信号检测有多出色,都无济于事。

我花了很多时间调优系统提示,最终编写了一个 JSON 提取函数,可以处理原始 JSON、Markdown 代码块,以及混在自然语言中的 JSON。这项工作并不有趣,但它决定了多智能体流水线究竟能真正运行,还是只是演示起来效果不错。

我会优先改进什么

目前,PRR 计算仍是点估计。生产级药物警戒系统需要卡方置信区间和贝叶斯 IC 评分。数据模型中已经接入了 ic_score 字段 — 但它现在使用的是近似值,而不是正确的贝叶斯计算。如果有更多时间,这是我首先会改进的地方。

系统目前还会将“视力模糊”和“视力丧失”视为不同事件。下一步是引入具备 MedDRA 本体感知能力的反应分组,让系统能够跨相关术语捕捉信号,而不是将每个字符串都视为彼此独立。之后,我会将 EudraVigilance 数据与 FAERS 数据一并引入,用于跨洲相关性分析。

更宏观的一点

每年有 200 万份不良事件报告摆到某个人的案头,而当前的应对方式,是增加更多分析师,进行更多人工审查。PHAROS 的主张是:答案在于让智能体运行 WHO 统计分析、生成文书,并将问题升级给正确的人 — 而这一切都发生在分析师打开笔记本电脑之前。

PHAROS 基于 MIT 许可证开源。如果您从事药物警戒或监管事务工作,并希望用真实数据运行这套系统,欢迎与我联系。

Prajwal Sutar

独立开发者 ,

Prajwal Sutar 是一名专注于 AI 系统和大规模数据流水线的独立开发者。

GitHub · 演示 · LinkedIn

本文中描述的任何功能或功能性的发布和时间均由 Elastic 自行决定。当前尚未发布的任何功能或功能性可能无法按时提供或根本无法提供。

在本博文中,我们可能使用或提到了第三方生成式 AI 工具,这些工具由其各自所有者拥有和运营。Elastic 对第三方工具没有任何控制权,对其内容、操作或使用不承担任何责任或义务,对您使用此类工具可能造成的任何损失或损害也不承担任何责任或义务。请谨慎使用 AI 工具处理个人、敏感或机密信息。您提交的任何数据都可能用于 AI 训练或其他目的。Elastic 不保证您所提供信息的安全性或保密性。在使用任何生成式 AI 工具之前,您都应自行熟悉其隐私惯例和使用条款。

Elastic、Elasticsearch 及相关标志是 Elasticsearch B.V. 在美国及其他国家/地区的商标、徽标或注册商标。所有其他公司和产品名称均为其相应所有者的商标、徽标或注册商标。