Entity Resolution: The Hard Part of Due Diligence Nobody Wants to Own
Two rows with the same ICO are easy. Two rows where one says 'Navigara s.r.o.' and the other says 'NAVIGARA SRO - in likvidaci' are where entity resolution earns its keep. Here's how Prismatic does it.
Every due diligence team eventually hits the same wall: the data is there, but it refuses to line up. One source calls the company "Navigara s.r.o." Another calls it "NAVIGARA SRO - v likvidaci". A third has the right ICO but the wrong address. Collapsing those three rows into one entity — and knowing when not to collapse — is entity resolution, and it is where most DD stacks quietly lose.
The three failure modes
- Under-merging — two rows that should be one entity stay separate. You miss connections. You miss risk.
- Over-merging — two unrelated entities collapse into one. You flag innocent people. You get sued.
- Temporal drift — the same entity at t=0 and t=1 looks different (address change, ownership change) and you treat them as two.
A system that ignores any of the three is not doing entity resolution. It is doing string matching with good marketing.
Blocking + scoring + graph
Prismatic's resolver runs three stages:
1. Blocking. Reduce O(n²) comparisons to O(n·k) by bucketing on cheap keys (ICO prefix, normalized name trigrams, country + postal code):
def block(entities) do
Enum.group_by(entities, fn e ->
{country(e), trigram_head(e.name), ico_prefix(e)}
end)
end
2. Scoring. Within a block, compute a weighted similarity across ICO exact match, name Jaro-Winkler, address Levenshtein, beneficial-owner overlap:
def score(a, b) do
0.45 * ico_match(a, b) +
0.25 * name_sim(a, b) +
0.15 * address_sim(a, b) +
0.15 * bo_overlap(a, b)
end
3. Graph. Promote high-confidence pairs into a knowledge graph edge, then use community detection to find over-merges (one "entity" that is actually five) and missing merges (two communities sharing a directing mind).
The golden record is a lie
Most stacks flatten resolved entities into a single "golden record." That is convenient and wrong. A resolved entity has a history — each merge is an event with a timestamp, a score, a reason, and a reversibility flag. If a later signal contradicts an earlier merge, you split, not overwrite.
%EntityNode{
id: "ent_01HX...",
canonical: %{name: "Navigara s.r.o.", ico: "..."},
merges: [
%{source_id: "...", score: 0.97, merged_at: ~U[...], reason: :ico_exact},
%{source_id: "...", score: 0.82, merged_at: ~U[...], reason: :name_address}
],
reversible: true
}KYC and the regulator test
Under KYC obligations, "we merged because the names looked similar" is not a defensible answer. "We merged because ICO matched exactly and beneficial ownership overlapped 100%, here is the audit trail" is. Entity resolution without an audit trail is a liability.
Where to go next
- Academy: DD Investigation — full case walkthrough
- Academy: Decision Core Fundamentals — how resolved entities feed the decision engine
- Glossary: Entity Resolution, Entity Graph, Due Diligence, KYC, Knowledge Graph
Get the merges right and the rest of DD is plumbing. Get them wrong and everything downstream is noise.
Dive deeper into the Prismatic Platform -- explore our architecture, agents, and epistemic framework.