RAG

LLM의 한계

LLM(Large Language Model)은 방대한 텍스트 데이터로 학습되어 다양한 질문에 답변할 수 있다. 하지만 다음과 같은 근본적인 한계가 있다.

  1. 환각(Hallucination) : 학습 데이터에 없는 내용을 그럴듯하게 지어내는 현상
  2. 최신 정보 부재 : 학습 데이터 이후의 정보는 알 수 없음
  3. 도메인 지식 부족 : 특정 회사/서비스의 내부 정보는 학습되지 않음

가장 간단한 방법은 관련 자료를 프롬프트에 직접 포함시키는 것이다.

# 기존 방식 (환각 위험)
"Yes24 총알배송이 뭔가요?"
 
# 개선된 방식 (컨텍스트 포함)
"다음 자료를 참고해서 답변해주세요:
[총알배송 정책 문서 내용...]
 
질문 : Yes24 총알배송이 뭔가요?"

이 방식의 장점:

  • LLM이 실제 자료를 기반으로 답변
  • 환각 가능성 대폭 감소
  • 최신 정보 반영 가능

코드

# 컨텍스트를 포함한 프롬프트 구성
from langchain_core.prompts import ChatPromptTemplate
 
prompt_template = ChatPromptTemplate.from_messages([
    ("system", """당신은 AI 온라인 서점의 고객 서비스 상담원입니다.
다음 자료를 참고하여 고객의 질문에 정확하게 답변해주세요.
자료에 없는 내용은 "해당 정보는 제공된 자료에 없습니다"라고 답변하세요.
 
[참고 자료]
{context}"""),
    ("human", "{question}")
])
 
# 질문
question = "Yes24에서 총알배송이 뭔가요? 어떤 조건에서 가능한가요?"
 
# 프롬프트 생성 후 LLM 직접 호출
messages = prompt_template.format_messages(context=doc_content, question=question)
response = llm.invoke(messages)
 
print("📝 질문:", question)
print("\n🤖 자료 기반 응답:")
print(response.content)

비교표

비교 항목LLM만자료 포함
정확도낮음 (추측/환각)높음 (자료 기반)
신뢰도낮음높음
한계도메인 지식 부재자료 크기 제한

의미 기반 검색 + DB화 기초

**임베딩(Embedding)**이란?

  • 텍스트를 숫자 벡터로 변환하는 기술
  • 의미가 비슷한 텍스트는 비슷한 벡터를 가짐
  • 예: “총알배송” - “빠른 배송” - “신속 배송” (벡터 유사도 높음)

VectorDB란?

  • 임베딩 벡터를 저장하고 검색하는 데이터베이스
  • 쿼리 벡터와 유사도가 높은 문서를 빠르게 검색
  • 대표 예 : ChromaDB, Pinecone, FAISS
[의미 기반 검색 흐름]
질문: "빠른 배송 알려줘"
    ↓ 임베딩
질문 벡터: [0.2, -0.5, 0.8, ...]
    ↓ 유사도 검색
가장 유사한 문서: "총알배송_서비스 혜택.pdf" (유사도 0.92)

코사인 유사도(Cosine Similarity)의 수학적 배경

Vector Store에서 “유사한 문서”를 찾을 때 가장 많이 사용하는 방법은 코사인 유사도이다.

코사인 유사도

  • 두 벡터 사이의 각도를 측정 (크기는 무시)
  • 값의 범위 : -1 ~ 1 (임베딩 벡터의 각 차원 값은 양수&음수 모두 가능하며, 코사인 유사도는 벡터 간 방향 유사성을 측정)
  • 1에 가까울수록 의미가 유사, 0에 가까울수록 관련 없음
유사도 방식특징적합한 상황
유클리드 거리벡터 간 직선 거리크기도 중요한 경우
코사인 유사도벡터 간 각도 (크기 무시)텍스트 유사도 측정
내적(Dot Product)빠른 계산정규화된 벡터

텍스트 임베딩에서 코사인 유사도가 선호되는 이유:

  • 긴 문서와 짧은 문서를 공정하게 비교 가능
  • 의미적 방향성만 비교하므로 직관적

청킹 전략

청킹 : 긴 문서를 적절한 크기의 조각(chunk)으로 나누는 기법

청킹이 필요한 이유:

  1. 토큰 제한 : LLM에는 입력 토큰 제한이 있음
  2. 검색 정확도 : 작은 단위일수록 관련성 높은 정보만 검색
  3. 비용 효율 : 불필요한 토큰 사용 감소

