Welcome

To get started:

  1. Visit: console.neo4j.io and create an account
  2. Create a free instance:
  3. Visit llm-graph-builder.neo4jlabs.com
  4. Connect to your instance: (Use the credentials file downloaded on instance creation)

Slides: resolveworks.github.io/nodes2025

Finding Connections

Who am I?

I'm Johan

What we'll cover today

  • How GraphRAG works
  • What is it made of?

Let's understand the underlying concepts & technologies

So you can use these tools in your own work

So, what is GraphRAG?

A way of getting better answers from a language model by feeding it information from a knowledge graph

Graph + RAG

What is a graph?

And, what is RAG?

So first things first...

What is a graph?

Nodes and edges.

Or: Entities and their relations.

owns

employs

employs

initiated

authorized

processed at

occurred in

Company A

Company B

Person C

Person D

Transaction E

Location F

Bank G

But what is RAG?

R(etrieval) A(ugmented) G(eneration)

Putting relevant information into context

1. Find relevant information that can be used to answer your question

2. Provide the language model with the retrieved information alongside the question.

3. Get better answers.

Sounds familiar?

So, what is relevant?

What information do you give to the model?

There's many ways

Deep Research probably uses keyword search

But RAG systems often use "semantic search"

Because it handles paraphrasing and synonyms

And understands context and intent

Semantic search

Also called similarity search

It works by converting text into numerical representations (vectors) that can be compared

These vectors are called embeddings

Because we're "embedding" language

Into numerical space

While trying to preserve semantic relationships

Okay... Embeddings

How do we create them?

How do we compare them?

To create these vectors we use specialized
"embedding models"

These models are trained for this task specifically


Anchor: "The cat sat on the mat" 
Positive: "A feline rested on the rug" → Pull closer 
Negative: "Stocks rose 3% today"       → Push apart
          

Maximize similarity for positive pairs, minimize for negative

Positive pairs: paraphrases, similar sentences, consecutive sentences

Negative pairs: random unrelated sentences

There's more to it

But we're not here to train our own embedding model

Let's see what this looks like using all-MiniLM-L6-v2


from sentence_transformers import SentenceTransformer

model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")

sentence = "The sun is shining"
embedding = model.encode(sentence)
            

"The sun is shining" becomes...

 5.42898139e-04  1.02551401e-01  8.36703405e-02  9.39253941e-02
 2.07145736e-02 -1.12772360e-02  9.93586630e-02 -4.44610193e-02
 3.98165248e-02 -2.13668533e-02  2.09684577e-02 -3.66757251e-02
 7.11042061e-03  4.15845886e-02  1.01847239e-01  7.34366402e-02
 1.43970475e-02  4.65401914e-03  9.70691442e-03 -4.16579135e-02
-2.30006799e-02 -2.27442160e-02 -2.73266062e-02 -1.22460043e-02
-8.25940259e-03  6.72213510e-02 -2.77701933e-02  2.63383351e-02
-6.12662174e-02 -1.37788551e-02  2.81139649e-02  5.81778679e-03
... etc ... ...

384 dimensions
            

Great. Now that we have our numerical representation, how do we compare it to others?

A vector can be thought of as a list of numbers, or a coordinate or direction in a space

Embedding models are trained so that vectors with similar direction are semantically similar

Why compare directions and not distance between coordinates?

Because high-dimensional geometry is weird

All points become equidistant with Euclidean distance

So most of the times, we compare vectors by their angles. By calculating Cosine Similarity

Let's see what that looks like

384 dimensions is hard to visualize though...

So let's imagine this in 2 dimensions

−1.0 −0.8 −0.6 −0.4 −0.2 0.0 0.2 0.4 0.6 0.8 1.0 −1.0 −0.8 −0.6 −0.4 −0.2 0.0 0.2 0.4 0.6 0.8 1.0 Vector A (0.15, 0.72) Vector B (0.51, 0.43) θ = 38.1° Similarity = 0.787 cos(θ) = A·B / |A|·|B|

So to come back to "The sun is shining"...

Text Similarity
Bring an umbrella 0.320
The weather is great 0.418
It's daytime 0.622

Great, we can find similar things!

But... how do we get the things?

Structured data extraction

From unstructured text to structured graph

Transformers are great for chatting

But they can also return structured data

Let's say we have this schema


class CalendarEvent(BaseModel):
  name: str
  date: str
  participants: list[str]
          

We can pass it to our LLM


response = client.responses.parse(
  input=[
    {"role": "system", "content": "Extract the event information."},
    {
      "role": "user",
      "content": "Alice and Bob are going to a science fair on Friday.",
    },
  ],
  text_format=CalendarEvent,
)
          

To get back structured data


CalendarEvent(
  name='Science Fair',
  date='Friday',
  participants=['Alice', 'Bob']
)
          

We can't be sure to get the right information

But we can be sure it fits our schema

So how does that work?

Constrained Decoding or "guided generation"

Transformers generate one token at a time

A probability distribution over possible tokens

"The quick brown fox jumps over ..."

With a schema, we constrain this set of tokens

Only valid tokens that match the schema

{"chart": "tokens", "color": "blue"

This allows us to extract anything from any text

So... GraphRAG

How are these these technologies combined?

Let's visualize a simplified GraphRAG process

First the dataset is preprocessed...

Entities and relations are extracted from documents

Embedded for semantic search

Added to a graph database

And possibly, communities are calculated

Then, when querying the data...

Source Text Entity

We find similar entities through semantic search

Source Text Entity

The graph provides related entities and communities

Source Text Entity

And the texts that reference them

That information, together with the question, gets sent to the language model

---Role---
You are a helpful assistant responding to questions about
data in the tables provided.

---Goal---
Generate a response that responds to the user's question,
summarizing all information in the input data tables, and
incorporating any relevant general knowledge. If you don't
know the answer, just say so. Do not make anything up. Do
not include information where the supporting evidence for it
is not provided.

---Data tables---
{context_data}

Which should lead to answers based on the information in your graph

But why is this cool?

Because we can learn a lot from this technique

GraphRAG is a gateway technology

An intersection of key technologies whose impact is
still being discovered

Should you GraphRAG?

I don't know...

Should you play with these underlying concepts?

Definitely.

To get started:

  1. Visit: console.neo4j.io and create an account
  2. Create a free instance:
  3. Visit llm-graph-builder.neo4jlabs.com
  4. Connect to your instance: (Use the credentials file downloaded on instance creation)

GraphRAG tools

  1. github.com/microsoft/graphrag
    github.com/noworneverev/graphrag-visualizer Widely adopted, popular
  2. github.com/gusye1234/nano-graphrag
    Small, easy to hack on
  3. llm-graph-builder.neo4jlabs.com
    github.com/neo4j/neo4j-graphrag-python Neo4j ecosystem

Parsing documents

Language model ❤️ markdown

  1. github.com/docling-project/docling
    IBM Research
  2. github.com/microsoft/markitdown
    Microsoft