PHAROS : 4 agents, 60 secondes, 1 signal de sécurité des médicaments manqué à un pas de la catastrophe

Hackathon Elasticsearch Agent Builder

pharos-blog.png

La FDA reçoit environ deux millions de rapports d’événements indésirables liés aux médicaments chaque année. Les sociétés pharmaceutiques sont légalement tenues de détecter les signaux de sécurité dans les 15 jours calendaires suivant un rapport sérieux. En pratique, les analystes en pharmacovigilance vérifient manuellement des documents dispersés dans le système de rapports d’événements indésirables de la FDA (FAERS), EudraVigilance, les dossiers de santé électroniques (DSE) et les réseaux sociaux. La détection prend des semaines à des mois, et chaque signal consomme plus de 40 heures de temps d’analyste.

Le coût de la lenteur n’est pas abstrait. Le fait que Merck n’ait pas détecté les signaux d’alerte cardiaque liés au Vioxx a coûté 4,85 milliards de dollars en indemnités. Un seul signal manqué peut entraîner des amendes comprises entre 100 millions et 1 milliard de dollars. Mais le véritable coût, ce sont les patients qui prennent des médicaments qui auraient dû être signalés alors que personne ne s’en est aperçu assez rapidement.

Je suis Prajwal Sutar, un développeur indépendant qui a passé l’année dernière à traiter des données réelles via l’ingestion de pipelines basés sur de grands modèles de langage (LLM), l’orchestration asynchrone et la coordination multi-agents. Je n’ai trouvé aucun outil existant qui combine la détection des signaux, la génération de rapports et la remontée d’informations au sein d’un même pipeline automatisé. Alors, j’en ai développé un pendant le hackathon Elasticsearch Agent Builder.

Ce que fait PHAROS

PHAROS (Pharmacovigilance Autonomous Reasoning and Oversight System) extrait les rapports d’événements indésirables de l’API FAERS de la FDA, effectue une analyse statistique conforme aux normes de l’OMS pour identifier les signaux liés à la sécurité, génère les documents réglementaires (par exemple, les formulaires MedWatch 3500A, les sections PSUR et les récits de cas) et envoie des alertes à Slack, Jira et par e-mail.

De l’ingestion des données à l’alerte envoyée, le processus prend moins de 60 secondes.

Voici le fonctionnement du début à la fin. 50 rapports d’effets indésirables concernant un médicament fictif appelé CARDIVEX nous parviennent. Ils font tous état d’une perte soudaine de la vision, concentrée au Japon, en Corée et en Inde. Ils sont indexés. En l’espace d’une minute, le système a détecté un rapport proportionnel (PRR) de 18,94 pour CARDIVEX/perte de vision, identifié le groupe géographique JP/KR/IN, généré un formulaire MedWatch 3500A et une section PSUR, lancé une alerte Slack à #safety-critical, créé un ticket Jira P1 et envoyé un e-mail au responsable de la sécurité. Chaque action a été enregistrée dans pharos-audit-log, parce qu’en pharmacie, si vous ne l’avez pas enregistrée, c’est qu’elle n’a pas eu lieu.

Quatre agents s'en occupent, chacun ayant un travail distinct.

Pourquoi quatre agents plutôt qu’un seul ?

J’ai divisé le système parce que les tâches sont trop différentes pour qu’un seul agent soit médiocre dans toutes. Surveiller les pics de volume n’est pas la même compétence que de calculer des ratios statistiques, ce qui n’est pas la même chose que de rédiger des documents réglementaires, ni que de décider qui appeler à 2 h du matin. Chaque agent reçoit une invite système ajustée à sa tâche spécifique et des paramètres de température qui correspondent : ANALYST fonctionne à 0,0 car il faut éviter que les valeurs PRR soient fantaisistes. SCRIBE fonctionne à 0,2 pour la génération de texte contrôlée. SENTINEL à 0,1.

La sentinelle

SENTINEL surveille l’index pharos-adverse-events concernant les pics de volume. Il utilise ES|QL pour comparer les volumes de rapport des 7 derniers jours à une base de référence de 90 jours. Si un médicament affiche une augmentation de 3 fois, SENTINEL déclenche un workflow Elastic qui lance ANALYST. Lors de l’exécution CARDIVEX, il a détecté un pic de 15 fois.

