Skip to main content
Jonathan Andrei
Retour aux billets
Août 202613 min de lecture

Unsay : un agent IA de sécurité médicamenteuse qui revient et défait ce qu'il a dit. Quand la FDA élève un rappel, il corrige chaque patient nommé qu'il avait déjà rassuré, en quelques secondes.

La mémoire d'agent a un mode d'échec que la qualité de récupération ne peut pas réparer : le contexte périmé. La similarité à une mémoire stockée ne prouve pas que la mémoire est encore vraie. Pour la plupart des agents c'est embarrassant. Dans une pharmacie c'est un rappel de Classe I. Le correctif d'Unsay est un schéma bitemporel sur CockroachDB (`valid_from`/`valid_to` pour le monde, `asserted_at`/`retracted_at` pour la croyance de ce système), et un join qu'aucune base vectorielle ne peut exprimer : chaque réponse encore debout qui s'appuyait sur une version d'affirmation qu'on ne croit plus. La relecture fonctionne encore au-delà de l'horizon AS OF SYSTEM TIME, et chaque correction est exactly-once à travers une panne de région.

CockroachDBHackathonCockroachDBBitemporalVector SearchMCPAWS LambdaAmazon BedrockopenFDAAgent MemoryPython
J'ai créé ce billet et le projet Unsay pour le Build the Future of AI Agent Memory with CockroachDB hackathon. #CockroachDBHackathon
Deux revendications, toutes deux démontrées : les autres mémoires d'agent peuvent vous dire ce qu'elles savaient, Unsay revient et corrige ce qu'il a dit. Et sa relecture fonctionne encore dans six mois, quand le voyage MVCC a expiré après vingt-cinq heures. Dans la démo hébergée, douze patients à qui on a délivré un vrai lot rappelé (amlodipine/benazépril GB01616, NDMA au-dessus de la limite d'absorption acceptable) ; le sweep examine les douze, en inverse neuf, et en laisse trois tranquilles parce qu'on leur avait déjà dit d'arrêter. Personne ne reçoit un message dont la réponse n'a pas réellement changé.
Démo hébergée d'Unsay : quatre étapes numérotées sur une page blanc cassé chaud. 1) Un patient demande « Mon amlodipine et benazépril vient du lot GB01616. Est-ce sûr de continuer à le prendre ? » 2) Le monde change : la FDA élève le lot GB01616 en Classe I, une transaction qui rétracte l'ancienne affirmation et affirme la nouvelle. 3) Réparation rétroactive : « Chaque réponse encore debout qui s'est appuyée sur une version d'affirmation qu'on ne croit plus. » 4) Corrections, par nom. Chips d'en-tête : aws-us-east-1 serving, survive zone, 554 affirmations, 12 standing.
Toute l'app en une page. De vraies données openFDA, 554 affirmations en direct, hébergée sur une seule Function URL AWS Lambda devant un cluster CockroachDB Cloud. La première requête après une période calme paie un cold-start de quelques secondes ; rien après.

Revendication 1 : réparer, pas seulement rappeler

Quand la FDA publie un rappel, un certain nombre de réponses déjà données sont maintenant fausses. Elles étaient correctes quand elles ont été données. Rien à leur sujet n'a changé ; le monde a bougé en dessous. Les trouver est une requête SQL. Lisez-la en français : chaque réponse encore debout qui s'est appuyée sur une version d'une affirmation qu'on ne croit plus. Une base vectorielle ne peut pas exprimer cette requête. Ni lentement, ni approximativement, pas du tout. Elle n'a pas la notion qu'une mémoire a des versions, et aucun enregistrement de quelle version une réponse donnée a consommé. Le plus qu'elle puisse renvoyer, ce sont les voisins les plus proches d'aujourd'hui, ce qui ne vous dit rien sur ce que vous avez dit en mars. Reconstruire l'histoire est une couche d'insight ; Unsay ferme la boucle en re-décidant chaque réponse affectée contre la mémoire actuelle, et là où le verdict bouge, rédige une correction et met en file une notification pour une personne nommée.

