AI 개발 독학 4주 - 3주차 RAG 완전정복

타리스만에서는 유익한 AI 기술을 전달합니다
제휴 링크로 판매시 수수료를 제공받습니다

LLM은 여러분이 따로 가진 문서, 예를 들어 특정 규정집이나 매뉴얼, 개인 메모의 내용은 알지 못합니다. 학습된 데이터 밖의 질문을 받으면 그럴듯한 거짓말(할루시네이션)을 지어내곤 합니다.

RAG(Retrieval-Augmented Generation)는 바로 이 문제를 해결하는 방식입니다. 질문에 맞는 문서를 먼저 찾아, 프롬프트에 근거 자료로 넣어주는 것이지요. 이번 주에는 그 원리를 이해하고, 실제로 RAG를 구현한 예제 코드 rag.py를 읽을 수 있는 수준까지 함께 가 보겠습니다.

이번 주 목표

RAG 6단계를 설명할 수 있고, 키워드 검색과 벡터(의미) 검색의 차이를 이해합니다. 코사인 유사도로 '닮은 문서 찾기'를 직접 돌려보고, 예제 rag.py의 구조를 읽어 봅니다.

이번 주 계획

요일학습 내용시간
RAG가 왜 필요한가 + 6단계 큰 그림1시간
청킹(문서 쪼개기) 원리와 실습1시간
임베딩, 벡터, 코사인 유사도 개념1.5시간
키워드 vs 벡터 검색 + 하이브리드 방식1시간
문서 파싱(Upstage)과 벡터DB(SingleStore) 포지션1시간
예제 rag.py 코드 해부2시간
주말 미니프로젝트: 코사인 유사도 미니 검색기2시간
시간 없으면 이것만

RAG 6단계 순서 외우기, 코사인 유사도 위젯 실행, 예제 rag.py의 청킹 부분 눈으로 확인하기.

RAG 6단계

  • ① 문서 수집: 규정집, 매뉴얼, FAQ, 정리해 둔 메모 등 근거가 될 원본 문서를 모읍니다.
  • ② 청킹(Chunking): 긴 문서를 검색하기 좋은 크기로 쪼갭니다.
  • ③ 임베딩(Embedding): 텍스트를 컴퓨터가 이해하는 숫자 목록(벡터)으로 변환합니다.
  • ④ 벡터DB 저장: 변환된 벡터와 원본 텍스트를 데이터베이스에 저장합니다.
  • ⑤ 유사도 검색: 질문이 들어오면 벡터DB에서 가장 비슷한 문서(top-k)를 찾습니다.
  • ⑥ 컨텍스트 주입 및 생성: 찾은 문서를 근거로 프롬프트에 넣어 LLM이 답변을 생성합니다.

청킹(Chunking)

문서를 너무 크게 자르면 검색할 때 관계없는 내용(노이즈)이 섞입니다. 반대로 너무 작게 자르면 문맥이 끊겨 의미를 잃습니다. 가장 효과적인 전략 중 하나는 제목(헤더) 단위로 자르는 것입니다.

import re

doc = "## 환불 규정\n본문 내용입니다.\n## 배송 안내\n안내 표입니다."
chunks = re.split(r"\n(?=## )", doc)

for i, chunk in enumerate(chunks):
    title = chunk.split("\n")[0]
    length = len(chunk)
    print(f"청크 {i+1}: {title} (길이: {length})")

위 코드는 ## 가 나타나는 위치를 기준으로 문자열을 나눕니다. 예제 rag.py가 문서를 H2(##) 제목 단위로 잘라 검색하는 방식과 똑같습니다.

임베딩과 코사인 유사도

임베딩은 문장을 방향과 크기를 가진 숫자 목록(벡터)으로 바꾼 것입니다. 두 벡터의 방향이 비슷할수록, 즉 코사인 유사도가 1에 가까울수록 두 문장의 의미가 서로 가깝다고 판단합니다.

import math

def cosine_sim(v1, v2):
    dot = sum(a * b for a, b in zip(v1, v2))
    norm1 = math.sqrt(sum(a * a for a in v1))
    norm2 = math.sqrt(sum(b * b for b in v2))
    return dot / (norm1 * norm2)