L'analyste

ANALYST est l’outil qui permet la détection réelle. Il exécute le calcul du WHO PRR de l’OMS entièrement dans ES|QL — STATS pour les comptages, EVAL pour les calculs de ratio et WHERE pour les seuils — sur les paires médicament-réaction. Ensuite, il effectue une analyse temporelle avec BUCKET(report_date, 1 week) pour détecter les regroupements hebdomadaires, l’agrégation géographique sur geo.country_code, et une recherche hybride BM25 + vecteur dense pour trouver des signaux historiques similaires. La classification de la gravité se fait par niveaux : PRR ≥ 5,0 avec plus de 5 cas est critique, PRR ≥ 2,0 avec plus de 3 cas est haut, et tout ce qui est supérieur à 1,5 passe à la surveillance. Les signaux confirmés sont écrits dans pharos-signals.

Le scribe

SCRIBE capte les signaux confirmés et génère trois types de documents : MedWatch 3500A, PSUR Section VI et un rapport de cas. Il extrait jusqu’à 100 rapports de cas justificatifs de l’index des événements indésirables, produit les documents et les indexe dans pharos-regulatory-reports.

Le héraut

HERALD est la couche d’action. Les signaux critiques reçoivent une alerte Slack (formatage Block Kit), un ticket Jira P1 et des e-mails sont envoyés au responsable de la sécurité et au vice-président de la sécurité. Les signaux importants reçoivent une alerte Slack, un ticket Jira P2 et un e-mail sont envoyés au responsable de la sécurité. Les signaux de surveillance sont regroupés dans un résumé hebdomadaire. Un délai d’escalade de 2 heures relance le vice-président de la sécurité si un signal critique n’est pas pris en compte.

Les transferts entre agents passent tous par des workflows Elastic : neuf workflows au total couvrant la coordination agent à agent, l’ingestion FAERS nocturne selon une planification cron, l’envoi de messages Slack/Jira/e-mail, le logging d’audit et le délai d’escalade.

Conserver les statistiques au sein d’Elasticsearch

J’ai délibérément choisi de conserver le calcul du PRR dans ES|QL plutôt que d’extraire les données dans Python. Au départ, j’ai supposé que j’aurais besoin de pandas pour le travail statistique. J’avais tort.

La formule complète WHO PRR, le comptage, les calculs de ratio, les seuils et les buckets temporels sont tous exécutés sous forme de requêtes ES|QL. Les agents appellent les outils ES|QL, raisonnent sur les résultats et écrivent en retour : pas de pandas, pas de calcul externe et pas de ralentissement du transfert de données. Les statistiques évoluent en fonction du cluster.

ES|QL est moins flexible que pandas pour les analyses complexes. Cependant, pour la formule de l’OMS et les agrégations hebdomadaires BUCKET, il fonctionne parfaitement. La suppression de la couche Python intermédiaire a simplifié l’architecture plus que prévu : les agents se contentent d’interroger et de raisonner, et le risque de dysfonctionnement est réduit d’un point.

La conception de l’index qui permet son fonctionnement

PHAROS fonctionne sur quatre indices Serverless Elasticsearch, et c’est sur le principal, pharos-adverse-events, que j’ai passé le plus de temps de conception.

Il dispose d’un clinical_text_analyzer personnalisé avec un stemming boule de neige pour la recherche narrative, d’un analyseur de nom de médicament avec un générateur de tokens de mot-clé pour la correspondance exacte des médicaments, de champs dense_vector (1 536 dimensions) pour les enchâssements narratifs, de geo_point pour le clustering géographique et de mappings imbriqués pour les réactions. Toutes les requêtes dont les agents ont besoin, recherche narrative approximative, recherche exacte de médicaments, agrégation géographique, similarité sémantique, sont prises en charge par la conception de l’index. Les trois autres index sont plus simples : pharos-signals stocke les signaux détectés avec les scores PRR et la chaîne de raisonnement de l’analyste, pharos-regulatory-reports contient les rapports générés, et pharos-audit-log horodate chaque action de l’agent.

Le problème peu prestigieux qui a failli interrompre le pipeline

