LangChain으로 만드는 RAG 기반 문서 챗봇: 벡터 DB 활용하기

KangHo Lee's avatar
Jul 07, 2025
LangChain으로 만드는 RAG 기반 문서 챗봇: 벡터 DB 활용하기
  • RAG (Retrieval-Augmented Generation) 는 문서 기반 질의응답 시스템을 구현할 때 핵심이 되는 기법입니다.
  • 이번 글에서는 LangChain과 OpenAI를 이용해 PDF 문서를 기반으로 질문에 답변하는 RAG 챗봇을 만들어 보겠습니다.

1️⃣ PDF 문서 불러오기

PDF 파일을 파싱해서 텍스트 데이터를 추출합니다.
LangChain의 PyPDFLoader는 페이지별로 Document 객체를 생성해줍니다.
from langchain_community.document_loaders import PyPDFLoader loader = PyPDFLoader("data/policy.pdf") documents = loader.load()

2️⃣ 텍스트 청크 분할 (Chunking)

LLM은 긴 텍스트를 한 번에 처리할 수 없기 때문에 문서를 청크로 나눕니다.
chunk_size=1000, overlap=100 설정으로 문맥이 자연스럽게 이어지도록 합니다.
from langchain_text_splitters import RecursiveCharacterTextSplitter splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100) chunks = splitter.split_documents(documents)

3️⃣ 임베딩: 문서를 벡터로 변환

텍스트 청크를 고차원 벡터로 변환해 유사도를 계산할 수 있게 합니다.
OpenAI의 임베딩 모델을 사용해 "문장 → 숫자 벡터"로 변환합니다.
from langchain_openai import OpenAIEmbeddings from dotenv import load_dotenv import os load_dotenv() embeddings = OpenAIEmbeddings( model="text-embedding-3-large", api_key=os.getenv("OPENAI_API_KEY") )

4️⃣ 벡터 DB 저장 및 불러오기 (Chroma)

임베딩된 벡터들을 벡터 DB(여기선 Chroma)에 저장해 빠르게 검색할 수 있도록 합니다.
persist_directory 설정으로 디스크에 저장하고 재사용할 수 있습니다.
from langchain_chroma import Chroma db_path = "chroma_store" vectorstore = Chroma.from_documents( documents=chunks, embedding=embeddings, persist_directory=db_path )
이미 저장된 경우에는 이렇게 불러올 수 있습니다:
vectorstore = Chroma( persist_directory=db_path, embedding_function=embeddings )

5️⃣ 관련 문서 검색 (Retriever)

사용자의 질문을 임베딩한 뒤, 유사한 벡터를 벡터DB에서 검색합니다.
RAG의 Retrieval 단계이며, 가장 유사한 청크 k개를 반환합니다.
retriever = vectorstore.as_retriever(k=3) results = retriever.invoke("서울시의 환경 정책은?") for doc in results: print(doc.page_content)

6️⃣ LLM을 이용해 답변 생성

검색된 문서를 context로 전달해 LLM이 자연어 응답을 생성하게 합니다.
LangChain의 create_stuff_documents_chain은 여러 문서를 한 번에 처리할 수 있습니다.
from langchain.chains.combine_documents import create_stuff_documents_chain from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain_openai import ChatOpenAI chat = ChatOpenAI(model="gpt-4o-mini") prompt = ChatPromptTemplate.from_messages([ ("system", "아래 context를 참고해 질문에 답하세요:\n\n{context}"), MessagesPlaceholder("messages"), ]) qa_chain = create_stuff_documents_chain(chat, prompt)

7️⃣ 대화 이력 저장 및 실행

질문과 답변을 저장해 대화를 이어갈 수 있도록 합니다.
LangChain의 ChatMessageHistory를 사용하면 대화형 챗봇에 활용할 수 있습니다.
from langchain.memory import ChatMessageHistory history = ChatMessageHistory() history.add_user_message("서울시의 온실가스 정책 알려줘") answer = qa_chain.invoke({ "messages": history.messages, "context": results, }) history.add_ai_message(answer) print(answer)

8️⃣ 질의 확장 (Query Augmentation)

사용자가 모호하게 질문한 경우, 기존 대화 내용을 참고해 명확한 질문으로 바꿉니다.
예: "서울은?""서울시의 환경 정책은 무엇인가요?"
from langchain_core.output_parsers import StrOutputParser qa_prompt = ChatPromptTemplate.from_messages([ MessagesPlaceholder("messages"), ("system", "질문을 더 명확하게 바꿔 주세요:\n\n{query}"), ]) qa_chain = qa_prompt | chat | StrOutputParser() augmented_query = qa_chain.invoke({ "messages": history.messages, "query": "서울은?", }) print("확장된 질문:", augmented_query)

✅ 전체 요약

단계
설명
PDF 불러오기
문서에서 텍스트 추출
청크화
문서를 분할해 문맥 단위로 처리
임베딩
텍스트를 벡터로 변환해 유사도 기반 검색 가능
벡터DB
벡터를 저장하고 검색하는 인프라
Retriever
질문과 가장 유사한 문서를 찾는 단계
LLM 응답
검색된 문서를 바탕으로 자연어로 답변 생성
질의 확장
모호한 질문을 명확하게 재구성

📌 마무리

  • LangChain, OpenAI, ChromaDB를 조합하면 나만의 도메인 문서를 기반으로 한 챗봇을 간단히 만들 수 있습니다.
  • 여기에 UI(예: Streamlit) 또는 Tool Agent까지 붙이면 실용적이고 확장성 있는 챗봇으로 발전시킬 수 있습니다.
 
Share article

devleekangho