1.RAG?
- 검색(Retrieval): 질문과 관련된 문서 조각을 데이터베이스에서 먼저 찾아온다
- 증강(Augumented): 찾아온 문서 조각을 LLM 프롬프트에 근거로 추가(증강)한다
- 생성(Generation): LLM이 그 근거를 바탕으로 답변을 생성한다
문제 상황
LLM은 학습 시점까지의 데이터만 알고 있다.
회사 내부 정책 문서, 최신 이용약관, 특정 서비스의 세부 매뉴얼처럼 모델이 애초에 본 적 없는 정보는 답변에 반영할 수 없다.
예를 들어 쇼핑몰 CS 챗봇이 "이번에 바뀐 해외배송 환불 규정"을 답변에 반영하려면, 그 규정 문서를 어떻게든 모델에게 전달해야 한다.
이걸 해결하는 가장 직관적인 방법은 "필요한 정보를 프롬프트에 직접 넣어주는 것"인데,
문서 전체를 매번 통째로 넣으면 비용이 크고 비효율적이다 (게다가 모델이 한 번에 처리할 수 있는 길이에도 한계가 있다).
RAG의 해법: 질문과 관련된 부분만 먼저 찾아서(검색, Retrieval), 그 부분만 LLM에게 넣어준다(생성, Generation).
카테고리
RAG는 여러 기술을 조합하는 시스템 설계 패턴이다.
CPU와 RAM이 "부품"이고 그걸 조합하는 게 "컴퓨터 아키텍처"인 것처럼,
임베딩과 LLM이 부품이고 RAG는 "그 부품들을 어떤 순서로 호출할지에 대한 규칙"이다.
[ML] 벡터 임베딩(Vector Embedding) - 의미를 벡터로
cf) TF-IDF [ML] TF-IDF(Term Frequency-Inverse Document Frequency)1. TF-IDF?정의단어의 빈도(TF)와 역문서빈도(IDF)를 곱해서, 문서 내 각 단어의 "중요도"를 가중치로 매기는 방법단순히 "몇 번 나왔는가"만 세는 빈
blog.chaenii.me
[ML] 트랜스포머와 LLM(Large Language Model) - 텍스트를 생성하는 신경망
1. 임베딩과의 관계 임베딩은 숫자로 끝나고, LLM은 다음 단어를 계속 만들어낸다. 임베딩은 "텍스트→벡터" 변환만 하고 끝남LLM은 같은 트랜스포머 계열 구조를 쓰지만 벡터에서 다시 텍스트를
blog.chaenii.me
파이프라인
사용자 질문
↓
[검색 단계] 질문과 관련된 문서 조각(청크) 찾기
↓
[생성 단계] 찾은 조각들을 프롬프트에 넣고 LLM에게 질문 + 근거 전달
↓
LLM이 근거를 참고해서 답변 생성
2. 검색 단계
청킹(chunking)
문서 전체를 검색 단위로 쓰면 비효율적이다(문서가 길면 그 안의 관련 없는 부분도 다 끌려옴).
그래서 문서를 의미 있는 단위(예: 정책 문서의 "조항" 단위, FAQ의 "항목" 단위)로 잘라서 저장해두고,
검색은 이 조각(청크) 단위로 한다.