vec_a = [0.9, 0.1, 0.0] # "여름 원피스 추천"
vec_b = [0.8, 0.2, 0.1] # "시원한 여름옷"
vec_c = [0.0, 0.1, 0.9] # "겨울 코트 세탁법"

print(f"비슷한 문서 유사도: {cosine_sim(vec_a, vec_b):.3f}")
print(f"다른 문서 유사도: {cosine_sim(vec_a, vec_c):.3f}")

키워드 vs 벡터 검색

구분원리장점약점사용 시기
키워드 검색정확히 같은 단어가 포함되었는지 확인빠르고 정확함, 고유명사 검색에 강함표현이 다르면(동의어) 검색 실패모델명, 특정 제품명 검색
벡터 검색문장의 의미(벡터 방향)가 비슷한지 확인동의어, 의역, 문맥 파악에 강함계산 비용 발생, 고유명사 오타에 취약개념, 규정, 원인 분석 검색

실무에서는 둘을 합친 하이브리드 검색이 정답인 경우가 많습니다. 예제 rag.py도 기본은 키워드 점수로 문서를 찾고, 임베딩이 설정되면 벡터 유사도로 점수를 보강하는 하이브리드 방식을 씁니다.

문서 파싱과 Upstage

PDF, 엑셀, 스캔 문서는 구조가 지저분해서 그대로 넣으면 검색 품질이 떨어집니다. Upstage의 Document Parse는 OCR과 레이아웃 분석을 통해 표와 제목 구조를 살려 마크다운으로 바꿔 줍니다. RAG 앞단(청킹 전)에서 문서를 정리해 주는 역할이지요. 견적서나 재무제표처럼 표가 많은 문서에 특히 쓸모가 있습니다.

벡터DB와 SingleStore

벡터를 저장하고 빠르게 유사도 검색을 해 주는 데이터베이스를 벡터DB라고 합니다. 벡터 전용 DB와, 기존 DB에 벡터 기능을 더한 겸용형으로 나뉩니다. SingleStore는 벡터 검색과 일반 SQL을 하나의 분산 DB에서 함께 처리합니다. 매출 집계표 같은 정형 데이터(SQL)와 문서 임베딩(벡터)을 한곳에서 다룰 수 있는 포지션이지요.

예제 rag.py 코드 해부

