Field Log · Entry

Microsoft GraphRAG 해부: Local·Global·DRIFT Search (2/10)

TextUnit에서 entity·relation을 추출해 Leiden community report를 만들고 entity 중심 local, corpus 중심 global, 반복 탐색 DRIFT로 routing하는 Microsoft GraphRAG 구조

오늘의 결론

  • Microsoft GraphRAG는 모든 graph 기반 RAG의 일반 이름이 아니라, unstructured text에서 graph와 community report를 만드는 구체적인 pipeline입니다.
  • Local search는 특정 entity 주변의 graph·원문을, global search는 계층별 community report를 map-reduce해 corpus 전체 질문을 다룹니다.
  • DRIFT는 관련 community report로 넓은 출발점을 만든 뒤 local follow-up을 반복해 breadth와 detail을 조합합니다.
  • Community report는 원문이 아니라 LLM이 만든 파생 summary입니다. Entity·relationship·report에서 TextUnit까지 provenance를 유지합니다.
  • Indexing은 많은 LLM 호출과 storage를 요구할 수 있습니다. Basic/vector baseline과 질문 유형별 route 없이는 비용 대비 이득을 판단할 수 없습니다.

앞 글에서는 Knowledge Graph의 entity·relation·claim·provenance를 배웠습니다. 이번에는 그 원리를 실제로 널리 알려진 Microsoft GraphRAG architecture에 대입합니다.

먼저 용어를 구분해야 합니다.

graph RAG
= graph를 검색·추론에 사용하는 넓은 architecture 범주

Microsoft GraphRAG
= Microsoft Research가 공개한 논문·구현의 구체적인
  indexing knowledge model + query methods

다른 graph RAG가 반드시 Leiden community나 community report를 쓰는 것은 아닙니다.

Microsoft GraphRAG의 indexing pipeline과 basic, local, global, DRIFT query route

그림 1. Indexing은 TextUnit에서 graph와 계층적 community report를 만들고, query time에는 질문의 scope에 따라 basic·local·global·DRIFT route를 선택한다.


왜 “From Local to Global”인가?

기본 vector RAG는 query와 가까운 text chunk를 찾습니다. 다음 질문에는 잘 맞습니다.

“문서에서 Scrooge는 누구인가?”

하지만 다음 질문은 query와 직접 비슷한 한 문단이 없습니다.

“이 corpus 전체에서 반복되는 핵심 주제와
그 주제들이 서로 충돌하는 방식은 무엇인가?”

이것은 query-focused summarization 문제입니다. 전체 dataset의 여러 부분을 집계해야 합니다. Edge 등의 GraphRAG 논문은 entity graph를 community로 나누고 각 community를 미리 요약한 뒤, query 때 관련 report를 map-reduce하는 접근을 제안했습니다.

핵심 idea:

raw documents
→ local entity relationships
→ communities of related entities
→ summaries at multiple levels
→ global corpus reasoning

현재 공식 Knowledge Model의 Artifact

Microsoft GraphRAG 공식 dataflow는 다음 object를 정의합니다.

Artifact의미원문과의 연결
Document입력 문서TextUnit 목록
TextUnitgraph extraction 단위인 chunkDocument ID·text
EntityTextUnit에서 추출한 대상source TextUnit
Relationship두 entity의 연결source TextUnit
Covariate시간 범위를 가질 수 있는 claimsource TextUnit
Communityentity graph의 계층 clusterentity·relation
Community Reportcommunity의 LLM summarycommunity 구성요소

Covariate라는 이름은 처음 보면 통계 변수처럼 느껴지지만 공식 dataflow에서는 extracted claim artifact를 가리킵니다.

중요한 현재 동작:

  • Claim extraction은 optional입니다.
  • 공식 문서상 기본으로 꺼져 있습니다.
  • 유용하게 쓰려면 domain에 맞는 prompt tuning이 일반적으로 필요합니다.
  • FastGraphRAG indexing option에서는 NLP로 entity·relation을 추출해 LLM 자원을 줄이고 claim extraction을 생략합니다.

