Sitemap

Understanding GraphRAG

Through Hands-On Implementation

Introduction

When I first came across Microsoft’s article “From Local to Global: A GraphRAG Approach to Query-Focused Summarization” [2], I was excited to understand how GraphRAG could be applied to unstructured text. In this post, I’ll walk you through how I implemented the GraphRAG approach step by step.

Important Links:

This is the original GitHub page. You can also read the blog post here. If you want to apply the GraphRAG directly to your own documents, take a look at this GitHub link. Moreover, you may find one of the implementations of the article here.

What is my goal?

Based on what I understood from the article and observed in the GitHub repository, I implemented how the process works — with help from ChatGPT. My goal here is to break it down in a simpler way and share what I’ve done with you. (There may be missing parts.) You can access my code here.

Warning:

In GitHub pages, there are warnings about token usage.

⚠️ Warning: The GraphRAG Accelerator uses multiple Azure services and may incur substantial costs. It is meant to host a high-utilization API with auto-scaling and user access control. Please see the deployment bicep for further detail on the services used. [link]

⚠️ Warning: GraphRAG indexing can be an expensive operation, please read all of the documentation to understand the process and costs involved, and start small. [link]

Let’s start! There are so many parts we need to cover :)

What is GraphRAG?

GraphRAG uses LLM-generated knowledge graphs to provide substantial improvements in question-and-answer performance when conducting document analysis of complex information. This builds upon our recent research, which points to the power of prompt augmentation when performing discovery on private datasets. [3]

The problem with classical RAG is that it typically retrieves 5 to 10 chunks that are semantically similar to the user query and tries to generate an answer from them. However, it doesn’t identify the relationships between these chunks. Also, it doesn’t construct higher-level summaries. While some chunking techniques segment the documents more intelligently, the fundamental limitation remains: classical RAG treats chunks as independent units.

Moreover, most source documents are written for humans — not for LLMs. Humans can infer relationships from previous chapters or sections, but LLMs rely on explicit structure and context, which is often missing in isolated chunks.

Another important reason to use GraphRAG:

Baseline RAG struggles with queries that require aggregation of information across the dataset to compose an answer. Queries such as “What are the top 5 themes in the data?” perform terribly because baseline RAG relies on a vector search of semantically similar text content within the dataset. However, with GraphRAG we can answer such questions. [3]

I want to pause here to tell you what I've done so far with “GraphRAG”. In general, I’m using GraphRAG in my projects following the architecture below:

  • Take the data — For example, book_names, publishers, authors, book_genre, ids, target audience, etc.
  • Store the data in a graph database, like Neo4J or Amazon Neptune.
  • Get the embeddings for relevant fields.
  • Get a question from the user. Use an LLM to generate queries from the user’s question in Gremlin or Cypher.
  • Execute the graph query to find relevant relationships and return the answer.

This method works well if your data is already structured. But if you have unstructured data — like PDF documents — things can get messy. That’s where this article presents a solution: using a graph structure to enable Retrieval-Augmented Generation even for unstructured data.

Steps of GraphRAG

Press enter or click to view image in full size
ss from the article [2]
Chunks → (Entity & Relationship Extraction) → Knowledge Graph → 
(Community Detection) → Community Summaries →
(Map: Answer per community → Filtered & Scored) →
(Reduce: Final Global Answer)

I will show you the practical implementation in 6 steps. However, I wasn’t able to use an LLM to generate the summarized version of the text — it would have been too costly. Instead, I’ll demonstrate the output produced after using the relevant prompt for each step.

Practical Implementation — Summary

  • 3.1.1 Source Documents → Text Chunks
  • 3.1.2 Text Chunks → Entities & Relationships
  • 3.1.3 Entities & Relationships → Knowledge Graph
  • 3.1.4 Knowledge Graph → Graph Communities
  • 3.1.5 Graph Communities → Community Summaries
  • 3.1.6 Community Summaries → Community Answers → Global Answer

You may find the prompts here.👀

According to the GitHub page: This document (Operation Dulce) is an AI-generated science fiction novella, included here for the purposes of providing a starting point for notebook experimentation.

The original data is essentially a narrative-style story. You can find the full document here.

# Example part of the story:

The thrumming of monitors cast a stark contrast to the rigid silence enveloping the group. Agent Alex Mercer, unfailingly determined on paper, seemed dwarfed by the enormity of the sterile briefing room where Paranormal Military Squad's elite convened. With dulled eyes, he scanned the projectors outlining their impending odyssey into Operation: Dulce.

