Linkr
Home Resources Tools Documentation Blog Demo
FR
  • What is Linkr?
  • Deployment modes
  • Quick start
  • Local install
  • With Docker
  • Manual install
  • Client-only
  • Install LibreChat
  • Your first project
  • Linkr in a clinical data warehouse
  • Workspaces and projects
  • The data pipeline
  • Entities and sharing
  • Versioning and collaboration
  • Overview
  • Projects
  • Wiki
  • Plugins
  • Members and roles
  • Settings
  • Schemas
  • Getting and exploring
  • Mapping
  • Databases
  • Derived sub-databases
  • Data quality
  • Data catalog
  • Build and publish
  • Anonymize
  • SQL script collections
  • ETL pipelines
  • Building and running
  • Generating the scripts
  • Overview
  • Mapping projects
  • Global view
  • Target concepts
  • Mapping editor
  • Suggestions
  • AI agent
  • Evaluation
  • Export
  • Overview
  • Databases
  • Concepts
  • Cohorts
  • Building
  • Results, SQL and report
  • Patient data
  • Pipeline
  • Datasets
  • IDE
  • Web apps
  • Versioning
  • Overview
  • Tabs and widgets
  • Built-in widgets
  • Analysis widgets
  • Control charts (SPC)
  • Surveys and eCRF
  • R and Python code
  • Filters, settings and export
  • Overview
  • Presentation mode
  • Exporting a report
  • Agents
  • MCP server
  • Skills
  • Import and export
  • Git versioning
  • Community catalog
  • Publishing content
  • Production install
  • Configuration
  • Database connectors
  • Authentication and permissions
  • Files on the server
  • Backup and restore
  • Contributing code
  • Glossary
  • Keyboard shortcuts
  • Release notes
Documentation Concept mapping Suggestions

Suggestions

Pre-ranked OMOP candidates for each source concept: precomputed syntactic and semantic scores, and suggestions from an AI agent that applies the concept-mapping skill live in the project, through the MCP server.

Summary

The Suggestions tab shows, for each source concept, OMOP candidates ranked by a combined score. These suggestions come from two places: precomputed scores — syntactic and semantic — produced by two scripts run in a terminal, then loaded into Linkr; and the proposals of an AI agent, which follows the concept-mapping skill and works live in the project through the MCP server, with no export. Either way, you are the one who creates each mapping.

Client Available in client-only mode — runs entirely in the browser, no backend. Backend Available with the FastAPI backend.

Why a separate tab

Mapping 1,000 local codes by hand against the 4 million concepts of the OMOP vocabulary is slow. Each algorithm family has its own bias:

  • String comparison misses synonyms (“HR” vs “heart rate”).
  • Semantic embeddings occasionally confuse close-but-different concepts (“serum creatinine” vs “urine creatinine”).
  • An AI agent adds clinical context but is expensive to run over 4 million pairs.

The Suggestions tab combines several methods into a single score and surfaces the per-method details, so you keep the final decision.

Where suggestions come from

Precomputed scores

Jaro-Winkler and BioLORD, for every source concept at once.

Computed outside Linkr by two Python scripts, then loaded into the Suggestions tab. Works in both deployment modes. See Precomputing the scores.

AI agent suggestions

A few justified candidates per concept, batch by batch.

Written live into the project by an agent connected to Linkr’s MCP server. Server mode only. See Mapping with an AI agent.

The two complement each other: the agent reads the precomputed scores when there are any, and uses them to find its candidates faster — especially for drugs.

The Suggestions tab in Linkr

demo.linkr.interhop.org
Concept setsSearchSuggestions
ScoreMethodsVocabConcept nameCodeStd
92%
95%88%91%
LOINCCreatinine [Mass/volume] in Serum or Plasma2160-0S
81%
83%79%82%
LOINCCreatinine [Mass/volume] in Blood38483-4S
76%
80%71%77%
LOINCCreatinine [Moles/volume] in Serum or Plasma14682-9S
68%
72%64%
SNOMEDSerum creatinine measurement113075003S
54%
58%51%
LOINCCreatinine renal clearance2164-2S
8 / 24 results⚙
The Suggestions tab: combined score on the left, colour-coded method dots, and an action bar to map or manage scores.

Similarity methods

Four method families can feed the candidate ranking. Each attacks the problem from a different angle, and their failure modes don’t overlap — which is why combining them beats any single family.

Syntactic

Compares the label character strings.

Semantic

Compares meaning via biomedical embeddings.

Statistical

Compares the distributions of numerical values.

AI

An LLM re-reads top candidates with the clinical context.