두 가지 검색 방식
| 키워드 검색 | 벡터(임베딩) 검색 | |
| 작동 방식 | 질문에 등장한 단어가 문서 조각에 몇 번 나오는지 카운팅 | 질문과 문서 조각을 각각 임베딩 후 코사인 유사도 계산 |
| 동의어·유사 의미 | ✗ — 정확히 같은 단어만 | ✓ — "강아지"로 검색해도 "개" 관련 문서 찾을 수 있음 |
| 비용 | 없음 | 임베딩 API 호출 비용·속도 |
| 예측 가능성 | 높음 (결정적, 룰베이스에 가까움) | 낮음 — 의미상 무관한데 우연히 벡터가 가까운 경우(false positive) 발생 가능 |
하이브리드 검색: 둘을 합치기
실전에서는 둘 다 계산해서 점수를 합친다:
$$
\text{최종점수} = w_v \times \text{벡터유사도} + w_k \times \text{키워드점수}
$$
예: $w_v=0.7$, $w_k=0.3$. 벡터 검색이 "의미"를, 키워드 검색이 "정확한 용어 일치"를 보완해서 둘 중 하나만 쓸 때보다 안정적인 결과를 낸다.
키워드 점수는 보통 등장 횟수를 정규화해서 0~1 범위로 맞춘다(예: 등장 5회 이상이면 만점).
숫자로 감을 잡아보면: 청크 A가 벡터유사도 0.85, 키워드점수 0.2(질문 단어가 거의 안 겹침)라 하고, 청크 B가 벡터유사도 0.60, 키워드점수 1.0(질문 단어가 정확히 다 들어있음)이라 하자. $w_v=0.7$, $w_k=0.3$을 적용하면:
$$
\text{청크 A} = 0.7 \times 0.85 + 0.3 \times 0.2 = 0.595 + 0.06 = 0.655
$$
$$
\text{청크 B} = 0.7 \times 0.60 + 0.3 \times 1.0 = 0.42 + 0.30 = 0.72
$$
벡터유사도만 봤다면 A가 이겼겠지만, 키워드까지 합치면 B가 더 높은 점수로 역전된다.
"의미는 약간 덜 비슷해도 질문의 핵심 단어를 정확히 담고 있는 문서"가 최종적으로 더 우선시되는 경우를 수치로 보여준다.
후포 풀 (candidate pool)
- 1차 후보 추출: 벡터 검색으로 최종 목표보다 넉넉하게 후보를 뽑는다 (예: 최종 4개가 목표라면 최소 12~16개)
- 2차 정렬: 그 후보들 안에서 하이브리드 점수(벡터+키워드)로 다시 정렬한다
- 최종 선택: 상위 N개(예: 4개)만 LLM 프롬프트에 넣는다
벡터 검색으로 거칠게 거른 뒤 하이브리드 기준으로 정밀하게 추리는 2단계 구조.
처음부터 모든 청크에 하이브리드 점수를 계산하면 느리므로, 먼저 후보 풀을 좁히고 그 안에서만 정밀 계산하는 방식이다.
3. 청크 크기 설계 — 작게 vs 크게
청크를 어떤 크기로 자르느냐는 RAG 품질에 큰 영향을 미친다.
- 너무 작게 자르면
- 검색 정밀도는 높아지지만, 하나의 LLM이 답변에 필요한 앞뒤 문맥을 잃는다.
- 청크 안에 맥락이 충분히 담기지 않기 때문.
- e.g) 조항 하나를 세 문장씩 잘랐을 때. "단, 다음의 경우는 제외한다"는 절이 이전 청크에 있어서 조건을 놓치는 경우
- 너무 크게 자르면
- 청크 하나 안에 관련 없는 내용이 섞여 들어온다.
- 컨텍스트 윈도우를 빨리 소진한다
- Parent Document Retriever 패턴
- 두 문제를 절충하는 흔한 방법.
- 문서를 작은 청크로 잘라서 임베딩·검색하되, 실제로 LLM에 넘기는 건 그 청크가 속한 상위 단락(parent document)으로 확장한다.
- 작은 단위로 정밀하게 찾고, 큰 단위로 맥락을 제공하는 2단계 구조.
4. 재순위화(Reranking)
벡터 검색(bi-encoder) 후 LLM에 넣기 전에, 검색 결과를 cross-encoder 모델로 한 번 더 정렬하는 패턴.
- Bi-encoder(임베딩 검색)
- 질문과 청크를 각각 따로 임베딩해서 코사인 유사도 계산.
- 빠르지만 질문과 청크를 함께 보지 않아서 미묘한 관련성 판단이 떨어짐
- Cross-encoder(재순위화 모델)
- 질문과 청크를 같이 입력으로 받아 관련도 점수를 계산.
- 두 텍스트를 함께 보므로 정확하지만 느림
흐름:
질문 → [임베딩 검색] → 후보 20개 → [Cross-encoder 재순위화] → 상위 4개 → LLM
재순위화는 속도 때문에 전체 청크에 적용하지 않고, 임베딩 검색으로 좁힌 후보에만 적용한다.
Cohere Rerank, BGE-reranker 같은 모델이 자주 쓰인다.
5. 메타데이터 필터링
벡터 검색 이전에 메타데이터로 후보를 먼저 좁히는 방법. 벡터 유사도 계산 전에 조건을 거는 pre-filter다.
예시:
- 날짜 필터: "2024년 이후에 업데이트된 문서만"
- 카테고리 필터: "환불 정책 관련 문서만"
- 출처 필터: "공식 약관 문서만, 고객 리뷰 제외"
메타데이터를 청크 저장 시 함께 저장해두고, 벡터 DB의 필터 쿼리(where 조건)로 적용한다.
전체 청크 수를 줄여서 검색 속도도 올라가고, 관련 없는 카테고리의 false positive도 줄어든다.
6. RAG 품질 평가
RAG 시스템이 잘 작동하는지 어떻게 측정하는가? 단순히 "답변이 자연스러운가"로는 부족하다. 주요 평가 축 세 가지:
- Context Relevancy(검색 품질)
- 검색된 청크들이 실제 질문과 관련 있는가.
- 관련 없는 청크가 많이 섞이면 LLM이 엉뚱한 정보를 참조할 수 있다
- Faithfulness(근거 충실도)
- LLM의 답변이 검색된 청크에 근거하고 있는가.
- 청크에 없는 내용을 만들어냈다면(환각) faithfulness가 낮다
- Answer Relevancy(답변 관련성)
- 최종 답변이 원래 질문에 잘 답하고 있는가
이상적인 RAG = 높은 Context Relevancy + 높은 Faithfulness + 높은 Answer Relevancy
RAGAS 같은 평가 프레임워크는 이 세 가지를 자동으로 측정해준다.
LLM을 평가자로 써서 "이 답변이 이 청크에 근거하고 있는가"를 판단하는 방식이다.
7. 우아한 성능저하(graceful degradation)
벡터 검색에 필요한 임베딩 API나 벡터 데이터베이스가 장애가 나면 어떻게 할까?
시스템이 통째로 멈추는 대신, 키워드 검색만으로라도 동작하게 폴백시키는 설계가 일반적이다.
RAG는 "검색이 아예 안 되는 것"보다 "정확도가 조금 떨어지는 검색"이 낫다는 전제로 설계되는 경우가 많다.
8. 데이터 적재: 변경감지
RAG가 검색할 문서(정책·약관 등)는 시간이 지나면서 바뀐다. 매번 전체를 다시 임베딩하면 비용이 크므로 변경감지 패턴을 쓴다:
- 청크를 임베딩할 때 원문의 해시값(예: SHA-256)도 함께 저장한다
- 다음번 수집 시 해시를 비교해서 변경된 청크만 감지한다
- 바뀐 청크만 (필요하다면 LLM으로 요약·재작성한 뒤) 재임베딩한다
9. RAG의 본질: 사실은 룰베이스적 절차다
흥미로운 점: RAG 자체는 "학습"되지 않는다.
"벡터검색을 먼저 하고, 그 결과를 LLM 프롬프트에 어떤 형식으로 넣어라"는 절차는 사람이 코드로 짠 고정된 규칙이다.
학습되는 부분은 그 안에 들어가는 임베딩 모델과 LLM뿐이다.
그래서 RAG는 분류상 "머신러닝 기법"이 아니라 "머신러닝 구성요소를 활용하는 시스템 설계"라고 보는 게 정확하다.
10. 정리
- RAG는 알고리즘이 아니라 "검색 후 그 결과를 LLM에 넣는다"는 시스템 설계 패턴
- 키워드 검색과 벡터 검색을 하이브리드로 합쳐서 서로의 약점(동의어 vs 정확 일치)을 보완
- 청킹 → 임베딩 → 변경감지 기반 재처리까지가 데이터 적재 파이프라인
- 임베딩 API 장애 시 키워드 검색만으로라도 동작하는 우아한 성능저하 설계가 일반적
RAG의 검색을 한 단계 더 확장한 게 GraphRAG다. 벡터 검색에 지식그래프 기반 탐색을 추가해서, 직접 비슷하진 않지만 "같은 근거를 공유하는" 문서까지 찾아낸다.
[AI설계] GraphRAG - 지식그래프로 확장한 RAG
RAG의 한계에서 출발RAG는 질문과 벡터가 "직접 비슷한" 문서만 찾는다. 그런데 실제 질문은 종종 여러 단계를 거쳐야 답이 나오는 경우가 있다.e.g) "해외배송 상품을 환불하면 위약금은 얼마인가?
blog.chaenii.me
GitHub 댓글