---
title: "LangChain RAG 복습 04: hybrid search와 parent document retrieval"
slug: "langchain-rag-notes-04-hybrid-parent-retrieval"
canonicalUrl: "https://moonshotnotes.com/posts/langchain-rag-notes-04-hybrid-parent-retrieval/"
sourceUrl: "https://moonshotnotes.com/posts/langchain-rag-notes-04-hybrid-parent-retrieval/"
markdownUrl: "https://moonshotnotes.com/agent/posts/langchain-rag-notes-04-hybrid-parent-retrieval.md"
language: "ko"
category: "AI Backend"
updatedAt: "2026-07-15"
agentTokenEstimate: 2265
---

# LangChain RAG 복습 04: hybrid search와 parent document retrieval

LangChain RAG 학습 흐름을 따라 keyword search, semantic search, hybrid search, parent document retrieval, multi vector retrieval로 문서 구조를 살려 검색하는 방법을 정리합니다.

## Agent metadata

- Source: https://moonshotnotes.com/posts/langchain-rag-notes-04-hybrid-parent-retrieval/
- Markdown: https://moonshotnotes.com/agent/posts/langchain-rag-notes-04-hybrid-parent-retrieval.md
- Language: ko
- Category: AI Backend
- Tags: LLM, RAG, LangChain, Hybrid Search, Parent Retrieval
- Updated: 2026-07-15
- Estimated tokens: 2265

## 작은 chunk만으로는 부족한 순간

3편에서는 retriever 결과를 그대로 믿지 않고 metadata filter, multi-query retrieval, context compression, reranking으로 후보를 좁히는 흐름을 봤다. 그 단계까지 지나면 prompt에 넣을 context를 고를 준비가 어느 정도 된다.

그런데 검색 후보를 잘 좁혔는데도 답변이 어색한 경우가 있다. 이번에도 1편부터 이어 온 가상의 학습용 문서 `sample-office-guide.md`를 사용한다. 실제 내부 문서가 아니라 검색 흐름을 설명하기 위해 만든 예시다. 사용자가 이렇게 묻는다.

```text
# 사용자의 질문은 회의실 예약 조건과 장비 대여 조건을 함께 묻는다.
회의실에서 쓰는 모니터는 어디서 빌리고, 예약은 언제까지 해야 하나?
```

이 질문은 한 가지 section에만 들어 있지 않다. "회의실"과 "예약"은 `reservation`에 있고, "모니터"와 "빌리다"는 `equipment`에 가깝다. 작은 chunk만 보면 각각의 문장은 잘 검색될 수 있지만, 답변을 만들 때는 두 section을 함께 읽어야 한다.

이때 문제는 세 가지로 나뉜다.

| 문제 | 증상 | 필요한 검색 설계 |
|---|---|---|
| 표현 차이 | 문서에는 `장비 대여`, 질문에는 `모니터 빌리기`가 나온다 | keyword search와 semantic search를 함께 본다 |
| 맥락 부족 | 작은 chunk는 맞지만 주변 조건이 빠진다 | 작은 chunk로 찾고 부모 문서 단위로 가져온다 |
| 관점 차이 | 같은 문서를 제목, 요약, 본문 표현으로 다르게 찾고 싶다 | 한 문서를 여러 vector 표현으로 인덱싱한다 |

4편은 이 세 문제를 따라간다. 핵심은 검색을 더 복잡하게 만드는 것이 아니라, "어떤 단위로 찾고 어떤 단위로 읽을 것인가"를 분리하는 것이다.

이 글에서 남길 적용 기준은 단순하다. 질문 표현을 놓치면 hybrid search를 검토하고, 작은 chunk만으로 답변 맥락이 부족하면 parent document retrieval을 검토하고, 같은 문서를 여러 관점으로 찾아야 하면 multi-vector retrieval을 검토한다.

## keyword search와 semantic search는 서로 다른 실패를 낸다

semantic search는 의미가 비슷한 문장을 찾는 데 강하다. 반대로 keyword search는 특정 단어가 반드시 들어간 문서를 찾는 데 강하다. 둘 중 하나만 쓰면 실패 방식이 달라진다.

예시 문서를 조금 늘려 보자.

