
RAG(Retrieval Augmented Generation)는 검색된 문서를 기반으로 LLM이 답변을 생성하는 기법입니다. 본 포스트에서는 지금까지 배운 내용을 조합하여 완전한 RAG 파이프라인을 구축하는 방법을 정리합니다.
RAG 파이프라인 구축에 필요한 패키지를 설치합니다.
pip install langchain langchain-openai langchain-chroma langchain-huggingface langchain-community python-dotenv
| 패키지 | 설명 |
|---|---|
langchain | LangChain Hub, RunnablePassthrough 등 |
langchain-openai | ChatOpenAI LLM |
langchain-chroma | Chroma 벡터 스토어 |
langchain-huggingface | HuggingFace 임베딩 모델 |
langchain-community | TextLoader 등 Document Loaders |
python-dotenv | .env 파일에서 환경 변수 로드 |
RAG 파이프라인은 다음과 같은 흐름으로 동작합니다.
사용자 질문: "이색적인 숙소에는 어떤 곳이 있을까?"
↓
[Retriever]
(질문과 유사한 문서 검색)
↓
Context: "이색적인 숙소에서 머물기. 트리하우스, 동굴호텔..."
↓
[Prompt Template]
(Context + User Input 조합)
↓
[LLM]
(프롬프트 기반 답변 생성)
↓
[Output Parser]
(응답 텍스트만 추출)
↓
답변: "이색적인 숙소로는 트리하우스, 동굴호텔, 빙하호텔..."
MMR 방식으로 다양성을 고려한 Retriever를 생성합니다.
retriever = db.as_retriever(
search_type="mmr",
search_kwargs={
"k": 4,
"fetch_k": 20,
"lambda_mult": 0.5
}
)
| 파라미터 | 설명 |
|---|---|
k | 최종 반환할 문서 개수 |
fetch_k | 초기에 가져올 후보 문서 개수 |
lambda_mult | 다양성 조절 (0.5 = 유사도와 다양성 균형) |
LangChain에서 미리 만들어둔 RAG 프롬프트를 사용할 수 있습니다.
from langchain import hub
# LangChain Hub에서 RAG 프롬프트 템플릿 가져오기
prompt_template = hub.pull("langchain-ai/retrieval-qa-chat")
# 프롬프트 내용 확인
print(prompt_template)
| 메시지 타입 | 내용 |
|---|---|
| System | "Answer any use questions based solely on the context below" |
| Context | 검색된 문서들이 삽입되는 위치 |
| Human | 사용자 질문 (input) |
🔗 참고: https://smith.langchain.com/hub/langchain-ai/retrieval-qa-chat
RunnablePassthrough는 입력값을 그대로 다음 단계로 전달합니다.
from langchain_core.runnables import RunnablePassthrough
# 사용자 질문 "여행 계획 세우기" → 그대로 input에 전달
| 컴포넌트 | 역할 |
|---|---|
context: retriever | 질문으로 검색한 문서들을 context에 전달 |
input: RunnablePassthrough() | 사용자 입력을 그대로 input에 전달 |
모든 컴포넌트를 연결하여 완전한 RAG 체인을 만듭니다.
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
# Output Parser 준비
parser = StrOutputParser()
# RAG 체인 구축
rag_chain = {
"context": retriever,
"input": RunnablePassthrough()
} | prompt_template | llm | parser
{"context": retriever, "input": RunnablePassthrough()} - 입력 데이터 구성| prompt_template - 프롬프트 메시지 생성| llm - LLM에 전송하여 응답 받기| parser - 응답에서 텍스트만 추출# 관련 내용이 있는 질문
result = rag_chain.invoke("이색적인 숙소에는 어떤 곳이 있을까?")
print(result)
# → "이색적인 숙소로는 트리하우스, 동굴호텔, 빙하호텔 등이 있습니다."
# 관련 내용이 없는 질문
result = rag_chain.invoke("우주의 역사에 대해서 알려줘")
print(result)
# → "제공된 문서 내용에는 우주의 역사에 관한 정보가 포함되어 있지 않습니다."
from dotenv import load_dotenv
load_dotenv()
from langchain_openai import ChatOpenAI
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import CharacterTextSplitter
from langchain_chroma import Chroma
from langchain import hub
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
# 1. LLM 준비
llm = ChatOpenAI(model="gpt-4o-mini")
# 2. 임베딩 모델 준비
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-m3")
# 3. 문서 로드 및 분할
text_splitter = CharacterTextSplitter(
separator="\n",
chunk_size=100,
chunk_overlap=0
)
loader = TextLoader("./docs/travel.txt", encoding="utf-8")
documents = loader.load_and_split(text_splitter=text_splitter)
# 4. VectorStore 생성
db = Chroma.from_documents(documents, embeddings)
# 5. Retriever 생성 (MMR 방식)
retriever = db.as_retriever(
search_type="mmr",
search_kwargs={"k": 4, "fetch_k": 20, "lambda_mult": 0.5}
)
# 6. 프롬프트 템플릿 가져오기
prompt_template = hub.pull("langchain-ai/retrieval-qa-chat")
# 7. Output Parser 준비
parser = StrOutputParser()
# 8. RAG 체인 구축
rag_chain = {
"context": retriever,
"input": RunnablePassthrough()
} | prompt_template | llm | parser
# 9. 질의응답 테스트
print("=== 관련 내용이 있는 질문 ===")
result = rag_chain.invoke("이색적인 숙소에는 어떤 곳이 있을까?")
print(result)
print("\n=== 관련 내용이 없는 질문 ===")
result = rag_chain.invoke("우주의 역사에 대해서 알려줘")
print(result)
출력 예시:
=== 관련 내용이 있는 질문 ===
이색적인 숙소로는 트리하우스, 동굴호텔, 빙하호텔 등이 있습니다.
이러한 독특한 숙소 체험을 통해 특별한 여행을 할 수 있습니다.
=== 관련 내용이 없는 질문 ===
죄송하지만, 제공된 문서 내용에는 우주의 역사에 관한 정보가 포함되어 있지 않습니다.
다른 질문이나 여행에 관련된 내용에 대해 궁금한 점이 있으시면 답변해 드리겠습니다.
| 구성요소 | 역할 | 코드 |
|---|---|---|
| Retriever | 질문과 유사한 문서 검색 | db.as_retriever() |
| Prompt Template | Context + Input 조합 | hub.pull("...") |
| LLM | 프롬프트 기반 답변 생성 | ChatOpenAI() |
| Output Parser | 응답 텍스트 추출 | StrOutputParser() |
| RunnablePassthrough | 입력값 그대로 전달 | RunnablePassthrough() |
| 개념 | 설명 |
|---|---|
| RAG | Retrieval Augmented Generation, 검색 증강 생성 |
| RAG Chain | Retriever + Prompt + LLM을 연결한 완전한 QA 파이프라인 |
| hub.pull() | LangChain Hub에서 프롬프트 템플릿 가져오기 |
| RunnablePassthrough | 입력값을 변환 없이 그대로 다음 단계로 전달 |
| Context | Retriever가 검색한 관련 문서들 |
| Hallucination 방지 | "모르면 모른다고 답해" 지시로 환각 현상 방지 |
| 일반 LLM만 사용 | RAG 사용 |
|---|---|
| 학습 데이터 기반 답변 (오래된 정보 가능) | 최신 문서 기반 답변 |
| 특정 도메인 지식 부족 | 특정 도메인 전문 지식 활용 |
| Hallucination 발생 가능 | 출처가 명확한 신뢰할 수 있는 답변 |
| - | "모르면 모른다" 응답으로 Hallucination 방지 |