주요 파라미터:

  • chunk_size : 청크의 최대 크기 (문자 수)
  • chunk_overlap : 청크 간 겹치는 부분 (문맥 유지)
[청킹 예시]
원본 : "ABCDEFGHIJ" (10글자)
chunk_size=5, overlap=2
-> ["ABCDE", "DEFGH", "GHIJ"]

청킹 전략별 Trade-off 심화 분석

청킹 설정은 검색 품질, 비용, 응답 정확도 사이의 균형을 결정합니다.

chunk_size Trade-off

chunk_size장점단점적합한 상황
작음 (100~200)세밀한 검색, 정확한 매칭문맥 부족, 청크 수 증가FAQ, 정의/용어 검색
중간 (300~500)균형 잡힌 검색최적점 찾기 어려움일반 문서, 정책 안내
큼 (1000+)풍부한 문맥노이즈 포함, 검색 정확도 하락긴 설명문, 연속적 내용

chunk_overlap Trade-off

overlap 비율장점단점
0%중복 없음, 저장 효율적문맥 단절 위험, 문장 중간 절단
10~20%문맥 연결 유지, 적절한 중복표준적인 선택
30%+강한 문맥 유지과도한 중복, 저장/비용 증가

실전 선택 기준:

문서 특성 판단:
├── FAQ 형태 (짧은 Q&A)
│   └── chunk_size: 100~200, overlap: 0~10%
├── 정책 문서 (조건/단서 많음)
│   └── chunk_size: 300~400, overlap: 15~20%
├── 기술 문서 (연속적 설명)
│   └── chunk_size: 500~800, overlap: 10~15%
└── 법률/계약 문서 (정확성 중요)
    └── chunk_size: 200~300, overlap: 20~25%

청킹 전략 선택 기준표

chunk_size특징적합한 상황
작음 (100~200)세밀한 검색, 청크 수 많음FAQ, 짧은 문단
중간 (300~500)균형 잡힌 검색일반 문서
큼 (1000+)넓은 문맥, 청크 수 적음긴 설명, 연속적 내용

overlap 설정:

  • 0: 문맥 단절 위험
  • chunk_size의 10~20%: 적절한 문맥 유지
  • 너무 큼: 중복 정보, 비용 증가

전체 RAG 파이프라인 (LangGraph)

LangGraph : LangChain 팀에서 개발한 상태 기반 워크플로우 프레임워크

왜 LangGraph인가?

  1. 명시적 상태 관리 : 각 노드가 상태를 읽고 쓰며, 데이터 흐름이 명확함
  2. 모듈화된 노드 : 검색, 생성 등 각 단계를 독립적인 노드로 분리
  3. 확장성 : 조건부 분기, 반복, 병렬 실행 등 복잡한 워크플로우 지원
  4. 디버깅 용이 : 각 노드의 입출력을 추적하기 쉬움

LangGraph 핵심 구성요소:

  • State : 그래프 전체에서 공유되는 데이터 구조
  • Node : 상태를 변환하는 함수 (검색, 생성 등)
  • Edge : 노드 간 연결 (순차 실행, 조건부 분기)
[LangGraph RAG 파이프라인 구조]
 
        START
          │
          ▼
    ┌─────────────┐
    │   retrieve  │  ← 질문으로 관련 문서 검색
    │   (Node 1)  │
    └─────────────┘
          │
          ▼
    ┌─────────────┐
    │   generate  │  ← 검색된 문서로 답변 생성
    │   (Node 2)  │
    └─────────────┘
          │
          ▼
         END

LCEL 체인 vs LangGraph:

비교 항목LCEL 체인LangGraph
상태 관리암시적명시적 (TypedDict)
분기/반복제한적유연함
디버깅어려움용이함
확장성단순 파이프라인 적합복잡한 워크플로우 적합

RAG 효과 정리

비교 항목LLM onlyLangGraph RAG
정확도낮음 (추측/환각)높음 (자료 기반)
신뢰도낮음높음
최신 정보학습 시점까지만업데이트 가능
도메인 지식제한적문서에 따라 확장
출처 추적불가능가능
확장성제한적노드 추가로 유연한 확장

LangGraph의 장점:

  • 각 노드(retrieve, generate)의 역할이 명확히 분리됨
  • 상태(State)를 통해 데이터 흐름이 투명함
  • 향후 조건부 분기, 반복 등 복잡한 로직 추가 용이

핵심 포인트: LangGraph RAG를 통해 정확한 답변 생성이 가능해졌고, 워크플로우 확장도 용이하다.