```python
from langchain_core.documents import Document

# 같은 사내 안내 문서를 section 단위 chunk로 나눠 검색 차이를 비교한다.
docs = [
    Document(
        page_content="회의실 예약은 사용 시작 1시간 전까지 가능합니다.",
        metadata={"source": "sample-office-guide.md", "section": "reservation"},
    ),
    Document(
        page_content="대형 모니터와 화상 회의 장비는 안내 데스크에서 대여 기록 후 사용할 수 있습니다.",
        metadata={"source": "sample-office-guide.md", "section": "equipment"},
    ),
    Document(
        page_content="방문자는 보안 데스크 승인 후 임시 출입 카드를 발급받아야 합니다.",
        metadata={"source": "sample-office-guide.md", "section": "security"},
    ),
]
```

질문이 `모니터 빌리기`라면 keyword search는 `모니터`라는 단어를 직접 잡을 수 있다. 하지만 사용자가 `화면 공유 장비`라고 물으면 keyword만으로는 놓칠 수 있다. semantic search는 표현 차이를 줄여 주지만, 반대로 "회의실"이라는 큰 주제만 보고 예약 chunk를 먼저 올릴 수도 있다.

그래서 hybrid search는 두 결과를 함께 본다.

```python
# keyword 결과와 semantic 결과를 합친 뒤 중복을 제거하고 다시 점수화한다.
keyword_hits = keyword_retriever.invoke("모니터 빌리기")
semantic_hits = semantic_retriever.invoke("회의실에서 화면 공유 장비를 쓰려면 어떻게 하나?")

candidate_docs = merge_unique_docs(
    keyword_hits,
    semantic_hits,
    key=lambda doc: (doc.metadata["source"], doc.metadata["section"], doc.page_content),
)
```

중요한 것은 "hybrid를 쓰면 좋아진다"가 아니다. keyword가 잡은 문서와 semantic이 잡은 문서를 나란히 봐야 한다. 둘이 같은 section을 가리키면 좋은 신호다. 서로 전혀 다른 section을 가리키면 질문이 여러 의도를 담고 있거나, 검색 기준이 너무 넓다는 뜻일 수 있다.

## hybrid search는 결과를 합친 뒤 다시 읽어야 한다

hybrid search의 실수는 두 검색 결과를 단순히 이어 붙이는 것이다. 그렇게 하면 후보가 늘어날 뿐 prompt context가 좋아지는 것은 아니다.

아래처럼 결과를 비교한다.

| 검색 방식 | 상위 결과 | 읽을 점 |
|---|---|---|
| keyword | `equipment`: 대형 모니터와 화상 회의 장비 | 질문의 장비 표현을 직접 잡았다 |
| semantic | `reservation`: 회의실 예약은 1시간 전까지 | 회의실 이용 맥락을 잡았다 |
| hybrid merge | `equipment` + `reservation` | 답변에는 두 section이 모두 필요하다 |

이 질문에서는 두 결과가 모두 필요하다. "모니터는 어디서 빌리나"와 "예약은 언제까지 하나"가 한 질문 안에 들어 있기 때문이다. 반대로 질문이 `모니터는 어디서 빌리나요?` 하나뿐이라면 reservation chunk는 빼는 편이 낫다.

hybrid search 다음에는 최소한 다음 항목을 남긴다.

- keyword가 잡은 문서
- semantic이 잡은 문서
- 둘이 겹치는 문서
- prompt에 넣을 최종 문서
- 제외한 문서와 제외 이유

이 기록이 없으면 hybrid search는 품질 개선이 아니라 `k` 값을 늘린 것과 비슷해진다. 검색 후보가 많아졌는데 왜 좋아졌는지 설명할 수 없기 때문이다.

## parent document retrieval은 찾는 단위와 읽는 단위를 나눈다

작은 chunk는 검색에 좋다. 문장이 짧고 초점이 분명하면 질문과의 유사도 비교가 쉬워진다. 하지만 답변에는 주변 맥락이 필요할 때가 많다. 이때 parent document retrieval이 등장한다.

아이디어는 단순하다.

```text
# 검색은 작은 단위로 시작하고 답변 context는 더 큰 parent 단위로 되돌린다.
작은 child chunk로 검색한다
-> child chunk가 속한 parent document id를 찾는다
-> prompt에는 parent document나 parent section을 넣는다
```

예를 들어 `equipment` section 안에 아래 세 문장이 있다고 하자.

> 대형 모니터와 화상 회의 장비는 안내 데스크에서 대여 기록 후 사용할 수 있습니다.
>
> 장비는 사용 후 전원을 끄고 원래 위치에 반납해야 합니다.
>
> 고장이나 분실이 있으면 총무팀에 즉시 알려야 합니다.

질문이 `모니터는 어디서 빌리나요?`라면 첫 문장만 검색되어도 충분해 보인다. 하지만 답변에는 반납 조건이나 문제 발생 시 연락 기준이 함께 필요할 수 있다. 작은 chunk만 prompt에 넣으면 답변은 짧고 정확해 보이지만, 실제 안내로는 부족할 수 있다.

