RAG 파이프라인: 청킹·임베딩·검색·리랭킹·생성 단계별 설계

상위: 00.LLMOps & RAG MOC

개요

**RAG(Retrieval-Augmented Generation)**는 LLM의 세 가지 구조적 한계 — 환각(hallucination), 최신성 부족, 컨텍스트 창 한계 — 를 외부 지식 검색으로 보완하는 패턴이다. 모델 파라미터에 지식을 욱여넣는 대신, 질문 시점에 관련 문서를 검색해 프롬프트에 넣어 “근거에 기반한” 답을 생성한다. 핵심은 RAG를 하나의 마법 상자가 아니라 독립적으로 튜닝 가능한 단계들의 파이프라인으로 보는 것이다. 각 단계(로딩 → 청킹 → 임베딩 → 벡터 저장 → 검색 → 리랭킹 → 생성)에서 품질이 조금씩 새면 마지막 답변이 무너지므로, 어느 단계가 병목인지 계측·평가로 짚어야 한다.

원리: 단계별 파이프라인

1. 로딩·파싱

PDF·HTML·DB·오피스 문서 등에서 원문을 수집하고 텍스트로 정규화한다. 표·코드·수식이 많은 문서는 레이아웃을 살리는 파서(layout-aware)를 써야 뒤 단계가 살아난다. 여기서 깨진 텍스트는 이후 어떤 튜닝으로도 복구되지 않는다.

2. 청킹(chunking)

문서를 검색 단위로 자른다. 임베딩·컨텍스트 창 제약 때문에 통문서를 넣을 수 없으므로 적절히 쪼갠다.

  • 재귀적 분할(recursive): 문단 → 문장 → 단어 순으로 경계를 존중하며 목표 크기로 자른다.
  • 크기(chunk size)와 오버랩(overlap): 크기가 너무 크면 검색 정밀도가 떨어지고, 너무 작으면 문맥이 잘린다. 인접 청크가 문장을 공유하도록 오버랩(예: 10~20%)을 둬 경계에서 문맥이 끊기는 것을 완화한다.

3. 임베딩

각 청크를 의미 벡터로 변환한다. 임베딩 모델 선택이 도메인 적합성을 좌우한다. 색인(문서)과 질의(질문)를 반드시 같은 모델·같은 정규화로 임베딩해야 한다. 모델을 바꾸면 전체 재색인이 필요하다.

4. 벡터 저장

임베딩을 벡터 DB에 적재하고 ANN 인덱스를 붙인다. 이 볼트에서는 pgvector(PostgreSQL 확장)를 표준으로 쓴다. 인덱스·필터·적재 튜닝의 상세는 중복을 피해 DB 노트로 교차링크한다: pgvector - 셋업과 HNSW 인덱스 · pgvector - 대규모 임베딩 적재 튜닝(31.Database).

5. 검색(retrieval)

질의 임베딩과 가까운 상위 top-k 청크를 찾는다.

방식원리강점
Dense임베딩 유사도(ANN)의미·동의어에 강함
Sparse키워드(BM25)고유명사·정확 일치에 강함
Hybriddense+sparse 결합(RRF 등)양쪽 장점, 실무 기본

메타데이터 필터(예: tenant_id, 날짜)와 ANN 인덱스의 상호작용에 주의한다(아래 필터 스타베이션).

6. 리랭킹(reranking)

검색이 넉넉히(top-k=50) 후보를 뽑으면, cross-encoder 리랭커가 (질의, 청크) 쌍을 함께 인코딩해 정밀 점수를 매기고 상위 몇 개만 남긴다. 임베딩은 질의와 문서를 따로 인코딩(bi-encoder)해 빠르지만 거칠고, cross-encoder는 함께 인코딩해 느리지만 정밀하다. “빠른 넓은 검색 → 정밀 재정렬”의 2단 구성이 정밀도를 크게 올린다.

7. 생성(generation)

최종 컨텍스트 + 질문으로 프롬프트를 구성해 LLM에 넣는다. 근거를 명시하고(인용/출처), 컨텍스트 밖 추측을 억제하며, 컨텍스트가 없으면 “모른다”고 답하게 시스템 프롬프트로 유도한다.

실전: 최소 파이프라인 스니펫

from langchain_text_splitters import RecursiveCharacterTextSplitter
 
# 2. 청킹: 크기 800, 오버랩 120 (문맥 경계 완화)
splitter = RecursiveCharacterTextSplitter(chunk_size=800, chunk_overlap=120)
chunks = splitter.split_text(raw_text)
 