“I assume, Agent Mercer, you’re not having second thoughts?” It was Taylor Cruz’s voice, laced with an edge that demanded attention.

Alex flickered a strained smile, still thumbing his folder's corner. "Of course not, Agent Cruz. Just trying to soak in all the details." The compliance in his tone was unsettling, even to himself.

✅ 3.1.1 Source Documents → Text Chunks

🔸 This is where raw documents are split into 600-token chunks with optional overlaps.

  • The GitHub repo has already performed this step and saved the results in its preprocessed dataset.

✅ 3.1.2 Text Chunks → Entities & Relationships

🔸 This is where an LLM extracts:

  • Entities (name, type, description)
  • Relationships (source, target, description, strength)

This process is completed by the prompt:

-Goal-
Given a text document that is potentially relevant to this activity and a list of entity types, identify all entities of those types from the text and all relationships among the identified entities.

-Steps-
1. Identify all entities. For each identified entity, extract the following information:
- entity_name: Name of the entity, capitalized
- entity_type: One of the following types: [{entity_types}]
- entity_description: Comprehensive description of the entity's attributes and activities
Format each entity as ("entity"{tuple_delimiter}<entity_name>{tuple_delimiter}<entity_type>{tuple_delimiter}<entity_description>)

2. From the entities identified in step 1, identify all pairs of (source_entity, target_entity) that are *clearly related* to each other.
For each pair of related entities, extract the following information:
- source_entity: name of the source entity, as identified in step 1
- target_entity: name of the target entity, as identified in step 1
- relationship_description: explanation as to why you think the source entity and the target entity are related to each other
- relationship_strength: a numeric score indicating strength of the relationship between the source entity and target entity
Format each relationship as ("relationship"{tuple_delimiter}<source_entity>{tuple_delimiter}<target_entity>{tuple_delimiter}<relationship_description>{tuple_delimiter}<relationship_strength>)

The output data is given in the GitHub repo. I’ve downloaded:

  • entities.parquet ⟶ maps to extracted entities
  • relationships.parquet ⟶ maps to extracted relationships
Press enter or click to view image in full size
entities_df
Press enter or click to view image in full size
relationships_df

relationships_df gives us the relationship between source and target. Even this information gives us a lot about a raw PDF.

Let’s use it to understand the rest of the algorithm.

✅ 3.1.3 Entities & Relationships → Knowledge Graph

From those (entity, relationship) tuples, GraphRAG builds a knowledge graph:

  • Nodes = entities
  • Edges = relationships (with strength scores and descriptions)
  • Claims (facts about entities) can also be stored and attached to nodes or edges

My question to ChatGPT is whether I should use embedding models after finding the entities from the chunk:

❌Are Embeddings Used After Chunking?

No — not after the graph construction. Embeddings are only used in traditional vector RAG approaches. GraphRAG replaces this with structured graph-based summarization. (ChatGPT 🍒)

How to build a graph?

import pandas as pd
import igraph as ig

# Load your DataFrames
# entities_df = pd.read_parquet("entities.parquet")
# relationships_df = pd.read_parquet("relationships.parquet")

# Filter to only include unique entities with names
unique_entities = entities_df.drop_duplicates(subset="name").copy()
entity_names = unique_entities["name"].tolist()

# Create igraph graph
g = ig.Graph()

# Add vertices
g.add_vertices(entity_names)

# Add metadata (descriptions and types) to vertices
for idx, row in unique_entities.iterrows():
name = row["name"]
v_idx = g.vs.find(name=name).index
g.vs[v_idx]["type"] = row["type"]
g.vs[v_idx]["description"] = row["description"]

# Add edges with descriptions and weights
edges = []
edge_weights = []
edge_descriptions = []

for _, row in relationships_df.iterrows():
src = row["source"]
tgt = row["target"]

if src in entity_names and tgt in entity_names:
try:
edges.append((src, tgt))
edge_weights.append(row["weight"])
edge_descriptions.append(row["description"])
except:
continue # Skip if nodes not in graph

# Convert names to indices
indexed_edges = [(g.vs.find(name=src).index, g.vs.find(name=tgt).index) for src, tgt in edges]

# Add edges and attributes
g.add_edges(indexed_edges)
g.es["weight"] = edge_weights
g.es["description"] = edge_descriptions