Version에 따라 설정과 artifact schema가 바뀔 수 있으므로 설치한 release의 공식 문서와 migration note를 고정합니다.

Indexing 6단계

Phase 1. Document → TextUnit

문서를 graph extraction용 chunk로 나눕니다.

Document D1
├─ TextUnit T1
├─ TextUnit T2
└─ TextUnit T3

공식 문서의 현재 default dataflow 설명은 기본 chunk size를 1,200 token으로 제시하지만 이는 보편적 최적값이 아닙니다. 큰 TextUnit은 호출 수를 줄이는 대신 extraction fidelity와 source localization을 떨어뜨릴 수 있습니다.

평가할 값:

  • entity mention이 chunk boundary에서 잘리는 비율
  • 한 TextUnit에 섞인 topic 수
  • relation 양 끝 entity가 같이 들어오는 비율
  • token·LLM call 비용
  • provenance span의 정밀도

Phase 2. Document와 TextUnit 연결

Document table과 TextUnit mapping을 만듭니다. Query에 Document가 직접 쓰이지 않더라도 citation, 삭제, revision propagation에 필요합니다.

{
  "document_id": "doc:42",
  "revision": "7",
  "text_unit_ids": ["tu:4201", "tu:4202", "tu:4203"]
}

Phase 3. Graph Extraction

각 TextUnit에서 entity와 relationship을 추출합니다.

TextUnit
→ entities(title, type, description)
→ relationships(source, target, description)

공식 default dataflow는 같은 title과 type의 entity description을 모으고, 같은 source-target relation description을 모은 뒤 LLM으로 단일 description을 요약합니다.

여기에는 주의가 필요합니다.

같은 title + type
≠ 언제나 같은 현실 entity

동명이인과 tenant별 namespace가 있는 production data에서는 31편의 entity resolution을 별도 보강해야 합니다.

Claim extraction을 켜면 status와 time-bound가 있는 positive factual statement를 Covariate로 만들 수 있습니다. 부정·불확실성·인용 주체가 중요한 domain은 default prompt와 schema가 충분한지 직접 검증합니다.

Phase 4. Hierarchical Community Detection

Entity graph에 hierarchical Leiden algorithm을 적용해 community hierarchy를 만듭니다.

Level 0: 전체 graph
├─ Community A: 공급망
│  ├─ A1: 반도체 업체
│  └─ A2: 물류 업체
└─ Community B: 보안 사고
   ├─ B1: 계정 탈취
   └─ B2: 데이터 유출

Community는 미리 정한 topic label이 아니라 graph 연결 구조에서 나온 cluster입니다. 잘못 추출된 high-degree node가 있으면 unrelated entity가 한 community로 뭉칠 수 있습니다.

관찰할 값:

  • community size distribution
  • hierarchy depth
  • singleton·giant community 비율
  • cross-community edge 비율
  • seed·algorithm version에 따른 안정성

Phase 5. Community Report

각 community의 entity·relationship·claim을 LLM으로 요약합니다.

{
  "community_id": "community:A1",
  "level": 2,
  "title": "반도체 공급망",
  "summary": "...",
  "findings": ["..."],
  "source_entity_ids": ["org:1", "org:2"],
  "source_relationship_ids": ["rel:8", "rel:9"],
  "report_version": "community-report@4"
}

Report는 검색을 빠르게 하고 global overview를 가능하게 하지만 압축 손실과 hallucination이 있습니다.

원문 오류
→ extraction 오류
→ graph 오류
→ community 구성 오류
→ report summary 오류

따라서 report에서 entity·relationship·TextUnit로 내려가는 lineage가 필요합니다.

Phase 6. Embedding

공식 default dataflow는 downstream vector search가 필요한 artifact를 embedding합니다. 현재 문서에는 entity description, TextUnit text, community report text가 기본 대상으로 설명됩니다.

GraphRAG도 vector search를 사용합니다. “Graph 대 Vector”가 아니라 graph artifact를 vector로 찾아 graph structure를 확장하는 hybrid입니다.

