Les LLM écrivent du code plausible, et ils l'écrivent vite. Mais quiconque a piloté un agent de codage sur un projet dépassant le stade du jouet connaît le mode d'échec : donnez-lui assez de corde et il dérive. Les noms de champs changent d'une session à l'autre, le modèle de données mute, et les conventions d'hier se réinventent discrètement. Pour une page d'accueil, c'est une nuisance. Pour un logiciel de santé, « improviser » n'est pas une option.
Nous avons passé du temps au laboratoire de Health Samurai à tester une hypothèse : FHIR n'est pas seulement un standard de données — c'est un cadre inhabituellement bien adapté au développement assisté par IA. Cet article explique pourquoi, et ce qui s'est passé quand nous avons construit le même produit deux fois pour le vérifier.
Le développement agentique a besoin de rails
Pointez un agent capable vers une pile générique et quatre problèmes reviennent sans cesse :
- Les agents dérivent. Sans modèle fixe, chaque session invente des noms de champs, des formes et des conventions légèrement différents. Les modifications cessent de se composer — chaque nouvelle fonctionnalité entre en conflit avec la précédente.
- Des structures de données hallucinées. Demandez à un agent « d'ajouter des allergies » et il inventera joyeusement un schéma. Rien ne rejette une structure invalide avant qu'elle n'atteigne la production et que quelqu'un ne remarque que les données sont incorrectes.
- Pas d'auto-validation. Une pile générique ne donne à l'agent aucun moyen de vérifier sa propre production au-delà de « est-ce que ça compile ? » — ce qui n'est pas la même question que « s'agit-il de données de santé correctes ? »
- Les cadres génériques ne connaissent pas la santé. React, Rails et Django ne savent rien de
Patient,EncounterouObservation. Chaque projet réinvente la même modélisation du domaine de zéro, et l'agent la réinvente un peu différemment à chaque fois.
Le fil conducteur est l'absence de rails — un cadre solide et opinionné qui contraint ce que l'agent peut produire et lui indique immédiatement quand il se trompe. Plus les rails sont bons, moins l'agent a de marge pour dériver, et plus vite il peut se corriger.
FHIR comme cadre de travail
Voici l'idée clé : FHIR fournit déjà presque tous ces rails, et il les fournit spécifiquement pour la santé. Un modèle de données fixe répond au problème de dérive ; la validation côté serveur répond aux structures hallucinées et donne à l'agent un moyen de se vérifier lui-même ; et l'ensemble est natif au domaine de la santé par construction, de sorte que rien n'a besoin d'être remodélisé d'un projet à l'autre.
Une pile native FHIR comporte trois parties qui travaillent ensemble — le serveur FHIR est le backend, l'application est construite sur des SDK FHIR, et le développeur travaille aux côtés d'un copilote IA qui parle déjà FHIR. La majeure partie de cette pile est prête avant même que vous n'écriviez une ligne de code applicatif ; la seule partie que vous construisez réellement est l'application qui relie les pièces FHIR entre elles.
FHIR SDK
UI
Examinons ce que chaque couche apporte à l'agent.
Un modèle de données hérité, non conçu
FHIR R4 définit plus de 150 ressources de santé — Patient, Condition, Observation, Encounter, MedicationStatement, DocumentReference, et bien d'autres. Stockées nativement par un serveur FHIR comme Aidbox, elles n'exigent aucune conception de schéma.
Cela compte plus qu'il n'y paraît au premier abord. Quand l'agent a besoin de stocker des allergies, il n'invente pas une table — il utilise AllergyIntolerance, qui possède déjà les bons champs, les bonnes cardinalités et les bonnes liaisons. Les concepts de domaine personnalisés deviennent des ressources FHIR personnalisées — dans notre propre application, un PhrUser — avec leur propre StructureDefinition, et non pas une table sur mesure avec des colonnes inventées par l'agent. Vous héritez de deux décennies de modélisation du domaine de la santé, et l'agent aussi.
La validation comme boucle de rétroaction
Chaque écriture sur un serveur FHIR est validée par rapport à la StructureDefinition de la ressource : cardinalité, types, liaisons, invariants. Les données invalides n'entrent jamais dans la base de données.
Pour un développeur humain, c'est un filet de sécurité. Pour un agent, c'est quelque chose de plus précieux — une boucle de rétroaction précise et serrée. Au lieu d'une erreur 500 trois couches plus bas, l'agent reçoit quelque chose comme :
OperationOutcome: Patient.gender must be a code from
http://hl7.org/fhir/administrative-gender (male | female | other | unknown)
C'est exactement le type de signal sur lequel un agent s'autocorrige. La validation basée sur les profils vous permet de resserrer les règles pour votre propre projet — champs obligatoires, systèmes de codage fixes — sans écrire une seule ligne de code de validation. L'agent écrit moins, et le serveur lui indique immédiatement quand il se trompe.
Terminologie, formulaires et analytique — déjà résolus
Un serveur FHIR expédie de nombreux problèmes difficiles déjà résolus, et chacun est un problème que l'agent tenterait autrement de construire manuellement :
- Terminologie.
ValueSet,CodeSystemet les opérations comme$expand,$validate-codeet$lookupsont intégrés. LOINC, SNOMED, RxNorm, ICD — tous accessibles via des points de terminaison FHIR standard. Votre application ne fournit pas un système de codage ; elle en interroge un (par exemple, Termbox, un serveur de terminologie FHIR dédié). La terminologie cesse d'être un billet « À faire : trouver une bibliothèque ». - SQL on FHIR. Définissez une
ViewDefinitionqui aplatit les ressources FHIR en colonnes tabulaires, puis interrogez la vue avec du SQL ordinaire. Les équipes d'analytique obtiennent du SQL pour les rapports ; les données restent en FHIR — pas de pipeline de transformation à écrire. Cela abaisse aussi la barre pour les tableaux de bord intégrés à l'application : un développeur d'application qui peine à agréger des ressources FHIR imbriquées peut écrire un simpleGROUP BYcontre une vue, de sorte qu'un graphique dans le produit et un rapport pour les analystes lisent à partir de la même définition. Et uneViewDefinitionest elle-même une ressource FHIR, ce qui signifie que l'agent peut en écrire une — déclarer des colonnes est une tâche bien plus restreinte que de construire manuellement une logique de traversée sur des tableaux imbriqués. - Structured Data Capture (SDC). Les ressources
QuestionnaireFHIR décrivent des formulaires ; les utilisateurs les remplissent et les réponses sont stockées commeQuestionnaireResponse— liées, validées et consultables comme toute autre donnée FHIR. Les implémentations SDC apportent des constructeurs de formulaires, des moteurs de rendu, une logique d'extraction et des galeries de formulaires prêts à l'emploi.
Types et compétences pour l'agent
Les couches ci-dessus contraignent les données. Deux autres ferment la boucle autour de l'agent.
- SDK typés. Parce que chaque ressource possède une
StructureDefinitionlisible par machine, les types client peuvent être générés plutôt qu'écrits — pour TypeScript, Python, C#, Java et d'autres. Le bénéfice pour un agent dépasse l'autocomplétion : un champ mal orthographié ou une mauvaise énumération échoue au niveau du type, avant qu'une requête ne soit envoyée, et une source de vérité unique générée est partagée par le serveur et le client, de sorte que les deux côtés d'une fonctionnalité ne peuvent pas diverger. Des générateurs disponibles publiquement font de cela une étape de construction, pas un projet. - Compétences d'agent. La couche la plus récente est une documentation rédigée pour les agents plutôt que pour les humains. Parce que FHIR est un standard public avec des API de serveur publiques, cette connaissance est réutilisable d'un projet à l'autre — comment formuler une requête de recherche, comment fonctionnent les politiques d'accès, quand recourir à SQL on FHIR — plutôt que quelque chose que chaque équipe doit réenseigner. Pointer un agent vers une documentation à jour l'empêche également de s'appuyer sur ce qu'un modèle a absorbé au moment de l'entraînement, ce qui, pour une spécification qui publie de nouvelles versions, est une vraie source de code faux mais affirmé avec assurance.
Ces deux éléments se configurent une seule fois, et l'agent en bénéficie ensuite à chaque tâche.
L'expérience : la même application, une fondation différente
La théorie ne coûte pas cher, alors nous l'avons testée. Notre terrain d'expérimentation était le Dossier de santé personnel (PHR) interne de Health Samurai — un vrai produit pour notre propre équipe et les familles dont elle prend soin. Sa portée est véritablement non triviale : gérer des dossiers pour soi-même et ses personnes à charge, saisir des conditions, médicaments, allergies et procédures, téléverser des PDF médicaux, des consultations par clavardage avec des médecins, un résumé du patient, et une assistance IA ancrée dans les propres données FHIR du patient.
Nous l'avons construit deux fois avec Claude Code — même portée, fondation différente. La première fois, nous avons laissé l'agent construire de zéro ; la deuxième fois, nous avons reconstruit toute l'application avec FHIR comme cadre.
| v1 — de zéro | v2 — sur FHIR | |
|---|---|---|
| Pile | React + Node + Postgres ordinaires | Backend Aidbox, types FHIR générés, composants UI Aidbox open source, compétences d'agent |
| Modèle de données | Inventé par l'agent, par session | FHIR R4, fixe |
| Validation | Ce que l'agent a écrit | Côté serveur, à chaque écriture |
| Nouvelle fonctionnalité | Réenseigner les conventions dérivées | Choisir une ressource, générer les types, câbler l'interface |
| Résultat | Continuait à dériver | Plus petite, cohérente — celle qui valait la peine d'être conservée |
Dans v1, l'agent a inventé son propre modèle de données, sa propre forme d'API, ses propres règles de validation. Chaque nouvelle fonctionnalité signifiait réenseigner à l'agent les conventions dont il s'était déjà éloigné. Dans v2, reconstruire la même application sur FHIR a produit une base de code plus petite et plus cohérente — l'agent s'est appuyé sur FHIR plutôt que de le réinventer.
En pratique, cela se manifeste dans le code ordinaire. Les types générés combinés à un ensemble de compétences Claude Code open source pour travailler avec Aidbox ont permis à l'agent d'écrire une création de patient comme celle-ci, sans aucun schéma à concevoir :
// The agent writes against generated types — the shape is not up for negotiation
const patient = await aidbox.create<Patient>({
resourceType: "Patient",
name: [{ given: [body.givenName], family: body.familyName }],
birthDate,
gender: body.gender || undefined,
active: true,
});
// ...and the server validates it on write.
Rien de remarquable, ce qui est précisément le but : il n'y avait aucune décision à prendre sur les noms de champs ou le stockage, et rien dont l'agent pourrait s'éloigner à la prochaine session.
Trois enseignements se sont démarqués :
- Moins de code. Le cadre gère les parties ennuyeuses — schéma, API, validation — de sorte que l'agent en écrit simplement moins.
- Une boucle de rétroaction plus serrée. Le serveur rejette les écritures invalides avec des erreurs précises, et l'agent s'autocorrige — pas d'erreur 500 mystérieuse trois couches plus bas.
- Les fonctionnalités deviennent des conversations, pas des projets. Ajouter un concept clinique est une seule étape — choisir une ressource FHIR, générer les types, câbler l'interface — pas cinq.
L'exemple le plus clair : nous avons demandé une fonctionnalité de résumé du patient. Sur une pile générique, cela signifie concevoir un schéma de résumé, décider comment référencer les dossiers sous-jacents, et construire une API autour de cela. Sur FHIR, l'agent a utilisé la ressource Composition — le propre modèle du standard pour un document clinique structuré et sectionné — et l'a implémentée proprement : des sections référençant les ressources Condition, MedicationStatement et AllergyIntolerance existantes du patient, sans schéma sur mesure inventé. Une fonctionnalité qui aurait été un petit projet est devenue une seule conversation, parce que FHIR l'avait déjà modélisée.
Comment l'essayer vous-même
Si vous souhaitez donner à votre agent les mêmes rails, la recette de départ est courte :
- Choisissez un serveur FHIR comme backend (par exemple, Aidbox).
- Générez les types FHIR avec
@atomic-ehr/codegenpour que l'agent code contre de vraies structures. - Installez les compétences Claude Code pour que l'agent parle FHIR dès le départ.
Le code source du PHR est également ouvert, si vous souhaitez voir un exemple complet : github.com/HealthSamurai/phr.
Et ensuite : retirer le programmeur de la boucle ?
Voici la question que l'expérience a soulevée. Si FHIR donne à un agent suffisamment de structure pour construire une vraie application — que se passe-t-il si l'utilisateur de l'agent n'est pas du tout un développeur ?
Imaginez une plateforme où les médecins construisent leurs propres applications, en itérant avec un assistant IA en langage courant, et ces applications se branchent directement sur l'infrastructure existante de l'organisation de santé :
- Les médecins comme bâtisseurs — pas d'équipe de développement intermédiaire ; décrivez le flux de travail, obtenez une application fonctionnelle.
- Itérer avec l'IA — modifier un formulaire, ajuster une règle, ajouter un résumé. Une conversation, pas un billet.
- S'intègre à la pile hospitalière — parle FHIR au DSE, respecte l'authentification, l'audit et les politiques existants. Pas un silo, mais un citoyen de l'infrastructure.
C'est le même pari que toute cette expérience, un niveau au-dessus : FHIR comme fondation qui permet à l'IA de construire des logiciels de santé pour les personnes qui en ont réellement besoin.
Vous souhaitez discuter de l'application de cette approche à votre propre pile ? Contactez Aleksandr Kislitsyn, ou explorez le dépôt PHR open source et ses compétences Claude Code.