print(f"Graph built with {g.vcount()} nodes and {g.ecount()} edges.")

What does it look like?

This is the visualization that shows all the relationships and connected nodes of Alex Mercer. You can access the visualization code from the notebook.)

Press enter or click to view image in full size

Yes, there are so many connections. So, let’s visualize only the relationships between people.

Press enter or click to view image in full size

These are the top 30 entities:

Press enter or click to view image in full size

✅ 3.1.4 Knowledge Graph → Graph Communities

We already have:

  • Graph g with entity names, types, and descriptions as node attributes
  • Weighted edges with relationship descriptions

Now we’ll apply the Leiden algorithm to partition this graph into communities of tightly connected entities with the code below. If you want to learn more about the Leiden Algorithm, please take a look at my post.

import leidenalg

# Apply Leiden algorithm with modularity-based partitioning
partition = leidenalg.find_partition(
g,
leidenalg.ModularityVertexPartition,
weights=g.es["weight"]
)

# Inspect the result
print(f"Detected {len(partition)} communities.")

# Loop through the first 3 communities and show members
for i in range(3):
community = partition[i] # each community is a list of vertex indices
node_names = [g.vs[idx]["name"] for idx in community]
print(f"\nCommunity {i + 1} ({len(community)} nodes):")
print(node_names)

This code gives me:

Detected 8 communities.

Community 1 (41 nodes):
['ALEX MERCER', 'TAYLOR CRUZ', 'JORDAN HAYES', 'SAM RIVERA', 'PARANORMAL MILITARY SQUAD', 'OPERATION: DULCE BRIEFING ROOM', 'DULCE BASE', 'AGENTS', 'DULCE MILITARY BASE', 'CENTRAL COMMUNICATIONS HUB', 'DISTRESS SIGNAL', 'BLACKOUT', 'PROJECTOR', 'DULCE MILITARY COMPLEX', 'ALIEN MESSAGE', 'COMMAND CENTER', 'GALACTIC SIGNAL', 'ALIEN SIGNAL', 'COSMIC SIGNAL', 'DR. HAYES', 'EXTRATERRESTRIAL INTELLIGENCE', 'HUMANITY', 'ELECTRONIC SYMPHONY', 'DULCE TEAM', 'UNDERGROUND COMMAND CENTER', 'COSMIC CONCERTO', 'PARANORMAL MILITARY SQUAD TEAM', 'TOWER OF BABEL', 'ROSSETTA STONE', 'ROSETTA STONE', 'ALIEN INTELLIGENCE', 'SIGNAL', 'CELESTIAL CONGRESS', 'COSMIC THRESHOLD', 'CENTRAL TERMINAL', 'VIEWING MONITORS', 'CENTRAL COMMS ARRAY', 'COSMIC RELATIONSHIP', 'COSMIC SIGNALS', "DULCE BASE'S SUBTERRANEAN COMMAND CENTER", 'INTERSTELLAR CONTINUUM']

Community 2 (27 nodes):
['OPERATION: DULCE', 'JORDAN', 'ALEX', 'DULCE', 'AGENT HAYES', 'CRUZ', 'FACILITY', 'TAYLOR', 'MISSION', 'EQUIPMENT', 'AGENT CRUZ', 'ELEVATOR', 'SAM', 'RIVERA', 'DRONE', 'MAINFRAME ROOM', 'TEAM', 'MERCER', 'SUBTERRANEAN SILENCE OF DULCE', 'COMMUNICATION HUB', 'AUTOMATED DISTRESS MARKER', 'WASHINGTON', 'NEW MEXICO', 'LINGUISTIC LOCK', "DULCE'S MAINFRAME CHAMBER", 'HISTORICAL SIGNIFICANCE', 'CULTURE']

