RAG (Retrieval-Augmented Generation)
외부 지식 베이스에서 관련 정보를 검색해 LLM 프롬프트에 주입하는 기법. 환각(hallucination)을 줄이고 최신 정보를 활용 가능.
기본 구조
[사용자 질문]
↓
임베딩 변환
↓
벡터 DB 유사도 검색
↓
관련 문서 청크 추출
↓
프롬프트에 컨텍스트 삽입
↓
LLM 생성
↓
최종 답변
핵심 컴포넌트
| 컴포넌트 | 역할 | 예시 |
|---|---|---|
| Embedding Model | 텍스트 → 벡터 변환 | text-embedding-3, BGE |
| Vector DB | 벡터 저장 및 유사도 검색 | Pinecone, Chroma, Weaviate, pgvector |
| Chunking | 문서를 적절한 크기로 분할 | 고정 크기, 문장 단위, 재귀적 |
| Retriever | 쿼리와 유사한 청크 검색 | 시맨틱 검색, BM25, 하이브리드 |
| LLM | 검색 결과 기반 답변 생성 | GPT-4, Claude |
구현 예시 (LangChain)
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_chroma import Chroma
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.chains import RetrievalQA
# 1. 문서 청킹
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.split_documents(documents)
# 2. 벡터 DB 생성
vectordb = Chroma.from_documents(
chunks,
embedding=OpenAIEmbeddings()
)
# 3. RAG 체인
qa = RetrievalQA.from_chain_type(
llm=ChatOpenAI(model="gpt-4o"),
retriever=vectordb.as_retriever(search_kwargs={"k": 4})
)
result = qa.invoke("질문 내용")RAG 유형
| 유형 | 설명 |
|---|---|
| Naive RAG | 기본 검색 → 생성 파이프라인 |
| Advanced RAG | 쿼리 재작성, 재순위, 하이브리드 검색 |
| Modular RAG | 컴포넌트 교체 가능한 유연한 구조 |
| GraphRAG | 지식 그래프 기반 검색 |
| Self-RAG | LLM이 검색 필요 여부를 스스로 판단 |
청킹 전략
고정 크기 청킹 — 단순, 문맥 단절 위험
문장/단락 단위 — 문맥 보존, 크기 불균일
재귀적 청킹 — 계층적으로 분할 (권장)
시맨틱 청킹 — 의미 유사도 기반 분할
주요 벡터 DB
| DB | 특징 |
|---|---|
| Pinecone | 관리형 클라우드, 빠른 시작 |
| Chroma | 로컬/임베디드, 오픈소스 |
| Weaviate | 하이브리드 검색, 오픈소스 |
| pgvector | PostgreSQL 확장, 기존 DB 활용 |
| Qdrant | 고성능, Rust 기반 |
| Milvus | 대규모 분산 처리 |
RAG Evaluation (평가 지표)
RAG Evaluation은 파이프라인의 어느 단계에서 품질 문제가 발생하는지 진단하기 위해 검색 단계와 생성 단계를 분리해 측정한다.
검색 품질 지표
검색 품질은 "올바른 문서를 얼마나 잘 찾아왔는가"를 측정한다.
| 지표 | 설명 | 계산 |
|---|---|---|
| Recall@k | 상위 k개 결과 중 정답 문서 포함 비율 | 정답 문서 수 / 전체 정답 수 |
| Precision@k | 상위 k개 결과 중 관련 문서 비율 | 관련 문서 수 / k |
| MRR (Mean Reciprocal Rank) | 첫 번째 정답 문서의 평균 역순위 | mean(1 / rank of first hit) |
| NDCG@k | 순위를 반영한 관련성 점수 | 순위 가중 DCG / 이상적 DCG |
| Context Relevance | 검색된 컨텍스트가 쿼리와 얼마나 관련 있는가 | LLM 기반 평가 |
생성 품질 지표
생성 품질은 "검색된 컨텍스트를 얼마나 잘 활용해 답변했는가"를 측정한다.
| 지표 | 설명 |
|---|---|
| Faithfulness | 답변이 컨텍스트에 근거하는 정도 (환각 탐지) |
| Answer Relevance | 답변이 질문에 얼마나 적절히 응답하는가 |
| Answer Correctness | 정답 레이블과 비교한 정확도 |
| RAGAS | Faithfulness + Answer Relevance + Context Recall을 통합한 프레임워크 |
평가 프레임워크
# RAGAS 예시
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy, context_recall
result = evaluate(
dataset,
metrics=[faithfulness, answer_relevancy, context_recall]
)Optimization Loop (하이퍼파라미터 최적화)
RAG 파이프라인은 여러 하이퍼파라미터가 서로 연동되어 있어 단일 파라미터만 조정하면 다른 지표가 저하될 수 있다. 평가 지표를 기준으로 반복적으로 조정한다.
Chunk Size (청크 크기)
청크 크기는 검색 정밀도와 컨텍스트 풍부함 사이의 트레이드오프를 결정한다.
| 청크 크기 | 특성 | 적합한 경우 |
|---|---|---|
| 소형 (128~256 토큰) | 정밀한 검색, 컨텍스트 부족 위험 | 사실 조회, 키워드 중심 쿼리 |
| 중형 (512~1024 토큰) | 균형점, 가장 많이 사용됨 | 일반 목적 |
| 대형 (2048+ 토큰) | 풍부한 컨텍스트, 노이즈 증가 | 요약, 문서 전체 이해 필요 시 |
탐색 기준: Context Relevance가 낮으면 청크를 줄이고, Faithfulness가 낮으면 늘리는 것을 우선 시도한다.
Overlap (청크 겹침)
인접 청크 간 텍스트를 일정 토큰만큼 겹쳐 경계에서 문맥이 단절되는 것을 방지한다.
# chunk_size=512, overlap=50일 때
# Chunk 1: 토큰 0~511
# Chunk 2: 토큰 461~972 (50 토큰 겹침)
splitter = RecursiveCharacterTextSplitter(
chunk_size=512,
chunk_overlap=50 # 청크 크기의 5~15% 권장
)Overlap이 클수록 청크 수가 많아지고 인덱스 크기가 커진다. 경계 문맥 손실 문제가 두드러진다면 Overlap을 늘리기 전에 Semantic Chunking도 고려한다.
Retrieval top-k
top-k는 retriever가 반환하는 후보 문서 수다. LLM 컨텍스트에 직접 넣는 최종 k와, reranker를 거치기 전 후보 풀 크기로 나눠 관리하는 것이 좋다.
retriever = vectordb.as_retriever(
search_kwargs={
"k": 50 # reranker 전 후보 풀
}
)
# reranker 후 최종 컨텍스트: top-5| top-k 증가 시 | 효과 |
|---|---|
| Recall 상승 | 정답 문서를 포함할 확률 증가 |
| Precision 하락 | 관련 없는 문서도 많아짐 |
| 레이턴시/비용 증가 | reranker 부하 증가, 컨텍스트 길이 증가 |
Reranker Threshold
Reranker가 계산한 관련성 점수에 임계값(threshold)을 설정해, 점수 미달 문서를 컨텍스트에서 제외한다.
def apply_reranker_threshold(candidates, reranker, threshold=0.5):
scores = reranker.predict([(query, doc.content) for doc in candidates])
return [doc for doc, score in zip(candidates, scores) if score >= threshold]Threshold가 높을수록 Precision이 올라가지만 Recall이 낮아진다. 임계값이 너무 높으면 컨텍스트가 비어 LLM이 "모르겠다"고 답변하는 빈도가 높아진다. 검증셋에서 F1 또는 Answer Correctness를 최대화하는 값을 탐색한다.
Fusion Strategy
복수의 retriever를 사용할 때 결과를 어떻게 병합할지 결정한다. 전략별 특성 요약:
| 전략 | 조정 파라미터 | 특성 |
|---|---|---|
| Naive Merge | retriever 순서 | 가장 단순, 순서에 민감 |
| Score Fusion | 가중치, 정규화 방법 | 스코어 분포 분석 필요 |
| Reciprocal Rank Fusion | k 값 (기본 60) | 강건성 높음, 튜닝 포인트 적음 |
| Reranker-centric | 후보 수, threshold | 가장 높은 정밀도, 레이턴시 비용 |
전략 선택 기준: 레이턴시 제약이 없다면 RRF + Reranker 조합이 일반적으로 가장 안정적이다.
최적화 순서 권장안
파라미터 간 의존성이 있으므로 다음 순서로 탐색하면 효율적이다.
1. Chunk Size / Overlap 결정 → Context Relevance 측정
2. top-k (후보 풀) 결정 → Recall@k 측정
3. Fusion Strategy 선택 → MRR / NDCG 측정
4. Reranker Threshold 결정 → Precision / Faithfulness 측정
5. 최종 top-k (컨텍스트 크기) → Answer Correctness 측정