이럴 때 child chunk에는 parent id를 남긴다.

```python
# 검색은 작은 chunk로 하고, 답변 context는 parent section으로 되돌린다.
child_chunk = Document(
    page_content="대형 모니터와 화상 회의 장비는 안내 데스크에서 대여 기록 후 사용할 수 있습니다.",
    metadata={
        "source": "sample-office-guide.md",
        "section": "equipment",
        "parent_id": "sample-office-guide.md#equipment",
    },
)

parent_section = Document(
    page_content=(
        "대형 모니터와 화상 회의 장비는 안내 데스크에서 대여 기록 후 사용할 수 있습니다. "
        "장비는 사용 후 전원을 끄고 원래 위치에 반납해야 합니다. "
        "고장이나 분실이 있으면 총무팀에 즉시 알려야 합니다."
    ),
    metadata={
        "source": "sample-office-guide.md",
        "section": "equipment",
        "parent_id": "sample-office-guide.md#equipment",
    },
)
```

parent document retrieval을 쓰면 검색과 답변의 역할이 분리된다. child chunk는 "어디를 볼지"를 찾고, parent section은 "무엇을 읽고 답할지"를 제공한다.

## parent를 크게 잡으면 노이즈도 같이 커진다

parent document retrieval은 항상 좋은 선택이 아니다. parent 단위가 너무 크면 prompt에 불필요한 문장이 많이 들어간다. 작은 chunk로 잘 찾았는데, 다시 큰 parent를 가져오면서 불필요한 문맥을 되살릴 수 있다.

parent 단위는 다음 기준으로 잡는다.

| parent 단위 | 적합한 경우 | 위험 |
|---|---|---|
| 같은 section | 사내 안내, 제품 문서처럼 섹션 의미가 분명할 때 | section 안에 여러 주제가 섞이면 노이즈가 생긴다 |
| 같은 page | FAQ나 문서 페이지 하나가 한 주제를 다룰 때 | 페이지가 길면 prompt 비용이 커진다 |
| 같은 heading subtree | Markdown 문서의 H2/H3 구조가 안정적일 때 | heading 파싱이 깨지면 parent가 흔들린다 |
| 원문 전체 | 짧은 정책 문서나 짧은 공지 | 대부분의 기술 문서에서는 너무 넓다 |

`sample-office-guide.md`에서는 section 단위가 적당하다. 회의실, 장비, 보안이 서로 다른 업무 흐름이기 때문이다. 질문이 둘 이상의 section을 걸치면 필요한 parent section만 여러 개 가져오면 된다. 문서 전체를 넣을 필요는 없다.

## multi-vector retrieval은 한 문서를 여러 관점으로 찾는다

parent document retrieval이 "작게 찾고 크게 읽기"라면, multi-vector retrieval은 "같은 문서를 여러 표현으로 찾기"에 가깝다.

하나의 section을 본문 그대로만 embedding하면 질문 표현과 맞지 않을 수 있다. 그래서 같은 parent에 대해 여러 검색용 표현을 만들 수 있다.

```python
# 하나의 parent section을 여러 검색 표현으로 인덱싱한다.
index_docs = [
    Document(
        page_content="대형 모니터와 화상 회의 장비 대여",
        metadata={"parent_id": "sample-office-guide.md#equipment", "view": "title"},
    ),
    Document(
        page_content="회의실에서 화면 공유 장비를 쓰려면 안내 데스크에서 장비 대여 기록을 남긴다.",
        metadata={"parent_id": "sample-office-guide.md#equipment", "view": "summary"},
    ),
    Document(
        page_content="대형 모니터와 화상 회의 장비는 안내 데스크에서 대여 기록 후 사용할 수 있습니다.",
        metadata={"parent_id": "sample-office-guide.md#equipment", "view": "body"},
    ),
]
```

사용자가 `화면 공유 장비`라고 물으면 요약 표현이 잘 걸릴 수 있다. 사용자가 `모니터 대여`라고 물으면 제목이나 본문 표현이 잘 걸릴 수 있다. 검색된 표현은 다르지만 최종 답변 context는 같은 parent section으로 돌아간다.

multi-vector retrieval에서 중요한 것은 metadata다. 여러 vector가 같은 parent를 가리킨다는 연결이 남아 있어야 한다. 그렇지 않으면 같은 문서의 title, summary, body가 서로 다른 문서처럼 prompt에 중복으로 들어갈 수 있다.

