Getting Started¶
LinkingTK unifies four text-linking tasks behind one common interface:
- Entity Alignment (EA) — matching entities between two knowledge graphs.
- Entity Linking (EL) — linking mentions in text to a knowledge base.
- Word Sense Disambiguation (WSD) — linking mentions in text to dictionary senses.
- Word Sense Alignment (WSA) — matching senses between two dictionaries.
Every task is modeled as matching entities from one dataset to another, so
the same Entity, BlockingStrategy and
Evaluator types work across all four. This page walks through that
interface end-to-end; see Key Concepts for how the
pieces fit together.
Installation¶
pip install linkingtk
The core install covers the shared interface and the transformer-based linkers. Optional extras add support for specific algorithms and data sources — pull them in as you need them:
| Extra | Adds |
|---|---|
graph |
NetworkX / RDFLib graph helpers |
nlp |
spaCy |
kge |
PyKEEN and the knowledge-graph-embedding EA linkers |
wn |
WordNet via the wn package |
wikipedia |
Wikipedia / Wikidata HTTP sources |
vector-index |
FAISS + sentence-transformers vector indexes |
llm |
OpenAI / Anthropic clients for LLM-based linkers |
server |
FastAPI / Uvicorn REST server mode |
all |
everything above |
pip install "linkingtk[graph,nlp]"
To work on LinkingTK itself, clone the repository and use uv:
git clone https://github.com/teangaNLP/linkingtk.git
cd linkingtk
uv sync --extra graph --extra nlp --group dev
The core interface¶
Every entity — a KG node, a text mention, a dictionary sense — is represented the same way:
from linkingtk.core import Entity
Entity(id="s1", labels=["cat"], description="A small domesticated feline.")
A linking algorithm implements
BaseLinker.link, taking two
datasets of entities and returning a list of
AlignmentResult — one predicted
source_id → target_id link per match, each with a confidence score.
Before scoring, a BlockingStrategy cuts the full dataset1 × dataset2
cross-product down to a smaller set of plausible candidate pairs. The
simplest is ExactMatch, which pairs
entities that share a label:
"""Minimal end-to-end example: block candidate pairs and evaluate them.
Run with: `uv run python examples/basic_exact_match.py`
"""
from linkingtk.blocking import ExactMatch
from linkingtk.core import Entity
from linkingtk.eval import Evaluator
def main() -> None:
dataset1 = [
Entity(id="s1", labels=["cat"]),
Entity(id="s2", labels=["dog"]),
]
dataset2 = [
Entity(id="t1", labels=[("cat", "en")]),
Entity(id="t2", labels=["fish"]),
]
pairs = ExactMatch().candidate_pairs(dataset1, dataset2)
predictions = [(e1.id, e2.id) for e1, e2 in pairs]
print("Candidate pairs:", predictions)
ground_truth = [("s1", "t1"), ("s2", "t2")]
report = Evaluator.evaluate(predictions=predictions, ground_truth=ground_truth)
print("Metrics:", report.metrics)
blocking_report = Evaluator.evaluate_blocking(
candidate_pairs=predictions,
ground_truth=ground_truth,
dataset1_size=len(dataset1),
dataset2_size=len(dataset2),
)
print("Blocking metrics:", blocking_report.metrics)
if __name__ == "__main__":
main()
Run with:
uv run python examples/basic_exact_match.py
Candidate pairs: [('s1', 't1')]
Metrics: {'precision@1': 1.0, 'recall': 0.5, 'f1': 0.6666666666666666}
Blocking metrics: {'pair_completeness': 0.5, 'reduction_ratio': 0.75}
Evaluator.evaluate scores
predictions against known ground truth; the same call is used for every
task, and
Evaluator.evaluate_blocking
scores the blocking step on its own via Pair Completeness (the fraction of
true matches blocking kept) and Reduction Ratio (the fraction of the full
cross-product it eliminated).
Putting it together across all four tasks¶
ExactMatch only works when source and target already share a label. A
real linker like
StringSimilarityLinker
scores pairs by similarity instead, so it can match "cat" against a
differently-worded description. The same linker class, and the same
Evaluator.evaluate call, works unchanged across EA, EL, WSD and WSA —
only the dataset and the fields being compared change per task:
"""String-similarity baselines on all four tasks.
Runs the same StringSimilarityLinker baseline (LeskLinker for WSD is a
preconfigured instance of it) against a bundled toy dataset for each of Entity
Alignment (EA), Entity Linking (EL), Word Sense Disambiguation (WSD) and Word
Sense Alignment (WSA), scoring each with Evaluator.evaluate.
Run with: `uv run python examples/mvp_benchmark.py`
"""
from __future__ import annotations
from linkingtk.algorithms.string_similarity import StringSimilarityLinker
from linkingtk.algorithms.wsd import LeskLinker
from linkingtk.blocking import LabelOverlap
from linkingtk.core import AlignmentResult
from linkingtk.datasets import ToyEADataset, ToyELDataset, ToyWSADataset, ToyWSDDataset
from linkingtk.eval import Evaluator
def _score(results: list[AlignmentResult], ground_truth: list[tuple[str, str]]) -> dict[str, float]:
predictions = [(r.source_id, r.target_id) for r in results]
return Evaluator.evaluate(predictions=predictions, ground_truth=ground_truth).metrics
def run_ea() -> dict[str, float]:
kg1, kg2, ground_truth = ToyEADataset().load()
linker = StringSimilarityLinker(source_field="label", target_field="label", metric="jaccard")
results = linker.link(kg1, kg2, blocking=LabelOverlap(max_matches=3))
return _score(results, ground_truth)
def run_el() -> dict[str, float]:
mentions, kb, ground_truth = ToyELDataset().load()
linker = StringSimilarityLinker(
source_field="context", target_field="description", metric="word_overlap"
)
results = linker.link(mentions, kb)
return _score(results, ground_truth)
def run_wsd() -> dict[str, float]:
mentions, senses, ground_truth = ToyWSDDataset().load()
results = LeskLinker().link(mentions, senses)
return _score(results, ground_truth)
def run_wsa() -> dict[str, float]:
dict1, dict2, ground_truth = ToyWSADataset().load()
linker = StringSimilarityLinker(
source_field="description", target_field="description", metric="word_overlap"
)
results = linker.link(dict1, dict2)
return _score(results, ground_truth)
def main() -> None:
tasks = {"EA": run_ea, "EL": run_el, "WSD": run_wsd, "WSA": run_wsa}
for name, task in tasks.items():
print(f"{name}: {task()}")
if __name__ == "__main__":
main()
Run with:
uv run python examples/mvp_benchmark.py
EA: {'precision@1': 1.0, 'recall': 1.0, 'f1': 1.0}
EL: {'precision@1': 1.0, 'recall': 1.0, 'f1': 1.0}
WSD: {'precision@1': 1.0, 'recall': 1.0, 'f1': 1.0}
WSA: {'precision@1': 1.0, 'recall': 1.0, 'f1': 1.0}
LeskLinker used for WSD above is just a preconfigured
StringSimilarityLinker. All four calls load a
bundled toy dataset — small, synthetic, and included with
the package so this runs offline with no downloads.
Where to go next¶
- Datasets — swap the toy datasets above for real ones (Wikidata, WordNet, OpenEA, UFSAC, ...).
- Examples — task-specific algorithms, from simple baselines to full reproductions of published methods.
- API Reference — full docs for every public class and function.