In Linkr, every method is represented next to the combined score by a coloured dot (colours above) followed by its percentage. On hover, a tooltip names the exact algorithm. This helps understand why a score is high: a candidate scoring 92% from the syntactic method alone is likely a textual false friend, while one scoring 80% from semantic + AI is much more solid.

1. Syntactic — compare character strings

We compare label strings as-is, without any understanding of their meaning. Useful because source and OMOP labels often follow different writing conventions: case, spaces or underscores, accents, abbreviations, word order, punctuation. Three algorithms in the catalogue:

  • Jaro-Winkler — letter-level proximity with a bonus for shared prefixes. Matches “hemoglobin_a1c” with “Hemoglobin A1c” despite the case and separator differences.
  • Token-Sort — splits labels into words, sorts them alphabetically, then compares. Word-order agnostic (“blood pressure systolic” ≈ “systolic blood pressure”).
  • N-gram IDF — splits into character n-grams (subsequences of N letters), weights each n-gram by its rarity in the vocabulary. Usagi’s historical method.
Source
hemoglobin_a1c(source label, no accents, no spaces)
Hemoglobin A1c
0.93
A1c hemoglobin
0.78
Hemoglobin
0.62
Glucose
0.14

Jaro-Winkler — letter-level proximity, bonus for shared prefix; Token-Sort lets "A1c hemoglobin" match the source despite word-order differences.

Jaro-Winkler applied to a snake_case source label: the OMOP labels with the highest letter-level proximity bubble to the top, despite differences in case, spacing and word order.

Strengths — catches near-literal matches despite writing variants (case, spaces, abbreviations, word order), very fast (milliseconds per pair).

Limits — does not account for synonyms: “creatinine” and “creat” are close, but “heart rate” and “pulse” score zero.

2. Semantic — compare meaning

Each label is encoded as a numerical vector through a sentence-embedding model, then we measure the angle between vectors via cosine similarity. Two labels with close meanings will produce close vectors, even if the words differ.

From label to embedding vector

BioLORD-2023-M projects each label into a 768-dimensional space. Here are 24 dimensions as a heatmap — each cell is one dimension, colour encodes the value.

−1
+1Dimension value
Heart rate
…/768
Pulse
…/768
Blood pressure
…/768

Cosine similarity

cos(θ) = (A · B) / (‖A‖ × ‖B‖)
Heart ratevsPulse
0.94
Heart ratevsBlood pressure
0.21

The patterns for "Heart rate" and "Pulse" are nearly identical cell by cell → very close vectors. "Blood pressure" has a different pattern → distant vector. The closer the cosine score gets to 1, the closer the meanings.

'Heart rate' and 'pulse' have almost the same vector (cos ≈ 0.94), even though the characters differ entirely. 'Blood pressure' sits in another region of the space.

Default model: BioLORD-2023-M, trained on biomedical text and multilingual (French, English, Spanish, German…).

Configurable alternatives — any Sentence-Transformers compatible BERT model:

  • PubMedBERT, SapBERT, ClinicalBERT — biomedical-specialised but English-only.
  • all-MiniLM-L6-v2 — general-purpose, English-only, much faster.
  • paraphrase-multilingual-MiniLM-L12-v2, LaBSE — general-purpose, multilingual.

Multilingual coverage

If your source labels are in French (or any non-English language), stick with BioLORD-2023-M or switch to an explicitly multilingual model. PubMedBERT, SapBERT, ClinicalBERT and all-MiniLM-L6-v2 will produce poor-quality embeddings on non-English text.

Strengths — catches synonyms (“heart rate” ≈ “pulse”), rephrasings, terminology variants.

Limits — can conflate two close-but-distinct concepts (“serum creatinine” vs “urine creatinine”). Expensive to pre-compute over the ~4M OMOP concepts (GPU recommended).

3. Statistical — compare value distributions

For numerical variables (Measurement only), we compare the statistical distribution of values between source concept and candidates: min, P25, median, P75, max, mean, standard deviation. Two variables taking values in the same range with the same shape are probably the same measurement.

Concretely, we use a statistical test to quantify the gap between the two distributions:

  • Kolmogorov-Smirnov test (KS) — maximum gap between cumulative distribution functions. KS = 0 → identical distributions; KS close to 1 → very different distributions.
  • Wasserstein distance — “cost” of transporting the mass of one distribution onto the other. More robust than KS when supports are shifted (unit change).

Source concept: serum creatinine (µmol/L)

12,450 measurements, 1,842 patients