RAG를 구현한 예제 코드 rag.py를 열고, 다음 지점들을 하나씩 확인해 봅니다.

  • 청킹: 문서를 H2(##)로 자르는 부분입니다. 청크가 너무 길면 문단 경계에서 한 번 더 쪼개는 상한 로직이 들어 있습니다.
  • 메타데이터 읽기: 문서 맨 위 frontmatter(--- 사이의 title, category, status, tags 같은 항목)를 읽어 검색 조건으로 활용합니다.
  • 필터링: status가 폐기/구버전인 문서는 검색에서 제외합니다. exclude_types로 특정 문서 유형을 통째로 빼기도 합니다.
  • 하이브리드 검색: search() 함수에서 키워드 점수를 먼저 매깁니다. 맨 끝 블록에서 임베딩이 켜져 있으면 코사인 유사도로 가점해 결과를 보강합니다.
# H2(##) 제목 단위로 문서를 분할하는 정규식 예시
parts = re.split(r"\n(?=## )", body)

주말 미니프로젝트 — 코사인 유사도 미니 검색기

작은 문서 3개를 벡터로 표현하고, 질문 벡터와 비교해 가장 비슷한 문서를 찾는 미니 검색기를 만들어 봅니다. 이것이 예제 rag.py가 임베딩으로 하는 일의 뼈대입니다.

import math

def cosine_sim(v1, v2):
    dot = sum(a * b for a, b in zip(v1, v2))
    return dot / (math.sqrt(sum(a*a for a in v1)) * math.sqrt(sum(b*b for b in v2)))

docs = [
    ("여름 원피스 추천", [0.9, 0.1, 0.0]),
    ("시원한 여름옷 코디", [0.8, 0.2, 0.1]),
    ("겨울 코트 관리법", [0.0, 0.1, 0.9])
]
query = [0.85, 0.15, 0.05] # "여름에 입기 좋은 옷" 질의 벡터

scores = [(title, cosine_sim(query, vec)) for title, vec in docs]
scores.sort(key=lambda x: x[1], reverse=True)

print("질의 '여름에 입기 좋은 옷'과 가까운 순:")
for rank, (title, score) in enumerate(scores, 1):
    mark = "  ← 가장 가까움" if rank == 1 else ""
    print(f"{rank}. {title}: {score:.3f}{mark}")

자가 체크리스트

  • RAG 6단계를 순서대로 설명할 수 있다.
  • 청킹을 할 때 문서를 너무 크거나 작게 자르면 안 되는 이유를 안다.
  • 코사인 유사도가 1에 가까울수록 어떤 의미인지 설명할 수 있다.
  • 키워드 검색과 벡터 검색의 장단점을 비교할 수 있다.
  • Upstage Document Parse가 RAG 파이프라인에서 어떤 역할을 하는지 안다.
  • SingleStore가 정형 데이터와 문서 검색을 동시에 처리할 수 있는 이유를 안다.
  • 예제 rag.py에서 문서를 H2 기준으로 자르는 코드 위치를 찾았다.
실습 코드와 연결하기

오늘 배운 청킹은 예제 rag.py의 H2 분할 로직에 해당합니다. 임베딩과 코사인 유사도 계산은 별도 파일 embed.py에서 외부 라이브러리 없이 순수 파이썬으로 구현되어 있습니다. 하이브리드 검색은 rag.py의 벡터 보강 블록에서 확인할 수 있습니다. Upstage Document Parse는 견적서나 재무제표처럼 표가 많은 문서(엑셀/PDF)를 깨끗한 텍스트로 바꿔, RAG 앞단에서 정리해 주는 후보 기술입니다.

이번 주 용어

용어설명
RAG검색 증강 생성. 외부 문서를 검색해 LLM의 답변 근거로 제공하는 기술.
청킹(Chunking)긴 문서를 검색 및 임베딩에 적합하도록 작은 조각으로 나누는 작업.
임베딩(Embedding)텍스트의 의미를 컴퓨터가 처리할 수 있는 숫자 배열(벡터)로 변환하는 과정.
벡터(Vector)방향과 크기를 가진 숫자의 목록. 텍스트 임베딩의 결과물.
코사인 유사도두 벡터 사이의 각도를 기반으로 유사성을 측정하는 지표. 1에 가까울수록 유사함.
벡터DB벡터 데이터를 저장하고 고속 유사도 검색을 지원하는 데이터베이스.
top-k검색 결과 중 유사도가 가장 높은 상위 k개의 항목을 반환하는 방식.
시맨틱 검색단어의 일치 여부가 아닌 문맥과 의미를 기반으로 하는 벡터 검색.
키워드 검색전통적인 검색 방식으로, 특정 단어가 정확히 일치하는지 확인.
하이브리드 검색키워드 검색과 벡터 검색의 결과를 결합해 정확도를 높이는 방식.
할루시네이션LLM이 사실이 아닌 정보를 그럴듯하게 지어내는 현상.
컨텍스트 주입LLM에게 질문과 함께 검색된 배경 지식(근거)을 프롬프트에 넣어주는 것.
frontmatter마크다운 파일 맨 앞에 위치하며 메타데이터(태그, 상태 등)를 담는 영역.
리랭킹(Reranking)1차 검색 결과를 더 정교한 모델로 다시 평가해 순위를 재조정하는 과정.
Document AI문서의 레이아웃, 표, 텍스트를 인식하고 구조화하는 기술 (예: Upstage).
SingleStore관계형 데이터(SQL)와 벡터 데이터를 단일 엔진에서 처리하는 분산 DB.

4주차에서는 SQL, txt2SQL, 그리고 에이전트를 다룹니다. 예제 파이프라인의 '모듈=에이전트' 구조를 교재 삼아, 실제 파이프라인 엔진이 어떻게 동작하는지 함께 살펴봅니다.

댓글 남기기