Keeping a Knowledge Graph Clean
Entity Resolution & Deduplication for GraphRAG and Agent Memory
Why It Matters
Knowledge graphs power GraphRAG and long-term agent memory — but they corrupt silently. Every ingest run risks creating duplicate nodes for the same real-world entity ("NYC" vs. "New York City"), or worse, merging two genuinely different entities because their names look alike. Over time this quietly poisons retrieval and reasoning. The fix is a disciplined entity resolution + deduplication pipeline.
"Similar names are not strong enough evidence that two entities are identical. Most practitioners conflate naming normalization with identity verification — and that creates silent corruption."
Entity Resolution
Normalizes names to a canonical form. Answers: "what should we call this?" Does not merge nodes.
Deduplication
Verifies identity. Answers: "are these the same real-world thing?" Only this step merges nodes.
The Five-Step Pipeline
1. LLM Extraction
An LLM pulls entities and relationships as (entity, relationship, entity) triplets, anchored to a defined ontology (e.g. POLE+O — Person, Object, Location, Event + Organization) so only relevant entity types are captured.
2. Entity Resolution (name normalization)
Assigns a canonical name via short-circuit matching: try exact match first, then fuzzy (typos, whitespace, casing), then semantic. Resolves "NYC" → "New York City" without merging any nodes yet.
3. Embedding
Embeds the full entity context — name + type + attributes — not just the bare name. This is what lets dedup distinguish two different "John Smith" people, or unify the same entity described differently.
4. Deduplication (identity verification)
Scores candidate pairs with a weighted blend: 0.7 × embedding similarity + 0.3 × fuzzy similarity. The score feeds a three-band routing decision.
5. Routing Decision
The confidence score decides the action — see the bands below.
Confidence Bands & The Gray Zone
Auto-merge
High enough confidence to merge nodes automatically.
Human review
Don't merge. Create a queryable SAME_AS edge and let a human confirm.
New node
Treat as a distinct entity and create a new node.
"Reversibility cost is the whole reason for the gray zone." A wrong merge is expensive to undo; a missed merge is cheap to fix later. So err toward caution — when in doubt, don't merge.
The Scoring Logic
# Weighted identity score over the FULL entity context
def identity_score(a, b) -> float:
emb = cosine_similarity(a.embedding, b.embedding) # semantic
fuzz = fuzzy_ratio(a.canonical_name, b.canonical_name) # lexical
return 0.7 * emb + 0.3 * fuzz
def route(a, b):
s = identity_score(a, b)
if s >= 0.95:
merge_nodes(a, b) # auto-merge
elif s >= 0.85:
create_edge(a, b, "SAME_AS") # gray zone -> human review
else:
create_node(b) # distinct entity
The "Dream" Pipeline
Entities ingested at the same time can't be deduped against each other in real time. A nightly "dream" pass re-compares recently added nodes, catching duplicates that slipped through during concurrent ingestion. Like memory consolidation during sleep, it keeps the graph coherent without blocking ingest.
Best Practices
Do This
- Separate name normalization from identity verification
- Embed full context (name + type + attributes)
- Keep a human-in-the-loop gray zone with SAME_AS edges
- Run a nightly dedup pass over recent nodes
- Anchor extraction to an ontology
Avoid This
- Merging just because names are similar
- Embedding bare names without context
- Auto-merging in the medium-confidence band
- Skipping the nightly pass for concurrent ingests
- Treating duplicates as harmless — they corrupt silently
Tools & References
Graph & Memory Platforms
Neo4j Labs, mem0 and cognee provide building blocks for entity resolution and agent memory graphs.
Neo4j Labs →Source Article
Paul Iusztin — "How to keep your knowledge graph clean," Decoding AI.
Read on Decoding AI →