Skip to main content

Graph RAG

Graph RAG는 문서에서 엔티티(개체)와 관계를 추출하여 지식 그래프를 구축하고, 이 그래프 구조를 검색에 활용하는 RAG 아키텍처입니다. 기존 RAG가 개별 문서 청크를 검색하는 반면, Graph RAG는 엔티티 간의 관계와 커뮤니티 구조를 활용하여 문서 전체에 걸친 질문에도 답변할 수 있습니다.

핵심 아이디어

기존 벡터 검색 기반 RAG는 “이 데이터셋의 주요 주제는 무엇인가?”와 같은 전체 문서에 대한 요약형 질문(global sensemaking question)에 취약합니다. 개별 청크에는 전체 맥락이 담기지 않기 때문입니다. Graph RAG는 이 문제를 두 단계로 해결합니다.
1

인덱싱: 지식 그래프 구축

LLM으로 문서에서 엔티티와 관계를 추출하고, 그래프를 구축한 뒤, 커뮤니티 탐지 알고리즘으로 관련 엔티티를 그룹화하여 각 커뮤니티의 요약을 미리 생성합니다.
2

검색: 그래프 기반 질의 응답

질문 유형에 따라 Local Search(특정 엔티티 주변 탐색) 또는 Global Search(커뮤니티 요약 기반 종합 답변)를 수행합니다.

인덱싱 파이프라인

단계설명
텍스트 청크 분할문서를 LLM 처리 가능한 크기로 분할
엔티티/관계 추출LLM이 각 청크에서 엔티티(인물, 조직, 개념 등)와 관계를 추출
지식 그래프 구축추출된 엔티티를 노드로, 관계를 엣지로 그래프 구성
커뮤니티 탐지Leiden 알고리즘 등으로 밀접하게 연결된 엔티티를 그룹화
커뮤니티 요약LLM이 각 커뮤니티의 엔티티와 관계를 요약하는 텍스트 생성

검색 방식: Global vs Local

아래 Global/Local Search 구분은 Edge et al. (2024) 원 논문과 Microsoft GraphRAG 구현을 기반으로 합니다. 원 논문은 주로 Global Search (QFS, Query-Focused Summarization) 를 중심으로 기여를 주장하며, Local Search는 Microsoft의 제품 구현에서 확장된 것입니다.
Graph RAG는 질문의 특성에 따라 두 가지 검색 전략을 제공합니다.

벡터 RAG와의 비교

항목벡터 RAG (Naive/Advanced)Graph RAG
인덱스 구조벡터 임베딩지식 그래프 + 커뮤니티 요약
검색 단위텍스트 청크엔티티, 관계, 커뮤니티
전역 질문취약 (개별 청크 한계)강점 (커뮤니티 요약 활용)
관계 질문취약 (의미적 유사도만 사용)강점 (그래프 구조 활용)
인덱싱 비용낮음 (임베딩만)높음 (LLM 추출 + 그래프 구축 + 요약)
업데이트쉬움 (청크 추가/삭제)복잡 (그래프 재구축 필요)

LangGraph 구현

상태 정의

from typing import TypedDict, List, Literal
from langchain_core.documents import Document

class GraphRAGState(TypedDict):
    question: str
    search_type: str  # "global" or "local"
    entities: List[dict]
    community_summaries: List[str]
    context: str
    generation: str

노드 함수

from langchain.chat_models import init_chat_model
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

llm = init_chat_model("gpt-4o-mini", temperature=0)

def classify_question(state: GraphRAGState) -> GraphRAGState:
    """질문이 Global(전체 요약)인지 Local(특정 엔티티)인지 분류합니다."""
    question = state["question"]

    prompt = ChatPromptTemplate.from_messages([
        ("system", (
            "질문의 유형을 분류하세요.\n"
            "- global: 전체 데이터에 대한 요약, 주제, 트렌드 질문\n"
            "- local: 특정 인물, 조직, 개념에 대한 구체적 질문\n"
            "'global' 또는 'local'만 출력하세요."
        )),
        ("human", "{question}"),
    ])

    chain = prompt | llm | StrOutputParser()
    search_type = chain.invoke({"question": question})
    return {"search_type": search_type.strip().lower()}

그래프 구성

from langgraph.graph import StateGraph, START, END

def route_search(state: GraphRAGState) -> Literal["global_search", "local_search"]:
    """질문 유형에 따라 검색 전략을 선택합니다."""
    if state.get("search_type") == "global":
        return "global_search"
    return "local_search"

workflow = StateGraph(GraphRAGState)

# 노드 추가
workflow.add_node("classify_question", classify_question)
workflow.add_node("global_search", global_search)
workflow.add_node("local_search", local_search)
workflow.add_node("generate", generate)

# 엣지 연결
workflow.add_edge(START, "classify_question")
workflow.add_conditional_edges("classify_question", route_search, {
    "global_search": "global_search",
    "local_search": "local_search",
})
workflow.add_edge("global_search", "generate")
workflow.add_edge("local_search", "generate")
workflow.add_edge("generate", END)

# 컴파일
app = workflow.compile()

실행

# Global 질문
result = app.invoke({"question": "이 데이터셋의 주요 주제와 트렌드는 무엇인가?"})

# Local 질문
result = app.invoke({"question": "A 회사와 B 기관의 협력 관계는?"})

print(result["generation"])
Graph RAG의 인덱싱 단계에서는 모든 문서 청크에 대해 LLM으로 엔티티/관계를 추출하고, 커뮤니티별 요약을 생성해야 합니다. 이로 인해 인덱싱 비용이 벡터 RAG 대비 크게 증가합니다. 대규모 문서에 적용할 때는 비용을 사전에 추정하세요.

적용 시나리오

사내 문서, 보고서, 이메일 등에서 조직 구조, 프로젝트 관계, 핵심 인물을 파악해야 하는 경우. Global Search로 전체 트렌드를 파악하고, Local Search로 특정 프로젝트 세부사항을 조회합니다.
대량의 논문에서 연구 트렌드, 저자 간 협력 관계, 주요 개념의 발전 과정을 분석하는 경우. 논문 간 인용 관계와 공통 키워드를 그래프로 구성합니다.
복잡한 규정 간의 상호 참조 관계를 파악하고, 특정 조항이 다른 조항에 미치는 영향을 추적하는 경우. 조항 간 참조를 엣지로 구성하여 연쇄적 영향을 분석합니다.

참고 논문

논문학회링크
From Local to Global: A Graph RAG Approach to Query-Focused Summarization (Edge et al., 2024)-arXiv 2404.16130