En résumé
L’onglet Suggestions affiche, pour chaque concept source, des candidats OMOP pré-classés par un score combiné. Ces suggestions ont deux origines : des scores précalculés — syntaxiques et sémantiques — produits par deux scripts lancés dans un terminal, puis chargés dans Linkr ; et les propositions d’un agent IA, qui suit le skill concept-mapping et travaille en direct dans le projet par le serveur MCP, sans export. Dans les deux cas, c’est vous qui créez chaque alignement.
Pourquoi un onglet séparé
Aligner 1 000 codes locaux à la main contre les 4 millions de concepts du vocabulaire OMOP est long. Trois familles d’algorithmes ont chacune leur biais :
- La comparaison de chaînes rate les synonymes (« HR » vs « heart rate »).
- Les embeddings sémantiques confondent parfois deux concepts proches (« créatinine sérique » vs « créatininémie urinaire »).
- Un agent IA ajoute du contexte clinique mais coûte cher si on l’applique à 4 millions de paires.
L’onglet Suggestions cumule plusieurs méthodes en un seul score et expose les détails ligne par ligne, pour que vous gardiez la main sur la décision finale.
D’où viennent les suggestions
Scores précalculés
Jaro-Winkler et BioLORD, pour tous les concepts source d’un coup.
Calculés hors de Linkr par deux scripts Python, puis chargés dans l’onglet Suggestions. Fonctionne dans les deux modes de déploiement. Voir Précalculer les scores.
Suggestions d’un agent IA
Quelques candidats justifiés par concept, lot par lot.
Déposées en direct dans le projet par un agent relié au serveur MCP de Linkr. Mode serveur uniquement. Voir Aligner avec un agent IA.
Les deux se complètent : l’agent lit les scores précalculés quand il y en a, et s’en sert pour trouver plus vite ses candidats — surtout pour les médicaments.
L’onglet Suggestions dans Linkr
| Score | Méthodes | Vocab | Nom du concept | Code | Std | |
|---|---|---|---|---|---|---|
92% | 95%88%91% | LOINC | Creatinine [Mass/volume] in Serum or Plasma | 2160-0 | S | |
81% | 83%79%82% | LOINC | Creatinine [Mass/volume] in Blood | 38483-4 | S | |
76% | 80%71%77% | LOINC | Creatinine [Moles/volume] in Serum or Plasma | 14682-9 | S | |
68% | 72%64% | SNOMED | Serum creatinine measurement | 113075003 | S | |
54% | 58%51% | LOINC | Creatinine renal clearance | 2164-2 | S |
Méthodes de similarité
Quatre familles de méthodes peuvent contribuer au pré-classement des candidats. Chacune attaque le problème sous un angle différent, et leurs erreurs ne se recoupent pas — c’est pourquoi les combiner donne de meilleurs résultats qu’utiliser une seule famille.
Syntaxique
Compare les chaînes de caractères des libellés.
Sémantique
Compare le sens via des embeddings biomédicaux.
Statistique
Compare les distributions de valeurs numériques.
IA
Un LLM relit les meilleurs candidats avec le contexte clinique.
Dans Linkr, chaque méthode est représentée à droite du score combiné par une pastille colorée (couleurs ci-dessus) suivie de son pourcentage. Au survol, une infobulle indique le nom exact de l’algorithme. Cela permet de comprendre l’origine d’un score élevé : un candidat noté 92 % uniquement par la méthode syntaxique est probablement un faux ami textuel, tandis qu’un candidat noté 80 % par sémantique + IA est plus solide.
1. Syntaxique — comparer les chaînes de caractères
On compare les chaînes des libellés telles quelles, sans rien comprendre à leur sens. Utile parce que les libellés sources et les libellés OMOP suivent souvent des conventions d’écriture différentes : casse, espaces ou underscores, accents, abréviations, ordre des mots, ponctuation. Trois algorithmes au catalogue :
- Jaro-Winkler — proximité lettre à lettre, avec bonus pour les préfixes communs. Rapproche « hemoglobin_a1c » et « Hemoglobin A1c » malgré la casse et le séparateur.
- Token-Sort — découpe les libellés en mots, les trie alphabétiquement, puis compare. Indifférent à l’ordre (« blood pressure systolic » ≈ « systolic blood pressure »).
- N-gram IDF — découpe en n-grammes de caractères (sous-séquences de N lettres), pondère chaque n-gramme par sa rareté dans le vocabulaire. Méthode historique d’Usagi.
hemoglobin_a1c(libellé source, sans accent ni espace)Hemoglobin A1cA1c hemoglobinHemoglobinGlucoseJaro-Winkler — proximité lettre à lettre, bonus pour préfixe commun ; Token-Sort permet de rapprocher « A1c hemoglobin » du libellé source malgré l'ordre des mots.
Forces — détecte les correspondances quasi-littérales malgré les variantes d’écriture (casse, espaces, abréviations, ordre des mots), très rapide (millisecondes par paire).
Limites — ne prend pas en compte les synonymes : « créatinine » et « creat » sont proches, mais « heart rate » et « pulse » sont à zéro.
2. Sémantique — comparer le sens
On encode chaque libellé en vecteur numérique via un modèle de phrase-embedding, puis on mesure l’angle entre les vecteurs via la similarité cosinus. Deux libellés au sens proche produiront des vecteurs proches, même si les mots sont différents.
Du libellé au vecteur d'embedding
BioLORD-2023-M projette chaque libellé dans un espace de 768 dimensions. Voici 24 dimensions sous forme de heatmap — chaque case est une dimension, la couleur encode la valeur.
Frequence cardiaquePoulsTension arterielleSimilarité cosinus
cos(θ) = (A · B) / (‖A‖ × ‖B‖)Frequence cardiaquevsPoulsFrequence cardiaquevsTension arterielleLes motifs de « Fréquence cardiaque » et « Pouls » sont presque identiques case par case → vecteurs très proches. « Tension artérielle » a un motif différent → vecteur éloigné. Plus le score cosinus approche 1, plus les sens sont proches.
Modèle par défaut : BioLORD-2023-M, entraîné sur des textes biomédicaux et multilingue (français, anglais, espagnol, allemand…).
Modèles alternatifs configurables — n’importe quel modèle BERT compatible Sentence-Transformers :
- PubMedBERT, SapBERT, ClinicalBERT — spécialisés biomédicaux mais anglais uniquement.
- all-MiniLM-L6-v2 — généraliste, anglais uniquement, beaucoup plus rapide.
- paraphrase-multilingual-MiniLM-L12-v2, LaBSE — multilingues généralistes.
Multilinguisme
Si vos libellés source sont en français (ou dans toute autre langue que l’anglais), gardez BioLORD-2023-M ou basculez sur un modèle explicitement multilingue. PubMedBERT, SapBERT, ClinicalBERT et all-MiniLM-L6-v2 produiront des embeddings de mauvaise qualité sur du texte non-anglais.
Forces — capte les synonymes (« heart rate » ≈ « pulse »), les reformulations, les variantes terminologiques.
Limites — peut rapprocher deux concepts proches mais distincts (« créatinine sérique » vs « créatininémie urinaire »). Coûteux à pré-calculer sur les ~4 M concepts OMOP (GPU recommandé).
3. Statistique — comparer les distributions de valeurs
Pour les variables numériques (Measurement uniquement), on compare la distribution statistique des valeurs entre concept source et candidats : min, P25, médiane, P75, max, moyenne, écart-type. Deux variables qui prennent leurs valeurs dans la même plage, avec la même forme, sont probablement la même mesure.
Concrètement, on utilise un test statistique pour quantifier l’écart entre les deux distributions :
- Test de Kolmogorov-Smirnov (KS) — écart maximal entre les fonctions de répartition. KS = 0 → distributions identiques ; KS proche de 1 → distributions très différentes.
- Divergence de Wasserstein — « coût » pour transporter la masse d’une distribution vers l’autre. Plus robuste que KS quand les supports sont déplacés (changement d’unité).
Concept source : créatinine sérique (µmol/L)
12 450 mesures, 1 842 patients
LOINC 2160-0 — Creatinine [Mass/volume] in Serum or Plasmaunité : mg/dLLOINC 14682-9 — Creatinine [Moles/volume] in Serum or Plasmaunité : µmol/LLOINC 2951-2 — Sodium [Moles/volume] in Serum or Plasmaunité : mmol/LTest de Kolmogorov-Smirnov : KS proche de 0 = distributions identiques ; KS > 0,5 = distributions très différentes. Les courbes lissées (estimation de densité) permettent de comparer la forme et la position des distributions sans qu'elles se masquent.
Forces — fonctionne même quand le libellé est ambigu ou absent. Très discriminant : difficile de confondre la créatinine (60–120 µmol/L) avec la natrémie (135–145 mmol/L).
Limites — uniquement applicable aux variables numériques avec assez de valeurs pour que la distribution soit représentative. Sensible aux artefacts de saisie (valeurs aberrantes, unités mélangées).
Pas encore de script pour ce score
Linkr sait afficher et pondérer des scores statistiques (méthodes statistical/…) quand un fichier de scores en contient, mais les deux scripts fournis ne les calculent pas encore. En pratique, c’est l’agent IA qui compare les distributions : il lit celles de chaque concept source dans ses métadonnées avant de choisir une cible.
4. IA agentique — laisser un LLM raisonner
Un LLM piloté de façon agentique — c’est lui qui décide quels outils appeler : lire un concept source et ses métadonnées, chercher dans le vocabulaire OMOP, consulter la hiérarchie d’un candidat — examine les meilleurs candidats avec tout le contexte disponible : libellé source, unité, distribution des valeurs, services où le code est utilisé. Il propose un ou plusieurs alignements assortis d’une équivalence SKOS (exactMatch, closeMatch, broadMatch, narrowMatch, relatedMatch) et d’un commentaire qui justifie le choix.
Ses propositions apparaissent dans l’onglet comme une méthode de plus, nommée ai/<modèle> d’après le modèle qui les a produites, dans la catégorie Agentique. La page Aligner avec un agent IA explique comment le lancer.
TA_systolique_invasiveICCA · M0118 · « Tension artérielle systolique invasive » · 24 380 mesures · médiane 122 mmHg
SNOMED 251071003 — Invasive systolic arterial pressure91%SNOMED 72313002 — Systolic arterial pressure78%LOINC 8480-6 — Systolic blood pressure74%
Le libellé « TA_systolique_invasive » désigne la pression artérielle systolique mesurée par cathéter artériel (ligne artérielle), pas au brassard. Le concept SNOMED 251071003 correspond exactement à cette définition — c'est un alignement direct. Le LOINC 8480-6 est un concept plus large (systolique, toutes méthodes confondues) : la cible est plus générale que la source, ce qui s'écrit broadMatch en SKOS. La distribution observée (médiane 122 mmHg, queue droite jusqu'à ~180) est cohérente avec une mesure invasive en réanimation.
SNOMED 251071003 — Invasive systolic arterial pressureConfiance: 97%skos:exactMatch — Le concept cible décrit précisément la même mesure.LOINC 8480-6 — Systolic blood pressureConfiance: 88%skos:broadMatch — Le concept cible est plus général (toutes méthodes de mesure), donc plus large que la source invasive.Forces — la seule méthode qui « comprend » la sémantique clinique (qualificatifs, contexte de mesure, équivalences nuancées). Sait dire « ce n’est pas exact, la cible est plus générale » via broadMatch, et dit dans son commentaire ce qui diffère.
Limites — lente et coûteuse en calcul, risque d’hallucinations sur les cas rares, nécessite toujours une validation humaine.
Principe de frugalité — utiliser le moins coûteux d'abord
Les méthodes forment des paliers de consommation de ressources, à peu près regroupés ainsi :
- Syntaxique et statistique — CPU léger, calculs locaux sur les chaînes ou les valeurs déjà présentes en mémoire. Ordre de grandeur : millisecondes par paire.
- Sémantique (BERT / embeddings) — un GPU est recommandé pour l’inférence et le pré-calcul des vecteurs OMOP. Ordre de grandeur : minutes à heures selon le matériel, puis quasi-gratuit en interrogation.
- IA agentique — appels au modèle, plusieurs allers-retours d’outils par concept. Ordre de grandeur : secondes à minutes par concept.
La règle : ne pas dégainer un palier plus coûteux quand un palier plus bas suffit.
- Lancer les scores syntaxiques en premier — c’est presque gratuit, et ça résout déjà une bonne partie des cas simples.
- Le sémantique prend le relais sur les concepts non tranchés (synonymes, reformulations, langues).
- L’agent travaille sur un périmètre choisi — une catégorie, les concepts les plus fréquents — et part des candidats déjà pré-classés, jamais des 4 M concepts OMOP en aveugle.
C’est à la fois une question de coût (calcul, GPU, et facture pour un modèle distant), d’empreinte écologique et de rapidité : faire passer un projet de 1 000 concepts entièrement par l’agent prend des heures, alors que les scores précalculés traitent la même charge en quelques minutes et laissent à l’agent le résidu difficile.
Le score combiné
Le score combiné (colonne de gauche, barre verte) est une moyenne pondérée des scores de chaque méthode. Les poids par défaut sont :
| Méthode | Poids | Rôle |
|---|---|---|
| Syntaxique | ×1 | Détecte les correspondances quasi-littérales malgré les variantes d’écriture. |
| Statistique | ×1 | Compare les distributions de valeurs (créatinine vs natrémie). |
| Sémantique | ×2 | Capte les synonymes et reformulations. |
| IA agentique | ×3 | Propositions justifiées d’un agent IA. |
Le bouton Gérer les suggestions (en haut à gauche) ouvre une boîte de dialogue avec des curseurs pour ajuster chaque poids en temps réel — le tableau se re-trie automatiquement. Vous y voyez aussi le nombre de scores chargés, et vous pouvez Importer un fichier ou Tout supprimer.
En mode serveur, la section Méthodes du fichier liste chaque méthode présente — syntactic/jaro-winkler, semantic/biolord, ai/<modèle>… — avec une corbeille : Retirer les suggestions d’une méthode supprime toutes ses lignes du fichier de scores, en conservant les autres méthodes et les alignements. C’est le moyen de retirer d’un coup ce qu’un agent a déposé.
Gérer les suggestions
Ajustez la pondération de chaque méthode dans le score combiné.
Pondération des méthodes
Méthodes du fichier
ai/mistral-small-3.2semantic/biolordsyntactic/jaro-winkler
similarity-scores.parquetSélection et alignement
SNOMED 251071003 — Invasive systolic arterial pressure | 91% | ||
SNOMED 72313002 — Systolic arterial pressure | 78% | ||
LOINC 8480-6 — Systolic blood pressure | 74% |
Les candidats déjà alignés pour ce concept source sont grisés et ne sont pas re-sélectionnables.
- Cliquez sur une ligne pour la sélectionner. La barre d’actions en haut affiche alors les boutons d’alignement habituels (Exact match + menu déroulant SKOS + bouton commentaire).
- Les candidats déjà alignés pour ce concept source sont grisés et ne sont pas re-sélectionnables.
- L’icône ⓘ en bout de ligne ouvre la fiche du concept OMOP cible (description, synonymes, hiérarchie).
Le bouton d’alignement principal « Exact » est pré-rempli avec l’équivalence SKOS de la suggestion sélectionnée — celle proposée par l’agent pour une ligne ai/… (exactMatch, closeMatch, broadMatch, narrowMatch, relatedMatch). Vous pouvez l’accepter en un clic, ou ouvrir le menu déroulant pour choisir une autre équivalence si vous n’êtes pas d’accord. Linkr n’aligne jamais seul à partir d’une suggestion : c’est ce clic qui crée l’alignement.
Suggestions d’un agent IA
Un agent IA peut déposer ses propres suggestions dans l’onglet, chacune avec une équivalence SKOS et un commentaire qui justifie le choix. La page Aligner avec un agent IA explique comment le faire travailler, ce qu’il écrit dans le projet et comment suivre son travail.
Précalculer les scores
Les scores syntaxiques (Jaro-Winkler) et sémantiques (embeddings BioLORD) donnent à chaque concept source une liste de candidats classés avant qu’aucun agent ne s’y penche. Ils se calculent hors de Linkr, dans un terminal, avec les deux scripts Python fournis avec le skill, dans son dossier scripts/. Le fichier produit se charge ensuite dans le projet ; cette partie fonctionne aussi dans une installation sans backend.
Prérequis
| Ressource | Pourquoi |
|---|---|
Python 3 + rapidfuzz, pandas, pyarrow, numpy, duckdb | Pour les deux scripts. |
sentence-transformers et faiss-cpu | Pour les scores sémantiques uniquement. |
Vocabulaires OHDSI (CONCEPT.parquet ou .csv) | Téléchargés depuis ATHENA. |
| Plusieurs Go de disque | Les embeddings du vocabulaire OMOP pèsent plusieurs Go. |
| GPU recommandé | Le calcul des embeddings peut prendre des heures sur CPU. |
Les deux scripts
embed_concepts.py— calcule les embeddings BioLORD des concepts OMOP et écritconcept_embeddings.parquetà côté deCONCEPT.parquet. À lancer une fois par version du vocabulaire : le fichier sert à tous les projets.compute_scores.py— compare les concepts source d’un projet aux concepts OMOP et écritsimilarity-scores.parquet. À lancer une fois par projet. Par défaut, il calcule Jaro-Winkler et BioLORD ; sansconcept_embeddings.parquet, seulement le score syntaxique.
Le script de scoring attend un CSV de concepts source à trois colonnes : terminology, concept_code et concept_name. Le source-concepts.csv d’un export Linkr ZIP du projet fait l’affaire, en renommant ses colonnes si besoin.
Toujours filtrer les cibles
Le calcul sémantique construit un index sur les concepts cibles. Sans filtre, cela fait environ 4 millions de vecteurs et jusqu’à 25 Go de mémoire : de quoi faire tomber un ordinateur portable. Passez toujours --only-standard --only-valid et un --domain (ou --vocabulary) qui correspond à ce que vous alignez. Les médicaments — environ 2 millions de concepts RxNorm — se traitent à part, seulement si vous en alignez.
python scripts/compute_scores.py \
--source source-concepts.csv \
--concept /chemin/vers/vocabulaire/CONCEPT.parquet \
--embeddings /chemin/vers/vocabulaire/concept_embeddings.parquet \
--only-standard --only-valid --domain Measurement Observation
Lancez-le dans votre propre terminal, où vous pouvez surveiller la mémoire et l’arrêter avec Ctrl-C. Chaque script affiche l’ensemble de ses options avec --help.
Charger les scores dans Linkr
Dans l’éditeur d’alignements, mode Suggestions : Gérer les suggestions → Importer un fichier, puis choisissez similarity-scores.parquet.
Charger un fichier remplace le précédent
Le fichier chargé remplace le fichier de scores du projet, y compris les suggestions déposées par un agent. Pour les garder, récupérez d’abord le fichier actuel : exportez le projet en Linkr ZIP en cochant Scores de similarité, puis passez le similarity-scores.parquet de l’archive au script avec --output. Le script ajoute ses lignes au fichier existant sans toucher aux autres.
Fichiers produits
Le script de scoring écrit deux fichiers :
source_embeddings.parquet— les vecteurs d’embeddings des concepts source. Conservé pour ré-utiliser les embeddings lors d’un prochain lancement sans avoir à tout ré-encoder.similarity-scores.parquet— un score par triplet(source, candidat, méthode). C’est ce fichier que vous chargez dans Linkr, et c’est aussi là que Linkr range les suggestions des agents.
Aperçu de source_embeddings.parquet :
source_embeddings.parquet| source_vocabulary_id | source_concept_code | source_concept_name | embedding (extrait) | model | created_at |
|---|---|---|---|---|---|
| ICCA | M0118 | TA_systolique_invasive | [ 0.42,-0.18, 0.71, 0.05,-0.33, 0.58, 0.22,-0.41, …] | BioLORD-2023-M | 2026-05-14T09:31:02Z |
| ICCA | M0042 | Frequence_cardiaque | [ 0.39,-0.22, 0.68, 0.08,-0.30, 0.61, 0.19,-0.38, …] | BioLORD-2023-M | 2026-05-14T09:31:02Z |
| ICCA | M0071 | Natremie | [-0.15, 0.44, 0.12, 0.59, 0.31,-0.22,-0.48, 0.27, …] | BioLORD-2023-M | 2026-05-14T09:31:02Z |
Le champ embedding contient le vecteur complet (768 valeurs) ; on n'en montre que les 8 premières dimensions ici pour la lisibilité.
Aperçu de similarity-scores.parquet pour un concept source (TA systolique invasive), après le passage d’un agent :
similarity-scores.parquet| source_vocabulary_id | source_concept_code | concept_id | method | score | equivalence | comment | created_at |
|---|---|---|---|---|---|---|---|
| ICCA | M0118 | 4353843 | syntactic/jaro-winkler | 0.83 | skos:exactMatch | — | 2026-05-14T09:32:11Z |
| ICCA | M0118 | 4353843 | semantic/biolord | 0.91 | skos:exactMatch | — | 2026-05-14T09:34:02Z |
| ICCA | M0118 | 4353843 | ai/mistral-small-3.2 | 0.97 | skos:exactMatch | Concept dédié à la pression systolique mesurée par cathéter artériel, comme la source ; la médiane observée de 122 mmHg est cohérente. | 2026-05-14T09:35:48Z |
| ICCA | M0118 | 3004249 | ai/mistral-small-3.2 | 0.88 | skos:broadMatch | La cible est plus générale : elle couvre toutes les méthodes de mesure et ne retient pas le caractère invasif de la source. | 2026-05-14T09:35:48Z |
Deux colonnes complètent le fichier : concept_set_uid et concept_set_source_repo, remplies quand une suggestion a été prise dans un concept set d’un dictionnaire de données.
Reprise sur interruption
Les scripts écrivent leur sortie de manière incrémentale, ce qui permet de reprendre là où le précédent run s’est arrêté :
embed_concepts.pyenregistre sa progression toutes les 50 batches (~25 000 concepts) — vous pouvez interrompre sans perdre le travail.compute_scores.pyenregistre toutes les 100 concepts source — les paires(source_vocabulary_id, source_concept_code)déjà scorées sont sautées au redémarrage.
C’est important pour les vocabulaires complets (4 M concepts) qui peuvent prendre des heures.
Pour aller plus loin
- Aligner avec un agent IA — faire travailler un agent, suivre et relire ses suggestions.
- Agents — ce qu’un agent peut faire dans Linkr, et comment vous gardez la main.
- Serveur MCP — créer sa clé API et brancher LibreChat ou OpenCode.
- Évaluation — relire et valider les alignements, y compris ceux créés par un agent.