Basic Search: 가장 싼 Baseline

Basic search는 top-k vector text retrieval에 가까운 route입니다.

query embedding
→ TextUnit vector search
→ top-k context
→ answer

다음 질문은 basic으로 충분할 수 있습니다.

“C-91 계약의 갱신일은?”
“문서에 적힌 환불 기한은?”

Graph index를 만들었다고 모든 query를 graph route로 보낼 이유는 없습니다.

Local Search: Entity 주변을 깊게 보기

Local search는 특정 entity를 이해하는 질문에 적합합니다.

“Nova Systems와 관련된 계약·사고·프로젝트는?”

공식 방법론의 흐름:

  1. Query와 semantically related한 entity를 entity description embedding에서 찾습니다.
  2. Entity에 연결된 후보를 확장합니다.
  3. Entity, relationship, TextUnit, community report, optional covariate를 수집합니다.
  4. 각 source를 rank·filter합니다.
  5. 정한 context window budget 안에 넣습니다.
  6. Conversation history와 함께 response를 생성합니다.

Context가 여러 table의 혼합이라는 점이 중요합니다.

local_context_budget:
  text_units: 45%
  relationships: 20%
  entities: 15%
  community_reports: 15%
  covariates: 5%

위 비율은 예시입니다. 실제 질문별로 text unit과 community report의 marginal value를 평가해야 합니다.

Local failure:

  • query entity가 잘못 mapping됨
  • high-degree entity 주변 noise 폭발
  • important relation이 entity summary에 묻힘
  • context budget을 report가 독점
  • 원문보다 파생 summary가 우선됨

Global Search: Community Report를 Map-Reduce

Global search는 dataset 전체의 theme·pattern을 묻는 질문에 맞습니다.

“전체 민원에서 반복되는 장애 유형과 조직별 대응 차이는?”

Map

선택한 hierarchy level의 community report를 token block으로 나누고, 각 block에서 query 관련 point와 중요도 rating을 생성합니다.

{
  "point": "인증 장애가 세 조직에서 반복됨",
  "importance": 87,
  "source_report_ids": ["cr:14", "cr:28"]
}

Reduce

중요 point를 filter·aggregate해 final context와 answer를 만듭니다.

community reports
→ parallel map responses
→ importance filtering
→ reduce synthesis

Hierarchy level trade-off:

  • 높은 level: report 수가 적고 넓지만 detail 손실
  • 낮은 level: detail이 많지만 token·latency·LLM 호출 증가

Global search는 query와 관련된 원문 몇 개를 찾는 방식보다 비용이 클 수 있습니다. Community report generation이라는 offline 비용과 map-reduce online 비용을 모두 계산합니다.

DRIFT Search: Global Primer에서 Local Follow-up으로

DRIFT는 Dynamic Reasoning and Inference with Flexible Traversal의 약자입니다. 공식 설명의 세 phase는 다음과 같습니다.

A. Primer

Query와 semantic하게 가까운 top-k community report를 찾아 broad initial answer와 follow-up question을 생성합니다.

B. Follow-Up

각 follow-up에 local search를 사용해 더 구체적인 intermediate answer를 만들고, 필요하면 다시 follow-up을 생성합니다.

C. Output Hierarchy

질문·답변 tree를 relevance와 confidence에 따라 정리해 final answer를 만듭니다.

query
→ community primer
  ├─ follow-up A → local evidence → answer A
  │  └─ deeper follow-up
  └─ follow-up B → local evidence → answer B
→ ranked synthesis

DRIFT는 local과 global의 단순 평균이 아닙니다. Community information으로 탐색의 breadth를 만들고 local search로 detail을 채우는 iterative query process입니다.

필수 budget:

drift_budget:
  primer_reports: 5
  max_depth: 2
  max_branches: 6
  max_model_calls: 12
  max_context_tokens: 24000
  deadline_ms: 20000

No-progress, duplicate question, evidence sufficiency stop rule가 없으면 search tree가 비용을 소모합니다.

네 Query Mode를 어떻게 고를까?

