Field Log · Entry
Hierarchical RAG: Parent-Child·RAPTOR·HippoRAG (4/10)
오늘의 결론
- Hierarchical RAG는 하나의 기법 이름이 아닙니다. 문서 구조, 의미 요약, entity 관계 중 무엇을 계층화하는지 먼저 구분해야 합니다.
- Parent-child는 작은 child로 정확히 검색하고 더 큰 parent를 문맥으로 복원하는 가장 실용적인 출발점입니다.
- RAPTOR는 chunk를 반복적으로 cluster·summarize하여 여러 추상도에서 검색할 수 있는 tree를 만듭니다.
- HippoRAG는 entity와 relation graph에서 연관 기억처럼 신호를 확산해 multi-hop evidence를 찾습니다.
- 고급 구조를 도입해도 최종 답의 근거는 원문 leaf와 source provenance로 내려갈 수 있어야 합니다.
앞 글에서는 이전 검색 결과가 다음 query를 만드는 multi-hop loop를 설계했습니다. 그런데 corpus가 길고 복잡하면 loop보다 먼저 풀어야 할 문제가 있습니다.
검색에 유리한 단위 = 짧고 한 주제에 집중된 chunk
답변에 유리한 단위 = 정의·조건·예외가 함께 있는 넓은 context
전체 질문에 유리한 단위 = 문서 여러 개를 압축한 고수준 summary
관계 질문에 유리한 단위 = entity와 relation으로 연결된 graph
모든 질문에 같은 500-token chunk를 반환하면 이 네 요구를 동시에 만족시키기 어렵습니다. 그래서 retrieval unit과 context unit을 분리하고, 여러 abstraction level을 연결합니다.
그림 1. 네 구조는 서로의 업그레이드 순서가 아니다. 보존하려는 관계가 다르므로 query 유형과 운영 제약에 맞춰 선택하거나 route한다.
먼저 “계층”이 무엇인지 정하자
Hierarchical RAG라는 표현 아래에는 서로 다른 구조가 섞여 있습니다.
| 구조 | 보존하는 관계 | 검색 단위 | 답변에 넘기는 단위 |
|---|---|---|---|
| Flat chunk | 없음 또는 metadata만 | chunk | chunk |
| Parent-child | 문서의 포함 관계 | child | parent 또는 주변 child |
| RAPTOR | 의미적 cluster와 summary | leaf·summary node | 선택한 node와 원문 |
| HippoRAG | entity-relation 연관 관계 | graph node·passage | 관련 passage·path |
| GraphRAG | entity community와 corpus theme | entity·community report | local evidence 또는 global report |
여기서 중요한 것은 tree와 graph의 차이입니다.
- Tree의 node는 보통 한 parent만 가집니다.
- Graph의 node는 여러 종류의 edge로 여러 node와 연결됩니다.
- 문서 목차는 작성자가 만든 구조입니다.
- RAPTOR tree는 embedding과 clustering이 만든 의미 구조입니다.
- HippoRAG graph는 추출된 entity와 relation이 만든 연상 구조입니다.
“계층형이 더 좋다”가 아니라 “어떤 관계를 검색에 보존해야 하는가”가 설계 질문입니다.
기준선: Flat Chunk RAG
가장 단순한 index는 각 chunk를 독립 record로 저장합니다.
{
"chunk_id": "doc-17#c08",
"text": "환불 요청은 결제일로부터 14일 이내...",
"embedding": [0.012, -0.034, 0.091],
"document_id": "doc-17",
"section": "4.2 환불"
}
이 구조는 이해와 운영이 쉽습니다.
- 증분 update가 쉽다.
- vector DB schema가 단순하다.
- chunk별 ACL과 metadata filter를 적용하기 쉽다.
- retrieval failure를 분석하기 쉽다.
하지만 작은 chunk가 검색되면 앞의 정의나 뒤의 예외가 사라질 수 있습니다. 반대로 chunk를 크게 만들면 embedding이 여러 주제를 평균내어 검색 정밀도가 낮아질 수 있습니다.
이를 granularity mismatch라고 볼 수 있습니다.
small chunk → high retrieval precision, low context completeness
large chunk → lower retrieval precision, higher local completeness
Parent-Child Retrieval: 작게 찾고 크게 읽기
Parent-child는 이 mismatch를 직접 해결합니다.
- 문서를 큰 parent section으로 나눕니다.
- 각 parent를 더 작은 child chunk로 나눕니다.
- child만 embedding하여 검색합니다.
- hit가 난 child의 parent를 hydrate합니다.
- parent가 너무 크면 hit 주변 sibling만 선택합니다.
Document
└─ Parent: 4. 환불 정책
├─ Child 4.1: 적용 대상
├─ Child 4.2: 14일 기한 ← vector hit
├─ Child 4.3: 디지털 상품 예외
└─ Child 4.4: 처리 절차
retrieve child 4.2
hydrate parent 4 or window [4.1, 4.2, 4.3]
최소 Data Model
type ChildRecord = {
childId: string;
parentId: string;
documentId: string;
ordinal: number;
text: string;
embedding: number[];
aclId: string;
sourceVersion: string;
};
type ParentRecord = {
parentId: string;
documentId: string;
titlePath: string[];
text: string;
childIds: string[];
aclId: string;
sourceVersion: string;
};
parentId만 있으면 충분해 보이지만 production에서는 다음도 필요합니다.
ordinal: 앞뒤 sibling을 복원합니다.titlePath: 목차 의미를 query와 reranker에 제공합니다.sourceVersion: child와 parent가 같은 문서 version인지 검증합니다.aclId: child hit가 권한 없는 parent를 노출하지 않게 합니다.- character/page offset: citation을 원문 위치로 되돌립니다.
Parent를 무조건 전부 넣지 않는다
Parent가 8,000 token이면 작은 child를 쓴 의미가 사라집니다. Hydration policy를 분리합니다.
function hydrate(hit: ChildHit, parent: Parent): ContextUnit[] {
if (parent.tokenCount <= 1200) return [parent];
return siblings(parent, {
centerOrdinal: hit.ordinal,
before: 1,
after: 2,
maxTokens: 1600,
});
}
질문에 따라 window를 바꿀 수도 있습니다.
- 정의 질문: hit section 전체
- 절차 질문: 앞뒤 순서가 있는 sibling window
- 비교 질문: 같은 heading 아래의 여러 child
- 정확한 수치: hit child + 표/각주 link
Parent-Child의 흔한 실패
Parent duplication
여러 child가 같은 parent를 hit해 context가 반복됩니다. parentId로 deduplicate한 뒤 child score를 집계해야 합니다.
Relevant child dilution
큰 parent 안의 irrelevant text가 generator의 주의를 분산합니다. Hit 위치를 표시하거나 reranker가 parent 내부 evidence span을 다시 고르게 합니다.
Version mismatch
새 child와 이전 parent가 섞이면 존재하지 않는 문맥이 만들어집니다. Index alias를 원자적으로 전환하거나 version을 강제합니다.
ACL widening
검색 가능한 child에서 더 넓은 parent를 읽을 때 권한 범위가 넓어질 수 있습니다. Retrieval과 hydration 모두에서 ACL을 다시 검사합니다.
RAPTOR: 의미를 압축해 Summary Tree 만들기
RAPTOR는 Recursive Abstractive Processing for Tree-Organized Retrieval의 약자입니다. 원문의 leaf chunk를 embedding하고 비슷한 node를 cluster한 뒤, 각 cluster를 요약하여 parent node를 만듭니다. 이 과정을 재귀적으로 반복합니다.
Level 2 [corpus-level summary]
/ \
Level 1 [topic A summary] [topic B summary]
/ | \ / \
Level 0 leaf leaf leaf leaf leaf
목차 tree와 달리 서로 멀리 떨어진 문서의 chunk도 의미가 비슷하면 같은 cluster에 들어갈 수 있습니다.
Indexing Flow
def build_raptor_tree(leaf_nodes, max_levels):
levels = [leaf_nodes]
current = leaf_nodes
for level in range(1, max_levels + 1):
if len(current) <= 1:
break
vectors = embed([node.text for node in current])
clusters = cluster_with_soft_membership(vectors)
parents = []
for cluster in clusters:
summary = summarize([current[i].text for i in cluster.members])
parents.append(Node(
level=level,
text=summary,
child_ids=[current[i].id for i in cluster.members],
source_leaf_ids=union_leaf_ids(cluster),
model_version=SUMMARY_MODEL_VERSION,
))
levels.append(parents)
current = parents
return levels
실제 RAPTOR 논문은 clustering 과정과 soft assignment를 더 정교하게 구성합니다. 위 코드는 운영에 필요한 핵심 data lineage를 보여 주는 단순화된 예시입니다.
왜 Summary Node를 검색하는가
질문이 leaf 표현과 직접 겹치지 않을 수 있기 때문입니다.
Leaf A: "팀은 cache hit가 낮아져 origin egress가 증가했다고 보고했다."
Leaf B: "동시 접속 증가로 database connection saturation이 발생했다."
Summary: "트래픽 성장에 따라 cache와 database 병목이 동시에 나타났다."
Question: "성장 과정에서 나타난 인프라 병목을 요약해 줘."
Summary는 여러 leaf를 묶는 질문과 더 가까울 수 있습니다. 반면 정확한 숫자나 예외 조항은 leaf가 더 잘 보존합니다.
Collapsed Retrieval과 Tree Traversal
RAPTOR류 tree에서 검색 전략은 크게 두 가지로 생각할 수 있습니다.
Collapsed retrieval
- 여러 level의 node를 하나의 후보 pool처럼 검색합니다.
- query와 가장 가까운 leaf·summary를 함께 고릅니다.
- 구현이 단순하지만 level별 score 분포가 다를 수 있습니다.
Tree traversal
- 상위 node에서 관련 branch를 고릅니다.
- 선택한 child로 내려가며 search space를 좁힙니다.
- 효율적일 수 있지만 상위 routing 오류가 관련 leaf를 통째로 제거할 수 있습니다.
Production에서는 level별 quota를 두는 방법도 유용합니다.
retrieval_budget:
leaf_nodes: 8
local_summaries: 4
global_summaries: 2
rerank_together: true
require_leaf_provenance: true
Summary는 Evidence가 아니라 Index Artifact다
Abstractive summary에는 세 가지 손실이 생길 수 있습니다.
- 세부 조건이 생략됩니다.
- 서로 다른 source의 문장이 과도하게 합쳐집니다.
- 요약 model이 source에 없는 연결을 만들 수 있습니다.
따라서 summary node에는 최소한 다음 lineage가 필요합니다.
{
"node_id": "raptor-l2-07",
"level": 2,
"summary": "...",
"child_ids": ["raptor-l1-12", "raptor-l1-19"],
"source_leaf_ids": ["doc-3#c2", "doc-8#c7", "doc-9#c1"],
"source_versions": ["2026-07-01"],
"summary_model": "model@version",
"prompt_hash": "sha256:..."
}
Summary로 recall을 얻고, 답변 전에는 source leaf를 hydrate하여 claim을 검증하는 편이 안전합니다.
HippoRAG: 연상 기억처럼 Graph를 탐색하기
HippoRAG는 인간의 장기 기억 체계에서 영감을 받아 LLM, knowledge graph, Personalized PageRank 계열 graph search를 결합한 연구입니다. 핵심 직관은 단어가 비슷한 passage만 찾는 대신 entity와 relation을 따라 관련 기억을 연상하는 것입니다.
단순화하면 indexing은 다음과 같습니다.
Passage
↓ Open Information Extraction
(subject, relation, object)
↓ entity normalization
Knowledge Graph
+ entity ↔ passage provenance edges
예를 들어 다음 두 passage가 있습니다.
P1: Aurora Labs developed the Zephyr compiler.
P2: Aurora Labs was acquired by Northstar Group in 2024.
Graph에는 다음 관계가 생깁니다.
Zephyr compiler ←developed— Aurora Labs —acquired by→ Northstar Group
↖ passage P1 ↗ passage P2
질문 entity에서 시작한 personalization signal을 graph에 확산하면 lexical overlap이 약한 연결 passage도 높은 score를 얻을 수 있습니다.
Personalized PageRank 직관
일반 PageRank가 graph 전체의 중요도를 찾는다면 Personalized PageRank(PPR)는 특정 seed에 자주 돌아오도록 random walk를 편향합니다.
p(next) = α · seed_distribution
+ (1 - α) · transition_matrixᵀ · p(current)
seed_distribution: query와 연결된 entity에 높은 확률을 줍니다.transition_matrix: relation·passage edge를 따라 이동합니다.α: seed로 돌아오는 정도입니다.
식은 간단하지만 성능은 graph quality에 강하게 의존합니다.
OpenIE가 틀리면 Graph도 틀린다
원문: "Northstar는 Aurora 인수를 검토했지만 철회했다."
잘못된 triple: (Northstar, acquired, Aurora)
Negation, modality, time, entity alias를 잃으면 graph traversal이 거짓 bridge를 강화합니다. Relation edge에 다음 정보를 붙여야 합니다.
- source passage ID와 exact span
- extraction confidence
- negation·modality
- valid time와 observed time
- extractor model·prompt version
- ACL label
Entity resolution도 중요합니다. Aurora, Aurora Labs, Aurora Lab Inc.가 같은 회사인지, 동명의 프로젝트인지 구분하지 못하면 edge가 잘못 합쳐집니다.
HippoRAG와 Microsoft GraphRAG는 무엇이 다른가
둘 다 graph를 사용하지만 주된 retrieval objective가 다릅니다.
| 질문 | HippoRAG식 연상 graph | Microsoft GraphRAG식 community |
|---|---|---|
| 핵심 목적 | entity 관계를 따라 multi-hop evidence 발견 | corpus의 local entity 또는 global theme 탐색 |
| 주요 index artifact | entity·relation·passage graph | entity·relationship·community report |
| 대표 scoring 직관 | query seed에서 graph signal 확산 | local context 조합 또는 community report map-reduce |
| 강점 후보 | 연결 고리가 필요한 구체 질문 | corpus-wide theme·요약 질문 |
| 대표 위험 | extraction·resolution edge 오류 | summary 비용·community report 손실 |
둘은 경쟁 제품 목록이 아닙니다. 예를 들어 global query는 community report로, entity chain query는 associative graph로 route할 수 있습니다.
네 구조를 Query Type으로 선택하기
Flat Chunk가 맞는 경우
- corpus가 작고 문서가 짧습니다.
- 질문이 명확한 keyword나 한 passage에 대응합니다.
- update가 매우 잦아 index rebuild를 최소화해야 합니다.
- 먼저 안정적인 baseline이 필요합니다.
Parent-Child가 맞는 경우
- 정책·매뉴얼·기술 문서처럼 section 구조가 안정적입니다.
- 정확한 hit와 주변 조건을 함께 읽어야 합니다.
- 원문 provenance와 증분 update가 중요합니다.
RAPTOR가 맞는 경우
- 여러 문서나 section을 가로지르는 thematic question이 많습니다.
- 여러 abstraction level에서 질문이 들어옵니다.
- offline summary build 비용과 version 관리가 가능합니다.
HippoRAG가 맞는 경우
- entity와 relation을 잇는 multi-hop 질문이 많습니다.
- lexical overlap이 약한 연결 evidence를 찾아야 합니다.
- 높은 품질의 extraction·entity resolution을 운영할 수 있습니다.
하나로 합친 Hybrid Architecture
모든 query를 가장 비싼 index로 보내기보다 route합니다.
Query classifier
├─ exact / local clause ─────→ child vector → parent hydration
├─ broad theme ──────────────→ RAPTOR summary + leaf drill-down
├─ entity chain ─────────────→ HippoRAG graph + passage hydration
└─ uncertain ────────────────→ parallel small budgets → fusion
결과는 공통 EvidenceCandidate contract로 정규화합니다.
type EvidenceCandidate = {
sourceId: string;
sourceVersion: string;
text: string;
retrievalChannel: "child" | "raptor" | "graph";
retrievalScore: number;
matchedNodeId: string;
leafProvenance: string[];
path?: Array<{ from: string; relation: string; to: string }>;
aclDecision: "allow" | "deny";
};
이후 reranker가 query relevance, evidence specificity, source authority, freshness를 함께 봅니다. 서로 다른 channel의 raw score를 그대로 비교하지 말고 rank fusion이나 calibrated score를 사용합니다.
Incremental Update는 구조마다 다르다
Parent-Child
변경된 document의 parent와 child만 다시 만듭니다. 가장 단순합니다.
RAPTOR
Leaf 하나가 바뀌면 cluster assignment와 상위 summary가 달라질 수 있습니다. 전체 rebuild가 비싸다면 affected subtree만 갱신하되, tree consistency를 검증해야 합니다.
changed leaf
→ re-embed
→ reassign cluster
→ regenerate affected parent
→ repeat toward root
→ atomically publish tree version
HippoRAG
새 triple을 추가하는 것만으로 끝나지 않습니다. Alias merge, obsolete relation, time validity, deleted passage의 dangling edge를 처리해야 합니다.
모든 경우에 query log가 참조한 index version을 남겨야 과거 결과를 재현할 수 있습니다.
계층형 RAG 평가 설계
최종 answer score만 보면 구조가 어디서 도움을 줬는지 알 수 없습니다.
Retrieval Metric
- Leaf Recall@k: 정답 원문 leaf가 후보에 있는가?
- Parent Coverage: 필요한 조건을 포함한 parent가 복원됐는가?
- Abstraction Match: 질문에 적절한 level의 node를 골랐는가?
- Path Recall: gold entity-relation path를 찾았는가?
- Source Coverage: summary가 가리키는 필수 source가 모두 내려왔는가?
Faithfulness Metric
- Summary claim이 child source에 의해 지지되는가?
- Graph edge가 exact source span과 일치하는가?
- Parent hydration이 hit와 무관한 claim을 끌어오지 않았는가?
Operational Metric
- index build 시간과 model token 비용
- document update 후 searchable time
- node·edge·vector storage
- query latency p50/p95
- retrieval channel별 cache hit
반드시 할 Ablation
A. flat child only
B. child + parent hydration
C. RAPTOR summary only
D. RAPTOR summary + leaf hydration
E. graph retrieval only
F. routed hybrid
같은 generator, 같은 prompt, 같은 context budget에서 비교해야 retrieval 구조의 효과를 볼 수 있습니다. Oracle leaf를 넣은 실험은 generation ceiling을 보여 줍니다.
Failure를 첫 단계 기준으로 기록하기
failure_stage:
- query_routing
- child_retrieval
- parent_hydration
- cluster_assignment
- summary_generation
- entity_extraction
- entity_resolution
- graph_traversal
- leaf_provenance
- context_selection
- answer_generation
예를 들어 정답을 못 맞힌 두 trace가 있어도 원인은 다릅니다.
Trace A: correct summary → missing leaf provenance → unsupported answer
Trace B: wrong entity merge → wrong PPR path → plausible false answer
구조별 관측 지점을 만들지 않으면 둘 다 answer_wrong으로 뭉개집니다.
구현 순서
1단계: Flat Baseline 고정
- parser와 chunk quality를 먼저 검증합니다.
- Recall@k, nDCG, answer faithfulness 기준선을 만듭니다.
- document·chunk·source version schema를 고정합니다.
2단계: Parent-Child
- child hit에서 parent를 안정적으로 복원합니다.
- duplicate parent와 context budget을 제어합니다.
- ACL widening test를 추가합니다.
3단계: Query Slice 분석
- broad synthesis failure가 많은지 봅니다.
- entity multi-hop failure가 많은지 봅니다.
- 실제 failure가 있을 때 RAPTOR나 graph를 선택합니다.
4단계: Advanced Index를 Shadow로 운영
- 기존 결과를 바꾸기 전에 offline replay합니다.
- 새로운 channel의 후보와 비용을 trace에만 기록합니다.
- quality-cost frontier가 좋아질 때 일부 traffic을 route합니다.
5단계: Provenance Gate
- summary-only answer를 금지합니다.
- graph path의 모든 relation이 source span을 가져야 합니다.
- 삭제되거나 권한 없는 leaf가 있으면 evidence를 제외합니다.
흔한 오해와 처방
1. RAPTOR는 단순 문서 목차 Tree다
RAPTOR의 상위 node는 의미적 clustering과 summarization으로 만들어집니다. 작성자 목차와 같지 않습니다.
처방: structural parent와 semantic parent를 다른 ID·edge type으로 저장합니다.
2. Summary가 더 짧으니 항상 더 정확하다
Summary는 scope query에 유리할 수 있지만 세부 사실과 예외를 잃을 수 있습니다.
처방: summary는 candidate discovery에 쓰고 leaf evidence로 검증합니다.
3. Graph에 넣으면 Multi-hop이 자동 해결된다
잘못된 extraction과 entity merge는 오류를 더 멀리 확산합니다.
처방: edge provenance, confidence, temporal qualifier와 graph-specific gold set을 둡니다.
4. Parent는 검색된 Child의 문서 전체다
문서 전체를 넣으면 context dilution과 token 폭증이 생깁니다.
처방: section parent 또는 sibling window를 budget 안에서 hydrate합니다.
5. 모든 Raw Score를 한 줄로 정렬한다
Vector similarity, summary score, graph PPR score는 의미와 분포가 다릅니다.
처방: channel별 rank를 fusion하거나 validation set으로 calibration합니다.
6. 고급 Index부터 만든다
Parser와 leaf quality가 나쁘면 tree와 graph가 그 오류를 증폭합니다.
처방: flat baseline과 failure slice를 먼저 고정합니다.
Production 체크리스트
- hierarchy가 structural·semantic·relational 중 무엇인지 정의했다.
- child, parent, summary, graph node의 ID namespace가 분리돼 있다.
- 모든 상위 node에서 source leaf로 내려갈 수 있다.
- parent hydration에 token budget과 deduplication이 있다.
- child와 parent의 document version 일치를 검사한다.
- summary model·prompt·tree version을 기록한다.
- graph edge에 source span·time·negation·confidence가 있다.
- entity resolution merge와 split을 감사할 수 있다.
- retrieval channel마다 ACL을 검사한다.
- 서로 다른 channel score를 calibration 또는 fusion한다.
- leaf recall·parent coverage·path recall을 따로 측정한다.
- flat baseline과 oracle ablation이 있다.
- 증분 update 후 dangling lineage를 검사한다.
- advanced index의 비용과 freshness SLO가 있다.
스스로 확인하기
- 작은 child로 검색한 뒤 큰 parent를 넣는 이유는 무엇인가?
- RAPTOR summary node를 그대로 최종 evidence로 사용하면 어떤 문제가 생기는가?
- HippoRAG식 graph와 GraphRAG community report의 retrieval objective는 어떻게 다른가?
- Leaf 한 개가 수정됐을 때 parent-child, RAPTOR, graph index의 update 범위는 어떻게 다른가?
- 여러분의 corpus에서 실패가 broad synthesis 때문인지 entity relation 때문인지 어떤 slice로 확인할 것인가?
다음 글에서는 텍스트만으로는 잃기 쉬운 표·차트·레이아웃을 다루기 위해 OCR/layout pipeline과 ColPali·VisRAG식 visual document retrieval을 비교합니다.
참고자료
- RAPTOR: Recursive Abstractive Processing for Tree-Organized Retrieval
- HippoRAG: Neurobiologically Inspired Long-Term Memory for Large Language Models
- From Local to Global: A Graph RAG Approach to Query-Focused Summarization
- The PageRank Citation Ranking: Bringing Order to the Web
- Open Information Extraction from the Web