Entity duplication when building a Neo4j GraphRAG pipeline, how do you handle it?

Hi all,

I've been building GraphRAG pipelines with Neo4j and I keep hitting the same wall: entity duplication at the ingestion step. The extractor (LangChain or LlamaIndex) has no memory of which mentions are the same node, so across different chunks "Apple Inc.", "Apple" and "AAPL" land as three separate nodes. The graph still works, but retrieval gets fragmented and you slowly lose trust in the answers.

I'm curious how you handle entity resolution in your own pipelines. Do you do a MERGE on a canonical property, run a cleanup pass after ingestion, or leave the duplicates in and deal with them at query time?

For context, here is what I ended up doing, and I'd love honest feedback on it. I built a small middleware that sits between the extraction framework and Neo4j, with three layers that short-circuit before anything reaches the store:

  1. Deterministic matching (rapidfuzz): exact matches, aliases, lexical core matches. Zero tokens, about 0.1 ms per entity.
  2. Semantic matching: cosine similarity on embeddings that you already have (I feed all-MiniLM-L6-v2 vectors through node properties). Zero tokens, about 0.5 ms.
  3. LLM arbiter: a single call, only for the ambiguous leftovers (e.g. "J. Dupont" vs "Jean Dupont").

The whole point is to never send every entity pair to an LLM. On a 500 document, 3,000 mention benchmark with 63 real identities, I measured 100% precision and 99.4% recall with just 7 LLM calls and 1,566 tokens, versus roughly 661k tokens (about $0.0334) for a full-LLM approach. The methodology and the repro command are in the repo's BENCHMARK.md.

The honest caveat: about 18 out of 3,000 mentions were missed merges without an alias map (around 4 with one). Stock tickers and rebrands sit below the cosine threshold and never reach the LLM, which is the tradeoff I'm still working on.

It's open source (MIT) if you want to look: GitHub - jules-gd-dev/autograft-lib: AutoGraft is a lightweight Python middleware for GraphRAG Entity Resolution. It intercepts entities before they hit Neo4j to eliminate duplicates using a 3-layer hybrid approach (Deterministic + Vector + LLM), reducing API costs by up to 100% compared to LangChain. Ā· GitHub. Sharing it as a reference, not trying to sell anything.

The real question I'd like input on: is doing dedup at ingestion the right layer, or do you prefer disambiguation at query time, or periodic merge jobs? I keep going back and forth on the tradeoffs, so I'd genuinely like to hear what works for you.