LangChain으로 RAG 파이프라인 직접 만들기
· RAG는 "관련 문서를 먼저 찾아와 그걸 근거로 답을 만드는" LLM 활용 방식입니다.
· 파이프라인은 불러오기 → 쪼개기 → 임베딩·저장 → 검색 → 생성의 다섯 단계로 이어집니다.
· LangChain은 이 단계를 부품처럼 조립할 수 있게 해줘, 몇 줄로 나만의 문서 QA 봇을 만들 수 있어요.
앞선 글에서 Retriever가 질문에 맞는 문서를 찾아오는 검색기라고 정리했습니다. 이번에는 그 검색을 실제 답변 생성까지 하나로 잇는 RAG(검색 증강 생성) 파이프라인을 직접 만들어 봅니다. 내 문서를 넣으면 그 내용을 근거로 답하는 챗봇, 그 뼈대가 어떻게 조립되는지 단계별로 따라가 보겠습니다.
🧠 RAG가 필요한 이유
LLM은 학습 시점까지의 데이터 안에서만 답을 만듭니다. 우리 회사 규정, 제품 매뉴얼, 어제 올라온 공지 같은 건 모델이 알 리가 없죠. 이럴 때 억지로 모델을 다시 학습(파인튜닝)시키는 건 비용도 크고 문서가 바뀔 때마다 반복해야 합니다.
RAG의 발상은 간단합니다. 모델을 바꾸지 말고, 답에 필요한 문서를 그때그때 찾아 프롬프트에 넣어주자.
덕분에 문서만 교체하면 답도 즉시 달라지고, 모델은 주어진 근거 안에서 답하므로 엉뚱한 환각도 줄어듭니다. RAG가 사내 문서 QA에 특히 잘 어울리는 이유예요.
🗺️ 파이프라인 전체 그림
RAG 파이프라인은 크게 준비 단계와 질의 단계로 나뉩니다. 준비 단계는 문서를 미리 검색 가능한 형태로 저장해 두는 과정이고, 질의 단계는 질문이 들어올 때마다 검색하고 답하는 과정입니다.
즉 무거운 저장 작업은 미리 해두고, 질문마다 가벼운 검색만 반복하는 구조입니다. 이 분리를 이해하면 전체 흐름이 한눈에 잡혀요.
🔍 단계별로 뜯어보기
| 단계 | 하는 일 | LangChain 부품 |
|---|---|---|
| ① 불러오기 | PDF·웹·텍스트 등 원본을 읽어 옴 | Document Loader |
| ② 쪼개기 | 긴 문서를 적당한 조각으로 분할 | Text Splitter |
| ③ 임베딩·저장 | 조각을 벡터로 바꿔 저장고에 넣음 | Embeddings + Vector Store |
| ④ 검색 | 질문과 가까운 조각을 골라 옴 | Retriever |
| ⑤ 생성 | 찾은 문서를 근거로 답을 작성 | Prompt + LLM |
핵심은 각 단계가 독립된 부품이라는 점입니다. 로더를 바꾸면 다른 형식의 문서를 다룰 수 있고, 임베딩 모델을 바꾸면 검색 품질이 달라지며, LLM만 교체해도 나머지는 그대로 재사용됩니다.
💻 코드로 조립하기
버전에 따라 이름은 조금씩 다르지만, 조립하는 흐름은 아래와 같습니다. 앞의 다섯 단계가 그대로 코드로 이어지는 걸 볼 수 있어요.
# ── 준비 단계 (한 번만) ──
# 1) 문서 불러오기
docs = PDFLoader("manual.pdf").load()
# 2) 적당한 크기로 쪼개기
chunks = TextSplitter(chunk_size=500, chunk_overlap=50).split(docs)
# 3) 임베딩해서 벡터 스토어에 저장
store = VectorStore.from_documents(chunks, embedding_model)
retriever = store.as_retriever(k=3) # 상위 3개만 검색
# ── 질의 단계 (매번) ──
question = "환불은 며칠 안에 신청해야 하나요?"
# 4) 관련 문서 검색
found = retriever.get_relevant(question)
# 5) 찾은 문서를 프롬프트에 붙여 답 생성
prompt = f"아래 문서만 근거로 답하세요.\n\n문서:\n{found}\n\n질문: {question}"
answer = llm.generate(prompt)
보다시피 준비 단계는 위쪽, 질의 단계는 아래쪽으로 깔끔하게 나뉩니다. LangChain에서는 이 뒷부분(검색+생성)을 하나의 체인으로 묶어 질문 한 줄만 넣으면 답이 나오게 감싸기도 합니다.
⚠️ 단계별 흔한 실수
파이프라인이 겉보기엔 잘 도는데 답이 부실하다면, 대개 특정 단계에서 새는 경우가 많습니다.
🔧 실무 팁
- 준비와 질의를 분리 — 임베딩·저장은 미리 한 번만 해두고, 서비스에서는 저장된 벡터 스토어를 불러 쓰는 게 빠르고 저렴합니다.
- 출처를 함께 저장 — 각 조각에 원본 위치(파일·페이지)를 붙여 두면, 답변에 근거를 표시해 신뢰도를 높일 수 있어요.
- 겹침(overlap)을 조금 주기 — 조각을 자를 때 앞뒤를 약간 겹치게 하면 문장이 중간에 끊겨 맥락을 잃는 걸 줄여줍니다.
- 한국어 문서엔 한국어 임베딩 — 검색 품질은 임베딩에 크게 좌우되니, 다루는 언어를 잘 처리하는 모델을 고르는 게 좋습니다.
❓ 자주 묻는 질문 (FAQ)
Q. RAG를 쓰면 모델을 다시 학습시키는 건가요?
A. 아니요. 모델은 그대로 두고, 답할 때마다 관련 문서를 찾아 프롬프트에 넣어주는 방식입니다. 그래서 문서를 바꾸면 별도 학습 없이 답도 바로 달라져요.
Q. 파인튜닝과 뭐가 다른가요?
A. 파인튜닝은 모델 자체에 지식을 새겨 넣는 것이고, RAG는 필요할 때 외부 문서를 꺼내 쓰는 것입니다. 자주 바뀌는 문서나 출처가 중요한 경우엔 RAG가 유리합니다.
Q. 문서가 아주 많아도 되나요?
A. 됩니다. 미리 벡터 스토어에 저장해 두면, 질문할 때는 그중 관련 조각 몇 개만 검색하므로 문서량이 많아도 답변 속도엔 큰 영향이 없습니다.
마무리
정리하면, RAG 파이프라인은 불러오기 → 쪼개기 → 임베딩·저장 → 검색 → 생성의 다섯 단계를 잇는 구조이고, LangChain은 각 단계를 부품처럼 갈아 끼울 수 있게 해줍니다. 무거운 저장은 미리, 가벼운 검색은 질문마다 — 이 분리만 기억하면 나만의 문서 QA 봇의 뼈대는 이미 손에 들어온 셈입니다. 다음 편에서는 이 파이프라인의 첫 단추인 Document Loader로 다양한 형식의 문서를 불러오는 이야기를 다뤄 보겠습니다.
댓글
댓글 쓰기