RAG에서 Retrieval은 단순히 "비슷한 문서를 찾는 기능"이 아니다.
사용자의 애매한 질문을 검색 가능한 형태로 바꾸고, 의미 검색과 키워드 검색을 조합하고, 권한과 날짜 같은 조건을 지키면서, LLM이 답변할 수 있는 가장 좋은 근거를 찾아오는 전체 과정이다.
사용자 질문
-> 질문 정리 / 재작성
-> 검색 방식 선택
-> 후보 문서 검색
-> 필터링
-> 재정렬
-> LLM에 넣을 근거 구성
-> 답변 생성
LLM이 아무리 좋아도 잘못된 문서를 주면 잘못된 답을 만든다. RAG의 품질은 상당 부분 Retrieval 품질에서 결정된다.
Retrieval은 도서관 사서 역할에 가깝다.
사용자가 사서에게 이렇게 묻는다고 하자.
"환불은 언제까지 돼요?"
사서는 전체 책을 다 읽어주는 것이 아니라, 이 질문에 답할 수 있는 가장 관련 있는 책과 페이지를 먼저 찾아준다. RAG에서 Retrieval도 같다.
사용자 질문: "환불은 언제까지 돼요?"
검색 대상: 약관, FAQ, 정책 문서, 상담 매뉴얼
Retrieval 결과: 환불 기간과 조건이 적힌 문서 조각
LLM 역할: 그 문서 조각을 읽고 자연어 답변 작성
중요한 점은 LLM이 답변을 만드는 사람이라면, Retrieval은 답변에 필요한 자료를 골라주는 사람이라는 것이다.
RAG는 Retrieval-Augmented Generation의 약자다.
Retrieval : 관련 문서 찾기
Augmented : 찾은 문서를 프롬프트에 추가하기
Generation : LLM이 근거를 보고 답변하기
전체 흐름은 다음과 같다.
[사용자 질문]
|
v
[질문 정리 / 재작성]
|
v
[검색]
- 키워드 검색
- 벡터 검색
- 하이브리드 검색
|
v
[후보 문서]
|
v
[필터링 / 재정렬]
|
v
[LLM Context 구성]
|
v
[답변 생성 + 출처 표시]
RAG 장애를 분석할 때는 "LLM이 틀렸다"라고 바로 말하면 안 된다. 보통은 다음 중 하나다.
| 문제 위치 | 증상 |
|---|---|
| 문서 수집 | 중요한 문서가 애초에 들어오지 않음 |
| Chunking | 문서가 이상하게 잘려 검색이 안 됨 |
| Embedding | 의미가 제대로 표현되지 않음 |
| Query rewrite | 사용자 의도와 다른 검색어로 바뀜 |
| Retrieval | 정답 문서가 top-k 안에 안 들어옴 |
| Filtering | 오래된 문서나 권한 없는 문서가 섞임 |
| Reranking | 정답 후보가 있었지만 아래로 밀림 |
| Context packing | 관련 없는 문서가 LLM에 같이 들어감 |
| Generation | 근거는 맞는데 LLM이 잘못 요약함 |
Retrieval에는 크게 세 가지 방식이 있다.
1. 키워드 검색
2. 벡터 검색
3. 하이브리드 검색
키워드 검색은 질문에 들어 있는 단어와 문서에 들어 있는 단어를 비교한다.
예를 들어:
질문: "API 429 에러 해결 방법"
이 경우 API, 429, 에러라는 정확한 단어가 중요하다. 의미가 비슷한 문서보다 429가 실제로 들어 있는 문서를 찾는 것이 더 중요할 수 있다.
대표 알고리즘은 BM25다.
BM25는 대략 다음을 본다.
이 단어가 문서에 자주 나오는가?
이 단어가 전체 문서에서는 드문 단어인가?
문서 길이에 비해 이 단어가 얼마나 중요한가?
장점:
단점:
벡터 검색은 단어가 아니라 의미를 비교한다.
예:
질문: "돈은 언제 다시 들어와요?"
문서: "환불은 영업일 기준 3~5일 내 처리됩니다."
두 문장은 단어가 많이 겹치지 않지만 의미는 가깝다. 이런 경우 벡터 검색이 강하다.
장점:
단점:
하이브리드 검색은 키워드 검색과 벡터 검색을 함께 쓰는 방식이다.
BM25 검색 결과 top 50
Vector 검색 결과 top 50
-> 병합
-> 중복 제거
-> 재정렬
-> 최종 top 5
예를 들어 다음 질문을 보자.
"API 429 에러가 나면 어떻게 해요?"
여기서는 429라는 정확한 키워드도 중요하고, "요청 제한", "rate limit", "too many requests" 같은 의미적 연결도 중요하다.
하이브리드 검색이 좋은 경우:
Embedding은 텍스트를 숫자 벡터로 바꾸는 과정이다.
"환불은 언제 가능한가요?"
-> [0.12, -0.44, 0.87, 0.03, ...]
이 숫자는 사람이 읽기 위한 값이 아니다. 컴퓨터가 문장 사이의 의미적 거리를 계산하기 위한 좌표다.
비유하면, 모든 문장을 거대한 의미 지도 위에 배치하는 것이다.
"환불 기간이 궁금해요"
"돈은 언제 돌아오나요"
"결제 취소 처리 기간은?"
이 문장들은 표현은 다르지만 의미가 비슷하므로 가까운 위치에 놓인다.
반면 다음 문장은 멀리 떨어진다.
"비밀번호를 변경하고 싶어요"
Dense embedding은 대부분의 RAG에서 쓰는 일반적인 임베딩이다. 문장 하나를 고정 길이 숫자 배열로 바꾼다.
문장 하나 -> 768차원 벡터
문장 하나 -> 1536차원 벡터
문장 하나 -> 3072차원 벡터
장점:
약점:
Sparse embedding은 단어 기반 신호를 더 강하게 보존한다. BM25, TF-IDF, SPLADE 같은 방식이 여기에 가깝다.
예:
"퇴직금 중간정산 조건"
이 검색에서는 퇴직금, 중간정산, 조건이라는 단어 자체가 중요하다.
Sparse 방식은 정확한 용어 검색에 유리하지만, 표현이 달라지면 약해질 수 있다.
일반 dense embedding은 문서 하나를 벡터 하나로 압축한다.
문서 chunk -> 벡터 1개
반면 ColBERT 같은 late interaction 방식은 토큰 단위 표현을 더 많이 보존한다.
문서 chunk -> 토큰별 벡터 여러 개
정확도는 좋아질 수 있지만 저장 공간과 계산 비용이 늘어난다.
Vector DB에는 보통 다음과 같은 데이터가 저장된다.
{
"id": "refund_policy_001_chunk_003",
"vector": [0.12, -0.44, 0.87, 0.03],
"payload": {
"text": "상품 수령 후 7일 이내에는 환불 신청이 가능합니다.",
"document_id": "refund_policy_001",
"title": "환불 정책",
"section": "일반 환불",
"language": "ko",
"created_at": "2026-01-10",
"effective_from": "2026-01-01",
"tenant_id": "company_a",
"permission_group": "customer_support"
}
}
핵심은 세 가지다.
| 구성 요소 | 의미 |
|---|---|
id | chunk를 식별하는 고유 ID |
vector | 검색용 숫자 벡터 |
payload / metadata | 원문, 제목, 출처, 날짜, 권한, 언어, 테넌트 정보 |
사용자가 질문하면 질문도 embedding된다.
질문: "환불 며칠 걸려요?"
-> query vector
Vector DB는 query vector와 가까운 document vector를 찾는다.
벡터 간 가까움을 계산할 때는 주로 다음을 쓴다.
| 방식 | 설명 |
|---|---|
| Cosine similarity | 방향이 얼마나 비슷한지 비교 |
| Dot product | 두 벡터의 내적값 비교 |
| Euclidean distance | 실제 거리 비교 |
검색 시스템에서는 embedding 모델이 어떤 유사도 방식을 전제로 학습되었는지 확인해야 한다. 모델은 dot product를 전제로 만들었는데 DB에서는 cosine을 쓰면 품질이 달라질 수 있다.
벡터가 1,000개라면 전부 비교해도 된다.
하지만 벡터가 1,000만 개라면 질문 하나가 들어올 때마다 1,000만 번 비교하는 것은 비싸다.
그래서 Vector DB는 빠른 탐색을 위한 인덱스를 만든다.
대표 인덱스:
| 인덱스 | 설명 |
|---|---|
| HNSW | 가까운 벡터끼리 그래프처럼 연결해 빠르게 탐색 |
| IVF | 벡터 공간을 여러 구역으로 나누고 가까운 구역만 탐색 |
| PQ | 벡터를 압축해 저장 비용을 줄임 |
| SQ | 숫자 정밀도를 줄여 메모리 사용량 감소 |
| DiskANN | 디스크 기반 대규모 벡터 검색에 유리 |
항상 trade-off가 있다.
정확도 recall
속도 latency
비용 cost
메모리 memory
업데이트 편의성
필터링 정확도
대표적인 Vector DB는 다음처럼 정리할 수 있다.
| Vector DB | 강점 | 적합한 상황 |
|---|---|---|
| Pinecone | 완전관리형, 서버리스, dense/sparse/full-text 검색 지원 | 인프라 운영 부담 없이 빠르게 프로덕션 RAG를 만들 때 |
| Weaviate | 스키마 기반 객체 관리, BM25 + vector hybrid search | 검색 기능을 애플리케이션 레벨에서 풍부하게 다루고 싶을 때 |
| Qdrant | Rust 기반, HNSW 중심, payload filtering 강점 | self-host, 필터링이 많은 RAG |
| Milvus | 대규모 분산 검색, 다양한 인덱스, 대용량 처리 | 수천만~수십억 벡터 규모 |
| pgvector | PostgreSQL 안에서 벡터 검색 가능 | 이미 Postgres 중심이고 규모가 중간 이하일 때 |
| Elasticsearch / OpenSearch | 키워드 검색, 필터링, 로그/문서 검색 강함 | 기존 검색 인프라가 ES 계열일 때 |
선택 기준은 다음 질문으로 정리할 수 있다.
데이터가 얼마나 큰가?
업데이트가 자주 일어나는가?
필터링 조건이 많은가?
권한 제어가 중요한가?
키워드 검색이 중요한가?
운영 인력이 있는가?
latency 목표가 얼마인가?
비용 제한이 있는가?
"가장 좋은 Vector DB"는 없다. 좋은 선택은 데이터 규모, 검색 패턴, 운영 방식에 맞는 DB를 고르는 것이다.
RAG에서 문서를 어떻게 자르느냐가 검색 품질을 크게 좌우한다.
나쁜 chunk:
제3조 회사는 다음과 같은 경우 이를 제한할 수 있다.
이 조각만 보면 무엇에 대한 내용인지 알 수 없다.
좋은 chunk:
환불 정책 - 환불 제한 조건
회사는 상품이 사용되었거나 포장이 훼손된 경우 환불을 제한할 수 있다.
좋은 chunk에는 문맥이 포함되어야 한다.
좋은 chunking 원칙:
"7일 이내 가능합니다."
무엇이 7일 이내인지 모른다.
환불, 배송, 쿠폰, 회원탈퇴 정책이 한 chunk에 모두 들어감
질문과 관련 없는 내용이 같이 들어가서 검색 정확도와 답변 품질이 떨어진다.
실무에서는 parent-child retrieval도 자주 쓴다.
검색은 작은 child chunk로 한다.
LLM에게는 해당 child가 포함된 큰 parent 문단을 준다.
이렇게 하면 검색 정확도와 답변 문맥을 동시에 챙길 수 있다.
실무 RAG에서 metadata filtering은 필수다.
예를 들어 같은 "환불 정책"이라도 사용자마다 봐야 하는 문서가 다를 수 있다.
{
"country": "KR",
"service": "premium",
"effective_from": "2026-01-01",
"document_status": "active",
"permission_group": "support_team",
"tenant_id": "company_a"
}
검색할 때는 이런 조건을 함께 적용해야 한다.
tenant_id = 현재 고객사
language = ko
effective_from <= 오늘
document_status = active
permission_group in 사용자 권한
필터링을 하지 않으면 다음 문제가 생긴다.
따라서 Retrieval은 단순 유사도 검색이 아니라 권한과 조건이 포함된 검색이어야 한다.
사용자 질문은 검색에 적합하지 않은 경우가 많다.
"그거 지난번에 말한 거 돼요?"
"이거 안 되면 돈 돌려받을 수 있어요?"
"로그인이 자꾸 안 됨"
Query rewrite는 이런 질문을 검색하기 좋은 형태로 바꾸는 과정이다.
예:
원 질문:
"이거 안 되면 돈 돌려받을 수 있어?"
재작성:
"서비스 장애 또는 상품 이용 불가 상황에서 환불 가능 조건과 환불 신청 절차는 무엇인가?"
동의어, 약어, 오탈자를 사전으로 처리한다.
취소 -> 취소, 환불, 반품
as -> A/S, 애프터서비스, 수리
로그인 안됨 -> 로그인 실패, 인증 오류
장점:
단점:
한국어에서는 형태소 분석이 특히 중요하다.
한국어는 조사가 붙고, 어미가 바뀌고, 띄어쓰기가 흔들린다.
"환불이 가능한가요"
"환불은 가능해요?"
"환불도 되나요?"
사람에게는 같은 말이지만 검색 시스템에는 다르게 보일 수 있다.
형태소 분석을 하면 핵심 단어를 뽑을 수 있다.
"배송조회번호를 못 찾겠어요"
-> 배송 / 조회 / 번호 / 찾다
한국어 RAG에서 중요한 처리:
예:
"퇴직금중간정산 되나요"
-> 퇴직금 / 중간정산 / 중간 정산 / 가능 / 조건
이 처리를 거치면 BM25와 hybrid search 품질이 좋아진다.
LLM에게 질문을 검색용 문장으로 바꾸게 한다.
원 질문:
"돈 언제 들어와?"
Rewrite:
"환불 승인 후 결제 수단별 환불 처리 기간은 얼마인가?"
장점:
단점:
그래서 원본 질문을 버리기보다 함께 쓰는 것이 좋다.
원본 질문
rewrite 질문
키워드 확장 질문
이렇게 여러 검색 쿼리를 만드는 방식을 multi-query retrieval이라고 한다.
BERT류 모델은 질문의 의미를 보존하면서 검색에 유리한 표현을 만들거나, query-document 관련도를 판단하는 데 사용할 수 있다.
활용 방식:
LLM rewrite보다 가볍게 운영할 수도 있고, 도메인 데이터로 fine-tuning하면 안정성이 좋아질 수 있다.
먼저 가볍게 검색한 뒤, 나온 결과의 용어를 보고 다시 검색한다.
1차 검색:
"환불"
검색 결과에서 발견된 표현:
"청약철회", "결제취소", "영업일 기준"
2차 검색:
"청약철회 결제취소 환불 처리 기간 영업일"
이 방식은 사내 문서처럼 고유한 표현이 많은 경우 유리하다.
단점은 1차 검색이 완전히 빗나가면 2차 검색도 같이 흔들릴 수 있다는 점이다.
HyDE는 Hypothetical Document Embeddings의 약자다.
질문을 바로 embedding하지 않고, 먼저 "이 질문에 답이 될 법한 가상의 문서"를 생성한다.
질문:
"환불 며칠 걸려요?"
가상 문서:
"환불은 상품 수령 후 7일 이내 신청할 수 있으며, 결제 취소는 영업일 기준 3~5일 내 처리됩니다."
그다음 이 가상 문서를 embedding해서 검색한다.
왜 효과가 있을까?
짧은 질문보다 답변처럼 생긴 문장이 실제 문서와 embedding 공간에서 더 가까울 수 있기 때문이다.
장점:
단점:
검색기는 보통 넓게 가져온다.
1차 검색: 후보 50개
Reranking: 최종 5개 선택
Vector DB는 빠르게 후보를 찾는 데 강하다. 하지만 top-1이 항상 정답은 아니다.
Reranker는 질문과 문서를 함께 보고, 진짜 관련도가 높은 순서로 다시 정렬한다.
예:
질문:
"환불 처리 기간은?"
후보 문서 A:
"환불은 영업일 기준 3~5일 내 처리됩니다."
후보 문서 B:
"환불 신청은 마이페이지에서 가능합니다."
후보 문서 C:
"배송은 영업일 기준 2~3일 소요됩니다."
벡터 검색에서는 A, B, C가 모두 비슷하게 나올 수 있다. Reranker는 A가 가장 직접적인 답이라는 것을 더 잘 판단한다.
일반적인 구조:
Retriever: 빠르게 top 50 검색
Reranker: 정확하게 top 5 재정렬
LLM: top 5 근거로 답변
Reranking은 특히 다음 상황에서 중요하다.
검색 결과를 그대로 LLM에 넣으면 안 된다.
LLM context에는 제한이 있고, 관련 없는 문서가 섞이면 답변 품질이 떨어진다.
Context packing은 검색된 문서를 LLM에 넣기 좋은 형태로 구성하는 과정이다.
고려할 점:
좋은 context:
[문서: 환불 정책 / 최신 버전 / 2026-01-01 시행]
상품 수령 후 7일 이내에는 환불 신청이 가능하다.
환불 승인 후 결제 취소는 영업일 기준 3~5일 내 처리된다.
나쁜 context:
환불 관련 문서 20개를 무작정 붙임
오래된 정책과 최신 정책이 함께 들어감
출처가 없음
권한 없는 문서가 섞임
LLM은 context가 지저분하면 답도 지저분해진다.
한국어 Retrieval은 영어보다 신경 쓸 것이 많다.
이유:
예:
"로그인이 안돼요"
"로그인 안 됨"
"로그인실패"
"login error"
"인증 오류"
사람은 비슷하게 이해하지만 검색 시스템은 다르게 볼 수 있다.
한국어 RAG에서는 다음 조합이 좋다.
원본 질문 보존
형태소 분석
핵심 명사 추출
동의어 확장
BM25 검색
Dense vector 검색
Hybrid fusion
Reranking
예:
사용자 질문:
"퇴직금중간정산 되나요"
처리:
퇴직금 / 중간정산 / 중간 정산 / 가능 / 조건
검색:
BM25로 정확한 용어 검색
Vector로 의미상 가까운 문서 검색
최종:
"퇴직금 중간정산은 주택 구입, 전세금 부담, 의료비 등 법정 사유가 있는 경우 가능합니다."
한국어에서는 dense vector만 믿으면 부족한 경우가 많다. 정확한 명사, 법률 용어, 정책 용어를 보존하기 위해 BM25 또는 sparse search를 함께 쓰는 것이 좋다.
Retrieval은 감으로 개선하면 안 된다. 평가셋이 있어야 한다.
예시 평가 데이터:
{
"question": "환불은 며칠 안에 가능한가요?",
"expected_documents": ["refund_policy_2026"],
"expected_answer": "상품 수령 후 7일 이내 신청 가능하며, 결제 취소는 영업일 기준 3~5일 소요됩니다."
}
주요 지표:
| 지표 | 의미 |
|---|---|
| Recall@K | 정답 문서가 top K 안에 들어왔는가 |
| Precision@K | 가져온 문서 중 관련 문서 비율 |
| MRR | 정답 문서가 얼마나 위에 있는가 |
| nDCG | 관련도 높은 문서가 상위에 잘 배치됐는가 |
| Answer accuracy | 최종 답변이 맞는가 |
| Citation accuracy | 답변의 출처가 맞는가 |
RAG에서는 답변만 평가하면 부족하다.
분리해서 봐야 한다.
검색이 실패했는가?
검색은 됐는데 reranking이 실패했는가?
근거는 맞는데 LLM이 답을 잘못 만들었는가?
근거가 부족했는가?
오래된 문서를 가져왔는가?
권한 필터가 실패했는가?
문제를 나눠야 고칠 수 있다.
권장 구조는 다음과 같다.
문서 수집
-> 문서 정제
-> 구조 보존
-> chunking
-> metadata 부착
-> embedding 생성
-> vector DB 저장
-> keyword index 저장
사용자 질문
-> query normalization
-> query rewrite
-> metadata filter 구성
-> BM25 검색
-> vector 검색
-> hybrid fusion
-> reranking
-> context packing
-> LLM answer generation
-> citation 표시
-> feedback logging
-> evaluation
구성 요소별 역할:
| 구성 요소 | 역할 |
|---|---|
| Ingestion pipeline | PDF, HTML, DOCX, DB 문서 수집 및 정제 |
| Chunker | 문서를 검색 가능한 조각으로 분할 |
| Metadata enricher | 출처, 권한, 날짜, 언어, 테넌트 정보 부착 |
| Embedder | chunk를 dense vector로 변환 |
| Keyword indexer | BM25/full-text 검색 인덱스 생성 |
| Vector DB | vector와 payload 저장 및 검색 |
| Query rewriter | 사용자 질문을 검색 친화적으로 변환 |
| Retriever | BM25/vector/hybrid 검색 수행 |
| Reranker | 후보 문서 재정렬 |
| Context packer | LLM에 넣을 근거 구성 |
| Generator | 근거 기반 답변 생성 |
| Evaluator | 검색과 답변 품질 측정 |
원인:
해결:
원인:
해결:
원인:
해결:
created_at, effective_from, document_status metadata를 관리한다.원인:
해결:
원인:
해결:
RAG의 핵심은 LLM만이 아니다. LLM은 주어진 근거를 바탕으로 답변한다. 따라서 좋은 답변을 만들려면 좋은 근거를 찾아야 한다.
Retrieval은 단순한 벡터 검색이 아니다.
질문을 이해하고,
검색하기 좋게 바꾸고,
키워드와 의미 검색을 조합하고,
권한과 날짜를 필터링하고,
후보 문서를 재정렬하고,
LLM에게 넣을 근거를 구성하는 전체 과정이다.
한 문장으로 정리하면 다음과 같다.
RAG Retrieval은 "질문과 가장 비슷한 문서 찾기"가 아니라,
"정답을 만들 수 있는 근거를 가장 안전하고 정확하게 찾는 시스템"이다.