Then, I will continue coding with the output dataset given on the GitHub page. (https://github.com/microsoft/graphrag/blob/6d21ef268377e319a165ca2250bd6841737df1ad/docs/examples_notebooks/inputs/operation dulce/create_final_community_reports.parquet)

Press enter or click to view image in full size

✅ 3.1.5 Graph Communities → Community Summaries

This part is inspiring. In the article, they mentioned about 2 strategies: Leaf-level communities and higher-level communities.

Get Pelin Balci’s stories in your inbox

Join Medium for free to get updates from this writer.

Higher-level communities

  • Instead of showing all raw entities/edges again, GraphRAG uses existing summaries from sub-communities
  • It merges those summaries into a broader one

But I will deep dive into Leaf-level communities here. Let’s pick community 1, index 0.

# Choose the community to extract (Community 1 = index 0)
community_index = 0
community_node_ids = partition[community_index]
community_names = [g.vs[idx]["name"] for idx in community_node_ids]

# Extract entities with metadata
community_entities = entities_df[entities_df["name"].isin(community_names)][["name", "type", "description"]].drop_duplicates().to_dict(orient="records")

# Extract internal relationships (both ends in community)
community_relationships = relationships_df[
relationships_df["source"].isin(community_names) &
relationships_df["target"].isin(community_names)
][["source", "target", "description", "weight"]].drop_duplicates().to_dict(orient="records")

With the code above, we get all the information from entities_df and relationships_df. Sort the relationships to get the important ones first.

# STEP 2: Compute degree-based importance
# Get degree of each node in the entire graph
node_degrees = {v["name"]: g.degree(g.vs.find(name=v["name"])) for v in g.vs}

# Add combined degree to relationships
for r in community_relationships:
r["combined_degree"] = node_degrees.get(r["source"], 0) + node_degrees.get(r["target"], 0)

# Sort relationships by importance
sorted_relationships = sorted(community_relationships, key=lambda r: r["combined_degree"], reverse=True)

Finally, we prepare the data to put it in LLM with a prompt.

# STEP 3: Prepare final input structure
community_data = {
"community_id": community_index,
"entities": community_entities,
"relationships": sorted_relationships # now ranked!
}

# Optional: Print preview
import json
print(json.dumps(community_data, indent=2)[:2000])

# Output
{
"community_id": 0,
"entities": [
{
"name": "ALEX MERCER",
"type": "PERSON",
"description": "Alex Mercer is a commanding and strategic figure prominently involved at Dulce Base and as a leader of the Paranormal Military Squad. He plays a crucial role in overseeing operations related to communication with extraterrestrial intelligence, ensuring the team's focus and operational integrity. His responsibilities include initiating broadcasts to communicate with extraterrestrial beings, decoding alien messages, and leading the team in understanding and responding to these communications. Alex Mercer is known for his intellectual curiosity and deep involvement in the philosophical and strategic aspects of interstellar communication, viewing these interactions as a form of cosmic dialogue.\n\nAt Dulce Base, Alex Mercer collaborates with team members like Jordan Hayes, exploring secured areas and engaging in high-stakes, secretive operations. He is depicted as a thoughtful mentor, particularly to Sam Rivera, emphasizing the importance of intuition and trust beyond protocol. His leadership is characterized by a blend of determination, compliance with mission protocols, and a protective approach towards his team. Alex Mercer's role extends to analyzing cosmic signals, strategizing contact efforts, and maintaining clarity in human-alien communication. His experiences during encounters with alien signals have led to profound changes, reinforcing his role as a key decision-maker and guardian in missions that transcend traditional boundaries."
},
{
"name": "TAYLOR CRUZ",
"type": "PERSON",
"description": "Taylor Cruz is a central and commanding figure within the Paranormal Military Squad and at Dulce Base, known for
......

The prompt will give us a structured output:

- TITLE: community's name that represents its key entities - title should be short but specific. When possible, include representative named entities in the title.
- SUMMARY: An executive summary of the community's overall structure, how its entities are related to each other, and significant information associated with its entities.
- IMPACT SEVERITY RATING: a float score between 0–10 that represents the severity of IMPACT posed by entities within the community. IMPACT is the scored importance of a community.
- RATING EXPLANATION: Give a single sentence explanation of the IMPACT severity rating.
- DETAILED FINDINGS: A list of 5–10 key insights about the community. Each insight should have a short summary followed by multiple paragraphs of explanatory text grounded according to the grounding rules below. Be comprehensive.


The output format will be:
{{
"title": <report_title>,
"summary": <executive_summary>,
"rating": <impact_severity_rating>,
"rating_explanation": <rating_explanation>,
"findings": [
{{
"summary":<insight_1_summary>,
"explanation": <insight_1_explanation>
}},
{{
"summary":<insight_2_summary>,
"explanation": <insight_2_explanation>
}}
]
}}

I am not sure how many tokens I will spend on these operations. So I will skip the generation part and use the final data.

Press enter or click to view image in full size

✅ 3.1.6 Community Summaries → Community Answers → Global Answer

Let’s move on to the final step. At query time, GraphRAG:

  • Loads all community summaries
  • Splits them into chunks (if needed)
  • Runs a “map” step to get partial answers from each summary
  • Scores each answer’s helpfulness (0–100)
  • Keeps top-scoring answers
  • Runs a “reduce” step: merges them into one final global answer

For this part, there are example notebooks:

https://github.com/microsoft/graphrag/blob/6d21ef268377e319a165ca2250bd6841737df1ad/docs/examples_notebooks/local_search.ipynb

But I want to write my code with ChatGPT :) Let’s use a free model. Remember, access to the Mistral model is restricted — you must request and be granted access. Authentication is required — your Python script must be logged in to Hugging Face.

!pip install transformers accelerate -q
# Paste your Hugging Face token here (get it from https://huggingface.co/settings/tokens)

from huggingface_hub import login
login()
# -----------------------------------------------
# STEP 1: Load Mistral (Transformer-based LLM)
# -----------------------------------------------

from transformers import AutoModelForCausalLM, AutoTokenizer
import torch

# Load model and tokenizer
model_id = "mistralai/Mistral-7B-Instruct-v0.1"

tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(
model_id,
torch_dtype=torch.float16,
device_map="auto" # This works with GPU (or CPU if needed, slower)
)

def ask_mistral(prompt, max_tokens=512):
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
outputs = model.generate(**inputs, max_new_tokens=max_tokens, do_sample=False)
return tokenizer.decode(outputs[0], skip_special_tokens=True

There are 2 steps after that. Map and Reduce.

# -----------------------------------------------
# STEP 2: Define Map Step
# -----------------------------------------------
import re

def extract_answer_and_score(response):
answer_match = re.search(r"<ANSWER>(.*?)</ANSWER>", response, re.DOTALL)
score_match = re.search(r"<SCORE>(\d+)", response)
answer = answer_match.group(1).strip() if answer_match else "No answer found."
score = int(score_match.group(1)) if score_match else 0
return answer, score

def map_step_answer(user_query, community_json, model_fn):
prompt = f"""
You are an assistant helping answer the following question:

QUESTION:
"{user_query}"

You are provided a summary report of a community.

TASK:
- Use only this summary to answer the question as clearly and informatively as possible.
- If the summary is unrelated, respond: "This summary does not address the question."
- Provide a score between 0 and 100 for how helpful this answer is in answering the question.

# COMMUNITY REPORT:
{community_json}

# RESPONSE FORMAT:
<ANSWER>
...your answer here...
</ANSWER>

<SCORE>
...number between 0 and 100...
</SCORE>
"""
raw_output = model_fn(prompt)
return extract_answer_and_score(raw_output)

What’s Happening in map_step_answer:

For each user query:

  • Loop over each community summary (one row in com_reports_df)
  • Send one prompt per summary, like: “Here’s a 3000-token report. Can you answer this question with it?”

Get back:

  • Partial answer
  • Helpfulness score (0–100)

💥 So if you have 100 communities, you send 100 prompts — this is the Map phase in Map-Reduce.

Why GraphRAG Does It This Way

  • Traditional RAG just retrieves top 3 chunks via embedding similarity (often shallow)
  • GraphRAG says: “Let’s try to answer from every meaningful group, then combine”. This gives much better global answers, especially for synthesis, explanation, and structured questions.
# -----------------------------------------------
# STEP 3: Define Reduce Step
# -----------------------------------------------
def reduce_step_generate_global_answer(user_query, top_community_answers, model_fn):
report_section = "\n\n".join(
f"### Partial Answer {i+1} (Score: {score})\n{answer}"
for i, (answer, score) in enumerate(top_community_answers)
)

prompt = f"""
You are a helpful assistant responding to a global query by synthesizing multiple perspectives.

# QUESTION:
{user_query}

# GOAL:
Generate a single, comprehensive, markdown-formatted answer that integrates all key points from the analyst reports below.

If the reports don’t answer the question, just say so. Otherwise, merge insights, eliminate redundancy, and present a unified view.

# ANALYST REPORTS:
{report_section}

# FORMAT:
- Use markdown with sections and clear structure
- Write fluently and formally, but stay concise
- Preserve modal verbs and data references if present

# RESPONSE:
"""
return model_fn(prompt, max_tokens=1024)
# -----------------------------------------------
# STEP 4: Run the Full Map + Reduce Pipeline
# -----------------------------------------------
def graphrag_query(user_query, com_reports_df, model_fn, top_k=5):
partial_answers = []

for idx, row in com_reports_df.iterrows():
community_id = row.get("id", idx)
content = row["full_content_json"] if "full_content_json" in row else row["summary"]
answer, score = map_step_answer(user_query, content, model_fn)
if score > 0:
partial_answers.append((answer, score))

# Sort by score and keep top N
top_answers = sorted(partial_answers, key=lambda x: x[1], reverse=True)[:top_k]

# Reduce step: merge into one final answer
global_answer = reduce_step_generate_global_answer(user_query, top_answers, model_fn)

return global_answer

Trial time!

user_query = "What are the key responsibilities and risks faced by the Paranormal Military Squad at Dulce Base?"
final_answer = graphrag_query(user_query, com_reports_df, ask_mistral)
print(final_answer)

The output is so detailed:

# RESPONSE:

# Introduction
The Paranormal Military Squad (PMS) at Dulce Base is a highly specialized unit tasked with investigating and mitigating paranormal threats. Their primary responsibilities include conducting research, gathering intelligence, and coordinating with other military and government agencies. However, their work also exposes them to significant risks and challenges.

# Key Responsibilities

## Research and Analysis
The PMS is responsible for conducting extensive research and analysis on paranormal phenomena, including UFO sightings, ghostly encounters, and other unexplained events. They use a variety of methods, including field investigations, interviews, and document analysis, to gather data and develop theories about these phenomena.

## Intelligence Gathering
In addition to their research, the PMS is also responsible for gathering intelligence on potential paranormal threats. This includes monitoring social media, satellite imagery, and other sources of information to identify potential threats and track their movements.

## Coordination and Collaboration
The PMS works closely with other military and government agencies to coordinate their efforts and share information. They also collaborate with scientists, researchers, and other experts in the field to develop new technologies and strategies for combating paranormal threats.

# Key Risks

## Physical Threats
The PMS faces a range of physical threats, including hostile aliens, rogue government agents, and other paranormal entities. They must be prepared to defend themselves and their base against these threats, using a variety of weapons and tactics.

## Psychological Threats
The work of the PMS can be mentally and emotionally taxing, as they often deal with strange and unsettling phenomena. They must be able to maintain their mental and emotional stability in the face of these challenges.

## Technological Threats
The PMS relies heavily on technology to conduct their research and gather intelligence. However, they must also be prepared to defend against technological attacks, including hacking and cyber warfare.

# Conclusion
The Paranormal Military Squad at Dulce Base faces a range of responsibilities and risks in their work. They must be prepared to conduct

What happens?

- Mapped that question across 8 community summaries

  • You didn’t just do top-k retrieval

- You checked every meaningful group to see if it helped answer the question

- Reduced the best answers into one coherent markdown-style report

- With sections, sub-points, risks, and responsibilities

- Output reads like a professional analyst briefing

Why It’s Impressive

  • Not keyword-based: this wasn’t just a TF-IDF match
  • Not narrow: you didn’t rely on a few top chunks
  • Context-aware: responsibilities, tech threats, mental strain — it’s nuanced

Most traditional RAG systems cannot synthesize like this unless the answer exists almost verbatim in a chunk. This is abstractive + compositional reasoning.

Final Thoughts

I’m glad I took the time to read this paper and understand its details. As mentioned on the GitHub page, the entire architecture relies heavily on prompt-based interactions.

It points to the power of prompt augmentation when performing discovery on private datasets.

I’m also happy to have learned how to convert a PDF into a structured format. I hope you’ll explore the article in detail and find helpful insights for your own projects. :)

Happy learning! 🌷

References

What is a knowledge graph? https://neo4j.com/blog/genai/what-is-knowledge-graph/ [1]

From Local to Global: A GraphRAG Approach to Query-Focused Summarization: https://arxiv.org/pdf/2404.16130 [2]

https://www.microsoft.com/en-us/research/blog/graphrag-unlocking-llm-discovery-on-narrative-private-data/ [3]

From Louvain to Leiden: guaranteeing well-connected communities: https://arxiv.org/pdf/1810.08473

Graph Search Algorithms: https://neo4j.com/blog/graph-data-science/graph-search-algorithm-basics/?utm_source=chatgpt.com

Leiden Algorithm: https://medium.com/@balci.pelin/understanding-the-leiden-algorithm-0b9fc95b277d