검색

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[답변 + 출처]

크게 두 단계로 나뉜다.

  1. 사전 준비(색인) — 문서를 청킹 → 임베딩 → 벡터 DB에 저장
  2. 질의 시점(검색) — 질문을 임베딩 → 유사한 청크 검색 → 프롬프트에 넣어 LLM 호출

예제

사내 규정 문서 200페이지가 있다고 하자. “연차는 며칠인가?”라는 질문이 들어오면:

  1. 질문을 벡터로 바꾼다
  2. 200페이지를 잘라둔 청크 중 의미가 가까운 3~5개를 찾는다
  3. 그 청크들을 프롬프트에 넣는다
  4. 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) 조정
답변이 여전히 부정확검색은 됐으나 프롬프트가 부실컨텍스트를 명시적으로 인용하라고 지시
응답이 느림매번 임베딩 재계산색인을 미리 만들어 저장
출처가 표시되지 않음메타데이터 미저장청크에 문서명·페이지를 함께 저장

실제 프로젝트 적용

아직 없음. 프로젝트 문서가 추가되면 여기에 연결한다.

관련 문서

참고 자료