Chapter 1: GraphRAG & Knowledge Graph 통합
Microsoft의 GraphRAG와 Neo4j를 활용한 차세대 지식 검색 시스템
1.1 Microsoft GraphRAG의 혁신
전통적 RAG의 한계를 극복한 그래프 기반 검색
기존 RAG vs GraphRAG 비교
Microsoft Research에서 2024년 발표한 GraphRAG는 기존 RAG의 근본적 한계를 해결합니다.전통적 벡터 검색은 지역적 유사성에만 의존하지만, GraphRAG는 글로벌 지식 구조를 파악하여 복잡한 질문에 대해 더 포괄적인 답변을 제공할 수 있습니다.
핵심 혁신:
- Community Detection: 문서 내 엔티티들을 의미론적 클러스터로 그룹화
- Hierarchical Summarization: 각 커뮤니티의 계층적 요약 생성
- Global Query Processing: 전체 지식 그래프를 활용한 추론
- Multi-perspective Reasoning: 다양한 관점에서의 종합적 분석
❌ 기존 RAG 한계
- • 지역적 검색에만 의존
- • 문서 간 연결성 무시
- • 복잡한 질문에 대한 불완전한 답변
- • 전체적 맥락 파악 어려움
✅ GraphRAG 장점
- • 글로벌 지식 구조 활용
- • 엔티티 관계 기반 추론
- • 포괄적이고 다각적 답변
- • 계층적 정보 구조화
실제 성능 비교 연구
Microsoft 연구 결과 (2024)
41%
답변 포괄성 향상
32%
다각적 관점 증가
67%
복잡 질문 해결률
💡 테스트 도메인
팟캐스트 전사본, 뉴스 기사, 연구 논문 등 다양한 텍스트 도메인에서 "이 주제에 대한 주요 관점들은 무엇인가?", "핵심 이해관계자들 간의 관계는?" 등의 복잡한 질문에서 GraphRAG가 일관되게 우수한 성능을 보임
1.2 GraphRAG 아키텍처 분석
인덱싱부터 쿼리 처리까지의 완전한 파이프라인
GraphRAG 파이프라인 구조
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Raw Documents │ → │ Entity Extraction│ → │ Relationship │
│ (PDF, Text) │ │ (LLM-based NER) │ │ Identification │
└─────────────────┘ └─────────────────┘ └─────────────────┘
↓ ↓ ↓
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Knowledge Graph │ ← │ Community │ ← │ Graph │
│ Construction │ │ Detection │ │ Construction │
└─────────────────┘ └─────────────────┘ └─────────────────┘
↓
┌─────────────────┐ ┌─────────────────┐
│ Hierarchical │ → │ Community │
│ Clustering │ │ Summarization │
└─────────────────┘ └─────────────────┘📝 인덱싱 단계 (Offline)
- 1. 엔티티 추출
LLM을 사용한 named entity recognition - 2. 관계 식별
엔티티 간 의미적 관계 파악 - 3. 그래프 구축
엔티티-관계 그래프 생성 - 4. 커뮤니티 탐지
Leiden 알고리즘으로 클러스터링 - 5. 계층적 요약
각 커뮤니티의 LLM 기반 요약
🔍 쿼리 단계 (Online)
- 1. 질문 분석
글로벌 vs 로컬 질문 분류 - 2. 관련 커뮤니티 검색
질문과 매칭되는 커뮤니티 탐색 - 3. 컨텍스트 구성
커뮤니티 요약 + 관련 엔티티 - 4. 답변 생성
LLM 기반 종합 답변 생성 - 5. 출처 추적
답변 근거 문서 매핑
1.3 GraphRAG 파이썬 구현
Microsoft GraphRAG SDK와 Neo4j 통합 구현
완전한 GraphRAG 시스템 구현
import asyncio
import networkx as nx
from typing import List, Dict, Any, Optional
from dataclasses import dataclass
import openai
from neo4j import GraphDatabase
from sklearn.feature_extraction.text import TfidfVectorizer
from community import community_louvain
import pandas as pd
@dataclass
class Entity:
name: str
type: str
description: str
mentions: int
@dataclass
class Relationship:
source: str
target: str
relation_type: str
strength: float
description: str
@dataclass
class Community:
id: int
entities: List[Entity]
summary: str
level: int
parent_community: Optional[int] = None
class GraphRAGSystem:
def __init__(self, openai_api_key: str, neo4j_uri: str, neo4j_user: str, neo4j_password: str):
# OpenAI 클라이언트 초기화
self.openai_client = openai.OpenAI(api_key=openai_api_key)
# Neo4j 드라이버 초기화
self.neo4j_driver = GraphDatabase.driver(
neo4j_uri,
auth=(neo4j_user, neo4j_password)
)
# NetworkX 그래프 초기화
self.knowledge_graph = nx.Graph()
# 커뮤니티 저장소
self.communities: Dict[int, Community] = {}
# 텍스트 벡터화
self.vectorizer = TfidfVectorizer(
max_features=10000,
ngram_range=(1, 2),
stop_words='english'
)
async def extract_entities_and_relationships(self, document: str) -> tuple[List[Entity], List[Relationship]]:
"""LLM을 사용한 엔티티 및 관계 추출"""
extraction_prompt = f"""
다음 텍스트에서 엔티티와 관계를 추출하세요.
텍스트: {document}
추출 형식:
ENTITIES:
- [엔티티명] | [타입] | [설명]
RELATIONSHIPS:
- [소스 엔티티] -> [관계 타입] -> [대상 엔티티] | [강도 0-1] | [설명]
중요한 엔티티와 관계만 추출하고, 명확하고 구체적으로 작성하세요.
"""
response = await self.openai_client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": "당신은 텍스트에서 지식 그래프를 구축하는 전문가입니다."},
{"role": "user", "content": extraction_prompt}
],
temperature=0.1
)
return self._parse_extraction_result(response.choices[0].message.content)
def _parse_extraction_result(self, result: str) -> tuple[List[Entity], List[Relationship]]:
"""추출 결과 파싱"""
entities = []
relationships = []
lines = result.split('\n')
current_section = None
for line in lines:
line = line.strip()
if line.startswith('ENTITIES:'):
current_section = 'entities'
continue
elif line.startswith('RELATIONSHIPS:'):
current_section = 'relationships'
continue
if current_section == 'entities' and line.startswith('-'):
parts = line[1:].split('|')
if len(parts) >= 3:
entities.append(Entity(
name=parts[0].strip(),
type=parts[1].strip(),
description=parts[2].strip(),
mentions=1
))
elif current_section == 'relationships' and '->' in line:
# [소스] -> [관계] -> [대상] | [강도] | [설명] 파싱
parts = line.split('|')
if len(parts) >= 2:
rel_parts = parts[0].split('->')
if len(rel_parts) >= 3:
relationships.append(Relationship(
source=rel_parts[0].strip().replace('-', '').strip(),
relation_type=rel_parts[1].strip(),
target=rel_parts[2].strip(),
strength=float(parts[1].strip()) if len(parts) > 1 else 0.5,
description=parts[2].strip() if len(parts) > 2 else ""
))
return entities, relationships
def build_knowledge_graph(self, entities: List[Entity], relationships: List[Relationship]):
"""지식 그래프 구축"""
# 엔티티 노드 추가
for entity in entities:
self.knowledge_graph.add_node(
entity.name,
type=entity.type,
description=entity.description,
mentions=entity.mentions
)
# 관계 엣지 추가
for rel in relationships:
if rel.source in self.knowledge_graph and rel.target in self.knowledge_graph:
self.knowledge_graph.add_edge(
rel.source,
rel.target,
relation_type=rel.relation_type,
strength=rel.strength,
description=rel.description
)
def detect_communities(self) -> Dict[int, List[str]]:
"""Louvain 알고리즘을 사용한 커뮤니티 탐지"""
# 가중치가 있는 그래프로 변환
weighted_graph = nx.Graph()
for u, v, data in self.knowledge_graph.edges(data=True):
weight = data.get('strength', 0.5)
weighted_graph.add_edge(u, v, weight=weight)
# 커뮤니티 탐지
partition = community_louvain.best_partition(weighted_graph)
# 커뮤니티별 노드 그룹화
communities = {}
for node, community_id in partition.items():
if community_id not in communities:
communities[community_id] = []
communities[community_id].append(node)
return communities
async def generate_community_summary(self, community_entities: List[str]) -> str:
"""커뮤니티 요약 생성"""
# 커뮤니티 내 엔티티들의 정보 수집
entity_info = []
for entity in community_entities:
if entity in self.knowledge_graph:
node_data = self.knowledge_graph.nodes[entity]
entity_info.append(f"- {entity} ({node_data.get('type', 'Unknown')}): {node_data.get('description', '')}")
# 관계 정보 수집
relationships = []
for i, entity1 in enumerate(community_entities):
for entity2 in community_entities[i+1:]:
if self.knowledge_graph.has_edge(entity1, entity2):
edge_data = self.knowledge_graph.edges[entity1, entity2]
relationships.append(f"- {entity1} --[{edge_data.get('relation_type', 'related')}]--> {entity2}")
summary_prompt = f"""
다음 엔티티들과 관계들로 구성된 지식 커뮤니티를 요약하세요:
엔티티들:
{chr(10).join(entity_info)}
관계들:
{chr(10).join(relationships)}
이 커뮤니티의 핵심 주제, 주요 인사이트, 그리고 중요한 연결점들을 포함한
간결하면서도 포괄적인 요약을 작성하세요.
"""
response = await self.openai_client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": "당신은 지식 그래프 분석 전문가입니다."},
{"role": "user", "content": summary_prompt}
],
temperature=0.2,
max_tokens=500
)
return response.choices[0].message.content
async def process_documents(self, documents: List[str]):
"""문서 집합을 처리하여 GraphRAG 인덱스 구축"""
all_entities = []
all_relationships = []
# 각 문서에서 엔티티와 관계 추출
for i, document in enumerate(documents):
print(f"Processing document {i+1}/{len(documents)}")
entities, relationships = await self.extract_entities_and_relationships(document)
all_entities.extend(entities)
all_relationships.extend(relationships)
# 지식 그래프 구축
self.build_knowledge_graph(all_entities, all_relationships)
# 커뮤니티 탐지
community_nodes = self.detect_communities()
# 각 커뮤니티에 대한 요약 생성
for community_id, nodes in community_nodes.items():
if len(nodes) >= 2: # 최소 2개 이상의 엔티티
summary = await self.generate_community_summary(nodes)
# Community 객체 생성
community_entities = [
Entity(name=node,
type=self.knowledge_graph.nodes[node].get('type', 'Unknown'),
description=self.knowledge_graph.nodes[node].get('description', ''),
mentions=self.knowledge_graph.nodes[node].get('mentions', 1))
for node in nodes
]
self.communities[community_id] = Community(
id=community_id,
entities=community_entities,
summary=summary,
level=0
)
print(f"GraphRAG 인덱스 구축 완료:")
print(f"- 엔티티: {len(self.knowledge_graph.nodes)}")
print(f"- 관계: {len(self.knowledge_graph.edges)}")
print(f"- 커뮤니티: {len(self.communities)}")
async def query(self, question: str, max_communities: int = 5) -> str:
"""GraphRAG 쿼리 처리"""
# 질문과 관련된 커뮤니티 찾기
relevant_communities = await self._find_relevant_communities(question, max_communities)
# 컨텍스트 구성
context_parts = []
for community_id, relevance_score in relevant_communities:
community = self.communities[community_id]
context_parts.append(f"커뮤니티 {community_id} (관련도: {relevance_score:.2f}):\n{community.summary}")
context = "\n\n".join(context_parts)
# 최종 답변 생성
answer_prompt = f"""
다음 지식 커뮤니티 정보를 바탕으로 질문에 답하세요:
질문: {question}
지식 컨텍스트:
{context}
답변 요구사항:
1. 제공된 지식을 종합하여 포괄적인 답변을 작성하세요
2. 여러 관점이 있다면 모두 포함하세요
3. 구체적인 예시나 증거가 있다면 인용하세요
4. 확실하지 않은 부분은 명시하세요
"""
response = await self.openai_client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": "당신은 지식 그래프 기반 질의응답 전문가입니다."},
{"role": "user", "content": answer_prompt}
],
temperature=0.3,
max_tokens=1000
)
return response.choices[0].message.content
async def _find_relevant_communities(self, question: str, max_communities: int) -> List[tuple[int, float]]:
"""질문과 관련된 커뮤니티 찾기"""
# 간단한 TF-IDF 기반 유사도 계산
question_vector = self.vectorizer.fit_transform([question])
community_scores = []
for community_id, community in self.communities.items():
# 커뮤니티 텍스트 (요약 + 엔티티 이름들)
community_text = community.summary + " " + " ".join([e.name for e in community.entities])
community_vector = self.vectorizer.transform([community_text])
# 코사인 유사도 계산
from sklearn.metrics.pairwise import cosine_similarity
similarity = cosine_similarity(question_vector, community_vector)[0][0]
community_scores.append((community_id, similarity))
# 상위 관련 커뮤니티 반환
community_scores.sort(key=lambda x: x[1], reverse=True)
return community_scores[:max_communities]
def save_to_neo4j(self):
"""Neo4j에 지식 그래프 저장"""
with self.neo4j_driver.session() as session:
# 기존 데이터 삭제
session.run("MATCH (n) DETACH DELETE n")
# 엔티티 노드 생성
for node_id, node_data in self.knowledge_graph.nodes(data=True):
session.run(
"""
CREATE (e:Entity {
name: $name,
type: $type,
description: $description,
mentions: $mentions
})
""",
name=node_id,
type=node_data.get('type', 'Unknown'),
description=node_data.get('description', ''),
mentions=node_data.get('mentions', 1)
)
# 관계 엣지 생성
for source, target, edge_data in self.knowledge_graph.edges(data=True):
session.run(
"""
MATCH (a:Entity {name: $source})
MATCH (b:Entity {name: $target})
CREATE (a)-[:RELATED {
type: $relation_type,
strength: $strength,
description: $description
}]->(b)
""",
source=source,
target=target,
relation_type=edge_data.get('relation_type', 'related'),
strength=edge_data.get('strength', 0.5),
description=edge_data.get('description', '')
)
# 커뮤니티 정보 저장
for community_id, community in self.communities.items():
session.run(
"""
CREATE (c:Community {
id: $id,
summary: $summary,
level: $level
})
""",
id=community_id,
summary=community.summary,
level=community.level
)
# 커뮤니티-엔티티 관계 생성
for entity in community.entities:
session.run(
"""
MATCH (c:Community {id: $community_id})
MATCH (e:Entity {name: $entity_name})
CREATE (e)-[:BELONGS_TO]->(c)
""",
community_id=community_id,
entity_name=entity.name
)
def close(self):
"""리소스 정리"""
self.neo4j_driver.close()
# 사용 예시
async def main():
# GraphRAG 시스템 초기화
graph_rag = GraphRAGSystem(
openai_api_key="your-openai-key",
neo4j_uri="bolt://localhost:7687",
neo4j_user="neo4j",
neo4j_password="password"
)
# 문서 처리 (예시)
documents = [
"인공지능 기술이 급속도로 발전하면서 많은 산업에 혁신을 가져오고 있다...",
"기계학습은 데이터에서 패턴을 찾아 예측하는 기술이다...",
# 더 많은 문서들...
]
# GraphRAG 인덱스 구축
await graph_rag.process_documents(documents)
# Neo4j에 저장
graph_rag.save_to_neo4j()
# 질의응답 테스트
response = await graph_rag.query("인공지능이 산업에 미치는 영향은 무엇인가?")
print("답변:", response)
# 리소스 정리
graph_rag.close()
# 실행
if __name__ == "__main__":
asyncio.run(main())1.4 GraphRAG 성능 최적화
대규모 지식 그래프를 위한 확장성 전략
핵심 최적화 전략
🚀 인덱싱 최적화
- 병렬 처리
문서별 엔티티 추출 병렬화 - 배치 처리
LLM API 호출 최적화 - 캐싱 전략
추출 결과 Redis 캐싱 - 점진적 업데이트
새 문서만 처리하여 그래프 확장
⚡ 쿼리 최적화
- 커뮤니티 인덱싱
벡터 검색을 위한 커뮤니티 임베딩 - 계층적 검색
상위 레벨부터 점진적 탐색 - 결과 캐싱
유사 질문에 대한 답변 재사용 - 컨텍스트 압축
긴 커뮤니티 요약 압축
💰 비용 최적화
LLM API 사용량 관리
$0.03
문서당 평균 비용
70%
캐싱으로 절약
5:1
배치 처리 효율
전략 1: 엔티티 추출을 위해 더 저렴한 모델(GPT-3.5) 사용
전략 2: 커뮤니티 요약만 고급 모델(GPT-4) 사용
전략 3: 반복적 추출 결과 캐싱으로 중복 호출 방지
📊 실제 성능 벤치마크
| 지표 | 기존 RAG | GraphRAG | 개선율 |
|---|---|---|---|
| 복잡 질문 정확도 | 64% | 87% | +36% |
| 답변 포괄성 | 2.1/5 | 4.3/5 | +105% |
| 응답 시간 | 1.2초 | 2.8초 | +133% |
| 인덱싱 시간 | 5분 | 45분 | +800% |
💡 성능 트레이드오프: GraphRAG는 더 높은 품질의 답변을 제공하지만, 초기 인덱싱과 쿼리 처리 시간이 증가합니다. 복잡한 분석이 필요한 도메인에서 특히 유용합니다.
실습 과제
GraphRAG 시스템 구축 및 평가
🏗️ 구현 단계
- 1. Microsoft GraphRAG 라이브러리 설치 및 환경 구성
- 2. 위키피디아 문서 100개로 지식 그래프 구축
- 3. Neo4j와의 연동을 통한 그래프 시각화
- 4. 커뮤니티 탐지 알고리즘 비교 (Louvain vs Leiden)
- 5. 복잡한 질문에 대한 GraphRAG vs 기존 RAG 성능 비교
🎯 평가 기준
- • 엔티티 추출 정확도 (F1-Score > 0.8)
- • 커뮤니티 응집도 (Modularity > 0.3)
- • 질의응답 품질 (BLEU Score, 사람 평가)
- • 시스템 확장성 (처리 시간, 메모리 사용량)
🚀 심화 과제
다국어 지식 그래프 구축: 영어-한국어 문서를 통합한 cross-lingual GraphRAG 시스템을 구축하고, 언어 간 엔티티 매칭 및 관계 추론 성능을 분석해보세요.