RAG Pipeline의 end-to-end 데이터 흐름. 인덱싱이 벡터 DB를 채우고, 검색·생성이 그걸 읽어 LLM으로 보냄.
💡 핵심 개념: RAG는 한 번에 끝나는 게 아니라 두 개의 시점(오프라인/온라인)으로 나뉨. 인덱싱은 배치로 천천히 돌고, 검색·생성은 사용자 요청마다 실시간. 둘을 잇는 게 벡터 DB.
flowchart LR
subgraph OFF["📥 인덱싱 (오프라인·배치)"]
D[문서]:::off
C[청크]:::off
E[임베딩]:::off
D --> C --> E
end
VDB[("🗄️ 벡터 DB")]:::store
subgraph ON["🔍 검색·생성 (온라인·추론)"]
Q[질의]:::on
R[검색·리랭킹]:::on
G[컨텍스트 주입]:::on
Q --> R --> G
end
LLM{{"🧠 LLM"}}:::llm
A["📨 응답"]:::resp
E ==>|"bulk upsert"| VDB
VDB ==>|"top-k retrieve"| R
G --> LLM --> A
classDef off fill:#fef3c7,stroke:#f59e0b,color:#000
classDef on fill:#dbeafe,stroke:#3b82f6,color:#000
classDef store fill:#e5e7eb,stroke:#6b7280,color:#000
classDef llm fill:#f3e8ff,stroke:#a855f7,color:#000
classDef resp fill:#dcfce7,stroke:#10b981,color:#000
🔧 단계별 책임
- Loader — PDF·HTML·DB 등 다양한 소스에서 텍스트 추출 (
Unstructured.io, PyPDF)
- Splitter — 긴 문서를 의미 단위로 분할 (512~1024 토큰이 일반적)
- Embedder — 텍스트 → 벡터 (
OpenAI text-embedding-3, Cohere embed-v3, BGE-M3)
- Vector DB — 벡터 저장·검색 (
Pinecone, Weaviate, Qdrant, pgvector)
- Retriever — query와 유사한 top-k chunk 검색 (cosine/dot product)
- Reranker — 검색 결과 재정렬로 정밀도↑ (
Cohere rerank, BGE reranker)
- Context Injector — chunk를 prompt 템플릿에 끼워 넣기
📋 구체적 예시 (한국어 사내 문서 QA):
- Notion 페이지 1000개를 Loader로 추출 (PDF, HTML)
- Splitter로 페이지당 평균 800 토큰 청크
- 임베딩 모델은
text-embedding-3-small (저렴) 또는 text-embedding-3-large (정밀)
- Pinecone 서버리스에 upsert
- 사용자 "연차 정책" 검색 → top-10 chunk → rerank → top-3
- Prompt:
"다음 문서 기반으로 답해. 문서: {chunk}. 질문: {query}"
⚠️ 흔한 함정:
- 청크 사이즈 잘못 — 너무 크면 검색 정밀도↓, 너무 작으면 맥락 손실. 도메인 PDF는 보통 512 토큰이 안전.
- 임베딩 모델 변경 시 재임베딩 필요 — 모델 바꾸면 모든 벡터 무효. 마이그레이션 계획 필수.
- 벡터 DB 메타데이터 누락 — chunk에 source/timestamp/author 저장 안 하면 필터링·디버깅 힘듦.
- Reranker 생략 — top-10을 그냥 LLM에 넣으면 노이즈 많음. rerank로 top-3~5 압축 권장.
🛠️ 관련 프레임워크: LlamaIndex(전체 파이프라인), Haystack(production DAG), LangChain(범용), Unstructured.io(Loader 전담).