LangChain을 이용한 2단계 RAG 구현
요약
LangChain과 FAISS를 활용하여 가장 기초적인 형태인 '2단계 RAG' 시스템을 구축하는 방법을 설명합니다. LLM의 지식 한계를 극복하기 위해 관련 문서를 검색하고 이를 바탕으로 답변을 생성하는 핵심 원리와 구현 과정을 다룹니다.
핵심 포인트
- RAG의 핵심 개념인 검색(Retrieve)과 생성(Generate) 단계 설명
- LangChain과 FAISS를 이용한 벡터 스토어 구축 방법
- HuggingFaceEmbeddings를 통한 텍스트의 벡터화 과정
- 지식 베이스 기반의 근거 있는 답변(Grounded Answer) 생성 원리
만약 당신이 ChatGPT에게 자신의 문서에 대해 질문했다가 잘못된 답변을 받은 적이 있다면, 당신은 이미 RAG가 해결하고자 하는 문제를 이해하고 있는 것입니다.
LLM (Large Language Models)은 강력하지만, 오직 학습된 데이터만을 알고 있습니다. 이들은 당신의 내부 문서, 회사의 위키(wiki), 또는 학습 중단 시점(training cutoff) 이후에 발생한 일들에 대해서는 전혀 알지 못합니다. RAG는 이 문제를 해결합니다.
이 포스트에서는 LangChain과 FAISS를 사용하여 가장 기본적인 버전의 RAG인 '2단계 RAG'를 처음부터 구축해 보겠습니다. 군더더기 없이, 실제 코드에서 작동하는 핵심 아이디어만을 다룹니다.
RAG란 무엇인가?
RAG는 **검색 증강 생성 (Retrieval-Augmented Generation)**의 약자입니다. 이름은 거창해 보이지만 아이디어는 간단합니다:
- 검색 (Retrieve) — 주어진 질문에 대해 가장 관련성이 높은 문서를 찾습니다.
- 생성 (Generate) — 해당 문서들을 LLM에 제공하여 답변하게 합니다.
LLM이 마법처럼 모든 것을 알기를 기대하는 대신, 먼저 올바른 정보를 전달한 다음 질문을 던지는 것입니다. 그것이 전부입니다.
왜 "2단계"인가?
흐름이 말 그대로 두 단계이기 때문입니다:
질문 (Query) → 관련 문서 검색 (retrieve relevant docs) → LLM이 문서 + 질문 확인 (LLM sees docs + query) → 답변 (answer)
에이전트(agents)도, 복잡한 체인(chains)도, 화려한 오케스트레이션(orchestration)도 없습니다. 그저 검색하고, 생성할 뿐입니다. 이것은 모든 고급 RAG 시스템이 그 위에 구축되는 기초입니다.
우리가 만들 것
우리는 LangChain 개념에 관한 5개의 문서로 구성된 작은 지식 베이스(knowledge base)를 가지고 있습니다. 사용자가 질문을 하면 우리는 다음과 같이 수행합니다:
- 해당 지식 베이스에서 가장 관련성이 높은 문서 2개를 찾습니다.
- 이를 컨텍스트 (context)로서 LLM에 전달합니다.
- 근거에 기반한 답변 (grounded answer)을 받습니다.
코드를 살펴보겠습니다.
1단계: 지식 베이스 (The Knowledge Base)
from langchain_core.documents import Document
docs = [
...
실제 프로젝트에서 이러한 문서들은 PDF, 데이터베이스, 웹사이트 또는 당신이 관심을 갖는 모든 데이터 소스에서 가져오게 됩니다. 여기서는 하드코딩된 문자열을 사용하여 간단하게 유지하겠습니다.
2단계: 벡터 스토어 (Vector Store) 구축
이 부분이 실제로 "마법"이 일어나는 곳이므로, 주의 깊게 나누어 살펴보겠습니다.
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_community.vectorstores import FAISS
...
여기서 어떤 일이 일어나고 있나요?
먼저, HuggingFaceEmbeddings는 텍스트를 벡터(vector) — 즉, 문장의 _의미(meaning)_를 나타내는 숫자 리스트 — 로 변환하는 모델을 로드합니다. 유사한 문장들은 수치적으로 서로 가까운 벡터를 생성합니다.
"RAG stands for Retrieval-Augmented Generation..."
↓
[0.87, 0.02, 0.11, ...] ← 384개의 숫자
그 다음 FAISS.from_documents()는 5개의 문서를 모두 가져와 각 문서를 벡터로 변환하고, 이를 FAISS 인덱스(index) — 빠른 유사도 검색(similarity search)을 위해 특별히 구축된 인메모리(in-memory) 데이터베이스 — 에 저장합니다.
마지막으로, .as_retriever(search_kwargs={"k": 2})는 이 모든 것을 리트리버(retriever) 객체로 감쌉니다. k=2는 누군가 질문을 했을 때, 가장 유사한 2개의 문서를 반환한다는 의미입니다.
단계 3: 2단계 RAG 함수
def answer(query: str) -> str:
# 단계 1: 검색 (Retrieve)
retrieved = retriever.invoke(query)
...
단계 1 — 검색 (Retrieve):
retriever.invoke(query)는 내부적으로 다음과 같은 작업을 수행합니다:
- 동일한 HuggingFace 모델을 사용하여 쿼리(query)를 벡터로 임베딩(Embed)합니다.
- 해당 벡터를 저장된 모든 문서 벡터와 비교합니다.
- 가장 유사한 상위 2개의 문서를 반환합니다.
query = "What is RAG?"
↓ 임베딩 (embed)
[0.85, 0.04, 0.10, ...]
...
단계 2 — 생성 (Generate):
검색된 문서들을 하나의 컨텍스트(context) 문자열로 엮고 프롬프트(prompt)를 구성합니다. "아래의 컨텍스트만을 사용하여 답변하세요(Answer using ONLY the context below)."라는 지시사항에 주목하세요. 이는 매우 중요한데, LLM이 정해진 범위를 벗어나거나 우리가 검색해온 내용 이외의 것을 지어내는 것(hallucination)을 방지하기 때문입니다.
전체 코드
import os
from dotenv import load_dotenv
...
샘플 출력
============================================================
Q: What is RAG?
A: RAG stands for Retrieval-Augmented Generation. It combines
...
LLM은 추측하는 것이 아니라, 검색된 문서를 읽고 그 내용으로부터 직접 답변하고 있습니다.
유의 사항
검색(Retrieval)의 품질이 답변의 품질을 결정합니다. 잘못된 문서가 검색되면, LLM이 올바르게 답변할 기회는 없습니다. 쓰레기가 들어가면 쓰레기가 나옵니다 (Garbage in, garbage out).
k는 트레이드오프(tradeoff) 관계에 있습니다. k 값이 높을수록 LLM이 활용할 수 있는 컨텍스트(context)는 많아지지만, 더 많은 노이즈(noise)가 유입되고 더 많은 토큰(token)을 사용하게 됩니다. 대부분의 초보자용 프로젝트에서는 k=2 또는 k=3이 합리적인 시작점입니다.
이것이 기초입니다. 리랭킹 (reranking), 하이브리드 검색 (hybrid search), 쿼리 확장 (query expansion), 에이전틱 RAG (agentic RAG)와 같은 모든 고급 RAG 기술은 이 동일한 핵심 루프(core loop)를 기반으로 구축됩니다. 일단 '검색 후 생성 (retrieve-then-generate)' 방식을 이해하고 나면, 나머지 모든 기술은 그 두 단계 중 하나를 개선하는 것에 불과합니다.
다음 단계
2단계 RAG에 익숙해졌다면, 다음 단계로 나아갈 방향은 다음과 같습니다:
- 메타데이터 필터링 (metadata filtering) 추가 — 단순히 유사도뿐만 아니라 카테고리나 날짜별로 문서 검색
- 리랭킹 (reranking) 시도 — 두 번째 모델을 사용하여 검색된 문서의 관련도에 따라 순서 재정렬
- 에이전틱 RAG (agentic RAG) 탐색 — LLM이 언제 검색할지, 그리고 무엇을 검색할지 스스로 결정하도록 함
하지만 솔직히 말해서, 대부분의 단순한 사용 사례에서는 이 기본적인 2단계 접근 방식이 놀라울 정도로 잘 작동합니다. 여기서 시작하여 어디에서 실패하는지 측정하고, 필요한 경우에만 복잡성을 추가하세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기