질문 유형추천 시작 route이유
정확한 사실·문서 lookupBasicgraph overhead 불필요
특정 entity와 주변 관계Localentity neighborhood + source text
corpus 전체 theme·집계Globalcommunity report map-reduce
넓은 질문에서 세부 원인 탐색DRIFTglobal primer + iterative local

Router input:

{
  "scope": "CORPUS_WIDE",
  "entity_count": 0,
  "requires_aggregation": true,
  "requires_multi_step": false,
  "latency_budget_ms": 12000,
  "cost_tier": "MEDIUM"
}

23편의 adaptive router처럼 static rule로 시작하고 confusion matrix를 만든 뒤 classifier나 learned policy로 발전시킵니다.

Indexing Cost를 계산하기

GraphRAG indexing 비용은 문서 크기만으로 결정되지 않습니다.

TextUnit extraction calls
+ entity/relationship summarization calls
+ optional claim extraction calls
+ community report generation calls
+ embedding calls
+ retry and failed parse cost

Run manifest 예:

{
  "index_run_id": "graphrag-index-20260716-03",
  "documents": 1280,
  "text_units": 18420,
  "entities": 93210,
  "relationships": 211840,
  "communities": 4250,
  "model_calls": 24780,
  "input_tokens": 39800000,
  "output_tokens": 7400000,
  "failed_units": 61,
  "graph_snapshot": "graph@31"
}

실제 값은 corpus와 config에 따라 크게 달라집니다. 시작 전에 작은 representative sample로 tokens per TextUnit, entities per unit, community count를 측정해 extrapolate합니다.

Incremental Update와 Provenance

문서 하나가 바뀌면 다음 파생물을 추적해야 합니다.

Document revision
→ changed TextUnits
→ entities/relationships/claims
→ affected communities
→ affected community reports
→ embeddings

전체 rebuild는 단순하지만 비쌉니다. Incremental update는 싸지만 stale edge와 community drift를 남길 수 있습니다.

정책 예:

  • 작은 append: changed TextUnit subgraph merge
  • entity resolution 충돌: affected component 재계산
  • ontology/extractor 변경: full or partition rebuild
  • community algorithm 변경: community/report full rebuild
  • source 삭제: lineage 기반 derived artifact delete

Snapshot alias를 atomic하게 바꿔 query가 old/new table을 섞지 않게 합니다.

Prompt Tuning이 필요한 이유

공식 GraphRAG 문서도 domain data에 prompt tuning을 권장합니다. Default entity type과 extraction instruction이 법률·의료·반도체 domain의 핵심 relation을 알 수 없습니다.

Prompt tuning checklist:

  • entity type 목록과 positive/negative example
  • relation direction과 allowed schema
  • acronym·alias·identifier rule
  • negation·uncertainty·time handling
  • source language와 multilingual name normalization
  • output schema와 repair policy
  • PII·secret redaction

Prompt를 바꿀 때 graph extraction gold set과 query end-to-end set을 모두 재평가합니다.

Community Report를 Evidence처럼 다루지 않기

Community report는 여러 extraction을 다시 요약한 text입니다. Citation에는 다음 chain을 유지합니다.

answer claim
→ map/reduce point
→ community report
→ entity/relationship/claim IDs
→ TextUnit IDs
→ Document revision + source spans

Report만 citation으로 보여 주면 사용자는 원문을 검증할 수 없습니다. High-stakes answer는 final generation 전에 source TextUnit를 hydrate하고 contradiction을 확인합니다.

Security와 Multi-Tenant Graph

Community는 여러 document의 정보를 섞습니다. 서로 다른 ACL의 TextUnit가 같은 report에 들어가면 query-time filter로 원문을 숨겨도 summary가 이미 private fact를 포함할 수 있습니다.

안전한 선택:

  • tenant·ACL boundary별 graph와 community를 분리
  • report 생성 전에 allowed source만 사용
  • report lineage에 source ACL union 저장
  • principal별 query context filter
  • shared graph가 필요하면 public artifact만 community summarization
  • 삭제·revocation 시 report 재생성