Source
Candidate
LOINC 2160-0 — Creatinine [Mass/volume] in Serum or Plasmaunit: mg/dL
Likely match
KS = 0.08Wass. = 0.12
LOINC 14682-9 — Creatinine [Moles/volume] in Serum or Plasmaunit: µmol/L
Likely match
KS = 0.06Wass. = 0.09
LOINC 2951-2 — Sodium [Moles/volume] in Serum or Plasmaunit: mmol/L
Different distribution
KS = 0.62Wass. = 0.71

Kolmogorov-Smirnov test: KS close to 0 = identical distributions; KS > 0.5 = very different distributions. Smoothed curves (kernel density estimate) let you compare shape and position without the distributions hiding each other.

The source distribution (creatinine in µmol/L) overlaps almost perfectly with two LOINC creatinine concepts, and diverges sharply from sodium — which sits in an entirely different range.

Strengths — works even when the label is ambiguous or missing. Very discriminating: hard to confuse creatinine (60–120 µmol/L) with sodium (135–145 mmol/L).

Limits — only applicable to numerical variables with enough values for the distribution to be representative. Sensitive to data-entry artifacts (outliers, mixed units).

No script for this score yet

Linkr can display and weight statistical scores (statistical/… methods) when a scores file contains them, but the two scripts provided do not compute them yet. In practice, the AI agent is what compares distributions: it reads each source concept’s distribution in its metadata before choosing a target.

4. Agentic AI — let an LLM reason about it

An LLM driven agentically — it decides which tools to call: reading a source concept and its metadata, searching the OMOP vocabulary, browsing a candidate’s hierarchy — examines the best candidates with all the context available: source label, unit, value distribution, wards where the code is used. It proposes one or more mappings carrying a SKOS equivalence (exactMatch, closeMatch, broadMatch, narrowMatch, relatedMatch) and a comment justifying the choice.

Its proposals show up in the tab as one more method, named ai/<model> after the model that produced them, in the Agentic category. The Mapping with an AI agent page explains how to start it.

Source concept
TA_systolique_invasive

ICCA · M0118 · "Invasive systolic arterial pressure" · 24,380 measurements · median 122 mmHg

Pre-ranked candidates
  • SNOMED 251071003 — Invasive systolic arterial pressure
    91%
  • SNOMED 72313002 — Systolic arterial pressure
    78%
  • LOINC 8480-6 — Systolic blood pressure
    74%
Agentic AI reasoning

The label "TA_systolique_invasive" refers to systolic arterial pressure measured via an arterial catheter (arterial line), not by cuff. SNOMED 251071003 matches this definition exactly — a direct mapping. LOINC 8480-6 is a broader concept (systolic, any method): the target is more general than the source, which is written broadMatch in SKOS. The observed distribution (median 122 mmHg, right tail up to ~180) is consistent with an invasive ICU measurement.

Proposed mappings
SNOMED 251071003 — Invasive systolic arterial pressureConfidence: 97%
SKOS equivalence : skos:exactMatch — The target concept describes precisely the same measurement.
LOINC 8480-6 — Systolic blood pressureConfidence: 88%
SKOS equivalence : skos:broadMatch — The target is more general (any measurement method), so broader than the invasive source.
The agentic AI re-reads the top three candidates, identifies SNOMED 251071003 as the exact match (a concept dedicated to invasive measurement), and additionally proposes LOINC 8480-6 as a broadMatch since the latter covers systolic pressure regardless of method.

Strengths — the only method that “understands” clinical semantics (qualifiers, measurement context, nuanced equivalences). Can say “this is not exact, the target is more general” via broadMatch, and says in its comment what differs.

Limits — slow and compute-hungry, risk of hallucinations on rare cases, still requires human validation.

Frugality principle — start with the cheapest method

The methods sit at different resource tiers, roughly grouped as:

  1. Syntactic and statistical — light CPU, local computations on strings or on values already in memory. Order of magnitude: milliseconds per pair.
  2. Semantic (BERT / embeddings) — a GPU is recommended for inference and for pre-computing the OMOP vectors. Order of magnitude: minutes to hours depending on hardware, then near-free at query time.
  3. Agentic AI — calls to the model, several tool round-trips per concept. Order of magnitude: seconds to minutes per concept.

The rule: don’t reach for a higher tier when a lower one suffices.

  • Run the syntactic scores first — they are almost free, and already resolve a good share of easy cases.
  • The semantic method takes over on unresolved concepts (synonyms, rephrasings, languages).
  • The agent works on a chosen scope — a category, the most frequent concepts — and starts from candidates already pre-ranked, never blindly from the 4M OMOP concepts.

