GraphRAG: Microsoft의 글로벌-로컬 이중 검색 전략

GraphRAG: Microsoft의 글로벌-로컬 이중 검색 전략
왜 기존 RAG는 "이 문서들의 주요 테마가 뭐야?"라는 질문에 답하지 못할까? Microsoft Research의 GraphRAG가 제시하는 커뮤니티 기반 검색의 비밀.
들어가며: 기존 RAG의 치명적 맹점
기존 RAG 시스템에게 이런 질문을 해보세요:
"이 1000개 문서에서 드러나는 주요 트렌드와 패턴은 무엇인가요?"
결과는? 실패. 혹은 아무 의미 없는 단편적 답변.
왜 실패하는가?
기존 RAG의 작동 방식을 떠올려보세요:
- 질문을 임베딩으로 변환
- 가장 유사한 청크 K개 검색
- 검색된 청크로 답변 생성
문제는 "유사한 청크"가 "전체를 대표하는 청크"가 아니라는 것입니다.
비유: 숲을 보려는데 가장 가까운 나무 3그루만 보여주는 격입니다.
GraphRAG란 무엇인가?
GraphRAG는 Microsoft Research가 2024년 4월에 발표한 새로운 RAG 패러다임입니다.
핵심 아이디어는 단순합니다:
"질문 시점이 아닌, 인덱싱 시점에 요약을 미리 만들어두자"
하지만 단순 요약이 아닙니다. 커뮤니티 기반 계층적 요약입니다.
4단계 파이프라인
Documents → Entity Extraction → Graph Construction → Community Detection → Hierarchical Summarization- Entity Extraction: 문서에서 엔티티와 관계 추출
- Graph Construction: 엔티티를 노드, 관계를 엣지로 하는 그래프 구축
- Community Detection: Leiden 알고리즘으로 밀접하게 연결된 엔티티 그룹화
- Hierarchical Summarization: 각 커뮤니티에 대해 미리 요약 생성
이제 "전체 테마가 뭐야?"라는 질문이 오면, 모든 커뮤니티 요약을 조합하여 답변합니다.
Local vs Global 검색
GraphRAG는 두 가지 검색 모드를 제공합니다.
Local Search
용도: 특정 엔티티에 대한 질문
예시: "알파테크는 어떤 회사와 협력하고 있나요?"
작동 방식:
- 쿼리에서 "알파테크" 엔티티 추출
- 그래프에서 알파테크의 이웃 노드 탐색
- 1-hop, 2-hop 관계 정보 수집
- 관련 정보로 답변 생성
Global Search
용도: 전체 데이터셋에 대한 질문
예시: "이 문서들의 주요 테마와 트렌드는?"
작동 방식:
- 모든 커뮤니티 요약 수집
- 각 요약에서 관련 정보 추출
- 부분 답변들을 종합하여 최종 답변 생성
이것이 기존 RAG가 불가능했던 "숲을 보는" 능력입니다.
실습 환경 설정
필수 패키지 설치
# Microsoft GraphRAG 공식 라이브러리
pip install graphrag
# 추가 의존성
pip install networkx matplotlib pandas numpy
pip install tiktoken openai python-dotenvPython 버전 요구사항
GraphRAG는 Python 3.10~3.12를 지원합니다.
Step 1: Entity Extraction (엔티티 추출)
GraphRAG의 첫 단계는 문서에서 엔티티와 관계를 추출하는 것입니다.
실제 GraphRAG는 LLM을 사용하지만, 핵심 로직을 이해하기 위해 직접 구현해봅시다.
샘플 데이터 준비
엔터프라이즈 시나리오를 시뮬레이션하기 위해 뉴스와 기술 문서를 혼합한 데이터를 사용합니다.
SAMPLE_DOCUMENTS = [
{
"id": "news_1",
"type": "news",
"title": "AI 스타트업 알파테크, 시리즈 B 투자 유치",
"content": """
AI 스타트업 알파테크가 VC인 블루벤처스로부터 500억원 규모의 시리즈 B 투자를 유치했다.
알파테크의 CEO 김철수는 "이번 투자로 RAG 기술 고도화에 집중하겠다"고 밝혔다.
알파테크는 삼성전자, LG전자와 협력하여 엔터프라이즈 AI 솔루션을 제공하고 있다.
"""
},
{
"id": "news_2",
"type": "news",
"title": "삼성전자, AI 반도체 신제품 발표",
"content": """
삼성전자가 차세대 AI 반도체 'Exynos AI'를 발표했다.
이 칩은 알파테크의 RAG 엔진과 호환되며, 현대자동차의 자율주행 시스템에도 탑재될 예정이다.
"""
},
# ... 더 많은 문서
]엔티티 데이터 구조
from dataclasses import dataclass, field
from typing import List
@dataclass
class Entity:
"""추출된 엔티티"""
name: str
type: str # ORGANIZATION, PERSON, TECHNOLOGY, PRODUCT
description: str = ""
source_docs: List[str] = field(default_factory=list)
@dataclass
class Relationship:
"""엔티티 간 관계"""
source: str
target: str
relation_type: str # INVESTED_IN, PARTNERED_WITH, DEVELOPED
weight: float = 1.0
source_docs: List[str] = field(default_factory=list)엔티티 추출기 구현
class EntityExtractor:
"""문서에서 엔티티와 관계를 추출"""
def __init__(self, entity_definitions: dict):
self.entity_definitions = entity_definitions
def extract_entities(self, documents: List[dict]) -> List[Entity]:
"""문서에서 엔티티 추출"""
entities = {}
for doc in documents:
content = doc['content']
doc_id = doc['id']
for name, (entity_type, description) in self.entity_definitions.items():
if name in content:
if name not in entities:
entities[name] = Entity(
name=name,
type=entity_type,
description=description,
source_docs=[doc_id]
)
else:
if doc_id not in entities[name].source_docs:
entities[name].source_docs.append(doc_id)
return list(entities.values())
def extract_relationships(self, documents: List[dict], entities: List[Entity]) -> List[Relationship]:
"""같은 문장에 등장하는 엔티티 간 관계 추출"""
relationships = []
entity_names = {e.name for e in entities}
for doc in documents:
sentences = doc['content'].split('.')
for sentence in sentences:
# 문장에 등장하는 엔티티들 찾기
found = [e for e in entities if e.name in sentence]
# 같은 문장의 엔티티들 간 관계 생성
for i, e1 in enumerate(found):
for e2 in found[i+1:]:
relationships.append(Relationship(
source=e1.name,
target=e2.name,
relation_type=self._infer_relation_type(e1, e2),
source_docs=[doc['id']]
))
return self._deduplicate(relationships)실행 결과:
추출된 엔티티: 39개
추출된 관계: 35개
=== 엔티티 유형별 분포 ===
ORGANIZATION: 15개
PERSON: 6개
TECHNOLOGY: 7개
PRODUCT: 5개
TOOL: 5개Step 2: Graph Construction (그래프 구축)
추출한 엔티티와 관계를 NetworkX 그래프로 구축합니다.
import networkx as nx
class KnowledgeGraph:
"""GraphRAG용 Knowledge Graph"""
def __init__(self):
self.graph = nx.Graph() # 무방향 (커뮤니티 탐지용)
self.directed_graph = nx.DiGraph() # 방향 (쿼리용)
self.entities = {}
def add_entities(self, entities: List[Entity]):
for entity in entities:
self.entities[entity.name] = entity
self.graph.add_node(
entity.name,
type=entity.type,
description=entity.description
)
def add_relationships(self, relationships: List[Relationship]):
for rel in relationships:
self.graph.add_edge(
rel.source, rel.target,
relation=rel.relation_type,
weight=rel.weight
)허브 노드 분석
연결이 많은 엔티티(허브 노드)를 찾으면 데이터셋의 핵심 주제를 파악할 수 있습니다.
degree_centrality = nx.degree_centrality(kg.graph)
top_hubs = sorted(degree_centrality.items(), key=lambda x: x[1], reverse=True)[:5]
print("=== 허브 노드 (연결이 많은 엔티티) ===")
for node, centrality in top_hubs:
print(f"{node}: {kg.graph.degree(node)}개 연결")=== 허브 노드 ===
알파테크: 15개 연결
삼성전자: 9개 연결
RAG: 6개 연결
LG전자: 6개 연결
현대자동차: 5개 연결Step 3: Community Detection (커뮤니티 탐지)
GraphRAG의 핵심 비밀: Leiden 알고리즘으로 밀접하게 연결된 엔티티들을 그룹화합니다.
왜 커뮤니티가 중요한가?
커뮤니티는 의미적으로 관련된 엔티티들의 집합입니다. 각 커뮤니티는 하나의 "주제" 또는 "테마"를 대표합니다.
예를 들어:
- Community 0: AI 스타트업 생태계 (알파테크, 블루벤처스, 투자자들)
- Community 1: 자율주행/반도체 (삼성전자, 현대자동차, NVIDIA)
- Community 2: 스마트홈 AI (LG전자, OpenAI, 아마존)
구현
from networkx.algorithms import community
class CommunityDetector:
"""커뮤니티 탐지 및 계층 구조 생성"""
def __init__(self, graph: nx.Graph):
self.graph = graph
self.communities = []
self.node_to_community = {}
def detect_communities(self, resolution: float = 1.0) -> List[set]:
"""
Louvain 알고리즘으로 커뮤니티 탐지
(Leiden 알고리즘의 간소화 버전)
"""
communities = community.louvain_communities(
self.graph,
resolution=resolution,
seed=42
)
self.communities = [set(c) for c in communities]
# 노드 → 커뮤니티 매핑
for i, comm in enumerate(self.communities):
for node in comm:
self.node_to_community[node] = i
return self.communities실행 결과:
=== 탐지된 커뮤니티 ===
Community 0 (11 members):
주요 멤버: 알파테크, 블루벤처스, 김철수, 이영희, 박지민
추정 주제: AI 스타트업 및 투자 생태계
Community 1 (8 members):
주요 멤버: 삼성전자, 현대자동차, NVIDIA, 테슬라, 웨이모
추정 주제: 자율주행 및 AI 하드웨어
Community 2 (9 members):
주요 멤버: LG전자, OpenAI, 구글, 아마존, 한미래
추정 주제: 스마트홈 및 AI 어시스턴트
Community 3 (6 members):
주요 멤버: RAG, Knowledge Graph, Vector Store, Embedding
추정 주제: RAG 및 검색 기술
Community 4 (5 members):
주요 멤버: LLM, 양자화, TensorRT, vLLM
추정 주제: LLM 최적화 및 추론Step 4: Hierarchical Summarization (계층적 요약)
각 커뮤니티에 대해 요약을 미리 생성합니다.
이것이 GraphRAG의 핵심 비밀: 질문 시점이 아닌, 인덱싱 시점에 요약을 생성합니다.
class CommunitySummarizer:
"""커뮤니티별 요약 생성"""
def __init__(self, graph: nx.Graph, communities: List[set]):
self.graph = graph
self.communities = communities
self.summaries = {}
def generate_summary(self, community_idx: int) -> str:
"""커뮤니티 요약 생성 (실제로는 LLM 사용)"""
members = list(self.communities[community_idx])
subgraph = self.graph.subgraph(members)
# 엔티티 정보 수집
entities_info = []
for node in members[:5]:
node_data = self.graph.nodes[node]
entities_info.append({
'name': node,
'type': node_data.get('type'),
'description': node_data.get('description')
})
# 관계 정보 수집
relations_info = []
for u, v, data in subgraph.edges(data=True):
relations_info.append({
'source': u,
'target': v,
'type': data.get('relation')
})
# 템플릿 기반 요약 생성
summary = f"""이 커뮤니티는 주로 {self._get_main_types(members)} 엔티티로 구성됩니다.
주요 엔티티:
"""
for e in entities_info:
summary += f"- {e['name']} ({e['type']}): {e['description']}\n"
summary += "\n핵심 관계:\n"
for r in relations_info[:5]:
summary += f"- {r['source']} --{r['type']}--> {r['target']}\n"
return summary요약 예시
=== Community 0 Summary ===
이 커뮤니티는 주로 조직, 인물 엔티티로 구성됩니다.
주요 엔티티:
- 알파테크 (ORGANIZATION): AI 스타트업, RAG 기술 전문
- 블루벤처스 (ORGANIZATION): 벤처캐피탈
- 김철수 (PERSON): 알파테크 CEO
- 이영희 (PERSON): 알파테크 CTO, 스탠포드 출신
- 박지민 (PERSON): 블루벤처스 파트너
핵심 관계:
- 알파테크 --PARTNERED_WITH--> 블루벤처스
- 알파테크 --EMPLOYS--> 김철수
- 알파테크 --EMPLOYS--> 이영희GraphRAG Query Engine 구현
이제 Local 검색과 Global 검색을 모두 지원하는 쿼리 엔진을 구현합니다.
class GraphRAGQueryEngine:
"""
GraphRAG 쿼리 엔진
- Local Search: 특정 엔티티 관련 질문
- Global Search: 전체 데이터셋 관련 질문
"""
def __init__(self, graph, communities, summaries, node_to_community):
self.graph = graph
self.communities = communities
self.summaries = summaries
self.node_to_community = node_to_community
def local_search(self, query: str, top_k: int = 5) -> dict:
"""
Local Search: 특정 엔티티에 대한 질문
"""
# 1. 쿼리에서 엔티티 찾기
found_entities = []
for entity_name in self.graph.entities.keys():
if entity_name.lower() in query.lower():
found_entities.append(entity_name)
if not found_entities:
return {'mode': 'local', 'context': "관련 엔티티를 찾지 못했습니다."}
# 2. 관련 노드 수집 (1-hop, 2-hop)
related_nodes = set()
for entity in found_entities:
neighbors = self.graph.get_neighbors(entity)
related_nodes.update(neighbors)
for neighbor in neighbors[:3]:
second_hop = self.graph.get_neighbors(neighbor)
related_nodes.update(second_hop[:2])
# 3. 컨텍스트 구성
context_parts = []
for node in list(related_nodes)[:top_k]:
node_info = self.graph.get_node_info(node)
if node_info:
context_parts.append(
f"- {node} ({node_info.get('type')}): {node_info.get('description')}"
)
return {
'mode': 'local',
'entities_found': found_entities,
'context': '\n'.join(context_parts),
'related_nodes': list(related_nodes)
}
def global_search(self, query: str) -> dict:
"""
Global Search: 전체 데이터셋에 대한 질문
"""
# 모든 커뮤니티 요약 수집
all_summaries = []
for idx, summary in self.summaries.items():
all_summaries.append(f"[Community {idx}]\n{summary}")
# 전역 컨텍스트 구성
global_context = f"""=== 데이터셋 개요 ===
총 {len(self.communities)}개의 커뮤니티, {sum(len(c) for c in self.communities)}개의 엔티티
=== 커뮤니티별 요약 ===
"""
global_context += '\n\n'.join(all_summaries)
return {
'mode': 'global',
'context': global_context
}
def search(self, query: str, mode: str = 'auto') -> dict:
"""통합 검색 인터페이스"""
if mode == 'local':
return self.local_search(query)
elif mode == 'global':
return self.global_search(query)
else:
# 자동 모드 결정
global_keywords = ['전체', '요약', '주요', '트렌드', '테마', '개요']
is_global = any(kw in query.lower() for kw in global_keywords)
if is_global:
return self.global_search(query)
else:
return self.local_search(query)테스트 결과
Query: 알파테크는 어떤 회사와 협력하고 있나요?
Mode: local
Found Entities: ['알파테크']
Context:
- 삼성전자 (ORGANIZATION): 대기업, 반도체/전자
- LG전자 (ORGANIZATION): 대기업, 전자/가전
- 블루벤처스 (ORGANIZATION): 벤처캐피탈
- 알파테크 --PARTNERED_WITH--> 삼성전자
- 알파테크 --PARTNERED_WITH--> LG전자
- 알파테크 --PARTNERED_WITH--> 블루벤처스Query: 이 데이터셋의 주요 테마와 트렌드는 무엇인가요?
Mode: global
Context:
=== 데이터셋 개요 ===
총 5개의 커뮤니티, 39개의 엔티티
=== 커뮤니티별 요약 ===
[Community 0]
AI 스타트업 및 투자 생태계...
[Community 1]
자율주행 및 AI 하드웨어...Microsoft GraphRAG 공식 라이브러리 사용법
위에서 핵심 로직을 직접 구현해봤습니다. 이제 MS 공식 라이브러리를 사용하는 방법을 알아봅시다.
CLI 사용법
# 1. 프로젝트 디렉토리 생성
mkdir -p ./my_graphrag/input
# 2. 입력 문서 저장 (.txt 파일들)
cp my_documents/*.txt ./my_graphrag/input/
# 3. 초기화
graphrag init --root ./my_graphrag
# 4. API 키 설정 (.env 파일)
echo "GRAPHRAG_API_KEY=your-openai-api-key" > ./my_graphrag/.env
# 5. 인덱싱 실행 (시간 소요)
graphrag index --root ./my_graphrag
# 6. Global 검색
graphrag query --root ./my_graphrag --method global \
--query "이 문서들의 주요 테마는?"
# 7. Local 검색
graphrag query --root ./my_graphrag --method local \
--query "알파테크에 대해 알려줘"Python API 사용법
import asyncio
from graphrag.query.indexer_adapters import (
read_indexer_entities,
read_indexer_relationships,
read_indexer_reports,
read_indexer_text_units,
)
from graphrag.query.llm.oai.chat_openai import ChatOpenAI
from graphrag.query.llm.oai.typing import OpenaiApiType
from graphrag.query.structured_search.global_search.community_context import GlobalCommunityContext
from graphrag.query.structured_search.global_search.search import GlobalSearch
# LLM 설정
llm = ChatOpenAI(
api_key="your-api-key",
model="gpt-4o-mini",
api_type=OpenaiApiType.OpenAI,
)
# 인덱스 데이터 로드
INPUT_DIR = "./my_graphrag/output/artifacts"
entities = read_indexer_entities(INPUT_DIR)
relationships = read_indexer_relationships(INPUT_DIR)
reports = read_indexer_reports(INPUT_DIR)
text_units = read_indexer_text_units(INPUT_DIR)
# Global Search 설정
context_builder = GlobalCommunityContext(
community_reports=reports,
entities=entities,
token_encoder=token_encoder,
)
global_search = GlobalSearch(
llm=llm,
context_builder=context_builder,
token_encoder=token_encoder,
)
# 쿼리 실행
result = await global_search.asearch("이 데이터셋의 주요 테마는?")
print(result.response)기존 RAG vs GraphRAG: 실제 비교
같은 질문에 대한 두 시스템의 응답을 비교해봅시다.
질문: "이 문서들의 주요 테마와 핵심 인물들은 누구인가요?"
기존 RAG 방식:
청크 1: AI 스타트업 알파테크가 VC인 블루벤처스로부터 500억원 규모...
청크 2: 삼성전자가 차세대 AI 반도체 'Exynos AI'를 발표했다...
청크 3: 현대자동차가 자율주행 레벨4 기술을 달성했다고 발표했다...
→ 문제: 개별 청크만 보여주고 "전체 테마"에 대한 답변 불가GraphRAG 방식:
=== 데이터셋 개요 ===
총 5개의 커뮤니티, 39개의 엔티티
핵심 테마:
1. AI 스타트업 생태계 (알파테크, 블루벤처스, 김철수, 이영희)
2. 자율주행/반도체 (삼성전자, 현대자동차, NVIDIA)
3. 스마트홈 AI (LG전자, OpenAI, 한미래)
4. RAG/검색 기술 (RAG, Knowledge Graph, Vector Store)
5. LLM 최적화 (LLM, 양자화, TensorRT)
→ 해결: 커뮤니티 요약을 통해 "숲"을 볼 수 있음비용과 성능 트레이드오프
GraphRAG는 강력하지만 비용이 있습니다.
인덱싱 비용
쿼리 비용
언제 GraphRAG를 사용해야 할까?
프로덕션 적용 가이드
1. 점진적 도입
모든 문서에 GraphRAG를 적용하지 마세요. 먼저:
- 가장 중요한 문서 집합 식별
- 작은 파일럿으로 시작 (100-1000개 문서)
- 비용과 품질 측정
- 점진적 확대
2. 프롬프트 튜닝
기본 프롬프트로는 충분하지 않습니다:
graphrag prompt-tune --root ./my_graphrag \
--config ./settings.yaml \
--no-entity-types도메인에 맞는 엔티티 유형과 관계 유형을 정의하세요.
3. 하이브리드 접근
실제 프로덕션에서는 하이브리드가 최선입니다:
def hybrid_search(query: str):
# 1. 질문 유형 분류
if is_global_question(query):
return graphrag.global_search(query)
elif contains_entity(query):
return graphrag.local_search(query)
else:
return traditional_rag.search(query)4. 캐싱 전략
커뮤니티 요약은 자주 변하지 않습니다. 캐싱으로 비용 절감:
# 커뮤니티 요약 캐시 (Redis 등)
community_summaries = cache.get("community_summaries")
if not community_summaries:
community_summaries = generate_all_summaries()
cache.set("community_summaries", community_summaries, ttl=3600)온톨로지 KG vs GraphRAG: 언제 무엇을?
이전 글에서 다룬 온톨로지 기반 Knowledge Graph와 GraphRAG는 다른 문제를 해결합니다.
추천 조합
- 정형 지식 + 비정형 문서: 온톨로지 KG + GraphRAG 병행
- 빠른 프로토타이핑: GraphRAG 먼저
- 높은 정확도 필요: 온톨로지 KG 필수
Summary
핵심 개념
- 문제: 기존 RAG는 "숲"을 보지 못함
- 해결: 커뮤니티 기반 계층적 요약
- Local Search: 특정 엔티티 → 이웃 탐색
- Global Search: 모든 커뮤니티 요약 → 통합 답변
구현 단계
- Entity Extraction (엔티티 추출)
- Graph Construction (그래프 구축)
- Community Detection (Leiden/Louvain)
- Hierarchical Summarization (계층적 요약)
- Query Engine (Local/Global 검색)
다음 단계
- Multi-hop QA: 다중 홉 추론 RAG 시스템
- Temporal KG: 시간 축을 가진 Knowledge Graph
- 자동 KG 구축: LLM 기반 트리플 자동 추출
References
- GraphRAG: From Local to Global - Microsoft Research 논문
- Microsoft GraphRAG GitHub - 공식 라이브러리
- GraphRAG Documentation - 공식 문서