PHAROS: 4명의 요원, 60초, 약물 안전 신호 1건 누락으로 인한 재난
Elasticsearch Agent Builder 해커톤

FDA는 매년 약 200만 건의 약물 부작용 보고를 접수합니다. 제약 회사는 중대한 보고가 접수된 후 15일 이내에 안전성 신호를 감지해야 할 법적 의무가 있습니다. 그러나 실제로 약물 감시 분석가들은 FDA 부작용 보고 시스템(FAERS), EudraVigilance, 전자 의료 기록(EHR), 소셜 미디어 등 여러 곳에 흩어진 문서를 수동으로 검토하고 있습니다. 이러한 과정에서 탐지는 몇 주에서 몇 달이 걸리며, 신호 하나당 분석가 시간이 40시간 이상 소요됩니다.
느린 과정의 비용은 관념적인 것에 그치지 않습니다.Merck는 Vioxx의 심장 신호를 포착하지 못해 48억 5천만 달러의 합의금을 지불하게 되었습니다. 단 한 번의 신호만 놓쳐도 1억 달러에서 10억 달러에 이르는 벌금이 부과될 수 있습니다. 하지만 진짜 대가는 경고가 지정되었어야 하는 약을 환자들이 복용하는 데도 누구도 제때 알아차리지 못했다는 것입니다.
저는 지난 1년 동안 실제 데이터를 대규모 언어 모델(LLM) 기반 파이프라인 수집, 비동기 오케스트레이션 및 다중 에이전트 조정을 통해 처리해 온 독립 개발자, Prajwal Sutar입니다. 저는 신호 탐지, 보고 생성 및 에스컬레이션을 하나의 자동화된 파이프라인으로 연결하는 기존 도구를 하나도 찾을 수 없었습니다. 그래서 Elasticsearch Agent Builder 해커톤 기간 동안 직접 만들었죠.
PHAROS의 역할
PHAROS(약물 감시 자율 추론 및 감독 시스템)은 FDA FAERS API에서 부작용 보고서를 가져오고, WHO 표준 통계 분석을 실행하여 안전 신호를 식별하며, 실제 규제 문서(예: MedWatch 3500A 양식, PSUR 섹션 및 사례 내러티브)를 생성하고, Slack, Jira 및 이메일로 알림을 전송합니다.
데이터 수집부터 경보 발송까지 60초 미만이 소요됩니다.
그 결과는 다음과 같습니다. 일본, 한국, 인도에서 갑작스러운 시력 상실을 보고한 가상의 약물 'CARDIVEX'에 대한 부작용 보고 50건이 접수됩니다. 해당 보고는 색인화됩니다. 1분도 되지 않아 시스템은 CARDIVEX/시력 상실에 대한 비례 보고 비율(PRR)이 18.94임을 감지하고, JP/KR/IN 지리적 클러스터를 식별한 뒤, MedWatch 3500A 양식 및 PSUR 섹션을 생성하여, #safety-critical에 Slack 알림을 보내고, Jira P1 티켓을 생성하고, 안전 담당자에게 이메일을 보냈습니다. 제약 업계에서는 로그하지 않으면 발생하지 않은 것으로 간주되기 때문에 모든 작업이 pharos-audit-log에 로그되었습니다.
서로 다른 역할을 맡은 4개의 에이전트가 이를 처리합니다.
에이전트가 하나가 아닌 네 개인 이유
시스템을 분할한 이유는 작업들이 서로 너무 달라서 단일 에이전트가 모든 작업에서 평범한 성능을 보일 것이기 때문입니다. 볼륨 급증을 모니터링하는 것은 통계적 비율을 계산하는 것과 다른 기술이며, 규제 문서 작성뿐만 아니라 새벽 2시에 누구를 호출할지 결정하는 데도 또 다른 기술이 사용됩니다. 에이전트마다 특정 작업에 맞춰진 시스템 프롬프트와 일치하는 온도가 설정됩니다. ANALYST는 창의적인 PRR 수치가 필요하지 않기 때문에 0.0에서 실행됩니다. SCRIBE는 제어된 텍스트 생성을 위해 0.2로 실행됩니다. SENTINEL은 0.1입니다.
SENTINEL
SENTINEL은 볼륨 급증을 감시하기 위해 pharos-adverse-events 인덱스를 확인합니다. ES|QL을 사용해 지난 7일간의 보고 볼륨을 90일 기준선과 비교합니다. 약물이 3배 급증하면 SENTINEL은 ANALYST를 시작하는 Elastic 워크플로우를 실행합니다. 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
HERALD는 실행 계층입니다. CRITICAL 신호가 발생하면 Slack 알림(Block Kit 형식), Jira P1 티켓이 생성되며 안전 담당자 및 안전 담당 부사장에게 이메일이 전송됩니다. HIGH 신호는 Slack 알림, Jira P2 티켓이 생성되며 안전 담당자에게 이메일이 전송됩니다. MONITORING 신호는 주간 요약 보고서에 누적됩니다. CRITICAL 신호에 대한 확인이 이루어지지 않을 경우, 2시간의 에스컬레이션 타임아웃이 발생하여 안전 담당 부사장에게 다시 알림이 전송됩니다.
에이전트 간의 핸드오프는 모두 Elastic 워크플로우를 통해 실행됩니다. 총 9개의 워크플로우가 에이전트 간 조정, 크론 스케줄에 따른 야간 FAERS 수집, Slack/Jira/이메일 발송, 감사 로깅, 그리고 에스컬레이션 타임아웃을 처리합니다.
Elasticsearch 내부에 통계 저장
저는 데이터를 Python에 가져오는 대신 의도적으로 PRR 계산을 ES|QL 내부에 유지하기로 결정했습니다. 시작하기 전에 통계 작업에 pandas가 필요할 거라고 생각했습니다. 제가 틀렸죠.
전체 WHO PRR 공식, 계산, 비율 계산, 임곘값, 임시 버켓팅까지 전부 ES|QL 쿼리로 실행됩니다. 에이전트는 ES|QL 도구를 호출하고, 결과를 분석한 후 다시 기록합니다. pandas도, 외부 컴퓨팅도, 데이터 전송 병목 현상도 없습니다. 통계는 클러스터에 따라 확장됩니다.
ES|QL은 임의 분석에 있어서는 pandas보다 유연성이 떨어집니다. 하지만 WHO 공식과 주간,BUCKET 집계의 경우, 작업을 깔끔하게 처리하죠. 에이전트는 쿼리하고 추론하기만 하면 되고, 문제가 발생할 여지가 하나 더 줄어들어 예상보다 아키텍처가 훨씬 단순해졌습니다.
작동을 가능하게 하는 인덱스 설계
PHAROS는 4개의 Elasticsearch Serverless 인덱스에서 실행되며, 이 중 주요 인덱스인 pharos-adverse-events는 제가 가장 많은 설계 시간을 할애한 인덱스입니다.
포함된 사용자 지정 clinical_text_analyzer는 내러티브 검색을 위한 스노우볼 어간 추출 기능을 제공하며, drug_name_analyzer는 정확한 약물 매칭을 위한 키워드 토큰화에 사용됩니다. dense_vector 필드(1,536차원)는 내러티브 임베딩을 지원하며, geo_point는 지리적 클러스터링을, nested 매핑은 반응 데이터를 처리합니다. 에이전트에 필요한 모든 쿼리(퍼지 내러티브 검색, 정확한 약물 검색, 지리적 집계, 의미적 유사성)는 인덱스 설계를 통해 지원됩니다. 나머지 세 개의 인덱스는 더 간단합니다. pharos-signals는 탐지된 신호를 PRR 점수와 분석가의 추론 체인과 함께 저장하며, pharos-regulatory-reports는 생성된 문서를 보관하고, pharos-audit-log는 모든 에이전트 작업의 타임스탬프를 기록합니다.
파이프라인을 거의 붕괴시킬 뻔한 중요하지만 눈에 띄지 않는 문제
LLM이 구조화된 JSON을 안정적으로 반환하게 만드는 것은 예상하지 못한 어려움이었습니다.
LLM에게 JSON을 요청하면 세 단락의 설명에 포함된 JSON, 마크다운 코드 펜스 안의 JSON, 또는 대화형 서문 뒤에 JSON과 유용한 요약이 이어지는 형태의 JSON을 받게 됩니다. 에이전트가 서로 정형 데이터를 전달하므로 모든 응답이 깔끔하게 구문 분석되어야 합니다. ANALYST의 출력을 SCRIBE가 안정적으로 읽을 수 없다면 신호 탐지 기능이 아무리 뛰어나더라도 의미가 없습니다.
시스템 프롬프트를 조정하는 데 많은 시간을 할애했고, 결국 원시 JSON, 마크다운 코드 펜스, 자연어 내에 포함된 JSON을 처리하는 JSON 추출 함수를 작성하게 되었습니다. 흥미로운 작업은 아니지만, 다중 에이전트 파이프라인이 실제로 작동하는지 아니면 단순히 데모만 잘하는지를 결정짓는 데 중요한 역할을 하는 작업입니다.
가장 먼저 수정할 부분
현재 PRR 계산은 포인트 추정치입니다. 생산 약물 안전 감시 시스템에는 카이제곱 신뢰 한계와 베이지안 IC 점수가 필요합니다. 데이터 모델에 이미 ic_score 필드가 연결되어 있으며, 적절한 베이지안 계산 대신 근사치가 사용되는 중입니다. 시간이 더 있다면 가장 먼저 수정할 부분입니다.
또한 시스템이 '시야 흐림'과 '시력 상실'을 별개의 사건으로 취급합니다. 즉각적인 다음 단계는 MedDRA 온톨로지 인식 반응 그룹화로, 시스템이 각 문자열을 독립적으로 취급하는 대신 관련 용어 전반에 걸쳐 신호를 포착할 수 있도록 합니다. 그 후, 대륙 간 상관관계를 파악하기 위해 EudraVigilance 데이터와 FAERS 데이터를 함께 가져올 것입니다.
더 넓은 관점
매년 2백만 건의 부작용 보고서가 누군가의 책상에 쌓이며, 현재의 해결책은 더 많은 분석가가 더 많은 수동 검토를 수행하는 것입니다. PHAROS는 WHO 통계를 실행하고, 서류를 생성하며, 적절한 사람에게 전달하는 에이전트가 해결책이라고 주장합니다. 게다가 이 모든 것이 분석가가 노트북을 열기도 전에 이루어지죠.
PHAROS는 MIT 하의 오픈 소스입니다. 약물 감시 또는 규제 업무 분야 종사자이며 실제 데이터를 기반으로 이 작업을 수행하고자 하는 분의 의견을 듣고 싶습니다.
이 게시물에서 설명된 모든 기능이나 성능의 출시와 일정은 Elastic의 단독 재량에 따라 결정됩니다. 현재 제공되지 않는 기능이나 성능은 예정된 시간에 출시되지 않을 수도 있으며 아예 제공되지 않을 수도 있습니다.
이 블로그 게시물에서는 타사 생성형 AI 도구를 사용하거나 언급했을 수 있으며 이러한 도구는 각각의 소유자가 소유하고 운영합니다. Elastic은 이러한 타사 도구에 대해 어떠한 통제권도 없으며 해당 도구의 콘텐츠, 운영, 사용뿐 아니라 사용으로 인해 발생할 수 있는 손실이나 손해에 대해 어떠한 책임도 지지 않습니다. 개인 정보, 민감한 정보 또는 기밀 정보를 AI 도구와 함께 사용할 때는 주의하시기 바랍니다. 제출된 모든 데이터는 AI 학습이나 기타 목적으로 사용될 수 있습니다. 제공한 정보가 안전하게 보호되거나 비밀로 유지된다는 보장은 없습니다. 생성형 AI 도구를 사용하기 전에 해당 도구의 개인정보 보호 관행과 이용 약관을 숙지하시기 바랍니다.
Elastic, Elasticsearch 및 관련 마크는 미국 및 기타 국가에서 Elasticsearch B.V.의 상표, 로고 또는 등록 상표입니다. 그 외의 모든 회사 및 제품 이름은 해당 소유자의 상표, 로고 또는 등록 상표입니다.