# 3~4. 임베딩 후 pgvector 적재 (색인/질의 동일 모델)
embs = embed_model.encode([c for c in chunks], normalize_embeddings=True)
cur.executemany(
    "INSERT INTO document_chunks (content, embedding, tenant_id) VALUES (%s, %s, %s)",
    [(c, e.tolist(), tenant) for c, e in zip(chunks, embs)],
)
 
# 5. 검색: top-k=50 (리랭킹 여유분 확보)
q = embed_model.encode(query, normalize_embeddings=True).tolist()
cur.execute("""
    SELECT content FROM document_chunks
    WHERE tenant_id = %s
    ORDER BY embedding <=> %s::vector   -- 코사인 거리
    LIMIT 50
""", (tenant, q))
candidates = [r[0] for r in cur.fetchall()]
 
# 6. 리랭킹: cross-encoder로 상위 5개만
scores = reranker.predict([(query, c) for c in candidates])
top = [c for _, c in sorted(zip(scores, candidates), reverse=True)][:5]
 
# 7. 생성: 근거 컨텍스트 + 질문
prompt = f"다음 컨텍스트만 근거로 답하라.\n---\n{chr(10).join(top)}\n---\n질문: {query}"
answer = llm.generate(prompt)

심화·평가·트레이싱

  • Self-RAG: 검색 필요 여부와 근거 충실성을 모델이 스스로 판단(reflection token)해 불필요한 검색·환각을 줄인다.
  • GraphRAG: 문서에서 엔티티·관계 그래프를 뽑아 커뮤니티 요약을 만들고, 단순 top-k로는 답하기 어려운 전역적·다중 홉 질문에 대응한다. (심화 검색 개념은 27.Technique)
  • 평가(RAGAS): faithfulness(답이 컨텍스트에 근거하는가), answer relevancy, context precision/recall을 정량화해 어느 단계가 약한지 진단한다.
  • 트레이싱(Langfuse): 검색·리랭킹·생성 각 단계를 span으로 남겨 지연·비용·품질을 추적한다. OpenTelemetry - 분산 추적과 결합해 인프라 추적과 LLM 도메인 추적을 잇는다.

함정과 베스트프랙티스

함정증상대응
청킹 경계가 문맥을 절단정답 문장이 두 청크로 쪼개져 검색 실패오버랩·구조 기반 분할·문서 구조 존중
임베딩 모델 미스매치색인·질의 모델이 달라 유사도 무의미색인/질의 동일 모델·정규화, 교체 시 전량 재색인
dense 단독 의존고유명사·코드 검색이 약함hybrid(BM25+dense)
필터 스타베이션엄격한 메타 필터 + ANN post-filter가 빈 결과hnsw.iterative_scan·pre-filter 전략(pgvector - 셋업과 HNSW 인덱스)
리랭킹 없이 top-k 그대로관련 없는 청크가 컨텍스트 오염cross-encoder 리랭커로 정밀 재정렬
컨텍스트 과다 주입lost-in-the-middle, 비용↑리랭킹 후 소수만, 근거 순서 배치
  • 넓게 검색 → 좁게 리랭킹: top-k를 넉넉히 뽑고 cross-encoder로 압축하는 2단 구성이 정밀도·비용 균형에 유리하다.
  • 단계별 평가 게이트: 검색 recall과 생성 faithfulness를 분리 측정해야 병목을 정확히 고친다.
  • 필터와 인덱스의 궁합을 항상 확인한다. 멀티테넌시에서 특히 필터 스타베이션이 잦다.

정리

RAG는 로딩 → 청킹 → 임베딩 → 벡터 저장 → 검색 → 리랭킹 → 생성의 독립 튜닝 가능한 파이프라인이다. 청킹의 크기·오버랩이 검색 품질을 좌우하고, 색인·질의는 동일 임베딩 모델을 써야 하며, dense+sparse hybrid 검색에 cross-encoder 리랭킹을 얹어 정밀도를 끌어올린다. 벡터 저장·필터 튜닝은 pgvector - 셋업과 HNSW 인덱스·pgvector - 대규모 임베딩 적재 튜닝으로, 서빙은 오픈소스 서빙 - KServe·Triton·vLLM으로, 평가·트레이싱은 RAGAS와 [OpenTelemetry - 분산 추적]으로 확장한다. 현업의 핵심 함정은 청킹 경계 절단, 임베딩 미스매치, 필터 스타베이션 셋이다.

키워드: #rag #llmops #vector-db 청킹 임베딩 리랭킹 hybrid-search RAGAS