It’s at once a question of cost (compute, GPU, and the bill for a remote model), environmental impact and speed: running a 1,000-concept project entirely through the agent takes hours, where the precomputed scores handle the same load in minutes and leave the agent the hard residue.

The combined score

The combined score (left column, green bar) is a weighted average of each method’s score. Default weights are:

MethodWeightRole
Syntactic×1Catches near-literal matches despite writing variants.
Statistical×1Compares value distributions (creatinine vs sodium).
Semantic×2Picks up synonyms and rephrasings.
Agentic AI×3Justified proposals from an AI agent.

The Manage suggestions button (top left) opens a dialog with sliders to tune each weight in real time — the table re-sorts automatically. It also shows the number of loaded scores, and lets you Import a file or Delete all.

In server mode, the Methods in the file section lists every method present — syntactic/jaro-winkler, semantic/biolord, ai/<model>… — each with a bin: Remove the suggestions of a method deletes all its rows from the scores file, keeping the other methods and the mappings. This is how you remove in one go what an agent left.

demo.linkr.interhop.org

Manage suggestions

Tune the weight of each method in the combined score.

Method weights

Syntactic
×1.0
Statistical
×1.0
Semantic
×2.0
Agentic AI
×3.0

Methods in the file

  • ai/mistral-small-3.2
  • semantic/biolord
  • syntactic/jaro-winkler
12,384 scores loaded from similarity-scores.parquet
The Manage suggestions dialog: per-method weight sliders, the methods in the file with their bin (server mode), count of loaded scores, and Import a file / Delete all actions.

Selection and mapping

demo.linkr.interhop.org
SNOMED 251071003 — Invasive systolic arterial pressure91%
SNOMED 72313002 — Systolic arterial pressure78%
LOINC 8480-6 — Systolic blood pressure74%

Candidates already mapped for this source concept are greyed out and not re-selectable.

When a row is selected, an action bar appears at the top with the usual mapping buttons. Candidates already mapped (here LOINC 8480-6) appear greyed out and struck through.
  • Click a row to select it. The action bar at the top then shows the usual mapping buttons (Exact match + SKOS dropdown + comment button).
  • Candidates already mapped for this source concept are greyed out and not re-selectable.
  • The ⓘ icon at the row end opens the target OMOP concept’s detail (description, synonyms, hierarchy).

The main alignment button “Exact” is pre-filled with the selected suggestion’s SKOS equivalence — the one the agent proposed, for an ai/… row (exactMatch, closeMatch, broadMatch, narrowMatch, relatedMatch). You can accept it in one click, or open the dropdown to pick a different equivalence if you disagree. Linkr never maps on its own from a suggestion: that click is what creates the mapping.

AI agent suggestions

An AI agent can leave its own suggestions in the tab, each with a SKOS equivalence and a comment justifying the choice. The Mapping with an AI agent page explains how to put it to work, what it writes into the project and how to follow its work.

Precomputing the scores

Syntactic (Jaro-Winkler) and semantic (BioLORD embeddings) scores give every source concept a ranked list of candidates before any agent looks at it. They are computed outside Linkr, in a terminal, with the two Python scripts that ship with the skill, in its scripts/ folder. The resulting file is then loaded into the project; this part also works in an install without a backend.

Requirements

ResourceWhy
Python 3 + rapidfuzz, pandas, pyarrow, numpy, duckdbFor both scripts.
sentence-transformers and faiss-cpuFor the semantic scores only.
OHDSI vocabularies (CONCEPT.parquet or .csv)Downloaded from ATHENA.
Several GB of diskThe OMOP vocabulary embeddings weigh several GB.
GPU recommendedComputing the embeddings can take hours on CPU.

The two scripts

  • embed_concepts.py — computes BioLORD embeddings of the OMOP concepts and writes concept_embeddings.parquet next to CONCEPT.parquet. Run it once per vocabulary release: the file serves every project.
  • compute_scores.py — compares a project’s source concepts with the OMOP concepts and writes similarity-scores.parquet. Run it once per project. By default it computes Jaro-Winkler and BioLORD; without concept_embeddings.parquet, only the syntactic score.

The scoring script expects a source concepts CSV with three columns: terminology, concept_code and concept_name. The source-concepts.csv from a Linkr ZIP export of the project does the job, with its columns renamed if needed.

Always filter the targets

The semantic step builds an index over the target concepts. Unfiltered, that is about 4 million vectors and up to 25 GB of memory: enough to bring down a laptop. Always pass --only-standard --only-valid and a --domain (or --vocabulary) matching what you are mapping. Drugs — about 2 million RxNorm concepts — are handled on their own, only if you are mapping them.

