How Semantica's EntityLinker Resolves Entities Across Multiple Data Sources

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 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 (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


# 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.

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.

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().

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →