RAG
외부 문서를 검색해 LLM 프롬프트에 넣어 답변 근거를 제공하는 기법
한 줄 정의
RAG(Retrieval-Augmented Generation)는 외부 문서를 검색해 LLM 프롬프트에 함께 넣어, 모델이 근거를 가지고 답변하도록 만드는 기법이다.
왜 필요한가
LLM만 쓰면 다음 문제가 생긴다.
- 학습 시점 이후의 정보를 모른다
- 사내 문서나 개인 자료를 알 수 없다
- 모르는 내용을 그럴듯하게 지어낸다(할루시네이션)
- 답변의 출처를 확인할 수 없다
모델을 다시 학습시키는 것은 비용이 크고 자주 할 수 없다. RAG는 모델을 건드리지 않고 프롬프트에 근거를 넣어 이 문제를 해결한다.
핵심 개념
| 용어 | 설명 |
|---|---|
| Chunking | 긴 문서를 검색 단위로 자르는 것 |
| Embedding | 텍스트를 의미를 담은 벡터로 바꾸는 것 |
| Vector Store | 벡터를 저장하고 유사도 검색을 제공하는 저장소 |
| Retriever | 질문에 관련된 청크를 찾아오는 구성요소 |
| Re-ranking | 검색 결과를 다시 정렬해 정확도를 높이는 단계 |
동작 구조
flowchart LR
Q[질문] --> E[임베딩]
E --> V[(벡터 DB)]
V --> R[관련 청크]
R --> P[프롬프트 구성]
P --> L[LLM]
L --> A[답변 + 출처]
크게 두 단계로 나뉜다.
- 사전 준비(색인) — 문서를 청킹 → 임베딩 → 벡터 DB에 저장
- 질의 시점(검색) — 질문을 임베딩 → 유사한 청크 검색 → 프롬프트에 넣어 LLM 호출
예제
사내 규정 문서 200페이지가 있다고 하자. “연차는 며칠인가?”라는 질문이 들어오면:
- 질문을 벡터로 바꾼다
- 200페이지를 잘라둔 청크 중 의미가 가까운 3~5개를 찾는다
- 그 청크들을 프롬프트에 넣는다
- LLM이 그 내용만 근거로 답한다
200페이지 전체를 프롬프트에 넣을 필요가 없다. 컨텍스트 윈도우 제한과 비용 문제가 함께 해결된다.
코드
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import FAISS
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
# 사전 준비: 청크를 벡터로 만들어 저장
store = FAISS.from_texts(chunks, embeddings)
# 질의: 유사한 청크 4개를 찾는다
retriever = store.as_retriever(search_kwargs={"k": 4})
context = retriever.invoke("연차 휴가는 며칠인가?")
장점
- 모델 재학습 없이 최신 정보를 반영한다
- 출처를 함께 제시할 수 있어 검증이 가능하다
- 파인튜닝보다 비용이 훨씬 낮다
- 문서를 바꾸면 즉시 반영된다
단점
- 검색이 실패하면 답변 품질이 그대로 떨어진다
- 청킹 전략에 따라 결과가 크게 달라진다
- 검색·임베딩 단계가 늘어 응답이 느려진다
- 벡터 DB 운영 비용이 추가된다
자주 발생하는 문제
| 증상 | 원인 | 해결 |
|---|---|---|
| 관련 없는 문서가 검색됨 | 청크가 너무 크거나 작음 | 청크 크기와 겹침(overlap) 조정 |
| 답변이 여전히 부정확 | 검색은 됐으나 프롬프트가 부실 | 컨텍스트를 명시적으로 인용하라고 지시 |
| 응답이 느림 | 매번 임베딩 재계산 | 색인을 미리 만들어 저장 |
| 출처가 표시되지 않음 | 메타데이터 미저장 | 청크에 문서명·페이지를 함께 저장 |
실제 프로젝트 적용
아직 없음. 프로젝트 문서가 추가되면 여기에 연결한다.
관련 문서
참고 자료
- LangChain 공식 문서 — RAG 튜토리얼 (2026-07-25 확인)