cd /news/artificial-intelligence/keeping-match-confidence-on-the-grap… · home topics artificial-intelligence article
[ARTICLE · art-63019] src=dev.to ↗ pub= topic=artificial-intelligence verified=true sentiment=↑ positive

Keeping Match Confidence on the Graph Edge: Why Throwing Away Splink Scores Hurts Graph RAG

An engineer building er-api, a multilingual entity resolution service, preserved Splink match probabilities as edge properties in a knowledge graph instead of discarding them after thresholding. This enables per-use-case confidence filtering and worst-case confidence propagation in Graph RAG queries, allowing the same graph to serve both high-stakes legal compliance and exploratory business intelligence.

read4 min views1 publishedJul 17, 2026

Most entity resolution pipelines throw away the most useful number they produce.

You run a probabilistic linker — Splink, Dedupe, or a custom model — and it produces a match probability for every candidate pair. 0.95 for two records you are very confident share the same real-world entity. 0.71 for a pair that probably matches but has enough ambiguity to give you .

Then you apply a threshold, classify each pair as a match or non-match, and discard the probability. The downstream system — a knowledge graph, a database table, an analytics pipeline — gets a binary signal: same entity, or different entity.

This is a mistake when those resolved entities feed a knowledge graph.

The match probability is information about how certain the resolution was. Once you cross the threshold gate, that information is gone. The knowledge graph treats a 0.95-confidence merge and a 0.71-confidence merge identically. Both become unconditional identity claims: "this is the same entity."

For low-stakes applications, this is fine. For a knowledge graph that feeds retrieval systems, this is a quiet defect.

In er-api, a multilingual entity resolution service for Korean, Japanese, Chinese, and English corporate data, I kept the match probability from Splink as an edge property in the graph rather than discarding it at the resolution boundary.

The insert looks like this:

def create_identity_edge(
    source_node_id: str,
    target_node_id: str,
    match_probability: float,
    source_record_ids: list[str],
) -> dict:
    return {
        "source": source_node_id,
        "target": target_node_id,
        "relation": "SAME_AS",
        "properties": {
            "match_probability": match_probability,
            "source_record_ids": source_record_ids,
            "resolved_at": datetime.utcnow().isoformat(),
            "resolver": "splink-3",
            "review_status": "auto" if match_probability >= 0.90 else "pending",
        }
    }

A 0.95-confidence match between Samsung Electronics records gets review_status: "auto"

. A 0.71-confidence match gets review_status: "pending"

and is queued for human review.

The impact shows up most clearly at retrieval time.

When a Graph RAG query traverses the graph, it may cross one or more SAME_AS

edges to connect records from different source systems. If those edges have unknown confidence, the traversal has no way to signal how certain the identity claims it relied on actually are.

def traverse_with_confidence(
    start_node_id: str,
    relation: str,
    min_confidence: float = 0.80,
) -> list[dict]:
    results = []
    for path in graph.find_paths(start=start_node_id, edge_type=relation):
        identity_edges = [e for e in path.edges if e["relation"] == "SAME_AS"]
        if not identity_edges:
            results.append({"path": path, "path_confidence": 1.0})
            continue

        worst_case = min(e["properties"]["match_probability"] for e in identity_edges)
        if worst_case >= min_confidence:
            results.append({"path": path, "path_confidence": worst_case})

    return sorted(results, key=lambda x: x["path_confidence"], reverse=True)

Three things change when confidence propagates through the graph:

Per-use-case threshold filtering. A due-diligence query for legal compliance can set min_confidence=0.95

. An exploratory search for business intelligence can use min_confidence=0.70

. The same graph serves both use cases because the confidence data is available to filter on.

Worst-case confidence on retrieved paths. If a traversal crosses a 0.68-confidence merge, the retrieval result carries that number. The LLM prompt can surface it explicitly: "Note: this answer relies on an entity match with 68% confidence." The LLM cannot author this signal — it can only surface it if the retrieval layer passes it through.

Low-confidence merges become visible. Instead of being invisible assumptions baked permanently into graph structure, uncertain merges are flagged and queryable. You can find all SAME_AS

edges below a threshold and present them to domain experts for review without touching graph structure.

In practice, running entity resolution on East Asian corporate data means dealing with cases where the right answer requires domain knowledge — knowing that "삼성SDI" and "Samsung SDI" are the same entity requires understanding the Korean naming convention, not just string similarity.

The review_status: "pending"

flag on low-confidence edges gives you a clean queue:

def get_pending_review_queue(threshold: float = 0.90) -> list[dict]:
    return graph.find_edges(
        relation="SAME_AS",
        filters={"review_status": "pending", "match_probability": {"lt": threshold}},
        order_by="match_probability",
        order="asc",
    )

Reviewers see the lowest-confidence merges first. They can confirm, reject, or flag for escalation. Confirmed merges update review_status

to "verified"

. Rejected merges get split back into separate nodes.

The graph stays queryable during review. Downstream systems that need only high-confidence data can filter to match_probability >= 0.90

. Systems that can tolerate more uncertainty can include pending merges. Neither has to wait for the review queue to clear.

The common alternative to confidence-aware edges is running resolution at higher thresholds and treating everything as certain. This suppresses the 0.71-confidence merges entirely — they never enter the graph.

The problem is that some of those 0.71-confidence pairs are correct. Multilingual corporate data has noise that systematically pushes correct matches below the high-confidence threshold: inconsistent romanization, abbreviated legal suffixes, different subsidiary naming conventions across source databases. A threshold high enough to eliminate incorrect merges also eliminates a meaningful fraction of correct ones.

The confidence-on-edge approach lets you include them with explicit uncertainty rather than excluding them. Users and downstream systems can decide what confidence level they need. The probability on the edge is the mechanism that makes that decision possible.

The match probability is information. Throwing it away at the resolution gate means the knowledge graph permanently forgets something it knew about the quality of its own structure.

Building multilingual entity resolution and knowledge graph pipelines at er-api.hannune.ai and 2asy.ai. More at hannune.ai.

Cover: Piret Ilver on Unsplash

── more in #artificial-intelligence 4 stories · sorted by recency
── more on @splink 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/keeping-match-confid…] indexed:0 read:4min 2026-07-17 ·