Étape « Le monde change » : après publication du rappel Classe I, le chip de version montre « v1 → v2 · version antérieure rétractée » et le texte du rappel Classe I : « amlodipine-besylate-and-benazepril-hydrochloride lot GB01616 rappelé. Raison : NDMA au-dessus de la limite d'absorption acceptable. Cesser l'usage et retourner à la pharmacie. »
Une transaction : l'ancienne affirmation est rétractée et une nouvelle est affirmée à côté sous la même clé. Rien de déjà lu n'est muté. Chaque réponse qui a consommé v1 est inchangée dans la base jusqu'à ce que le sweep la re-décide.
Liste de corrections après le sweep : patients nommés (Tomas Alvarez, Samuel Adeyemi, Hiro Tanaka, Ruth Bernstein, Wei Chen, Colm Doherty, et d'autres) chacun avec un changement de verdict CAUTION → STOP, le texte du message corrigé, et une dedupe key à usage unique qui transforme un sweep relancé en no-op.
Neuf inversées, trois laissées tranquilles. Chaque correction est écrite dans la même transaction que le changement de statut, l'entrée outbox, et la ligne d'audit, donc un sweep tué à mi-course et relancé ne peut pas double-envoyer. Les rappels Classe I arrivent les mardis et vendredis ; cette boucle est ce qui rend possible d'y répondre le vendredi après-midi.

Revendication 2 : la relecture n'expire pas

La façon évidente de construire « ce que l'agent savait quand il a décidé » sur CockroachDB est de stocker le read timestamp et le rejouer avec `AS OF SYSTEM TIME`. C'est élégant, n'a pas besoin de tables supplémentaires, et est correct jusqu'à ce que le garbage collector passe au-delà du timestamp sauvegardé. La doc de CockroachDB le dit : `gc.ttlseconds` « n'est pas destiné à être une solution pour la rétention à long terme de l'historique ; pour ça, il faut gérer le versioning dans la conception du schéma au niveau applicatif. » La fenêtre par défaut est de 4 heures. 25 heures est la plus grande valeur régulièrement testée par Cockroach Labs. Le mécanisme durable d'Unsay est le schéma bitemporel que cette doc prescrit, et `AS OF SYSTEM TIME` reste comme chemin rapide à l'intérieur de la fenêtre. `scripts/expiry.py` pose une question dans les deux sens à trois âges différents : à l'intérieur de la fenêtre, les deux concordent exactement (un contrôle qui compte, parce qu'il prouve que le modèle bitemporel reconstruit une vraie histoire plutôt que d'en inventer une commode) ; au-delà de l'horizon, une route est partie et l'autre répond encore.

Sortie terminal de scripts/expiry.py comparant la reconstruction bitemporelle à AS OF SYSTEM TIME à trois âges. À l'intérieur de la fenêtre GC : les deux concordent, v[2] = v[2], agree=True. Au-delà de l'horizon : route A (bitemporelle) A RÉPONDU avec la bonne affirmation historique ; route B (AS OF SYSTEM TIME) A ÉCHOUÉ avec « batch timestamp must be after replica GC threshold ».
Le contrôle compte autant que l'échec. À l'intérieur de la fenêtre, les deux routes concordent exactement, donc le modèle bitemporel reconstruit une vraie histoire plutôt que d'en inventer une commode.

Les propriétés que le design porte

La provenance est écrite atomiquement avec la réponse. La ligne de décision et son read-set commitent ensemble ou pas du tout. Il n'y a pas de chemin de code qui stocke une réponse sans enregistrer ce qui l'a produite, parce qu'une réponse dont la provenance est perdue ne peut jamais être réparée, et un système de mémoire qui laisse tomber la provenance sous charge n'en a effectivement aucune. Les corrections sont exactly-once à travers une panne de région : la correction, le changement de statut, l'entrée outbox et la ligne d'audit sont une transaction. La dedupe_key de l'outbox est un hash déterministe de (décision, nouveau verdict, version de fait déclenchante), donc un sweep tué à mi-course et relancé recalcule la même clé et la contrainte d'unicité transforme la relecture en no-op. « Zéro notifications patient dupliquées » est une propriété du schéma, pas un espoir sur la façon dont le processus se termine. La résidence est appliquée par le stockage, pas par le code : `patient` est REGIONAL BY ROW, donc la mémoire d'un patient EU vit dans eu-west-1 parce que CockroachDB l'y met. Le cluster local tourne 9 noeuds à travers 3 régions AWS simulées sous SURVIVE REGION FAILURE, ce qui place 5 répliques de sorte qu'aucune région ne détient une majorité.

Extrait d'export d'audit signé montrant la chaîne de décision, decision_read, versions de faits, correction et lignes d'outbox pour un patient, scellé cryptographiquement et téléchargeable comme snapshot JSON.
L'export d'audit est le reçu que l'app remet à un pharmacien. Même requête que le sweep utilise, scellée avec une signature, pour qu'un régulateur voie exactement ce que le système a dit, ce qu'il a lu, et ce qu'il a corrigé.
Projet associé

Unsay: An AI Medication-Safety Agent that Goes Back and Un-Says What It Told You. When the FDA Escalates a Recall, It Corrects Every Named Patient It Already Reassured, in Seconds.

Voir le projet
Assay : un tracker soin de la peau avec une barre d'erreur. Recadrer la même photo déplace la texture de 5,81 points, alors un verdict ne s'affiche que si le changement dépasse ce plancher sur votre visage.
Article précédent

Assay : un tracker soin de la peau avec une barre d'erreur. Recadrer la même photo déplace la texture de 5,81 points, alors un verdict ne s'affiche que si le changement dépasse ce plancher sur votre visage.

La mesure existe : l'API YouCam Skin Analysis note seize résultats de peau depuis une photographie, et c'est un vrai bon instrument (entrée octet-identique donne sortie octet-identique). Le problème est qu'un score rapporté sans son erreur est un nombre avec lequel on ne peut pas prendre de décision. Sur mon propre visage avec une variable changée à la fois, la luminosité ±8 % déplace les boutons de 4,85 points et recadrer la même photo différemment déplace la texture de 5,81. Un effet de traitement réaliste sur quatre semaines est d'environ cinq. Assay mesure sa propre erreur d'abord, puis n'appelle un changement réel que quand il dépasse ce plancher.

Août 202611 min de lecture
Bloom : personne n'annule, on cesse simplement de venir. Bloom lit le rythme de visite propre à chaque client, signale ceux qui s'en écartent, et écrit une note personnelle à chacun. Les règles décident QUI, Gemini décide QUOI DIRE.
Article suivant

Bloom : personne n'annule, on cesse simplement de venir. Bloom lit le rythme de visite propre à chaque client, signale ceux qui s'en écartent, et écrit une note personnelle à chacun. Les règles décident QUI, Gemini décide QUOI DIRE.

Un salon typique perd environ 40 % de ses clients chaque année, un premier-timer qui ne re-réserve pas dans les 30 jours a environ une chance sur cinq de jamais revenir, et un habitué fidèle vaut plusieurs centaines de dollars par an. Le signal est invisible parce que c'est une absence, répartie sur des centaines de personnes qui ont chacune leur propre rythme. L'architecture de Bloom est l'idée : les règles décident QUI (moteur de risque déterministe sur l'écart de visite médian propre à chaque client), Gemini décide QUOI DIRE (une note courte dans la voix du propriétaire, référençant l'histoire réelle de cette personne). La démo de toute la thèse est deux clientes : Aisha et Jane sont toutes deux à 44 jours depuis leur dernière visite ; Aisha vient toutes les 8 semaines, Jane toutes les 4. Tout outil qui signale « pas de visite en 60 jours » se trompe sur l'une d'elles.

Juil.-août 202612 min de lecture