RAG 내부 동작 (기술 심화)
:::note 전제 07. RAG와 08. 임베딩과 벡터 DB에서 큰 그림을 봤다는 가정하에, 이 문서는 "어떻게 찾아와서 어떻게 반영하는가" 를 단계별로 기술적으로 다룹니다. :::
0. 두 개의 시점
RAG는 두 단계로 나눠 봐야 합니다.
[사전] 인덱싱(Indexing) : 문서를 미리 쪼개 벡터로 만들어 저장 (오프라인, 1회/갱신 시)
[질의] 검색·생성(Query) : 질문이 올 때마다 찾아와 답 생성 (온라인, 매 요청)
1. 인덱싱 파이프라인
원문서 → ① 로드/정제 → ② 청킹 → ③ 임베딩 → ④ 벡터 DB 저장
② 청킹(Chunking) — 품질의 8할
문서를 검색 단위 조각(chunk)으로 자릅니다. 어떻게 자르냐가 RAG 품질을 좌우합니다.
| 전략 | 설명 | 특징 |
|---|---|---|
| 고정 크기 | N토큰(예: 512)씩 자름 | 단순, 문맥이 중간에 잘릴 수 있음 |
| 오버랩 | 조각 간 일부 겹침(예: 50토큰) | 경계에서 문맥 손실 완화 |
| 의미 기반 | 문단·제목·문장 경계로 자름 | 문맥 보존 우수, 구현 복잡 |
| 구조 기반 | 마크다운 헤더·표 단위 | 기술문서에 적합 |
청크가 너무 크면 관련 없는 내용까지 섞여 노이즈가 되고, 너무 작으면 문맥이 끊깁니다. 보통 2561024토큰 + 1020% 오버랩에서 시작해 튜닝합니다.
③ 임베딩 — 의미를 벡터로
각 청크를 임베딩 모델에 넣어 d차원 벡터로 만듭니다(예: 768·1536·3072차원).
"제로트러스트는 절대 신뢰하지 않는다" → [0.021, -0.044, 0.198, ... ] (d차원)
의미가 비슷한 문장은 벡터 공간에서 가까이 위치합니다. 이것이 "의미 기반 검색"이 가능한 이유입니다.
④ 저장
(벡터, 원문청크, 메타데이터)를 벡터 DB(FAISS·pgvector·Milvus·Elasticsearch 등)에 색인합니다.
2. 유사도 계산 — 수학
질문도 같은 임베딩 모델로 벡터 q로 바꾼 뒤, 저장된 각 청크 벡터 d_i와의 유사도를 계산합니다.
코사인 유사도 : cos(q, d) = (q · d) / (‖q‖ · ‖d‖) # 방향(각도) 비교, -1 ~ 1
내적(dot) : q · d = Σ q_k · d_k # 크기까지 반영
유클리드 거리 : ‖q - d‖ = √Σ(q_k - d_k)² # 가까울수록 유사
- 임베딩을 정규화(‖v‖=1) 해두면 코사인 = 내적이 되어 계산이 빨라짐 → 다수 벡터 DB의 기본
- "유사도 상위 = 질문과 의미가 가까운 청크"
3. 검색(Retrieval)
Top-k
유사도 상위 k개 청크를 가져옵니다(예: k=5). k가 크면 근거는 풍부하나 노이즈·토큰비용 ↑.
ANN — 전수 비교는 느리다
청크가 수백만 개면 모든 벡터와 일일이 비교(brute force)하기 비쌉니다. 그래서 근사 최근접 이웃(ANN, Approximate Nearest Neighbor) 을 씁니다.
HNSW (대표 알고리즘) : 벡터들을 계층 그래프로 연결해 두고,
가까운 이웃을 따라 "점프"하며 탐색
→ 정확도를 조금 희생하고 속도를 크게 얻음
- 트레이드오프: 정확도(recall) ↔ 속도. 파라미터(ef_search 등)로 조절
하이브리드 검색
의미 검색(dense)만으로는 정확한 키워드·고유명사·코드를 놓칠 수 있습니다. 그래서 전통 키워드 검색(BM25, sparse)과 결합합니다.
최종 점수 = α · (의미 유사도) + (1-α) · (BM25 점수)
또는 RRF(Reciprocal Rank Fusion)로 두 순위를 병합
4. 리랭킹(Reranking)
1차 검색(빠르지만 거친)으로 후보 20~50개를 뽑고, 정밀한 reranker로 다시 정렬합니다.
1차: bi-encoder (질문·청크를 따로 임베딩) → 빠름, 덜 정밀
2차: cross-encoder (질문+청크를 함께 입력해 점수) → 느림, 정밀
→ 후보 중 상위 k개만 최종 선택
cross-encoder는 질문과 청크를 한 쌍으로 모델에 넣어 관련도를 직접 점수화하므로 훨씬 정확합니다(대신 느려 후보군에만 적용).
5. 컨텍스트 조립 & 주입
선택된 top-k 청크를 질문과 함께 하나의 프롬프트로 조립합니다.
System: 아래 "참고자료"만 근거로 답하라. 모르면 모른다고 하라.
참고자료:
[1] (출처: zero-trust.md) 제로트러스트는 ...
[2] (출처: msa.md) 마이크로서비스는 ...
질문: 제로트러스트가 MSA에서 왜 중요한가?
주의할 기술 포인트:
- 컨텍스트 윈도 한계: 청크 수×크기가 모델 최대 토큰을 넘지 않게
- Lost in the Middle: 모델은 긴 컨텍스트의 중간에 있는 정보를 잘 놓침 → 중요한 근거는 앞·뒤에 배치
- 출처 메타데이터를 함께 넣어 citation(출처 표시) 생성
6. 생성(Generation)
LLM은 주입된 참고자료를 근거로 답을 생성합니다. 이때 모델은 어텐션으로 질문 토큰과 참고자료 토큰의 관계를 계산해 답에 반영합니다. 잘 설계하면 출처를 각주로 달 수 있습니다.
7. 품질에 영향을 주는 변수
| 변수 | 영향 |
|---|---|
| 청크 크기·오버랩 | 문맥 보존 vs 노이즈 |
| 임베딩 모델 | 의미 포착력(도메인·다국어 적합성) |
| k (top-k) | 근거 풍부함 vs 노이즈·비용 |
| 하이브리드/리랭커 | 정확도(특히 키워드·고유명사) |
| 프롬프트·청크 순서 | Lost in the Middle 완화 |
8. 평가 지표
검색 품질 : Recall@k, Precision@k, MRR, nDCG (정답 청크를 잘 가져왔나)
생성 품질 : Faithfulness(근거 충실도), Answer Relevance, (환각 여부)
RAGAS 같은 프레임워크로 "검색"과 "생성"을 나눠 측정하면 병목(검색 vs 생성)을 짚을 수 있습니다.
9. 보안 한 줄
RAG는 문서 권한을 그대로 반영해야 합니다. 권한 없는 청크가 검색·답변에 섞이면 정보 유출입니다(OWASP LLM08 벡터·임베딩 취약점). → Securing AI / 위협 모델
10. 요약
[인덱싱] 문서 → 청킹 → 임베딩 → 벡터DB
[질의] 질문 임베딩 → (ANN)유사도 top-k → 하이브리드/리랭킹
→ 컨텍스트 조립·주입 → LLM 생성 → 출처 표시