Obtenir que les LLM renvoient du JSON structuré de manière fiable a été un combat auquel je ne m’attendais pas.

Vous demandez du JSON à un LLM, vous obtenez du JSON enveloppé dans trois paragraphes d’explication, du JSON à l’intérieur de balises de code markdown, ou un préambule conversationnel suivi de JSON et d’un résumé utile. Les agents se transmettent des données structurées, de sorte que chaque réponse doit être analysée correctement. Peu importe la qualité de votre détection de signal si la sortie de l’ANALYST ne peut pas être lue de manière fiable par SCRIBE.

J’ai passé beaucoup de temps à ajuster les invites système et j’ai fini par écrire une fonction d’extraction JSON qui gère le JSON brut, les clôtures de code markdown et le JSON enfoui dans le langage naturel. Ce n’est pas un travail intéressant, mais c’est le genre de chose qui détermine si un pipeline multi-agents fonctionne réellement ou s’il fait simplement de bonnes démonstrations.

Ce que je corrigerais en premier

Le calcul du PRR est actuellement une estimation ponctuelle. Un système de pharmacovigilance de production a besoin de limites de confiance du khi-deux et de notation IC bayésienne. Le modèle de données a déjà un champ ic_score, mais il utilise une approximation au lieu du calcul bayésien correct. C’est la première chose que je modifierais si j’avais plus de temps.

Le système considère également la « vision floue » et la « perte de vision » comme des événements distincts. L’étape suivante consiste à regrouper les réactions en fonction de l’ontologie MedDRA, de manière à ce que le système puisse détecter les signaux liés à des termes connexes au lieu de traiter chaque chaîne de manière indépendante. Ensuite, j’ajouterais les données d’EudraVigilance à celles de FAERS pour obtenir une corrélation intercontinentale.

En résumé

Deux millions de rapports d’événements indésirables atterrissent sur le bureau de quelqu’un chaque année, et la réponse actuelle consiste à augmenter le nombre d’analystes effectuant des examens manuels. PHAROS démontre que la solution réside dans des agents qui analysent les statistiques de l’OMS, génèrent des documents et s’adressent à la bonne personne, le tout avant que l’analyste n’ait ouvert son ordinateur portable.

PHAROS est open source sous MIT. Si vous travaillez dans le domaine de la pharmacovigilance ou des questions réglementaires et que vous souhaitez comparer ces données à des données réelles, n’hésitez pas à me contactez.

Prajwal Sutar

Développeur indépendant ,

Prajwal Sutar est un développeur indépendant spécialisé dans les systèmes d’IA et les pipelines de données à grande échelle.

GitHub · Démo · LinkedIn

La publication et la date de publication de toute fonctionnalité ou fonction décrite dans le présent article restent à la seule discrétion d'Elastic. Toute fonctionnalité ou fonction qui n'est actuellement pas disponible peut ne pas être livrée à temps ou ne pas être livrée du tout.

Dans cet article, nous sommes susceptibles d'avoir utilisé ou mentionné des outils d'IA générative tiers appartenant à leurs propriétaires respectifs qui en assurent le fonctionnement. Elastic n'a aucun contrôle sur les outils tiers et n'est en aucun cas responsable de leur contenu, de leur fonctionnement, de leur utilisation, ni de toute perte ou de tout dommage susceptible de survenir à cause de l'utilisation de tels outils. Veuillez faire preuve de prudence lorsque vous utilisez des outils d'IA avec des informations personnelles, sensibles ou confidentielles. Toute donnée que vous soumettez peut être utilisée pour entrainer l'IA ou à d'autres fins. Vous n'avez aucune garantie que la sécurisation ou la confidentialité des informations renseignées sera assurée. Vous devriez vous familiariser avec les pratiques en matière de protection des données personnelles et les conditions d'utilisation de tout outil d'intelligence artificielle générative avant de l'utiliser. 

Elastic, Elasticsearch et les marques associées sont des marques commerciales, des logos ou des marques déposées d'Elasticsearch B.V. aux États-Unis et dans d'autres pays. Tous les autres noms de produits et d'entreprises sont des marques commerciales, des logos ou des marques déposées appartenant à leurs propriétaires respectifs.