# How Semantica's EntityLinker Resolves Entities Across Multiple Data Sources

> Semantica's EntityLinker unifies data from multiple sources by normalizing identifiers into stable URIs and connecting them. Discover how it creates a queryable entity web.

- Repository: [Semantica /semantica](https://github.com/semantica-agi/semantica)
- Tags: how-to-guide
- Published: 2026-08-09

---

**Semantica's EntityLinker normalizes disparate identifiers from documents, APIs, and knowledge graphs into stable URIs and connects them via lexical similarity, explicit context relationships, and knowledge-graph lookups to create a unified, queryable entity web.**

The `EntityLinker` class in the [semantica-agi/semantica](https://github.com/semantica-agi/semantica) repository serves as the central component for entity resolution across heterogeneous data sources. It transforms fragmented entity mentions into a coherent knowledge graph by assigning canonical identifiers and discovering relationships across documents, external APIs, and knowledge-graph stores.

## URI Assignment and Normalization

Every incoming entity receives a stable, globally-unique identifier through the URI generation pipeline implemented in [`semantica/context/entity_linker.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/entity_linker.py) (lines 46‑82).

If the entity has a readable label, the linker generates a URL-safe string derived from that label using `quote(entity_text…)`. When no label is available, the system generates a deterministic MD5 hash of the entity ID. An optional type fragment (`#type`) is appended to produce canonical URIs such as `https://semantica.dev/entity/python#programming_language`. The mapping `entity_id → uri` is maintained in `self.entity_registry`, ensuring consistent identifier resolution across all subsequent operations.

## Cross-Source Linking Strategies

The linker discovers relationships between the current entity and all other sources through three complementary mechanisms.

### Knowledge-Graph Similarity Matching

When a knowledge graph is supplied via `self.knowledge_graph`, the linker scans its `entities` list and computes a lightweight word-overlap similarity using `_calculate_text_similarity`. Entities whose similarity exceeds the configurable `similarity_threshold` (default 0.8) are converted into `EntityLink` objects with a `same_as` or `related_to` type depending on the score (lines 331‑349).

### Contextual Relationship Extraction

When a `context` argument is supplied, the linker extracts explicit `source_id → target_id` relations from each context block. It creates corresponding `EntityLink` objects that preserve the original relationship type and confidence scores, enabling the integration of pre-defined ontologies or manual annotations (lines 511‑538).

### Bidirectional Symmetry Enforcement

For symmetric link types such as `related_to` and `same_as`, the linker automatically inserts a reverse link to ensure the entity graph remains navigable in both directions. This bidirectional symmetry prevents orphaned entities and supports graph traversal operations regardless of the initial linking direction (lines 311‑327).

## Entity Web Construction

After collecting all links, the `build_entity_web()` method aggregates them into a unified data structure (lines 506‑531). This structure contains:

- A flat map of `entity_id → {uri, link_count}`
- A list of link records with `source`, `target`, `type`, and `confidence` fields
- Basic statistics including total entities and total links

The resulting dictionary can be exported, visualized, or queried further by downstream components.

## Resolution APIs for Multi-Source Queries

The linker exposes several convenience methods that enable downstream components to resolve entities regardless of their originating source:

- **`get_entity_uri(entity_id)`** – Fetches the canonical URI for any registered entity
- **`resolve_uri(uri)`** – Reverses the mapping, returning the full entity record from the knowledge graph if present
- **`find_similar(entity, threshold)`** – Wraps the similarity engine for ad-hoc queries against the knowledge graph
- **`find_related(entity_id, max_hops)`** – Traverses the built web up to a configurable hop distance, aggregating related entities across all integrated sources

## Practical Implementation Example

```python

# Initialise the linker with an optional knowledge graph

from semantica.context import EntityLinker
kg = {
    "entities": [
        {"id": "e1", "text": "Python", "type": "PROGRAMMING_LANGUAGE"},
        {"id": "e2", "text": "Py", "type": "PROGRAMMING_LANGUAGE"},
    ],
    "nodes": [],  # optional for advanced lookup

}
linker = EntityLinker(knowledge_graph=kg, similarity_threshold=0.75)

# 1. Assign a stable URI

uri = linker.assign_uri("e1", "Python", "PROGRAMMING_LANGUAGE")
print(uri)   # → https://semantica.dev/entity/python#programming_language

# 2. Link a piece of text (cross‑document linking)

text = "Python is widely used for data science."
entities = [{"id": "e1", "text": "Python", "type": "PROGRAMMING_LANGUAGE"}]
linked = linker.link_text(text, entities)
print(linked[0].linked_entities)   # → EntityLink(same_as) to "e2" (similarity ≈ 0.86)

# 3. Resolve a URI back to its graph record

record = linker.resolve_uri(uri)
print(record)   # → {'entity_id': 'e1', 'uri': uri, 'text': 'Python', ...}

# 4. Retrieve the full entity web for export / visualisation

entity_web = linker.build_entity_web()
print(entity_web["statistics"])

# → {'total_entities': 2, 'total_links': 2}

```

## Summary

- **Source-agnostic normalization**: The `EntityLinker` converts disparate identifiers into a single URI namespace using URL-safe labels or MD5 hashes, stored in `self.entity_registry`.
- **Multi-factor linking**: Entities are connected via lexical similarity matching against knowledge graphs, explicit context relationships, and bidirectional symmetry enforcement.
- **Unified entity web**: The `build_entity_web()` method aggregates all connections into a queryable structure with full provenance tracking via `EntityLink` objects.
- **Cross-source resolution**: Methods like `resolve_uri()` and `find_related()` enable seamless traversal of entities regardless of their original data source.

## Frequently Asked Questions

### How does EntityLinker handle entities without readable labels?

When an entity lacks a readable label, the linker generates a deterministic MD5 hash of the entity ID to create the URI suffix. This ensures every entity receives a stable identifier even when textual descriptions are unavailable, as implemented in the URI assignment logic at lines 46‑82 of [`semantica/context/entity_linker.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/context/entity_linker.py).

### What similarity threshold should I use for cross-source linking?

The default `similarity_threshold` is 0.8, which provides conservative matching suitable for high-precision entity linking. You can adjust this parameter during initialization (e.g., `EntityLinker(similarity_threshold=0.75)`) to balance precision and recall based on your specific domain's lexical variability.

### How does the linker maintain consistency between related entities?

The linker enforces bidirectional symmetry for relationship types like `same_as` and `related_to` by automatically inserting reverse links during the linking phase (lines 311‑327). This ensures that if entity A links to entity B, entity B is immediately queryable as related to entity A, maintaining graph consistency.

### Can EntityLinker resolve entities across more than two data sources simultaneously?

Yes, the linker is designed to integrate any number of data sources including documents, knowledge-graph stores, and external APIs. The `build_entity_web()` method aggregates links from all processed contexts and knowledge graphs into a single unified structure, enabling multi-hop traversal across heterogeneous sources via `find_related()`.