How GraphBuilder Handles Entity Resolution in Semantica: A Deep Dive into the Source Code

Semantica's GraphBuilder activates an EntityResolver when merge_entities=True to automatically detect, merge, and remap duplicate entities before final graph assembly.

Entity resolution—the process of identifying and merging duplicate real-world entities—is a critical step in knowledge graph construction. In the semantica-agi/semantica repository, the GraphBuilder class orchestrates this workflow through a configurable pipeline that deduplicates entities, updates relationship references, and tracks resolution outcomes in graph metadata.

How Entity Resolution Gets Activated in GraphBuilder

The entity resolution pipeline begins at initialization. In semantica/kg/graph_builder.py, the constructor checks the merge_entities flag and creates a default EntityResolver instance if enabled.


# From graph_builder.py lines 18-25

def __init__(
    self,
    merge_entities: bool = False,
    entity_resolution_strategy: str = "fuzzy",
    # ... other parameters

):
    self.merge_entities = merge_entities
    if self.merge_entities:
        from semantica.kg.entity_resolver import EntityResolver
        self._default_resolver = EntityResolver(strategy=entity_resolution_strategy)

The EntityResolver receives the resolution strategy—"fuzzy" by default—though alternatives like "exact" can be specified. This strategy controls how DuplicateDetector identifies similar entities later in the pipeline.

The Resolution Decision Point at Build Time

When build() executes, the GraphBuilder determines which resolver to use. According to the source code at line 542 of graph_builder.py, the builder accepts either:

  • An entity_resolver passed directly to build(), or
  • The default resolver created during initialization

This design allows runtime override without reconstructing the builder.

The Core Resolution Workflow

After extracting entities from all sources, the builder invokes the resolution step. At line 748 of graph_builder.py, the critical call occurs:


# Line 748: The resolution execution

resolved_entities, merge_map = resolver_to_use.resolve_entities(all_entities)

The resolve_entities() method orchestrates two internal components:

  1. DuplicateDetector – Groups entities believed to represent the same real-world object based on the chosen strategy
  2. EntityMerger – Consolidates each duplicate group into a single canonical entity

The implementation tracks performance metrics—logging entity counts before and after resolution across lines 745–751—giving observability into deduplication effectiveness.

Remapping Relationship Endpoints After Entity Merging

Entity relationships are collected before resolution occurs, meaning their source_id and target_id values reference original (now potentially merged) entity identifiers. The GraphBuilder solves this reference integrity problem through _remap_relationship_endpoints(), called at line 761.

This method rewrites edge endpoints by consulting the merged_from field on each canonical entity—an array that preserves all original IDs that were consolidated. Any relationship pointing to a merged-away entity gets redirected to its canonical successor.

Verification Through Graph Metadata

The final graph includes provenance information about whether resolution occurred. At line 787 of graph_builder.py, the builder sets:

graph_metadata["entity_resolution_applied"] = self.merge_entities

This boolean flag allows downstream consumers to understand whether entity counts reflect raw extraction or deduplicated results.

Practical Examples: Configuring Entity Resolution

Example 1: Enable Default Fuzzy Resolution

from semantica.kg import GraphBuilder

builder = GraphBuilder(merge_entities=True)  # Activates EntityResolver with "fuzzy" strategy

graph = builder.build(
    sources=[
        {
            "entities": [
                {"id": "e1", "name": "Apple Inc.", "type": "Company"},
                {"id": "e2", "name": "Apple", "type": "Company"},
                {"id": "e3", "name": "Microsoft", "type": "Company"},
            ]
        }
    ]
)

print(graph["metadata"]["entity_resolution_applied"])  # True

print(len(graph["entities"]))                          # 2 (Apple entities merged)

Example 2: Supply a Custom Resolver

from semantica.kg import GraphBuilder, EntityResolver

# Create resolver with exact matching (no similarity tolerance)

custom_resolver = EntityResolver(strategy="exact", similarity_threshold=1.0)

builder = GraphBuilder(merge_entities=True, entity_resolution_strategy="exact")

graph = builder.build(
    sources=[{"entities": [...]}],
    entity_resolver=custom_resolver  # Overrides default resolver

)

Key Source Files in the Resolution Pipeline

File Purpose
semantica/kg/graph_builder.py Core orchestration: creates resolver, executes resolve_entities(), remaps relationship endpoints, sets metadata flags
semantica/kg/entity_resolver.py Implements resolution logic using DuplicateDetector and EntityMerger
semantica/deduplication/duplicate_detector.py Strategy-driven duplicate grouping (fuzzy matching, exact matching, etc.)
semantica/deduplication/entity_merger.py Canonical entity creation with merged_from provenance tracking

Summary

  • Activation: Set merge_entities=True in GraphBuilder to enable automatic entity resolution
  • Strategy choice: Default "fuzzy" matching can be overridden via entity_resolution_strategy or custom EntityResolver instances
  • Execution flow: build() → resolve_entities() → _remap_relationship_endpoints() with full logging
  • Reference integrity: Relationship endpoints are automatically rewritten to point to canonical merged entities
  • Observability: Check graph["metadata"]["entity_resolution_applied"] to verify resolution occurred

Frequently Asked Questions

What happens to relationships when entities are merged?

The GraphBuilder automatically remaps all relationship endpoints. The _remap_relationship_endpoints() method rewrites any source_id or target_id referencing a merged-away entity to point to the canonical surviving entity, using the merged_from field for lookup.

Can I use a custom entity resolution algorithm?

Yes. Pass a pre-configured EntityResolver instance to build() via the entity_resolver parameter. This overrides the default resolver created during initialization, allowing complete control over duplicate detection and merging behavior.

What resolution strategies does Semantica support?

The source code shows "fuzzy" as the default strategy with "exact" available. The EntityResolver passes the strategy to DuplicateDetector, which implements the matching logic—check semantica/deduplication/duplicate_detector.py for strategy-specific implementations.

How can I verify that entity resolution actually ran?

Inspect graph["metadata"]["entity_resolution_applied"] in the output. This boolean field is set at line 787 of graph_builder.py and directly reflects whether merge_entities was enabled during graph construction.

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 →