5장. 지식 그래프를 활용한 그래프 RAG
출처: 『RAG 마스터: 랭체인으로 완성하는 LLM 서비스』(브라이스 유·조경아·박수진·김재웅, 프리렉 2025) | 공식: https://www.langchain.com/
벡터 DB가 '유사한 문서 찾기'라면, 그래프 RAG는 '연결된 사실 따라가기'다. 지식 그래프의 노드-에지 구조로 데이터 간 관계를 모델링해 기존 RAG가 놓치는 전체적 패턴과 다층적 추론을 가능하게 한다. 마이크로소프트 GraphRAG 연구를 바탕으로 Neo4j와 랭체인까지 이어지는 실전 구현을 다룬다.
실습 — 책 공식 노트북: Ch05. GraphRAG 폴더 — Neo4j 기반 4단계. 코랩 바로 열기: DB 구축·Neo4j 저장·Retriever·질의 과정.
학습 목표
이 장을 끝내면 다음을 할 수 있다.
- 기존 벡터 RAG의 한계와 그래프 RAG가 이를 극복하는 원리를 설명한다.
- 지식 그래프의 구성 요소(노드·에지·속성)와 커뮤니티 탐지 알고리즘을 구분한다.
- Microsoft GraphRAG 파이프라인(문서 분할→엔티티 추출→그래프 증강→커뮤니티 요약)을 구현한다.
- Neo4j에 그래프 데이터를 임포트하고 Cypher 쿼리로 관계를 탐색한다.
GraphCypherQAChain을 활용해 자연어 질문을 Cypher로 자동 변환하는 그래프 RAG 시스템을 구현한다.
전체 흐름도
문서에서 지식 그래프를 구축하고 그래프 RAG로 질의하는 전체 흐름.
[ 원본 문서 ]
│ 텍스트 청크 분할 (600토큰 단위, Gleaning으로 보완)
▼
[ 엔티티·관계 추출 ]
│ LLM (또는 LLMGraphTransformer) → name·type·description·관계 레이블
▼
[ Neo4j 지식 그래프 구축 ]
│ 노드(엔티티)·엣지(RELATED) 임포트, 고유 제약 조건 설정
▼
[ 그래프 증강 ]
├─ 커뮤니티 탐지 (레이든 알고리즘, Level 0~2 계층)
└─ 그래프 임베딩 (Node2vec)
▼
[ 커뮤니티 요약 ]
│ LLM → 각 커뮤니티 요약문·임베딩 생성
▼
[ 질의 처리 ]
├─ 로컬 검색: 엔티티 임베딩 유사도 → 연관 컨텍스트 수집 → 리랭킹 → 답변
└─ 글로벌 검색: 커뮤니티 리포트 맵(Map) → 점수 기반 리듀스(Reduce) → 답변
▼
[ 최종 답변 ]
│ GraphCypherQAChain: 자연어 → Cypher 자동 변환 → Neo4j 실행 → LLM 답변
▼
[ 사용자 ]
0. 사전 필수 용어
- 지식 그래프(Knowledge Graph) — 노드(개체)와 에지(관계)에 의미론적 정보를 부여해 '애플 →(개발)→ 아이폰'처럼 사실을 구조화한 그래프. 일반 그래프가 단순 연결을 표현한다면, 지식 그래프는 관계의 종류와 속성까지 명시한다.
- 노드(Node) / 에지(Edge) — 그래프의 두 기본 요소. 노드는 개체(사람·조직·제품 등), 에지는 개체 간 관계(설립·개발·CEO 등). 수학적 맥락에서는 정점(Vertex)·간선(Edge), 응용 맥락에서는 개체·관계로 부른다.
- Neo4j — 전 세계에서 가장 널리 쓰이는 그래프 DBMS. 데이터를 노드·에지 형태로 저장하고 Cypher 쿼리 언어로 탐색한다. 클라우드 버전인 Neo4j Aura는 무료 티어를 제공한다.
- Cypher — Neo4j 전용 그래프 쿼리 언어. SQL과 유사한 선언적 구조를 가지며,
MATCH (a)-[:RELATED]->(b)처럼 패턴 매칭으로 노드·관계를 직관적으로 표현한다. - 엔티티/관계 추출(Entity & Relation Extraction) — 텍스트에서 핵심 개체(PERSON·ORG·PRODUCT 등)와 그들 사이의 관계를 자동으로 식별하는 작업. 전통적으로 규칙 기반·SVM·BERT를 썼고, 최근에는 LLM이 주류다.
- LLMGraphTransformer — 랭체인에서 제공하는 컴포넌트. 일반
Document객체를 LLM에 전달해 자동으로 노드·관계를 추출하고GraphDocument형태로 반환한다. - 커뮤니티(Community) — 지식 그래프 안에서 서로 밀접하게 연결된 엔티티 그룹. 로우벤·레이든 알고리즘으로 자동 탐지하며, 커뮤니티 요약본을 인덱스로 삼아 검색 효율을 높인다.
- GraphCypherQAChain — 랭체인에서 자연어 질문 → Cypher 쿼리 자동 생성 → Neo4j 실행 → 답변 생성을 한 파이프라인으로 엮는 체인.
1. 그래프 RAG 개요
그래프 RAG는 기존 RAG의 두 가지 구조적 한계를 극복하기 위해 탄생했다. 마이크로소프트가 2024년 arXiv에 발표한 연구("From local to global: A graph rag approach to query-focused summarization")를 기반으로 한다.
1.1 기존 RAG 방식의 한계
한계 1 — 정보 연결의 어려움
기존 RAG는 질문과 유사한 개별 문서 조각을 찾아 반환한다. 문서들이 각각 독립적으로 제공되어 "데이터셋 전체에서 이 정보들이 어떻게 연결되는가"를 파악하지 못한다. 예를 들어 "이 문서의 주요 주제는 무엇인가?"처럼 전체 맥락을 아우르는 질문에 적절히 답하기 어렵다.
한계 2 — 대규모 데이터 이해의 한계
데이터가 클수록 개념들을 전체적으로 파악하기 어렵다. "지난 10년간 연간 보고서를 바탕으로 회사의 지속가능성 전략 변화를 설명해 주세요"처럼 방대한 데이터셋에 걸쳐 패턴을 추적하는 질문에는 한계가 있다.
1.2 지식 그래프란?
그래프는 '네트워크 구조를 수학적으로 표현한 모델'이다. 소셜 미디어의 친구 관계, 도시 간 교통망, 인터넷의 서버 연결처럼 우리 일상 곳곳이 그래프 구조다. 이 구조의 공통점은 개체(노드) 와 관계(에지) 로 이루어진다는 점이다.
지식 그래프는 일반 그래프에 의미론적 정보를 추가한다.
| 항목 | 일반 그래프 | 지식 그래프 |
|---|---|---|
| 표현 | 노드·에지 연결 여부만 | 개체 속성(타입·설명) + 관계 종류(의미 있는 레이블) |
| 예시 | 애플 — 아이폰 (단순 연결) | 애플 →(개발, 2007년 이후, iOS 기반)→ 아이폰 |
| 추론 | 직접 연결만 파악 | 간접 관계 추론 가능 (스티브 잡스를 경유해 애플-픽사 관계 도출) |
지식 그래프의 3가지 특징
- 복잡한 정보 구조 표현 — 개체 속성(출시일·운영체제·크기)과 관계 속성(개발 시기·조건)을 함께 저장한다.
- 추론 능력 — '스티브 잡스 → 애플 공동창업자' + '스티브 잡스 → 픽사 CEO'라는 두 사실을 그래프로 연결하면 애플-픽사의 간접 연관성을 추론할 수 있다.
- 효율적 정보 검색 — 노드에서 시작해 에지를 따라 이웃 노드(맥북·스티브 잡스·팀 쿡 등)를 빠르게 탐색한다.
1.3 그래프 RAG의 동작 과정
그래프 RAG는 두 단계로 동작한다.
① 그래프 DB 구축 (오프라인) 문서 분할 → 지식 그래프 구축(엔티티·관계 추출) → 그래프 증강(커뮤니티 탐지 + 그래프 임베딩) → 커뮤니티 요약
② 질의 처리 (온라인) 질문의 성격에 따라 두 가지 검색 방식을 선택한다.
| 검색 방식 | 적합한 질문 | 동작 |
|---|---|---|
| 로컬 검색(Local Search) | "특정 제품의 출시일은?" 같은 세부·명시적 질문 | 관련 엔티티 식별 → 연관 컨텍스트(텍스트 청크·커뮤니티 리포트·관련 엔티티·관계·공변량) 수집 → 리랭킹 → 답변 |
| 글로벌 검색(Global Search) | "이 문서의 주요 주제는?" 같은 포괄적 질문 | 커뮤니티 레벨 선택 → 맵(Map): 커뮤니티 리포트를 배치로 분할해 중간 응답+중요도 점수 생성 → 리듀스(Reduce): 고득점 응답 통합해 최종 답변 |
2. 그래프 DB 구축
2.1 그래프 DB 구축 과정
4단계 파이프라인: 문서 분할 → 지식 그래프 구축 → 그래프 증강 → 커뮤니티 요약
단계 1: 문서 분할
청크 크기는 품질-비용 트레이드오프다. 연구 결과에 따르면 600토큰 청크가 2400토큰 대비 감지 엔티티 수가 거의 두 배 많다. 단, 비용도 4배 증가한다. Gleaning(이삭 줍기) 기법으로 초기 추출 후 누락 정보를 LLM에 재질의해 보완할 수 있다.
Gleaning의 효과: 청크 크기 600에서 3회 수행 시 초기 대비 약 3배 많은 엔티티(~28,000개)를 탐지할 수 있었으며, 더 큰 청크 크기(1200·2400)를 사용하더라도 성능 저하를 상당 부분 보완한다.
단계 2: 지식 그래프 구축 — 엔티티·관계 추출 기술 비교
| 방식 | 장점 | 단점 |
|---|---|---|
| 규칙 기반 | 구현 단순, 명확 | 패턴 정확 매칭만, 확장성·유연성 부족 |
| 기계학습(SVM·CRF) | 단어 특징·문맥 기반, 자원 효율 | 딥러닝 대비 복잡한 언어 처리 제한 |
| 딥러닝(BERT·Transformer) | 깊은 문맥 이해 | 대규모 데이터셋·고사양 리소스 필요 |
| LLM (GPT 등) | 높은 정확도, 전처리 불필요, 신속 구축 | API 비용·처리 속도·출력 일관성 |
LLM을 활용한 엔티티 추출 예시:
# LLM으로 엔티티·관계 추출하는 프롬프트 예시
input_text = "테슬라는 일론 머스크가 설립한 회사로, 전기차와 태양광 패널을 생산합니다."
prompt = """다음 글에서 엔티티의 이름, 타입, 설명을 추출하고,
엔티티들 간의 관계를 설명과 함께 1-10 사이의 강도로 표시하세요."""
# 기대 출력:
# 엔티티:
# - "테슬라" (ORGANIZATION, 전기차와 친환경 에너지를 생산하는 기업)
# - "일론 머스크" (PERSON, 테슬라의 설립자이자 현 CEO)
# - "전기차" (PRODUCT, 테슬라가 생산하는 주력 제품)
#
# 관계:
# - "일론 머스크" -> "테슬라" (설립자이자 CEO, 강도: 9)
# - "테슬라" -> "전기차" (주력 제품으로 설계 및 생산, 강도: 8)
지식 그래프 생성은 4단계 세부 프로세스로 진행된다.
# ① 엔티티·관계 추출 (각 엔티티: name, type, description)
# ② 그래프 통합 — 동일 이름+타입 엔티티 병합, 설명은 배열로 저장
# 예: "ELON MUSK" 설명 = ["트위터를 인수한 기업인", "스페이스X를 설립한 기업인"]
# ③ 설명 요약 — LLM으로 배열 설명을 하나의 간결한 설명으로 통합
# 결과: "ELON MUSK" 설명 = "스페이스X를 설립하고 트위터를 인수한 기업인"
# ④ 주장(Claim) 추출 (선택사항) — 시간·상태 정보 포함 구조화 주장
# 예: {"type": "ACQUISITION", "subject_id": "ELON MUSK",
# "object_id": "TWITTER", "start_date": "2022-10-27T00:00:00"}
단계 3: 그래프 증강
두 방향으로 진행된다.
커뮤니티 탐지 — 로우벤(Louvain)·레이든(Leiden) 알고리즘으로 밀접하게 연결된 엔티티 그룹을 자동 분류한다.
| 알고리즘 | 특징 | 장점 | 단점 |
|---|---|---|---|
| 로우벤(Louvain) | 모듈성 기반 그룹화, 4단계 반복 | 빠름, 구현 간단 | 비연결 커뮤니티 발생 가능, 초기 설정 민감 |
| 레이든(Leiden) | 로우벤 개선판(2018), 정제 단계 추가 | 연결된 커뮤니티 보장, 안정적·정확 | 정제 단계로 반복당 계산량 증가 |
마이크로소프트 GraphRAG는 레이든 알고리즘으로 3단계(Level 0~2) 계층적 커뮤니티를 구성한다. Root Level(0)은 포괄적, High Level(2)은 세부적이다.
Level 0: '전기차 산업' 같은 광범위한 커뮤니티
Level 1: 'Tesla', 'SpaceX', 'Twitter(X)' 등 주요 기업별
Level 2: 각 기업의 제품 라인업, 주요 인물, 주요 사건 등 세부 정보
계층적 커뮤니티의 장점: ① 정보의 효율적 조직화 — 질문 깊이에 맞는 레벨 선택 가능 ② LLM 토큰 제한 관리 — 질문 범위에 맞는 레벨만 선택적으로 활용
그래프 임베딩 — Node2vec(랜덤워크 기반)을 사용해 그래프 구조적 특성을 수치 벡터로 변환한다. 비슷한 컨텍스트의 노드 → 비슷한 임베딩. 파라미터 p(귀환 확률)와 q(탐색 폭-깊이)로 BFS/DFS 균형을 조절한다.
- p(Return Parameter): p값이 크면 새 경로 탐색, 작으면 이전 노드로 귀환
- q(In-out Parameter): q > 1이면 BFS(넓게), q < 1이면 DFS(깊게)
단계 4: 커뮤니티 요약
각 커뮤니티를 구성하는 요소를 연결 정도(degree)에 따라 중요도 정렬 → LLM에 순차 입력 → 커뮤니티 요약문 생성 → 임베딩 변환. 생성된 요약문은 검색 시 전체 문서 스캔 없이 관련 커뮤니티만 참조할 수 있게 한다.
커뮤니티 요약문의 구조: community(번호)·level(계층)·rank(중요도 점수)·title·summary·findings(발견사항 목록).
2.2 그래프 DB 구축 실습 (Microsoft GraphRAG)
# GraphRAG 환경 설정
!pip install graphrag
from pathlib import Path
# 작업 디렉토리 생성
working_dir = Path('working_directory')
working_dir.mkdir(parents=True, exist_ok=True)
# GraphRAG 초기화 → .env, settings.yaml 생성
!graphrag init --root ./working_directory
주요 settings.yaml 설정:
# 대규모 언어 모델 설정
models:
api_key: ${GRAPHRAG_API_KEY}
type: openai_chat
model: gpt-4o-mini # 비용 절감 시 gpt-4o-mini 권장
# 문서 분할 설정
chunks:
size: 300 # 청크 크기 (600토큰이 엔티티 탐지 최적)
overlap: 100 # 청크 겹침
# 엔티티 추출 설정
extract_graph:
entity_types: [organization, person, geo, event]
max_gleanings: 1 # Gleaning(이삭 줍기) 반복 횟수
# 주장 추출 (선택사항)
extract_claims:
enabled: false
# 커뮤니티 요약
community_reports:
max_length: 2000
max_input_length: 8000
# 입력 데이터 준비
input_dir = working_dir / 'input'
input_dir.mkdir(parents=True, exist_ok=True)
# 분석할 텍스트 파일을 input/ 폴더에 배치
# 그래프 DB 구축 실행 (Base processing + Enrichment 두 단계)
!graphrag index --root ./working_directory
# 결과: working_directory/output/ 폴더에 Parquet 파일들 생성
# 주요 파일: create_final_entities.parquet, create_final_relationships.parquet,
# create_final_communities.parquet, create_final_community_reports.parquet
GraphRAG 인덱싱 7단계 워크플로우:
| 단계 | 워크플로우 이름 | 수행 작업 |
|---|---|---|
| 1 | create_base_text_units |
문서 청크 분할 |
| 2 | create_base_extracted_entities |
엔티티 추출 |
| 3 | create_base_entity_graph |
그래프 통합·커뮤니티 탐지·임베딩 |
| 4 | create_final_covariates |
공변량(주장) 추출 |
| 5 | create_final_entities |
엔티티 정제·임베딩·데이터 구조 개선 |
| 6 | create_final_nodes |
노드 좌표 생성(UMAP 차원 축소로 그래프 시각화용) |
| 7 | create_final_community_reports |
커뮤니티 요약문·임베딩 생성 |
전체 워크플로우는 Base processing(문서→엔티티→기본 그래프)과 Enrichment(그래프 정제·커뮤니티 요약·시각화) 두 단계로 구분된다.
3. 그래프 RAG 질의
3.1 질의 과정
로컬 검색 3단계:
- 질문 임베딩 → 그래프 DB 엔티티 임베딩과 유사도 계산 → 관련 엔티티 식별
- 식별 엔티티에 연관된 5가지 정보 수집: 텍스트 청크 / 커뮤니티 리포트 / 연관 엔티티 / 관계 / 공변량(주장)
- 수집 정보 리랭킹(관련성 평가) → 최종 답변 생성
글로벌 검색 맵-리듀스:
- 맵(Map): 커뮤니티 리포트를 배치로 분할 → LLM이 각 배치에서 중간 응답 + 중요도 점수 생성
- 리듀스(Reduce): 중요도 점수 높은 응답 선별 → 통합해 포괄적 최종 답변 생성
커뮤니티 레벨 선택이 성능을 좌우한다. 하위 레벨(세부)은 정확하지만 느리고, 상위 레벨(포괄)은 빠르지만 세부 정보가 적다.
3.2 질의 실습
# 글로벌 검색 — 포괄적 질문에 적합 (문서 전체 주제·패턴)
!graphrag query \
--root ./ \
--method global \
--query "채권의 전환성 개념이 각 챕터들에서 어떻게 소개되어 있나요?"
# 로컬 검색 — 세부·명시적 질문에 적합 (특정 엔티티·관계)
!graphrag query \
--root ./ \
--method local \
--query "산업채권 평가 시 순유동자산(Net Quick Assets)을 어떻게 분석해야 하나요?"
글로벌 vs 로컬 검색 선택 기준:
| 질문 유형 | 추천 검색 | 이유 |
|---|---|---|
| 문서 전반의 주제·개념 흐름 파악 | 글로벌 | 커뮤니티 요약으로 전체 패턴 파악 |
| 특정 챕터·인물·제품의 세부 정보 | 로컬 | 엔티티 중심 정보 집중 수집 |
| 시간 흐름에 따른 변화 추적 | 글로벌 | 대규모 데이터 연결성 강점 |
| 특정 기준·방법론 적용 방법 | 로컬 | 구체적 데이터 소스 참조 포함 |
4. Neo4j와 랭체인을 활용한 GraphRAG 구현
4.1 지식 그래프와 Neo4j 통합
마이크로소프트 GraphRAG가 Parquet 파일로 출력한 결과물(엔티티·관계·커뮤니티)을 Neo4j에 저장해 영구적인 그래프 DB로 운영하고, 랭체인으로 자연어 질의를 Cypher로 자동 변환한다.
Neo4j Aura 연결 설정:
# 패키지 설치
!pip install neo4j langchain-neo4j
from neo4j import GraphDatabase
import pandas as pd
# Neo4j Aura 연결 정보 설정
NEO4J_URI = "neo4j+s://xxxxxxxx.databases.neo4j.io"
NEO4J_USERNAME = "neo4j"
NEO4J_PASSWORD = "your_password"
NEO4J_DATABASE = "neo4j"
driver = GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USERNAME, NEO4J_PASSWORD))
Cypher 제약 조건 설정 (중복 방지):
-- 고유 제약 조건 생성 (Cypher 문법)
CREATE CONSTRAINT chunk_id IF NOT EXISTS FOR (c:__Chunk__) REQUIRE c.id IS UNIQUE;
CREATE CONSTRAINT entity_id IF NOT EXISTS FOR (e:__Entity__) REQUIRE e.id IS UNIQUE;
CREATE CONSTRAINT community_id IF NOT EXISTS FOR (c:__Community__) REQUIRE c.community IS UNIQUE;
CREATE CONSTRAINT related_id IF NOT EXISTS FOR ()-[rel:RELATED]-() REQUIRE rel.id IS UNIQUE;
GraphRAG 결과를 Neo4j에 임포트:
GRAPHRAG_FOLDER = '/content/working_directory/output'
def batched_import(statement, df, batch_size=1000):
"""DataFrame을 배치 단위로 Neo4j에 임포트하는 함수."""
total = len(df)
for start in range(0, total, batch_size):
batch = df.iloc[start: min(start + batch_size, total)]
driver.execute_query(
"UNWIND $rows AS value " + statement,
rows=batch.to_dict("records"),
database_=NEO4J_DATABASE,
)
return total
# ① 문서 노드 생성
doc_df = pd.read_parquet(f"{GRAPHRAG_FOLDER}/create_final_documents.parquet",
columns=["id", "title"])
batched_import("""
MERGE (d:__Document__ {id: value.id})
SET d += value {.title}
""", doc_df)
# ② 텍스트 청크(Chunk) 노드 생성 + 문서와 연결
text_df = pd.read_parquet(f"{GRAPHRAG_FOLDER}/create_final_text_units.parquet",
columns=["id", "text", "n_tokens", "document_ids"])
batched_import("""
MERGE (c:__Chunk__ {id: value.id})
SET c += value {.text, .n_tokens}
WITH c, value
UNWIND value.document_ids AS document
MATCH (d:__Document__ {id: document})
MERGE (c)-[:PART_OF]->(d)
""", text_df)
# ③ 엔티티 노드 생성 + 청크와 연결
entity_df = pd.read_parquet(f"{GRAPHRAG_FOLDER}/create_final_entities.parquet",
columns=["title", "type", "description", "id", "text_unit_ids"])
batched_import("""
MERGE (e:__Entity__ {id: value.id})
SET e.name = coalesce(replace(value.title, '"', ''), 'Unknown'),
e.description = value.description
WITH e, value
UNWIND value.text_unit_ids AS text_unit
MATCH (c:__Chunk__ {id: text_unit})
MERGE (c)-[:HAS_ENTITY]->(e)
""", entity_df)
# ④ 관계(RELATED 엣지) 생성
rel_df = pd.read_parquet(f"{GRAPHRAG_FOLDER}/create_final_relationships.parquet",
columns=["source", "target", "id", "weight", "description"])
batched_import("""
MATCH (source:__Entity__ {name: replace(value.source, '"', '')})
MATCH (target:__Entity__ {name: replace(value.target, '"', '')})
MERGE (source)-[rel:RELATED {id: value.id}]->(target)
SET rel += value {.weight, .description}
""", rel_df)
# ⑤ 커뮤니티 노드 생성 + 엔티티와 연결
community_df = pd.read_parquet(f"{GRAPHRAG_FOLDER}/create_final_communities.parquet",
columns=["id", "level", "title", "relationship_ids"])
batched_import("""
MERGE (c:__Community__ {community: value.title})
SET c.title = value.title, c.level = value.level
WITH c, value
UNWIND value.relationship_ids AS rel_id
MATCH (start:__Entity__)-[:RELATED {id: rel_id}]->(end:__Entity__)
MERGE (start)-[:IN_COMMUNITY]->(c)
MERGE (end)-[:IN_COMMUNITY]->(c)
""", community_df)
랭체인으로 자연어 → Cypher 자동 변환 질의:
from langchain_neo4j import Neo4jGraph, GraphCypherQAChain
from langchain_openai import ChatOpenAI
# Neo4j 그래프 연결 (langchain-neo4j 파트너 패키지)
graph = Neo4jGraph(
url=NEO4J_URI,
username=NEO4J_USERNAME,
password=NEO4J_PASSWORD,
database=NEO4J_DATABASE,
)
# 그래프 스키마 자동 로드
graph.refresh_schema()
# GraphCypherQAChain 구성
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
chain = GraphCypherQAChain.from_llm(
llm=llm,
graph=graph,
verbose=True,
allow_dangerous_requests=True,
)
# 자연어 질문 → 자동 Cypher 생성 → 실행 → 답변
result = chain.invoke({"query": "테슬라와 관련된 주요 인물은 누구인가요?"})
print(result["result"])
# 위 질문이 자동 변환되는 Cypher 예시:
# MATCH (e:__Entity__ {name: 'TESLA'})-[:RELATED]-(person:__Entity__)
# WHERE person.type = 'PERSON'
# RETURN person.name, person.description
# LIMIT 10
Neo4j에서 직접 Cypher로 관계 탐색:
-- 특정 엔티티의 1-hop 관계 탐색
MATCH (e:__Entity__ {name: 'TESLA'})-[r:RELATED]-(neighbor)
RETURN e.name, type(r), neighbor.name, r.description
LIMIT 20;
-- 커뮤니티에 속한 엔티티 조회
MATCH (e:__Entity__)-[:IN_COMMUNITY]->(c:__Community__)
WHERE c.level = 1
RETURN c.title AS community, collect(e.name) AS entities
ORDER BY community;
-- 엔티티 간 최단 경로 탐색
MATCH path = shortestPath(
(a:__Entity__ {name: 'ELON MUSK'})-[:RELATED*]-(b:__Entity__ {name: 'APPLE'})
)
RETURN [n IN nodes(path) | n.name] AS path_nodes;
랭체인 Neo4j 로컬·글로벌 검색 구현 패턴:
# 로컬 검색 — Neo4jVector 기반 엔티티 유사도 검색
from langchain_community.vectorstores.neo4j_vector import Neo4jVector
from langchain_openai import OpenAIEmbeddings
embedding = OpenAIEmbeddings()
graph = Neo4jVector.from_existing_graph(
embedding=embedding,
node_label="__Entity__",
text_node_properties=["description"],
embedding_node_property="embedding",
password=NEO4J_PASSWORD
)
retriever = graph.as_retriever(search_type="similarity", search_kwargs={"k": 3})
# 검색 결과 엔티티 → fetch_entity_context() 로 텍스트청크·커뮤니티보고서·관련엔티티 수집
# 글로벌 검색 — 맵-리듀스 방식
def global_retriever(query: str, level: int) -> str:
community_data = neo4j_graph.query(
"MATCH (c:__Community__) WHERE c.level = $level RETURN c.full_content AS output",
params={"level": level},
)
# map_chain: 각 커뮤니티 → 중간 응답 + 중요도 점수
intermediate_results = [map_chain.invoke({"question": query, "context": c["output"]})
for c in community_data]
# reduce_chain: 고득점 중간 응답 통합 → 최종 답변
return reduce_chain.invoke({"report_data": intermediate_results, "question": query})
핵심 개념 정리
| 개념 | 한 줄 요약 |
|---|---|
| 그래프 RAG 등장 이유 | 기존 RAG의 정보 연결 부재 + 대규모 데이터 전체 파악 한계 해결 |
| 지식 그래프 | 노드·에지에 의미론적 속성을 부여해 복잡한 사실 관계를 구조화한 그래프 |
| 그래프 DB 구축 4단계 | 문서 분할 → 엔티티·관계 추출 → 그래프 증강(커뮤니티+임베딩) → 커뮤니티 요약 |
| 로컬 vs 글로벌 검색 | 로컬=특정 엔티티 심층 정보, 글로벌=전체 데이터 포괄 패턴(맵-리듀스) |
| Neo4j + 랭체인 통합 | GraphRAG Parquet → Neo4j 임포트 → GraphCypherQAChain으로 자연어 질의 |
| LLMGraphTransformer | 랭체인에서 Document → 자동 그래프 추출. 별도 파이프라인 불필요 |
| 커뮤니티 계층(Level 0~2) | Level 0=포괄적, Level 2=세부적. 질문 범위에 맞는 레벨로 답변 효율화 |
| Node2vec | 랜덤워크로 그래프 구조 임베딩. p(귀환)·q(탐색 폭-깊이)로 BFS/DFS 균형 조절 |
| Gleaning | 초기 추출 후 LLM에 재질의해 누락 엔티티 보완. max_gleanings로 반복 횟수 설정 |
| 공변량(Covariate) | 텍스트에서 추출한 주장(Claim). 시간·상태 정보 포함 구조화 정보 |
한 문장 요약 — 그래프 RAG는 텍스트를 노드-에지 지식 그래프로 구조화하고 커뮤니티 요약을 인덱스로 삼아, 개별 문서 검색(로컬)과 전체 데이터 주제 파악(글로벌) 두 방식으로 기존 벡터 RAG의 연결성·포괄성 한계를 보완하는 기법이다.
실무 체크리스트
- [ ] 벡터 RAG vs 그래프 RAG: "관계·패턴 파악이 필요한가?"로 판단한다. 단순 유사 문서 검색은 벡터 RAG, 엔티티 간 연결·전체 주제 파악은 그래프 RAG.
- [ ] 청크 크기는 엔티티 탐지 품질(작게=많이)과 API 비용(크게=적게)의 트레이드오프. 600토큰이 탐지 최적, 비용 고려 시 1200·2400도 Gleaning으로 보완 가능.
- [ ] 질의 방식 선택: 포괄적 주제·트렌드 질문 → 글로벌 검색, 세부 사실·관계 질문 → 로컬 검색.
- [ ] Neo4j 임포트 시 제약 조건(CONSTRAINT)을 먼저 설정해 중복 데이터를 방지한다.
- [ ]
langchain-neo4j파트너 패키지(GraphCypherQAChain)로 자연어→Cypher 자동화 가능. - [ ] 대규모 임포트는
batched_import()패턴으로 배치 처리해 메모리·타임아웃을 방지한다. - [ ] 그래프 시각화가 필요하면
create_final_nodes단계에서 그래프 임베딩 설정을 반드시 활성화한다. - [ ] 글로벌 검색의 커뮤니티 레벨 선택: 세부 정보 필요 시 하위 레벨(느리고 비용 높음), 전체 흐름 파악은 상위 레벨(빠르고 포괄적).
연습문제
-
개념. 기존 벡터 RAG와 그래프 RAG의 핵심 차이를 '정보 표현 방식'과 '검색 방식' 두 측면에서 설명하라.
-
비교. 로컬 검색과 글로벌 검색이 각각 적합한 질문 유형을 예시와 함께 제시하고, 그 이유를 동작 원리로 설명하라.
-
코드. 아래 자연어 문장에서 엔티티와 관계를 추출하고, Neo4j Cypher
MERGE문으로 노드와 관계를 생성하는 코드를 작성하라."구글은 2015년에 알파벳을 설립했으며, 순다르 피차이가 CEO를 맡고 있다."
-
적용. 그래프 DB 구축 4단계(문서 분할 → 지식 그래프 구축 → 그래프 증강 → 커뮤니티 요약)에서 Gleaning 기법은 어느 단계에 적용되며, 어떤 효과가 있는가?
-
설계. 사내 1,000개 기술 문서를 그래프 RAG로 구축하려 한다. ①벡터 RAG 대신 그래프 RAG를 선택하는 이유, ②청크 크기 전략, ③로컬/글로벌 검색 사용 시나리오를 포함한 시스템 설계를 논하라.
-
심화. Node2vec의 하이퍼파라미터 p와 q가 각각 어떤 역할을 하는지 설명하고, p=0.5, q=2로 설정했을 때 랜덤워크가 그래프를 어떻게 탐색하는지 서술하라.
최신 동향 (2026-05 기준)
최신 동향 (검증 2026-05-21) — 책은 2025년 기준이므로 아래를 추가로 확인하라.
langchain-neo4j파트너 패키지 분리. 책의GraphCypherQAChain은langchain-community에서langchain-neo4j파트너 패키지로 이동했다. 신규 코드는from langchain_neo4j import Neo4jGraph, GraphCypherQAChain사용이 권장된다. (출처: https://python.langchain.com/docs/integrations/graphs/neo4j_cypher/)LLMGraphTransformer안정화.langchain_experimental.graph_transformers.LLMGraphTransformer가 지식 그래프 구축의 표준 진입점으로 자리잡았다. 별도 GraphRAG 파이프라인 없이 랭체인만으로 문서→그래프 변환이 가능하다.- Microsoft GraphRAG v2.x 출시. 책 기반인 v1.x 대비 검색 API 구조가 변경됐다.
graphrag queryCLI 옵션 및settings.yaml스키마가 달라질 수 있으므로 공식 changelog를 확인하라. (출처: https://microsoft.github.io/graphrag/) - Neo4j GenAI 기능 확장. Neo4j 5.x에서 벡터 인덱스 내장(
vector index), GenAI 플러그인을 통한 임베딩 생성 등 LLM 통합 기능이 강화됐다. 그래프 RAG와 벡터 검색을 하나의 Neo4j 인스턴스에서 처리하는 하이브리드 패턴이 증가하고 있다. (출처: https://neo4j.com/docs/cypher-manual/current/indexes/semantic-indexes/vector-indexes/)
부록 A. 용어 사전
| 한글 | 영문 | 의미 |
|---|---|---|
| 지식 그래프 | Knowledge Graph | 노드(개체)와 에지(관계)에 의미론적 속성을 부여해 사실 관계를 구조화한 그래프 |
| 노드 | Node / Vertex | 그래프의 개체 단위. 사람·조직·제품 등 실체를 표현 |
| 에지 | Edge | 노드 간 관계를 나타내는 연결선. 레이블로 관계 종류를 명시 |
| 커뮤니티 | Community | 그래프 내 밀접하게 연결된 엔티티 그룹. 레이든 알고리즘으로 자동 탐지 |
| Cypher | Cypher | Neo4j 전용 그래프 쿼리 언어. 패턴 매칭으로 노드·관계를 선언적으로 탐색 |
| 로컬 검색 | Local Search | 특정 엔티티 중심으로 연관 컨텍스트를 수집해 세부 질문에 답하는 방식 |
| 글로벌 검색 | Global Search | 커뮤니티 요약을 맵-리듀스로 집계해 포괄적 주제 질문에 답하는 방식 |
| LLMGraphTransformer | LLMGraphTransformer | 랭체인 컴포넌트. Document를 LLM에 전달해 자동으로 GraphDocument를 추출 |
| GraphCypherQAChain | GraphCypherQAChain | 자연어 질문 → Cypher 자동 생성 → Neo4j 실행 → 답변을 한 파이프라인으로 연결 |
| Gleaning | Gleaning (이삭 줍기) | 초기 엔티티 추출 후 LLM에 재질의해 누락 정보를 보완하는 반복 기법 |
| 레이든 알고리즘 | Leiden Algorithm | 로우벤 알고리즘의 개선판. 커뮤니티 연결성을 보장하고 안정적으로 탐지 |
| Neo4j Aura | Neo4j Aura | Neo4j의 클라우드 관리형 서비스. 무료 티어로 그래프 DB 실습 가능 |
| Node2vec | Node2vec | 랜덤워크 기반 그래프 임베딩 기법. p·q 파라미터로 BFS/DFS 탐색 균형 조절 |
| 공변량 | Covariate | 텍스트에서 추출한 주장(Claim). 시간·상태 정보를 포함한 구조화된 사실 진술 |
| UMAP | UMAP | 고차원 노드 임베딩을 2D/3D로 축소해 그래프 시각화에 활용하는 차원 축소 기법 |
부록 B. 핵심 비교표
벡터 RAG vs 그래프 RAG
| 구분 | 벡터 RAG | 그래프 RAG |
|---|---|---|
| 정보 표현 방식 | 문서를 벡터로 변환, 독립 청크 단위 저장 | 엔티티·관계를 노드·에지로 구조화 저장 |
| 검색 방식 | 질문 벡터와 청크 벡터의 코사인 유사도 | 엔티티 임베딩 유사도 + 그래프 탐색 |
| 강점 | 구현 단순, 빠른 구축, 유사 문서 검색 | 관계 추론, 전체 패턴 파악, 다층적 질의 |
| 약점 | 정보 연결 부재, 전체 주제 파악 어려움 | 구축 비용·시간 높음, LLM API 비용 |
| 적합한 질문 | "이 제품의 출시일은?" (세부·명시적) | "회사 전략의 변화 흐름은?" (포괄·관계적) |
| 주요 도구 | FAISS, Chroma, Pinecone 등 벡터 DB | Neo4j, Microsoft GraphRAG, LangChain |
로컬 검색 vs 글로벌 검색
| 구분 | 로컬 검색 (Local Search) | 글로벌 검색 (Global Search) |
|---|---|---|
| 적합 질문 | 특정 엔티티·관계의 세부 정보 | 문서 전체의 주제·패턴·트렌드 |
| 동작 원리 | 엔티티 임베딩 유사도 → 연관 컨텍스트 수집 → 리랭킹 | 커뮤니티 리포트 맵(배치 중간 응답) → 리듀스(통합) |
| 속도 | 빠름 | 느림 (전체 커뮤니티 스캔) |
| 비용 | 낮음 | 높음 (커뮤니티 수 비례) |
| 예시 질문 | "일론 머스크와 관련된 기업은?" | "이 문서에서 반복적으로 등장하는 핵심 주제는?" |
로우벤 vs 레이든 알고리즘
| 구분 | 로우벤(Louvain) | 레이든(Leiden) |
|---|---|---|
| 출시 | 2008년경 | 2018년 |
| 핵심 개선 | 모듈성 기반 그룹화 | 정제 단계 추가 |
| 비연결 커뮤니티 | 발생 가능 | 연결성 보장 |
| 계산 효율 | 빠름 | 반복당 계산량 증가, 전체적으로는 효율적 |
| GraphRAG 채택 | X | O (Microsoft GraphRAG 기본 알고리즘) |
부록 C. 추천 참고 자료
검증된 외부 자료 (Tier 1 공식, 생존 확인 2026-05-21)
| 자료 | 링크 |
|---|---|
| Microsoft GraphRAG 공식 문서 | microsoft.github.io/graphrag |
| Microsoft GraphRAG 논문 (arXiv) | arxiv.org/abs/2404.16130 |
| LangChain Neo4j 통합 가이드 | python.langchain.com/docs/integrations/graphs/neo4j_cypher |
| LangChain Graph Transformers | python.langchain.com/docs/how_to/graph_constructing |
| Neo4j 공식 문서 | neo4j.com/docs |
| Neo4j Cypher 튜토리얼 | neo4j.com/docs/getting-started/current/cypher-intro |
| Neo4j Aura (클라우드 무료 티어) | neo4j.com/product/auradb |
| 실습 코드 (깃허브) | github.com/langchain-kr/langchain-tutorial (Ch05) |
| 자료 | 설명 |
|---|---|
| 책 2장 | 벡터 임베딩·청크 전략 기초 — 그래프 RAG의 문서 분할 이해에 필수 |
| 책 4장 | 고급 RAG 기법 — 리랭킹 등 로컬 검색에서 활용되는 기법 |
부록 D. 연습문제 풀이
-
(벡터 RAG는 독립 청크 벡터 유사도, 그래프 RAG는 노드-에지 구조 + 그래프 탐색) 벡터 RAG는 문서를 독립된 벡터 청크로 변환해 질문 벡터와의 코사인 유사도로 검색하므로 청크 간 연결 정보가 없다. 그래프 RAG는 엔티티를 노드, 관계를 에지로 구조화하고 엔티티 임베딩 유사도와 그래프 탐색을 결합해 "정보들이 어떻게 연결되는가"까지 파악한다. 결과적으로 벡터 RAG는 개별 문서 검색에 강하고, 그래프 RAG는 관계 추론·전체 패턴 파악에 강하다.
-
(로컬=세부 엔티티 질문, 글로벌=전체 주제·패턴 질문) 로컬 검색은 "일론 머스크와 관련된 기업은?"처럼 특정 엔티티와 그 1-hop 관계를 찾을 때 적합하다. 질문 임베딩으로 관련 엔티티를 식별한 뒤 텍스트 청크·커뮤니티 리포트·관계 등 5가지 연관 컨텍스트를 수집해 답변하기 때문이다. 글로벌 검색은 "이 문서의 주요 주제는?"처럼 전체 데이터셋에 걸친 포괄적 질문에 적합하다. 커뮤니티 리포트를 배치로 맵(Map)해 중간 응답과 중요도 점수를 생성하고, 리듀스(Reduce)로 고득점 응답을 통합하는 맵-리듀스 방식으로 전체 패턴을 아우른다.
-
(엔티티 추출 후
MERGE로 노드·관계 생성) 문장에서 엔티티는 "구글(ORGANIZATION)", "알파벳(ORGANIZATION)", "순다르 피차이(PERSON)"이고, 관계는 "구글→알파벳(설립, 2015년)", "순다르 피차이→알파벳(CEO)"이다.
-- 노드 생성
MERGE (google:__Entity__ {name: 'GOOGLE'})
SET google.type = 'ORGANIZATION', google.description = '알파벳을 설립한 기술 기업'
MERGE (alphabet:__Entity__ {name: 'ALPHABET'})
SET alphabet.type = 'ORGANIZATION', alphabet.description = '구글이 2015년 설립한 지주회사'
MERGE (sundar:__Entity__ {name: 'SUNDAR PICHAI'})
SET sundar.type = 'PERSON', sundar.description = '알파벳의 CEO'
-- 관계(RELATED 엣지) 생성
MERGE (google)-[r1:RELATED {id: 'google-alphabet-founded'}]->(alphabet)
SET r1.description = '2015년 설립', r1.weight = 9
MERGE (sundar)-[r2:RELATED {id: 'sundar-alphabet-ceo'}]->(alphabet)
SET r2.description = 'CEO', r2.weight = 8
-
(Gleaning은 문서 분할(단계 1) 직후 엔티티 추출(단계 2)에서 적용되며, 누락 엔티티를 보완한다) Gleaning(이삭 줍기)은 그래프 DB 구축 4단계 중 2단계인 지식 그래프 구축(엔티티·관계 추출) 단계에서 사용된다. 청크에서 1차 추출 후 LLM에 재질의해 누락된 엔티티와 관계를 보완하며,
settings.yaml의max_gleanings값으로 반복 횟수를 조절한다. 이를 통해 청크 크기를 크게 유지하면서도(비용 절감) 작은 청크 수준의 탐지 품질을 확보할 수 있다. -
(그래프 RAG 선택 이유·청크 전략·로컬/글로벌 시나리오를 포함한 설계) 1,000개 기술 문서는 개념·모듈·팀·제품 간 복잡한 관계가 있으므로 단순 유사도 검색보다 관계 추론과 전체 패턴 파악이 필요하다. 이것이 벡터 RAG 대신 그래프 RAG를 선택하는 이유다.
-
청크 크기 전략: 엔티티 탐지 품질을 우선할 때는 600토큰, API 비용을 줄일 때는 1200~2400토큰 + Gleaning(
max_gleanings: 1~2) 조합을 선택한다. 두 방식 모두settings.yaml의chunks.size와extract_graph.max_gleanings로 조정한다. - 로컬 검색 시나리오: "A 모듈의 의존 라이브러리는?", "B API의 파라미터 명세는?"처럼 특정 엔티티의 세부 정보를 조회할 때 사용한다.
-
글로벌 검색 시나리오: "마이크로서비스 아키텍처 관련 문서에서 반복되는 설계 패턴은?", "지난 2년간 인증 방식의 변화 흐름은?"처럼 전체 문서셋의 주제·트렌드를 파악할 때 사용한다.
-
(p는 귀환 확률, q는 탐색 폭-깊이; p=0.5·q=2 시 이전 노드 귀환 + BFS 우세) p(Return Parameter)는 이전 노드로 돌아갈 확률을 제어한다. p값이 작을수록(p=0.5) 이전 노드로 귀환 확률이 높아진다. q(In-out Parameter)는 탐색의 폭과 깊이를 결정한다. q > 1(q=2)이면 BFS에 가까워져 현재 노드 주변 이웃을 폭넓게 탐색한다. p=0.5, q=2 조합에서는 이전 노드로 귀환 가능성이 높으면서도 탐색 시에는 주변 노드를 넓게 방문하는 패턴이 형성된다. 이를 통해 국소 구조(커뮤니티)와 전역 구조(멀리 떨어진 노드 관계)를 균형 있게 반영한 임베딩을 생성할 수 있다.
클릭하거나 Space를 눌러 뒤집기