포스트

RAG 기초 (1) - Why RAG: 검색을 붙이는 이유

모델이 모르는 것을 확인하고 문서를 전부 프롬프트에 넣는 방식과 비교해 RAG가 필요한 조건과 색인, 질의 두 단계의 구조를 정리합니다.

RAG 기초 (1) - Why RAG: 검색을 붙이는 이유

RAG 기초 시리즈의 1편입니다. 전체 목차는 0편에 있습니다.

모델이 모르는 것

0편에서 만든 정책 문서를 주지 않고 그냥 물어봅니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
from anthropic import Anthropic

client = Anthropic()

def text_of(message):
    return "".join(b.text for b in message.content if b.type == "text")

message = client.messages.create(
    model="claude-opus-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "제주도 배송비가 얼마인가요?"}],
)
print(text_of(message))

돌아오는 답은 “쇼핑몰마다 다르며 보통 3,000원에서 5,000원의 추가 요금이 붙습니다” 같은 일반론입니다. 모델은 우리 쇼핑몰의 정책을 학습한 적이 없으므로 이것이 최선입니다.

모델이 막히는 상황은 세 가지입니다.

  • 비공개 정보 사내 위키, 계약서, 제품 매뉴얼은 학습 데이터에 없습니다
  • 최신 정보 학습 시점 이후에 바뀐 정책이나 가격은 모릅니다
  • 환각 모른다고 말하는 대신 그럴듯한 숫자를 지어내는 경우가 있습니다

세 번째가 가장 위험합니다. “제주도 추가 배송비는 3,000원입니다”라고 단정해 버리면 우연히 맞았는지 지어낸 것인지 구분할 수 없습니다.

문서를 넣으면 해결된다

해법 자체는 단순합니다. 답에 필요한 문서를 질문과 함께 넣어주면 됩니다.

1
2
3
4
5
6
7
8
9
10
11
policy = open("docs/shipping.md", encoding="utf-8").read()

message = client.messages.create(
    model="claude-opus-5",
    max_tokens=1024,
    system="아래 <문서>만 근거로 답한다. 문서에 없으면 '자료에 없습니다'라고 답한다.\n\n"
           f"<문서>\n{policy}\n</문서>",
    messages=[{"role": "user", "content": "제주도 배송비가 얼마인가요?"}],
)
print(text_of(message))
# 기본 배송비 3,000원에 제주 및 도서산간 추가 3,000원이 붙습니다.

이제 정확합니다. RAG는 이 방식을 자동화한 구조일 뿐이고, 자동화해야 하는 이유는 “어떤 문서를 넣을지” 를 사람이 매번 고를 수 없기 때문입니다.

전부 넣으면 안 되나

Claude 5 계열의 context window는 100만 토큰입니다. 문서가 웬만큼 많아도 다 들어갑니다. 그래서 검색 없이 전부 넣는 선택지가 실제로 존재합니다.

문서를 전부 넣는 방식이 더 나은 경우가 있습니다.

  • 문서 전체가 수만 토큰 이하로 고정되어 있다
  • 질문이 문서 여러 곳을 동시에 참조한다 (검색은 조각만 가져오므로 불리합니다)
  • 문서가 자주 바뀌지 않아 prompt caching으로 앞부분을 재사용할 수 있다

이 조건이면 검색 파이프라인을 만들 이유가 없습니다. 프롬프트 엔지니어링 기초 7편의 caching을 걸고 통째로 넣는 편이 간단하고 정확합니다.

반대로 RAG가 필요한 조건은 이렇습니다.

상황이유
문서가 수백만 토큰 이상넣을 수 없습니다
문서가 자주 바뀐다캐시가 매번 깨져 비용 이점이 사라집니다
질문마다 필요한 문서가 다르다관련 없는 내용이 많으면 정확도가 떨어집니다
호출량이 많다매 호출 수십만 토큰의 입력 비용이 누적됩니다

“context window가 커졌으니 RAG는 필요 없다”도, “무조건 RAG를 붙여야 한다”도 아닙니다. 문서 양과 변경 빈도와 호출량으로 정하는 문제입니다.

파이프라인 구조

RAG는 두 단계로 나뉩니다. 문서를 미리 검색 가능한 형태로 준비하는 색인 단계와, 질문이 들어올 때마다 도는 질의 단계입니다.

flowchart LR
    A[문서] --> B[chunk 분할]
    B --> C[embedding]
    C --> D[(vector store)]
    Q[질문] --> E[embedding]
    E --> F[유사 chunk 검색]
    D --> F
    F --> G[프롬프트에 삽입]
    G --> H[LLM 답변]

위쪽 줄이 색인이고 아래쪽 줄이 질의입니다. 색인은 문서가 바뀔 때만 돌리고, 질의는 질문마다 돕니다.

각 단계가 이 시리즈의 각 편에 대응합니다.

단계하는 일
chunk 분할긴 문서를 검색 단위로 자릅니다2편
embedding텍스트를 의미가 담긴 벡터로 바꿉니다3편
vector store벡터를 저장하고 가까운 것을 찾습니다4편
생성찾아온 조각을 근거로 답하게 합니다5편

어려운 지점은 검색이다

RAG를 붙였는데 답이 부실할 때, 원인은 대부분 생성이 아니라 검색에 있습니다. 모델은 받은 근거 안에서는 대체로 잘 답합니다. 문제는 엉뚱한 근거를 받았을 때입니다.

이 시리즈가 chunk 분할과 embedding에 두 편을 쓰고, 검색 품질 개선에 한 편을 더 쓰는 이유입니다. 그리고 어느 단계가 문제인지 판단하려면 검색과 생성을 나눠 채점해야 합니다. 그 방법이 7편에 있습니다.

다음 편에서 문서 세 개를 실제로 chunk로 자르고, 자르는 방식에 따라 결과가 어떻게 달라지는지 확인합니다.

다음 글: RAG 기초 (2) - Chunking: 문서를 검색 단위로 나누기

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.