python scripts/compute_scores.py \
  --source source-concepts.csv \
  --concept /path/to/vocabulary/CONCEPT.parquet \
  --embeddings /path/to/vocabulary/concept_embeddings.parquet \
  --only-standard --only-valid --domain Measurement Observation

Run it in your own terminal, where you can watch memory and stop it with Ctrl-C. Each script lists all its options with --help.

Loading the scores into Linkr

In the mapping editor, Suggestions mode: Manage suggestions → Import a file, then pick similarity-scores.parquet.

Loading a file replaces the previous one

The loaded file replaces the project’s scores file, including the suggestions an agent left. To keep them, fetch the current file first: export the project as Linkr ZIP with Similarity scores ticked, then pass the archive’s similarity-scores.parquet to the script with --output. The script adds its rows to the existing file without touching the others.

Output files

The scoring script writes two files:

  • source_embeddings.parquet — the embedding vectors of the source concepts. Kept so the embeddings can be reused on the next run without re-encoding everything.
  • similarity-scores.parquet — one score per (source, candidate, method) triple. This is the file you load into Linkr, and also where Linkr stores the agents’ suggestions.

Preview of source_embeddings.parquet:

demo.linkr.interhop.org
source_embeddings.parquet
3 rows out of 1,234 · 768-dimensional vectors
source_vocabulary_idsource_concept_codesource_concept_nameembedding (excerpt)modelcreated_at
ICCAM0118TA_systolique_invasive
[ 0.42,-0.18, 0.71, 0.05,-0.33, 0.58, 0.22,-0.41, …]
BioLORD-2023-M2026-05-14T09:31:02Z
ICCAM0042Frequence_cardiaque
[ 0.39,-0.22, 0.68, 0.08,-0.30, 0.61, 0.19,-0.38, …]
BioLORD-2023-M2026-05-14T09:31:02Z
ICCAM0071Natremie
[-0.15, 0.44, 0.12, 0.59, 0.31,-0.22,-0.48, 0.27, …]
BioLORD-2023-M2026-05-14T09:31:02Z

The embedding column holds the full vector (768 values); only the first 8 dimensions are shown here for readability.

One embedding vector (768 values) per source concept.

Preview of similarity-scores.parquet for one source concept (invasive systolic BP), after an agent’s pass:

demo.linkr.interhop.org
similarity-scores.parquet
4 rows out of 12,384
source_vocabulary_idsource_concept_codeconcept_idmethodscoreequivalencecommentcreated_at
ICCAM01184353843syntactic/jaro-winkler0.83skos:exactMatch—2026-05-14T09:32:11Z
ICCAM01184353843semantic/biolord0.91skos:exactMatch—2026-05-14T09:34:02Z
ICCAM01184353843ai/mistral-small-3.20.97skos:exactMatchConcept dedicated to systolic pressure measured by arterial catheter, like the source; the observed median of 122 mmHg is consistent.2026-05-14T09:35:48Z
ICCAM01183004249ai/mistral-small-3.20.88skos:broadMatchThe target is more general: it covers any measurement method and does not keep the invasive nature of the source.2026-05-14T09:35:48Z
One row per (source concept, target concept, method) triple. Syntactic and semantic rows always carry skos:exactMatch; an agent's ai/… rows add their own SKOS equivalence and a comment justifying the choice.

Two more columns complete the file: concept_set_uid and concept_set_source_repo, filled when a suggestion was taken from a data-dictionary concept set.

Resume on interrupt

The scripts write their output incrementally, so a subsequent run resumes where the previous one stopped:

  • embed_concepts.py checkpoints every 50 batches (~25,000 concepts) — you can interrupt without losing work.
  • compute_scores.py checkpoints every 100 source concepts — (source_vocabulary_id, source_concept_code) pairs already scored are skipped on restart.

This matters for complete vocabularies (4M concepts) that can take hours.

Going further

  • Mapping with an AI agent — putting an agent to work, following and reviewing its suggestions.
  • Agents — what an agent can do in Linkr, and how you stay in control.
  • MCP server — create your API key and connect LibreChat or OpenCode.
  • Evaluation — review and validate mappings, including those created by an agent.
PreviousMapping editorNextAI agent

Product

  • Home
  • Demo

Resources

  • Documentation
  • Resources
  • Tools
  • Blog

Community

  • Framagit source code
  • Github source code

About

  • InterHop.org
  • Contact

2021–2026 InterHop — CC BY-NC-SA 4.0 (site) · AGPLv3 (software)