To get started:
Slides: resolveworks.github.io/nodes2025
Let's understand the underlying concepts & technologies
So you can use these tools in your own work
A way of getting better answers from a language model by feeding it information from a knowledge graph
What is a graph?
And, what is RAG?
So first things first...
Nodes and edges.
Or: Entities and their relations.
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?
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
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
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
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?
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
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...
We find similar entities through semantic search
The graph provides related entities and communities
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:
Language model ❤️ markdown