RAG가 틀렸을 때 제일 먼저 듣는 말이 있습니다. "임베딩을 바꾸자."

물론 그게 답일 때도 있습니다. 하지만 제 경험에는 범인이 다른 곳에 있을 때가 더 많습니다. 문서 파싱, 자르기, 검색 방식, 그리고 "모른다"고 말할 줄 아는지까지. 이 글은 RAG 파이프라인을 5단계로 나눠 어디서 망가졌는지 찾는 순서를 정리합니다.

기본 개념은 RAG란 글과 Classic·Graph·Agentic RAG 글에 있습니다. 여기는 고장 수리 편입니다.

전체 지도: 답이 나오기까지 5개 관문

[1. 파싱] 문서 읽기 (PDF·표·스캔)
  → [2. 청킹] 자르기 (단위·겹침·메타데이터)
    → [3. 검색] 찾기 (벡터·키워드·하이브리드)
      → [4. 재순위] 고르기 (상위 후보 재정렬)
        → [5. 생성] 답하기 (인용·거절 포함)

사용자는 5번만 봅니다. "답이 틀렸네." 하지만 원인은 1~4번 어디에나 있을 수 있습니다. 순서대로 의심하는 게 핵심입니다. 뒤부터 고치면 돈과 시간만 듭니다.

1단계 파싱: 검색이 틀리기 전에 읽기가 틀린다

Mistral OCR 글의 핵심 문장을 다시 가져옵니다. 검색이 틀리기 전에 문서 파싱부터 틀릴 수 있다.

대표 증상들입니다.

증상 파싱 의심 신호
표 내용이 답에 안 나옴 표가 텍스트로 깨져서 들어감
"문서에 없는데요"가 자주 나옴 스캔·이미지 페이지가 통째로 비어 있음
페이지 번호·조항 인용이 어긋남 머리말·꼬리말·각주가 본문에 섞임

점검은 간단합니다. 검색을 거치지 말고 파싱 결과 원문을 눈으로 읽어보세요. 사람도 못 찾을 문서에서 검색이 찾을 리 없습니다.

점검 ①: 질문 5개의 근거 페이지를 파싱 원문에서 Ctrl+F로 찾기
→ 여기서 안 나오면 파싱 문제 확정. 임베딩을 만지지 마라.

2단계 청킹: 자르는 게 답의 단위를 정한다

파싱이 깨끗해도 자르기를 잘못하면 답이 안 나옵니다.

너무 크게 자르면: 검색은 맞았는데 쓰레기 정보까지 딸려와서 답이 흐려짐
너무 잘게 자르면: 맥락이 잘려서 "그래서 어쩌라는 거지" 상태가 됨
겹침 없이 자르면: 경계에 걸린 내용이 양쪽에서 다 사라짐
메타데이터 없이 자르면: "2024년 규정"과 "2026년 규정"이 섞임

실전 규칙 세 개만 기억하세요.

규칙 1: 자르는 단위를 문서 구조에 맞춘다 (조항·섹션·표 단위)
규칙 2: 겹침을 준다 (보통 10~20%, 경계 손실 방지)
규칙 3: 메타데이터를 붙인다 (문서명·날짜·버전·페이지)

특히 버전 섞임이 흔합니다. 규정·매뉴얼·약관은 날짜가 답의 일부입니다. 날짜 메타데이터 없이 벡터 유사도만 믿으면 구버전이 버젓이 1등으로 옵니다.

3단계 검색: 벡터만 쓰면 지는 게임이 있다

벡터 검색은 의미가 비슷하면 잘 찾습니다. 반대로 고유명사·제품 코드·조항 번호·숫자 같은 정확한 일치는 키워드(BM25 같은)가 더 잘 찾습니다.

벡터가 강한 경우: "환불 규정 알려줘" 같은 의미 질문
키워드가 강한 경우: "제14조 3항", "모델명 XB-200", "2026년 3월 공지"
실무 질문: 둘 다 섞여 있음 → 하이브리드가 기본값

하이브리드 구조는 이렇습니다.

질문 ──┬──→ 벡터 검색 상위 20개 ──┐
       └──→ 키워드 검색 상위 20개 ─┴──→ 합치기 → [4단계 재순위] → 상위 5개 → 생성

