개발자 쿠키

[AI] RAG 본문

AI

[AI] RAG

개발자 쿠키 2026. 8. 21. 13:24

RAG (Retrieval-Augmented Generation)

LLM이 모르는 것을 지어내지 않게 만드는 방법. 질문에 답하기 전에 외부 문서에서 근거를 먼저 찾아오는 구조

1. 왜 RAG가 필요한가

LLM 단독 사용의 한계는 크게 세 가지

한계 내용
환각 (Hallucination) 모르는 내용을 그럴듯한 문장으로 생성. 사실 여부와 무관하게 확신에 찬 어조 유지
도메인 지식 부재 사내 규정, 제품 매뉴얼, 내부 FAQ 등 학습 데이터에 없는 정보는 답변 불가
최신 정보 미반영 학습 시점(knowledge cutoff) 이후 정보 부재. 재학습 비용이 커서 실시간 반영 불가

파인튜닝은 모델 자체를 바꾸는 방식이라 비용과 주기가 부담.
RAG는 모델을 건드리지 않고 프롬프트에 근거 문서를 끼워 넣는 방식이라 문서만 갱신하면 즉시 반영

2. RAG 동작 흐름

크게 인덱싱과 서빙 두 단계. 시점이 다르다는 게 핵심

인덱싱  서비스 배포 전 미리 돌려두는 배치

문서 로딩 → 청킹 → 임베딩 → Vector Store 저장. 문서가 바뀔 때만 재실행

서빙  요청이 들어올 때마다 실시간

질문 임베딩 → 유사 조각 top-k 검색 → 컨텍스트로 프롬프트 구성 → LLM 답변 생성

핵심은 LLM에게 "기억해서 답해"가 아니라 "이 문서 보고 답해"로 바꾸는 것. 근거가 프롬프트 안에 있으니 환각 감소, 출처 표시도 가능

3. 검색 방식: 키워드 vs 의미 기반

구분 Lexical Search Semantic Search
기준 단어의 문자열 일치 벡터 공간에서의 의미 거리
대표 기법 TF-IDF, BM25 임베딩 + 코사인 유사도
강점 고유명사, 코드, 제품명 등 정확한 토큰 매칭 동의어와 문장 표현 차이 흡수
약점 "환불"과 "반품"을 다른 것으로 인식 희귀 고유명사에 취약, 임베딩 비용 발생

실무에서는 둘을 함께 쓰는 Hybrid Search가 일반적. BM25 점수와 벡터 유사도 점수를 가중 합산하거나 RRF(Reciprocal Rank Fusion)로 순위 병합

4. 임베딩과 코사인 유사도

임베딩은 텍스트를 고차원 실수 벡터로 변환하는 작업. OpenAI text-embedding-3-small은 1536차원

코사인 유사도

cos(A, B) = (A · B) / (|A| × |B|)

두 벡터가 이루는 각도의 코사인 값. 범위는 -1 ~ 1이고, 1에 가까울수록 의미가 유사. 벡터 크기(문서 길이)의 영향을 받지 않는 것이 유클리드 거리 대비 장점

"주문 취소하고 싶어요"와 "결제 철회 방법 알려줘"는 겹치는 단어가 하나도 없음. 키워드 검색으로는 못 찾지만 임베딩 공간에서는 각도가 좁아 가깝다고 판단. 이게 의미 기반 검색이 가능한 이유

5. Vector Store

임베딩 벡터를 저장하고 유사도 검색을 수행하는 데이터베이스. 전체 벡터를 일일이 비교하면 O(N)이라 느리므로 ANN(Approximate Nearest Neighbor) 알고리즘으로 근사 검색. HNSW, IVF 등이 대표적인 인덱스 구조

제품 특징
ChromaDB 로컬 임베디드 실행. 학습, 프로토타입용으로 진입 장벽 낮음
FAISS Meta 개발 라이브러리. 인메모리 기반이라 빠르지만 영속화는 직접 관리
Pinecone 완전 관리형 SaaS. 운영 부담 적고 스케일아웃 용이
pgvector PostgreSQL 확장. 기존 RDB 트랜잭션과 함께 관리 가능

6. 청킹 전략

긴 문서를 통째로 임베딩하면 의미가 뭉개지고 프롬프트 토큰 한도도 초과. 적절한 크기로 분할 필요

chunk_size  청크 최대 길이

작으면 검색 정밀도는 올라가지만 답변에 필요한 문맥이 잘림

크면 문맥은 살지만 노이즈가 섞여 유사도가 희석됨

chunk_overlap  청크 간 겹치는 구간

경계에서 문장이 끊기는 문제를 완화. 보통 chunk_size의 10 ~ 20% 수준

정답 없음. 문서 성격에 따라 다름. FAQ처럼 항목이 짧고 독립적이면 작게, 기술 문서처럼 맥락이 이어지면 크게. 실제 질의 셋으로 검색 결과를 눈으로 비교하며 튜닝하는 게 가장 확실

7. Retriever와 파이프라인

Retriever는 쿼리 임베딩과 Vector Store 검색을 감싼 컴포넌트. LangChain에서는 vectorstore.as_retriever(search_kwargs={"k": 4}) 형태로 생성. top-k 개수는 정확도와 토큰 비용의 트레이드오프

LangGraph StateGraph 구성

State에 question, context, answer 등을 정의

retrieve 노드에서 검색 결과를 State에 채움

generate 노드에서 context를 프롬프트에 주입해 LLM 호출

노드를 엣지로 연결하고 컴파일 후 invoke

단순 체인 대신 그래프로 구성하는 이유는 확장성. 검색 결과가 부실할 때 재검색하는 분기, 답변 검증 노드 추가 등 조건부 흐름을 붙이기 쉬움