En résumé
L’onglet Suggestions affiche, pour chaque concept source, des candidats OMOP pré-classés par un score combiné (syntaxique + sémantique + statistique + IA). Les scores ne sont pas calculés dans Linkr : ils sont produits par le Skill Claude concept-mapping, qui tourne dans Claude Code à partir d’un ZIP exporté du projet. Vous validez ensuite chaque alignement manuellement dans l’éditeur.
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 »).
- Une validation 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.
Le workflow
Exporter le projet
ZIP : project.json, mappings.json, source-concepts.csv
Skill concept-mapping
Calcul des scores · proposition d'alignements · LLM
Onglet Suggestions
Importer le fichier .parquet · pondérer · valider
- Dans Linkr — exportez le projet en ZIP depuis l’onglet Export. Le ZIP contient
project.json,mappings.jsonetsource-concepts.csv. - Dans Claude Code — lancez le skill
concept-mapping(voir plus bas). Il calcule les scores, propose des alignements via un LLM, et produit un fichiersimilarity-scores.parquet. - De retour dans Linkr — ouvrez l’éditeur d’alignements, basculez sur le mode Suggestions, cliquez Gérer → Importer et chargez le
.parquet.
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 384 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).
4. IA agentique — laisser un LLM raisonner
Un LLM (Claude), piloté de façon agentique (le LLM décide quels outils invoquer : lecture des libellés, consultation de la hiérarchie OMOP, calculs sur les valeurs source), relit les N meilleurs candidats sortis des autres méthodes avec tout le contexte disponible : libellé source, statistiques, unités, fréquence, exemples de valeurs. Il propose un ou plusieurs alignements assortis d’une équivalence SKOS (exactMatch, narrowMatch, broadMatch, relatedMatch) et d’une courte justification clinique.
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.Alternatives à Claude Code
Le Skill concept-mapping est livré pour Claude Code, mais le principe — un CLI agentique qui pilote des scripts Python et écrit un .parquet de scores — n’a rien de spécifique à Claude. D’autres CLI agentiques devraient pouvoir exécuter une logique équivalente : Mistral Vibe CLI, Codex CLI, Gemini CLI… En pratique seul Claude Code a été testé pour l’instant — adapter le Skill à un autre CLI demanderait de réécrire SKILL.md au format de l’outil cible.
Côté modèles locaux, Claude Code peut être branché sur Ollama pour faire tourner un LLM open-source (Llama, Mistral, Qwen…) sur votre machine au lieu d’appeler l’API d’Anthropic. Avantages : pas d’appels réseau, pas de coût par token, données qui ne quittent pas votre poste. Limites : qualité de raisonnement inférieure aux modèles frontaliers sur les cas cliniques complexes, et il vous faut le matériel (au moins 16 Go de VRAM pour un modèle utile).
Forces — la seule méthode qui « comprend » la sémantique clinique (qualifiers, contexte de mesure, équivalences nuancées). Sait dire « ce n’est pas exact, c’est un sous-type » via narrowMatch.
Limites — lent et coûteux (appels API tokenisés), 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 ~4 M vecteurs OMOP. Ordre de grandeur : minutes à heures selon le matériel, puis quasi-gratuit en interrogation.
- IA agentique — appels LLM tokenisés, multiples allers-retours d’outils par concept. Ordre de grandeur : secondes à minutes par concept, coût financier réel (centimes à dizaines de centimes la requête selon le modèle).
La règle : ne pas dégainer un palier plus coûteux quand un palier plus bas suffit.
- Lancer syntaxique + statistique en premier — c’est presque gratuit, ça résout déjà une grosse partie des cas simples.
- Le sémantique prend le relais sur les concepts non tranchés (synonymes, reformulations, langues).
- L’IA agentique ne devrait s’appliquer qu’au top N candidats déjà pré-classés par les paliers précédents — jamais sur les 4 M concepts OMOP en aveugle. Un alignement piloté par agent peut consommer plusieurs dizaines de fois plus de tokens qu’un appel LLM unique, parce que l’agent lit la hiérarchie, consulte les synonymes, recalcule, etc.
C’est à la fois une question de coût financier (tokens, GPU), écologique (empreinte carbone des inférences LLM) et de rapidité : un projet de 1 000 concepts traité tout en IA agentique coûterait des dizaines d’euros et plusieurs heures, alors qu’un pipeline syntactique + statistique → sémantique → agentique sur le résidu traite la même charge en quelques minutes pour quelques centimes.
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 | Validation finale par un LLM piloté de façon agentique. |
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 pouvez aussi y voir le nombre de scores chargés, importer un nouveau fichier, ou tout supprimer.
Gérer les suggestions
Ajustez la pondération de chaque méthode dans le score combiné.
Pondération des méthodes
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 proposée par l’IA agentique (exactMatch, narrowMatch, broadMatch, 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.
Le Skill Claude concept-mapping
Le skill est situé dans le dépôt Linkr, sous .claude/skills/concept-mapping/. C’est un dossier autonome qui contient :
SKILL.md— l’orchestrateur, lu par Claude Code quand vous tapez/concept-mapping.reference.md— patterns DuckDB, types TypeScript, heuristiques par domaine, schéma parquet.scripts/embed_concepts.py— génère les embeddings BioLORD pour le vocabulaire OMOP (à exécuter une fois par version de vocabulaire).scripts/compute_scores.py— calcule les scores pour un projet donné (à exécuter une fois par projet).scripts/update_state.py— recalculestate.json(progression, sessions, fichiers présents).review-template/— dashboard HTML statique copié dans le dossier projet la première fois que vous lancez le serveur de revue.
Et deux sous-skills complémentaires, invoqués automatiquement par l’orchestrateur :
concept-mapping-ai— Measurement, Condition, Procedure, Observation.concept-mapping-drug— médicaments (RxNorm, dosages, formes pharmaceutiques).
Prérequis
| Ressource | Pourquoi |
|---|---|
Claude Code installé (claude CLI) | Pour exécuter le skill. |
| Vocabulaires OHDSI locaux (parquet ou CSV) | Téléchargés depuis ATHENA. |
Python 3 + sentence-transformers, pandas, duckdb, pyarrow | Pour les scripts d’embedding et de scoring. |
| ~10–30 Go de disque | Le fichier concept_embeddings.parquet pour ~4 M concepts pèse plusieurs Go. |
| GPU recommandé | L’embedding initial peut prendre des heures sur CPU. |
Configuration
Plutôt que de répéter les chemins à chaque exécution, créez un config.local.json à la racine du projet :
{
"concept-mapping": {
"vocab_dir": "/path/to/ohdsi-vocabularies",
"models_dir": "/path/to/bert-models-cache",
"projects_dir": "/path/to/mapping-projects"
}
}
Détail des trois clés :
vocab_dir— dossier contenant les vocabulaires OHDSI téléchargés depuis ATHENA (CONCEPT.parquet,CONCEPT_SYNONYM.parquet,CONCEPT_RELATIONSHIP.parquet,CONCEPT_ANCESTOR.parquet, plusVOCABULARY.parquetetDOMAIN.parquet). Compte environ 10–30 Go selon les vocabulaires activés. Le skill charge ces fichiers en DuckDB au lancement.models_dir— cache local des modèles de phrase-embedding (par défaut BioLORD-2023-M, ~500 Mo). Si vous changez de modèle (PubMedBERT, SapBERT…), il est téléchargé ici à la première utilisation. Évite de re-télécharger plusieurs gigaoctets à chaque projet.projects_dir— racine des projets d’alignement. Chaque sous-dossier correspond à un projet exporté depuis Linkr (project.json,mappings.json,source-concepts.csv) et reçoit en retour les fichiers de scores (similarity-scores.parquet,source_embeddings.parquet,state.json).
Le skill lit ces valeurs au lancement et ne demande plus rien d’autre.
Les étapes du skill
L’orchestrateur enchaîne automatiquement les étapes suivantes :
Reprendre là où vous en étiez
Si vous avez déjà travaillé sur ce projet, le skill vous résume où vous en êtes (combien de concepts alignés, quelles méthodes ont déjà tourné, quand a eu lieu la dernière session) et vous propose d’ouvrir un tableau de bord local dans votre navigateur pour visualiser la progression.
Lire le projet
L’idéal est d’exporter votre projet depuis Linkr (onglet Export) et de donner au skill le chemin du ZIP ou du dossier décompressé. Le skill y trouve project.json, mappings.json et source-concepts.csv.
Choisir quels concepts traiter
Vous décidez du périmètre : tous les concepts non-alignés, une catégorie clinique précise, les plus fréquents, ou une liste de codes que vous fournissez. Inutile de tout passer en revue d’un seul coup.
Charger les vocabulaires OMOP
Le skill charge en mémoire les tables OHDSI dont il a besoin (noms des concepts, synonymes, hiérarchie) pour pouvoir comparer rapidement le concept source à chaque candidat.
Calculer les scores syntaxique + sémantique + statistique
Le skill lance les méthodes les moins coûteuses : comparaison de chaînes, embeddings, comparaison de distributions. Le calcul peut prendre du temps sur un gros vocabulaire (jusqu’à quelques heures sur CPU), mais il est reprenable — si vous interrompez et relancez, le travail déjà fait n’est pas refait.
Laisser l’IA agentique trancher
Le skill passe la main à un sous-skill spécialisé par domaine (mesures, conditions, procédures, médicaments). Le LLM lit chaque candidat avec son contexte clinique et propose un alignement justifié, accompagné d’une équivalence SKOS.
Écrire les résultats
Toutes les suggestions atterrissent dans un fichier similarity-scores.parquet que vous ré-importez ensuite dans Linkr via Gérer les suggestions → Importer. L’IA agentique apparaît alors comme une méthode supplémentaire dans l’onglet Suggestions.
L’agent réutilise les similarités produites par les méthodes décrites plus haut (syntaxique, sémantique, statistique) pour pré-classer les candidats avant de prendre sa décision — il n’invente pas un nouveau scoring. Il ajoute simplement sa propre ligne ai/<model-id> à côté des autres, avec une équivalence SKOS et un commentaire justifiant son choix.
Fichiers produits
Le skill écrit deux fichiers dans le dossier projet :
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 ré-importez dans Linkr.
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 (384 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) :
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 | — | — | 2026-05-14T09:32:11Z |
| ICCA | M0118 | 4353843 | semantic/biolord | 0.91 | — | — | 2026-05-14T09:34:02Z |
| ICCA | M0118 | 4353843 | ai/claude-sonnet-4-6 | 0.97 | exactMatch | Concept dédié à la mesure invasive (cathéter artériel). | 2026-05-14T09:35:48Z |
| ICCA | M0118 | 3004249 | ai/claude-sonnet-4-6 | 0.88 | broadMatch | LOINC plus général, couvre toutes les méthodes. | 2026-05-14T09:35:48Z |
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.