[RAG] RAG 기반 문서 질의응답 챗봇 구현하기 — LangChain, ChromaDB, Streamlit
by 우와한개발자
1. RAG란?
- Retrieval-Augmented Generation의 약자
- 외부 문서를 검색(Retrieval)해서 LLM의 답변 생성(Generation)에 활용하는 기법
- LLM이 학습하지 않은 사내 문서, PDF 등에 대한 질의응답이 가능해짐
2. 전체 처리 흐름
PDF 업로드
↓
PDF Loader → PDF를 페이지 단위로 읽기
↓
Chunking → 긴 문서를 일정 크기의 청크로 분할
↓
Embedding → 각 청크를 숫자 벡터로 변환
↓
Vector DB → 벡터를 ChromaDB에 저장
↓
사용자 질문 입력
↓
Retriever → 질문과 가장 유사한 청크 검색
↓
LLM (GPT) → 검색된 청크를 참고하여 답변 생성
↓
답변 출력
3. 설치
pip install streamlit langchain langchain-openai langchain-community
pip install chromadb pypdf python-dotenv
- langchain : LLM 파이프라인 구축 프레임워크
- langchain-openai : OpenAI 모델 연동
- langchain-community : PDF 로더 등 서드파티 연동 도구
- chromadb : 벡터 데이터베이스
- pypdf : PDF 파일 파싱
- python-dotenv : .env 파일로 환경변수 관리
4. 구성 요소
1) PDF 로딩 및 청크 분할
- PyPDFLoader : PDF를 페이지 단위로 로딩
- RecursiveCharacterTextSplitter : 긴 문서를 청크로 분할
- chunk_size=1000 : 청크당 최대 문자 수
- chunk_overlap=200 : 청크 간 겹치는 문자 수 → 문맥이 잘리지 않도록 유지
2) 임베딩 및 벡터 DB 저장
- OpenAIEmbeddings : 텍스트를 숫자 벡터로 변환
- Chroma.from_documents() : 변환된 벡터를 ChromaDB에 저장
3) 검색 및 답변 생성
- vectordb.as_retriever(search_kwargs={"k": 3}) : 질문과 유사한 청크 3개 검색
- ChatOpenAI(model="gpt-4o-mini", temperature=0) : 검색된 청크를 컨텍스트로 넣어 답변 생성
5. 전체 코드
import tempfile
import streamlit as st
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_chroma import Chroma
load_dotenv()
st.title("RAG 기반 문서 질의응답 챗봇")
# ① PDF 업로드
uploaded_file = st.file_uploader("PDF 업로드", type=["pdf"])
if uploaded_file:
# ② 임시 파일로 저장 후 로딩
with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp_file:
tmp_file.write(uploaded_file.read())
pdf_path = tmp_file.name
loader = PyPDFLoader(pdf_path)
documents = loader.load()
# ③ 청크 분할
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
chunks = splitter.split_documents(documents)
# ④ 임베딩 → 벡터 DB 저장
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectordb = Chroma.from_documents(documents=chunks, embedding=embeddings)
retriever = vectordb.as_retriever(search_kwargs={"k": 3})
st.success(f"문서 로드 완료 ({len(chunks)}개 청크)")
# ⑤ 세션 상태로 대화 이력 초기화
if "messages" not in st.session_state:
st.session_state.messages = []
# ⑥ 이전 대화 출력
for msg in st.session_state.messages:
with st.chat_message(msg["role"]):
st.write(msg["content"])
# ⑦ 사용자 질문 입력
if prompt := st.chat_input("질문을 입력하세요"):
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.write(prompt)
# ⑧ 관련 청크 검색 → LLM 답변 생성
docs = retriever.invoke(prompt)
context = "\\n\\n".join([doc.page_content for doc in docs])
llm_prompt = f"""
당신은 문서 기반 QA 시스템입니다.
다음 문서를 참고하여 답변하세요.
문서: {context}
질문: {prompt}
답변:
"""
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
response = llm.invoke(llm_prompt)
# ⑨ 답변 출력 및 저장
with st.chat_message("assistant"):
st.write(response.content)
st.session_state.messages.append({"role": "assistant", "content": response.content})
# ⑩ 검색된 청크 확인 (선택)
with st.expander("검색된 문서 보기"):
for i, doc in enumerate(docs):
st.write(f"Chunk {i+1}")
st.write(doc.page_content)
'AI' 카테고리의 다른 글
| [Harness] Harness란 무엇인가 — AI 검증, 품질 보증, Test Harness, Vibe Coding, CI/CD (0) | 2026.06.30 |
|---|---|
| [MCP] Next.js로 MCP Server와 Client 구현하기 — Notion 검색 웹앱 (0) | 2026.06.12 |
| [RAG] RAG 평가 지표와 도구 — Faithfulness, Answer Relevancy / RAGAS, LangSmith (0) | 2026.06.11 |
| [MCP] MCP 서버 연결하기 — Cursor · Claude Desktop · Claude Code에서 외부 MCP 서버 연결하기 (1) | 2026.06.07 |
| [AI 기초] AI가 학습(Learning)한다는 의미는 무엇일까? (0) | 2026.03.07 |
블로그의 정보
우와한개발자 님의 블로그
우와한개발자