FalkorDB
Solutions
Use cases
Why FalkorDB
Developers

Entity resolution: how to make your knowledge graph accurate at scale

GraphRAG-SDK entity resolution for knowledge graphs: Apple Inc., AAPL and Apple merge into one node, while Paris, France and Paris, Texas stay separate
GraphRAG-SDK · FalkorDB · Entity resolution

Entity resolution: how to make your knowledge graph accurate at scale

Apple Inc., AAPL and Apple are one company, however many documents mention them and however big your graph grows. Here is how entity resolution in GraphRAG-SDK brings them into one node, and how to turn it on in one line.

Eight mentions, five entities
Apple Inc. AAPL Apple applefruit IBM International Business Machines ParisFrance ParisTexas

5 nodes. Apple Inc., AAPL and Apple become one company, IBM becomes one, and the fruit and both Parises each keep their own node.

01

Why one node per thing matters

Entity resolution is the process of recognizing when different names refer to the same real-world thing and merging them into one node. A knowledge graph is built one document at a time. A report says Apple Inc., a market feed says AAPL, an article says Apple. Entity resolution recognizes all three as one company, so every fact about Apple sits on one node and every question about Apple finds them all. And Paris, France and Paris, Texas stay two nodes, each with its own facts.

  • Complete answers. Facts from every document about an entity meet in one place.
  • Connected paths. Relationships from different documents join up, so multi-hop questions find their way.
  • Every name still works. A merged entity keeps its other names: a search for “AAPL” finds Apple Inc.
Entity resolution does two things at once: it brings together the same thing written differently, and keeps apart different things written the same.
02

How it works, in three steps

Entity resolution runs when you call finalize() after ingesting. It looks only at what you just added, checks it against everything already in the graph, and lets an LLM make the call.

overviewOne resolution run, stage by stage. New entities start the search; the rest of the graph is searched, not re-checked.

1Check what is new against the whole graph

Each new entity’s name is turned into a vector, and FalkorDB‘s vector index finds the most similar names anywhere in the graph, not only in the same document. Identical spellings are matched too, and so are acronyms, such as IBM and International Business Machines. A new Apple Inc. finds Apple, AAPL and apple, the fruit, as its most similar names, and the four go to the judge together.

walkthroughSimilar names, one judge call, two merges. A second finalize() has nothing new to check.

2An LLM judge decides, with the facts in front of it

Spelling alone never merges two entities. For every candidate, the judge reads its name, label, description and the properties your ontology declares. It answers that Apple Inc., AAPL and Apple are one company and the fruit is something else. Properties settle the hardest cases: two people called George Bush, both presidents, are told apart by birth_year: 1924 and birth_year: 1946.

judgeSame name, label and description. The properties tell father and son apart.
Tip: declare what identifies an entity, such as a birth year or a ticker, and leave out metadata such as source file or confidence. In our namesake test, two identifying properties per entity took the judge from seven wrong merges per run to one.

3Merge without losing anything

Descriptions, other names, sources, properties and every relationship move to the surviving node. When two values conflict, one is kept and the other is saved as history. Every merge is planned before any is applied, and each one is a single database query, so you can stop a run at any point and the next finalize() finishes it.

03

The results

We added 5,000 new entities to real graphs of 10,000, 15,000 and 50,000 entities and let entity resolution run.

99%
of merges correct
50,000
entities in the largest graph tested
~3–4 min
for 5,000 new entities, at every graph size tested

Real runs: FalkorDB in Docker, gpt-4.1 as the judge, text-embedding-3-large for names. On the 50,000-entity graph, about 99% of all 2,459 merges were correct, including 124 of 125 among the hand-labeled entities. Time to add and resolve: 193 s at 10,000 and 15,000 entities, 248 s at 50,000.

04

Get started

  • FalkorDB 4.20 or later. GraphRAG-SDK checks the version and refuses older ones, which could drop vector-index entries.
  • Already have a graph? Run finalize(dedup_all=True) once to check every entity, not only new ones.
  dedup.py
import asyncio
from graphrag_sdk import (
    GraphRAG, ConnectionConfig, LiteLLM,
    LiteLLMEmbedder,
)

async def main():
    async with GraphRAG(
        connection=ConnectionConfig(
            host="localhost", graph_name="my_graph",
        ),
        llm=LiteLLM(model="openai/gpt-4.1"),
        embedder=LiteLLMEmbedder(
            model="openai/text-embedding-3-large",
            dimensions=256,
        ),
    ) as rag:
        await rag.ingest(
            text="AAPL rose 3% after Apple Inc. ...",
            document_id="doc-1",
        )

        # Required after ingest: resolves every new
        # entity against the whole graph, using the
        # llm above as the judge
        result = await rag.finalize()
        print(result.entities_deduplicated)

asyncio.run(main())

Call finalize() once per batch of documents. To use a different model as the judge, import LLMJudge from graphrag_sdk and pass it to finalize(): rag.finalize(judge=LLMJudge(LiteLLM(model=...))).

05

FAQ

What is entity resolution?

Entity resolution finds the mentions in your documents that refer to the same real-world thing, such as Apple Inc., AAPL and Apple, and merges them into one node. It also keeps apart different things that share a name, such as Paris, France and Paris, Texas.

How is it different from deduplication?

Deduplication usually removes exact or near-identical copies. Entity resolution also decides when different names mean the same thing, and when the same name means different things. GraphRAG-SDK does both: identical names, similar names and acronyms all go to an LLM judge, which decides.

Does it re-check the whole graph every time?

No. Only entities added since the last run are checked, but each one is compared with the most similar names across the entire graph. To check every entity, for example on a graph built before this feature, run finalize(dedup_all=True) once.

Which model is the judge?

By default, the llm you passed to GraphRAG. To use another model, import LLMJudge from graphrag_sdk and pass it to finalize(judge=...). You can also write your own judge by implementing DedupJudge.

Is anything lost when entities merge?

No. Other names, descriptions, sources, properties and every relationship move to the surviving node. When two values conflict, one is kept and the other is saved as history. A search for a merged name, such as AAPL, still finds the entity.

One thing, one node

Entity resolution turns extracted mentions into a graph you can trust. GraphRAG-SDK checks every new entity against the whole graph, lets an LLM judge decide with the facts in front of it, and keeps everything when it merges. Ingest your documents, call finalize(), and each real-world thing ends up as one node.

Author

  • Naseem Ali

    Software Engineer at FalkorDB, working across AI, GraphRAG, and developer platforms. He builds graph-powered AI solutions, contributes to GraphRAG, Snowflake integrations, and MCP tooling, and develops full-stack products while driving automation, testing, and open-source initiatives with Python and TypeScript.