개념 스케치로 흐름만 적어보면 이렇습니다.

# hybrid_search.py — 개념 스케치
def hybrid_search(query, top_n=5):
    dense_hits = vector_search(query, k=20)    # 의미 기반
    sparse_hits = keyword_search(query, k=20)  # 정확 일치 기반
    merged = reciprocal_rank_fusion(dense_hits, sparse_hits)
    reranked = rerank(query, merged[:20])      # 4단계
    return reranked[:top_n]

합치는 방법(가중합, RRF 등)과 가중치는 내 데이터로 정합니다. 남의 블로그 숫자를 그대로 가져오면 안 됩니다. 질문 30개짜리 평가 셋으로 재현하는 게 미니 eval 글의 5문항과 같은 원리입니다.

4~5단계 재순위와 생성: 마지막에 "모른다"가 있어야 한다

재순위는 후보 20개를 질문과의 관련도로 다시 줄 세워 상위 5개만 생성에 넘기는 일입니다. 비용은 조금 들지만 답 품질이 확 올라가는 구간이라 가성비가 좋습니다.

생성 단계에서는 환각 vs 답변 거부 글의 원칙이 들어옵니다. 근거가 없으면 "모른다"고 말하게 하세요. 최신 측정도 이 방향을 가리킵니다.

- Gemini 4 Argon: 환각률 15%, 선도 모델 중 최저 (정확도는 다소 낮아도 overall은 대등)
- GPT-6 Astra: 환각률 92% → 51%로 개선, 정확도도 같이 오름
→ "거절을 잘하는 모델"이 지식 작업에서 유리해지는 추세

Hebbia 사례도 있습니다. Opus 5.5로 인용 회수율 역대 최고를 찍으면서 토큰 효율도 챙겼다고 합니다. 인용을 강제하는 게 환각을 줄이는 실전 장치입니다.

생성 프롬프트에 넣을 세 줄:
1. 근거 청크 번호를 답 문장마다 표기할 것
2. 근거에 없으면 "문서에서 확인되지 않았습니다"라고 답할 것
3. 확신 없는 수치·날짜는 쓰지 말 것

CodeBridge Mini Lab: 실패 20개를 분류하기

① 틀린 질문 20개를 모은다 (사용자 로그·테스트에서)
② 답이 아니라 단계를 찾는다:

  파싱? → 파싱 원문에 근거가 있는가 (없으면 파싱)
  청킹? → 근거 청크를 읽으면 답이 나오는가 (맥락 잘림이면 청킹)
  검색? → 상위 20개 안에 근거가 있는가 (없으면 검색)
  재순위? → 20개 안에는 있는데 5개 안에 없는가 (있으면 재순위)
  생성? → 5개 안에 있는데 답이 틀렸는가 (있으면 생성·프롬프트)

③ 분포를 센다:
  예) 파싱 8, 청킹 5, 검색 4, 재순위 2, 생성 1
  → 1등부터 고친다. 임베딩은 검색이 1등일 때만 만진다.

이 분류가 끝나면 "임베딩을 바꾸자" 같은 막연한 처방이 사라집니다. 숫자가 다음 할 일을 정해줍니다.

결론: 앞에서부터 의심하세요

정리하면 순서가 이렇습니다.

파싱 원문 확인 → 청킹·메타데이터 점검 → 하이브리드 검색 → 재순위 → 인용 강제와 거절 허용

RAG는 모델이 아니라 파이프라인입니다. 사내 문서 챗봇 글에서 그린 구조도에 이 글의 점검 순서를 겹쳐보세요. 어디가 비어 있는지 바로 보일 겁니다. 그리고 기억하세요. "모른다"는 답도 성능입니다.

함께 읽으면 좋은 글

참고 자료

이 주제를 직접 따라가며 배우고 싶다면

파싱·청킹·검색·생성을 한 구조로 묶어 실무 챗봇까지 완성해보고 싶다면, Classic RAG에서 GraphRAG·Agentic RAG로 확장하는 설계 과정이 이 글의 5단계 점검과 바로 이어집니다.