본문 바로가기
AI2026년 8월 21일7분 읽기

GraphRAG — 지식 그래프로 다중 홉 질문 푸는 RAG

YS
김영삼
조회 3
GraphRAG — 지식 그래프로 다중 홉 질문 푸는 RAG

GraphRAG는 문서를 그냥 잘라 벡터로 검색하는 대신, 문서에서 엔티티(개체)와 관계를 뽑아 지식 그래프로 만든 뒤 그 그래프를 근거로 답을 생성하는 RAG 방식이다. "이 인물과 저 회사가 어떻게 얽혀 있나" 같은 여러 홉(multi-hop)에 걸친 질문에서, 조각난 벡터 청크로는 답이 안 나오던 문제를 풀기 위해 나왔다.

나는 사내 위키와 회의록 수천 건에 일반 RAG를 붙였다가 벽에 부딪혔다. "A 프로젝트에 관여한 사람들이 그 전에 같이 일한 팀은?" 같은 질문을 던지면, 벡터 검색은 A 프로젝트 문서만 잔뜩 물어오고 정작 "그 전에 같이 일한 팀"으로 이어지는 연결고리는 못 찾았다. 답이 여러 문서에 흩어져 있으면 유사도 검색만으로는 부족하더라.

일반 RAG가 막히는 지점

표준 RAG는 질문과 청크의 의미적 유사도로 검색한다. 그래서 "질문에 직접 나온 단어와 비슷한 청크"는 잘 찾지만, 답이 A→B→C처럼 관계를 타고 넘어가야 나오는 경우엔 약하다. 또 하나, "이 문서 전체의 핵심 주제를 요약해줘" 같은 전역적(global) 질문에도 약하다. 상위 k개 청크만 보니까 큰 그림을 못 그린다.

GraphRAG는 이 둘을 정면으로 노린다. 관계를 그래프로 명시해 다중 홉을 걷게 하고, 그래프를 커뮤니티로 묶어 요약해 두면 전역 질문에도 답할 수 있다.

파이프라인 — 인덱싱 단계

GraphRAG의 무게중심은 검색이 아니라 인덱싱에 있다. 오프라인에서 LLM을 돌려 그래프를 만드는 이 단계가 비용의 대부분을 차지한다.

  1. 청킹: 문서를 적당한 크기로 자른다(보통 겹침 포함).
  2. 엔티티·관계 추출: 각 청크에서 LLM으로 "개체와 그들 사이의 관계"를 뽑는다. 예: (홍길동)-[소속]->(A팀).
  3. 그래프 구축: 추출된 트리플을 노드·엣지로 합친다. 같은 엔티티는 병합한다.
  4. 커뮤니티 탐지: Leiden 같은 알고리즘으로 촘촘히 연결된 노드 묶음(커뮤니티)을 찾는다.
  5. 커뮤니티 요약: 각 커뮤니티를 LLM으로 요약해 둔다. 이게 전역 질문의 재료가 된다.
EXTRACT_PROMPT = """다음 텍스트에서 개체와 관계를 JSON으로 추출하라.
각 개체는 {name, type}, 각 관계는 {source, target, relation, desc} 형식.
텍스트:
{chunk}
"""

def extract_graph(chunks, llm):
    nodes, edges = {}, []
    for c in chunks:
        data = llm.json(EXTRACT_PROMPT.format(chunk=c))
        for e in data["entities"]:
            key = e["name"].strip().lower()
            nodes.setdefault(key, e)          # 같은 이름은 병합
        for r in data["relations"]:
            edges.append((r["source"], r["target"], r))
    return nodes, edges

여기서 삽질 포인트 하나. 엔티티 병합을 이름 문자열로만 하면 "홍길동"과 "길동 홍"이 따로 논다. 나는 처음에 이걸 대충 소문자 매칭만 했다가 그래프가 파편화됐다. 별칭 정규화나 임베딩 기반 병합을 한 겹 더 넣어야 그래프가 촘촘해진다.

검색 — 로컬 vs 글로벌

질문 종류에 따라 두 가지 검색 모드를 쓴다.

모드적합한 질문동작
로컬(local)특정 개체 중심 "X는 누구와 일했나"해당 노드에서 이웃을 따라 확장
글로벌(global)전체 주제 "핵심 이슈는 뭔가"커뮤니티 요약을 map-reduce로 종합

로컬 검색은 질문에서 엔티티를 찾아 그 노드의 이웃, 관련 청크, 관계를 컨텍스트로 모은다. 글로벌 검색은 커뮤니티 요약들을 각각 부분 답으로 만든 뒤(map), 그 부분 답들을 다시 합쳐(reduce) 최종 답을 낸다.

솔직한 트레이드오프

좋게만 들리지만 공짜가 아니다. 인덱싱에서 문서마다 LLM을 여러 번 호출하니 초기 비용과 시간이 일반 RAG의 수 배에서 수십 배다. 문서가 자주 바뀌면 그래프 갱신 비용도 만만치 않다. 그래서 나는 이렇게 선을 긋는다.

단순한 "이 문서에 뭐라고 쓰여 있나" 질문이 대부분이면 일반 RAG로 충분하다. GraphRAG는 개체들의 관계를 넘나드는 질문이나 전체를 요약해야 하는 분석형 질문이 핵심 가치일 때만 쓸 값어치가 있다.

실무에선 둘을 섞는 게 답이었다. 기본은 벡터 검색으로 빠르게 처리하고, 라우터가 "이건 관계형·전역 질문"이라고 판단하면 GraphRAG 경로로 보낸다. 전부를 그래프로 밀어붙이면 비용이 감당이 안 된다.

자주 묻는 질문

GraphRAG는 일반 RAG를 완전히 대체하나요?

아닙니다. 대부분의 단순 사실 조회 질문은 일반 벡터 RAG가 더 싸고 빠릅니다. GraphRAG는 여러 개체의 관계를 타고 넘어가는 다중 홉 질문이나, 문서 전체를 종합해야 하는 전역 요약 질문에서 강점이 있습니다. 실무에선 질문 유형에 따라 두 방식을 라우팅해 섞어 쓰는 경우가 많습니다.

인덱싱 비용이 그렇게 비싼 이유는?

문서의 모든 청크에서 엔티티와 관계를 뽑고, 커뮤니티를 요약하는 과정이 전부 LLM 호출이기 때문입니다. 문서 수에 비례해 호출이 늘어 초기 인덱싱이 일반 RAG보다 훨씬 비쌉니다. 값싼 소형 모델로 추출 단계를 처리하고, 요약만 상위 모델을 쓰는 식으로 비용을 조절합니다.

엔티티가 중복·파편화되는 문제는 어떻게 막나요?

이름 문자열 매칭만으로는 "홍길동"과 "길동 홍" 같은 표기 차이나 별칭을 하나로 못 묶습니다. 이름 정규화, 별칭 사전, 또는 임베딩 유사도 기반 엔티티 해상(entity resolution)을 한 단계 추가해야 그래프가 촘촘해지고 검색 품질이 올라갑니다.

문서가 자주 바뀌면 그래프를 매번 다시 만들어야 하나요?

전체 재구축은 비쌉니다. 변경된 문서에서 나온 엔티티·관계만 부분적으로 갱신하고 영향받은 커뮤니티만 다시 요약하는 증분 업데이트를 설계하는 편이 현실적입니다. 갱신 빈도가 매우 높은 데이터라면 GraphRAG 자체가 적합하지 않을 수 있습니다.

댓글 0

아직 댓글이 없습니다.
Ctrl+Enter로 등록