29편의 commit authorization과 달리 여기의 핵심은 read-time information flow입니다.

평가를 Query Mode별로 나누기

Index Quality

  • entity/relationship precision·recall
  • source TextUnit linkage accuracy
  • community coherence·coverage
  • report claim faithfulness

Local

  • seed entity accuracy
  • relevant relation·TextUnit Recall@k
  • neighborhood noise ratio
  • entity question answer accuracy

Global

  • corpus theme coverage
  • report-level and source-level faithfulness
  • map point redundancy
  • hierarchy level별 quality·cost

DRIFT

  • useful follow-up rate
  • duplicate branch rate
  • evidence gain per call
  • stop decision accuracy
  • depth·branch·token distribution

Route

  • basic/local/global/DRIFT confusion matrix
  • avoidable expensive route rate
  • quality under latency·cost budget

최소 Ablation

A  Basic vector search
B  Local without community reports
C  Local with community reports
D  Global at hierarchy level 0/1/2
E  DRIFT with depth 1/2/3
F  Source hydration off/on

전체 평균뿐 아니라 fact, entity, corpus-wide, exploratory question bucket별로 비교합니다.

흔한 실패 패턴

1. GraphRAG를 Library 이름이 아닌 만능 개념으로 사용

어떤 indexing과 query method인지 논의할 수 없습니다.

처방: generic graph RAG와 Microsoft GraphRAG 구현을 구분합니다.

2. Community Report를 원문 사실로 간주

여러 단계의 extraction·summary 오류가 누적됩니다.

처방: report→graph artifact→TextUnit→Document provenance를 유지합니다.

3. 모든 Query를 Global Search로 보냄

단순 fact에도 map-reduce 비용을 냅니다.

처방: basic baseline과 scope-aware router를 둡니다.

4. Default Prompt를 Domain Ontology로 착각

핵심 entity·relation을 놓치거나 title merge 오류가 납니다.

처방: gold extraction set으로 prompt와 resolution을 조정합니다.

5. Index Build 성공만 확인

Graph가 생성돼도 잘못된 community와 summary일 수 있습니다.

처방: artifact별 품질과 query mode별 end-to-end를 모두 평가합니다.

6. Incremental Update에서 Snapshot을 섞음

새 entity table과 오래된 report가 함께 검색됩니다.

처방: immutable snapshot과 atomic alias promotion을 사용합니다.

Production 체크리스트

  • generic graph RAG와 Microsoft GraphRAG를 구분했다.
  • Document→TextUnit→entity/relation/report lineage가 있다.
  • claim extraction의 optional/default-off 상태와 prompt 요구를 확인했다.
  • title·type merge 외에 domain entity resolution을 검증했다.
  • Leiden community size·depth·stability를 관측한다.
  • community report의 source faithfulness를 평가한다.
  • basic·local·global·DRIFT route의 질문 유형을 정의했다.
  • local mixed context의 source별 token budget이 있다.
  • global map·reduce 호출 수와 hierarchy level을 제한한다.
  • DRIFT depth·branch·call·deadline·no-progress budget이 있다.
  • indexing token·cost·failure manifest를 기록한다.
  • incremental update와 source deletion lineage가 있다.
  • ACL이 다른 source를 community report에 무심코 섞지 않는다.
  • query mode별 quality·latency·cost ablation이 있다.

스스로 확인하기

  1. Local과 global search가 각각 어떤 artifact를 중심으로 context를 만드는가?
  2. Community report가 원문 evidence와 같지 않은 이유는 무엇인가?
  3. DRIFT가 local+global 단일 호출과 다른 점은 무엇인가?
  4. Claim extraction이 현재 default dataflow에서 어떤 상태인지 설명할 수 있는가?
  5. 여러분의 query log를 basic·local·global·DRIFT 네 bucket으로 나눌 수 있는가?

다음 글에서는 정답을 찾기 위해 이전 observation으로 다음 query를 만드는 multi-hop retrieve↔reason loop, query decomposition, IRCoT, stop rule을 구현합니다.

참고자료