## 세 전략을 한 번에 켜지 않는다

hybrid search, parent document retrieval, multi-vector retrieval은 모두 검색 품질을 높일 수 있다. 하지만 한 번에 켜면 어떤 전략이 무엇을 바꿨는지 알기 어렵다.

같은 질문으로 작은 실험을 만든다.

```text
# 같은 질문으로 검색 전략을 하나씩 추가하며 결과 변화를 비교한다.
question: 회의실에서 쓰는 모니터는 어디서 빌리고, 예약은 언제까지 해야 하나?

baseline:
- semantic search top 3

experiment 1:
- keyword + semantic hybrid search

experiment 2:
- hybrid search
- child hit -> parent section fetch

experiment 3:
- parent section fetch
- title/summary/body multi-vector index
```

각 실험에서 보는 지표는 단순하다.

| 지표 | 질문 |
|---|---|
| 필요한 section이 들어왔는가 | `reservation`, `equipment`가 모두 잡혔는가 |
| 불필요한 section이 줄었는가 | `security`가 빠졌는가 |
| 답변 context가 충분한가 | 반납 조건이나 예약 조건이 빠지지 않았는가 |
| 중복이 생기지 않았는가 | 같은 parent가 여러 번 들어오지 않았는가 |

이렇게 보면 검색 설계가 감이 아니라 비교가 된다. 어떤 전략이 필요한지 질문 패턴과 문서 구조로 설명할 수 있다.

적용 예시는 이렇게 잡을 수 있다. 사내 문서 검색 챗봇에서 `회의실 모니터 대여` 질문이 들어왔는데 `reservation`만 잡히면 hybrid search를 켜서 `equipment` 후보가 함께 들어오는지 확인한다. 반대로 `equipment` parent를 너무 크게 잡아 보안, 방문자, 반납 안내까지 모두 섞이면 주의 사례로 남긴다. 이때는 parent 단위를 문서 전체가 아니라 section이나 heading subtree로 줄여야 한다.

## 다음 글로 넘길 기준

이 글까지 오면 RAG의 검색 쪽 흐름은 꽤 넓어진다. 문서는 chunk로 나뉘고, chunk는 vector store에 들어가고, retriever는 후보를 가져오고, 검색 후보는 filter, multi-query, compression, reranking을 거쳐 prompt context 후보가 된다. 여기에 hybrid search, parent document retrieval, multi-vector retrieval이 붙으면 검색 단위와 답변 단위를 분리해서 볼 수 있다.

다음 단계는 답변을 만드는 경계다. 이제 prompt에는 단순히 `{context}`를 넣는 것만으로 부족하다. context가 어떤 source에서 왔는지, 답변에 어떤 source를 표시할지, 모르면 모른다고 말할 조건을 어떻게 둘지 정해야 한다.

다음 글에서는 prompt template과 source attribution을 함께 본다. 검색된 context가 답변 문장으로 바뀌는 순간, RAG는 "검색이 됐다"에서 "근거를 가진 답변을 만들었다"로 넘어간다.

## 자주 묻는 질문

Q. hybrid search를 쓰면 semantic search만 쓸 때보다 항상 좋은가?

A. 아니다. 질문이 의미 표현만 다를 때는 좋아질 수 있지만, keyword 결과가 불필요한 문서를 많이 끌어오면 오히려 prompt context가 지저분해진다. hybrid 결과는 반드시 merge, dedup, rerank 단계를 거쳐야 한다.

Q. parent document retrieval에서 parent는 얼마나 커야 하나?

A. 답변에 필요한 주변 조건을 포함하되, 다른 주제가 많이 섞이지 않는 크기가 좋다. 사내 안내 문서라면 section 단위가 시작점이 될 수 있고, 긴 기술 문서라면 heading subtree나 작은 page 단위가 더 낫다.

Q. multi-vector retrieval은 문서를 복제하는 것인가?

A. 답변용 문서를 복제한다기보다 검색용 표현을 여러 개 두는 방식에 가깝다. title, summary, body처럼 서로 다른 view가 같은 parent를 가리키게 만들고, 최종 context는 parent 기준으로 중복 제거해야 한다.

이 글을 실제 프로젝트에 적용할 때는 먼저 baseline semantic search 결과를 저장하고, 그 다음 hybrid, parent fetch, multi-vector를 하나씩 추가한다. 각 단계에서 필요한 section이 늘었는지, 불필요한 section이 줄었는지, 같은 parent가 중복으로 들어오지 않는지를 확인하면 다음 글의 prompt와 출처 표시 단계로 넘어갈 기준이 생긴다.
