내부 자료 기반 질문 답변 AI: RAG로 뚝딱 만드는 사내 지식 챗봇 (Python 코딩 실습)
기업의 방대한 내부 문서를 효과적으로 활용하고, 직원들의 질문에 신속하고 정확하게 답변하는 것은 생산성 향상과 의사결정 속도 증진에 필수적입니다. 기존의 검색 시스템으로는 한계가 명확하며, LLM(대규모 언어 모델)을 단순히 사용하는 것만으로는 최신 내부 데이터나 특정 도메인 지식에 대한 환각(hallucination) 문제를 해결하기 어렵습니다. 오늘 우리는 이 문제를 해결할 강력한 기술인 RAG(Retrieval Augmented Generation) 패턴을 활용하여, 여러분의 사내 지식 기반 질문 답변 챗봇을 파이썬으로 직접 구축하는 실습을 진행할 것입니다. 이 강의를 통해 여러분은 기업의 핵심 지식을 LLM과 결합하여, 신뢰성 높고 유용한 AI 챗봇을 만드는 실무 역량을 갖추게 될 것입니다.
학습 목표
- RAG(Retrieval Augmented Generation)의 기본 개념과 작동 원리를 정확히 이해합니다.
- LangChain 라이브러리를 활용하여 문서 로드, 분할, 임베딩 생성 과정을 실습합니다.
- FAISS와 같은 벡터 데이터베이스를 사용하여 내부 문서를 효과적으로 관리하는 방법을 익힙니다.
- OpenAI API를 연동하여 실제 질문 답변 AI 시스템을 구축하고 테스트합니다.
- 사내 지식 챗봇 개발에 필요한 핵심 파이썬 코딩 기술을 습득하고, 실제 업무에 적용할 수 있는 기반을 마련합니다.
사전 준비 사항
본 실습은 여러분이 최적의 환경에서 원활하게 진행할 수 있도록 다음의 준비를 권장합니다.
권장 개발 환경
- 통합 개발 환경 (IDE): Visual Studio Code (VS Code) 최신 버전
- 운영 체제 (OS): Windows 10/11, macOS (Intel/Apple Silicon), 또는 Ubuntu 20.04+ (Linux)
- 파이썬 (Python) 버전: 3.9 ~ 3.11 (가상 환경 사용을 강력히 권장합니다.)
필수 라이브러리 설치
프로젝트를 시작하기 전에 필요한 파이썬 라이브러리들을 설치해야 합니다. VS Code 터미널 또는 명령 프롬프트/터미널에서 다음 명령을 실행합니다.
# 가상 환경 생성 (선택 사항이지만 강력히 권장)python -m venv venv# 가상 환경 활성화# Windows: .\venv\Scripts\activate# macOS/Linux: source venv/bin/activate# 필수 라이브러리 설치pip install langchain openai faiss-cpu pypdf tiktoken python-dotenvOpenAI API 키 준비: OpenAI의 GPT 모델과 임베딩 모델을 사용하기 위해 API 키가 필요합니다. OpenAI 플랫폼에서 API 키를 발급받아 `.env` 파일에 다음과 같이 저장해 두십시오. 이 키는 절대로 외부에 노출되어서는 안 됩니다.
OPENAI_API_KEY="YOUR_OPENAI_API_KEY_HERE"단계별 실습 과정
1단계: 프로젝트 환경 설정 및 데이터 준비
먼저, 프로젝트를 위한 디렉토리를 생성하고 필요한 샘플 내부 문서를 준비합니다. `data` 폴더 안에 PDF 또는 텍스트 파일을 넣어두면 됩니다. 여기서는 `company_policy.pdf`라는 가상의 회사 정책 문서를 사용한다고 가정하겠습니다.
# 프로젝트 디렉토리 생성mkdir rag_chatbot_projectcd rag_chatbot_project# 데이터 저장용 디렉토리 생성mkdir data# (선택 사항) .env 파일 생성 및 API 키 저장# touch .env# (위에서 설명한대로 OPENAI_API_KEY를 .env 파일에 추가)이제 `data` 폴더 안에 여러분의 내부 자료 (예: `company_policy.pdf`, `faq.txt` 등)를 몇 개 넣어두세요. 강의에서는 `company_policy.pdf` 파일을 예시로 사용하겠습니다. 이 파일은 회사의 복지 규정, 휴가 정책, 인사 규정 등을 담고 있다고 가정합니다.

출처: http://shop1.phinf.naver.net/20260122_230/1769068677531YBa0S_JPEG/11199993185468133_23369373.jpg
VS Code의 탐색기에서 프로젝트 구조가 위와 같이 보이는지 확인하세요. `rag_chatbot_project` 폴더 안에 `data` 폴더와 `main.py` 파일, 그리고 `.env` 파일이 위치해야 합니다. 가상 환경(`venv`)도 함께 보일 것입니다.
2단계: 문서 로드 및 전처리 (Document Loading & Preprocessing)
준비된 내부 문서를 로드하고, LLM이 처리하기 적합한 형태로 분할하는 과정입니다. LangChain의 문서 로더와 텍스트 분할기를 사용합니다.
`main.py` 파일을 생성하고 다음 코드를 작성합니다.
import osfrom dotenv import load_dotenvfrom langchain.document_loaders import PyPDFLoader, TextLoaderfrom langchain.text_splitter import RecursiveCharacterTextSplitter# .env 파일에서 환경 변수 로드load_dotenv()openai_api_key = os.getenv("OPENAI_API_KEY")# 1. 문서 로드def load_documents(data_path="./data"): documents = [] for filename in os.listdir(data_path): filepath = os.path.join(data_path, filename) if filename.endswith(".pdf"): loader = PyPDFLoader(filepath) documents.extend(loader.load()) elif filename.endswith(".txt"): loader = TextLoader(filepath) documents.extend(loader.load()) # 다른 문서 형식 추가 가능 (예: CSVLoader, UnstructuredHTMLLoader 등) print(f"총 {len(documents)}개의 페이지/문서 조각을 로드했습니다.") return documents# 2. 문서 분할 (Text Splitting)def split_documents(documents): text_splitter = RecursiveCharacterTextSplitter( chunk_size=1000, chunk_overlap=200, length_function=len, add_start_index=True, ) chunks = text_splitter.split_documents(documents) print(f"총 {len(chunks)}개의 청크로 분할했습니다.") return chunksif __name__ == "__main__": raw_documents = load_documents() text_chunks = split_documents(raw_documents) # print(text_chunks[0].page_content) # 첫 번째 청크 내용 확인 # print(text_chunks[0].metadata) # 첫 번째 청크 메타데이터 확인위 코드는 `data` 폴더 내의 PDF 및 TXT 파일을 읽어와서, 각 문서를 1000자 단위로 나누고 200자의 중복을 허용하여(chunk_overlap) 청크(chunk)를 생성합니다. 이렇게 하는 이유는 LLM이 한 번에 처리할 수 있는 토큰 수의 제한도 있지만, 질문과 관련된 정보를 더 잘 찾고 문맥을 파악하는 데 도움을 주기 위함입니다.
3단계: 임베딩 생성 및 벡터 스토어 구축 (Embedding & Vector Store)
분할된 텍스트 청크들을 숫자 벡터(임베딩)로 변환하고, 이를 검색 가능한 벡터 데이터베이스(여기서는 FAISS)에 저장합니다. 이 과정이 RAG의 핵심 중 하나입니다.
# main.py 파일에 이어서 작성from langchain.embeddings import OpenAIEmbeddingsfrom langchain.vectorstores import FAISS# 3. 임베딩 생성 및 벡터 스토어 구축def create_vector_store(text_chunks, api_key): # OpenAI 임베딩 모델 사용 embeddings = OpenAIEmbeddings(openai_api_key=api_key) # FAISS 인메모리 벡터 스토어 생성 vector_store = FAISS.from_documents(text_chunks, embeddings) print("FAISS 벡터 스토어 구축 완료.") return vector_storeif __name__ == "__main__": raw_documents = load_documents() text_chunks = split_documents(raw_documents) vector_store = create_vector_store(text_chunks, openai_api_key) # vector_store.save_local("faiss_index") # 필요시 인덱스를 로컬에 저장 # vector_store = FAISS.load_local("faiss_index", OpenAIEmbeddings(openai_api_key=openai_api_key)) # 로드 예시`OpenAIEmbeddings`는 텍스트를 고차원 벡터 공간의 숫자로 변환합니다. `FAISS`는 이렇게 생성된 벡터들을 효율적으로 저장하고, 나중에 질문 벡터와 유사한 문서 벡터를 빠르게 찾아주는 역할을 합니다. 이 과정이 완료되면, 우리의 내부 자료는 AI가 이해하고 검색할 수 있는 형태로 준비된 것입니다.

출처: http://shop1.phinf.naver.net/20260122_188/1769068690812NNiES_JPEG/103201605952070937_574674204.jpg
텍스트 청크가 임베딩 모델을 거쳐 벡터로 변환되고, 이 벡터들이 FAISS와 같은 벡터 스토어에 저장되어 검색 가능하게 되는 과정을 시각적으로 상상해 보세요. 질문이 들어오면 질문 또한 벡터로 변환되어 이 벡터 스토어에서 가장 유사한 문서 청크를 찾아오게 됩니다.
4단계: RAG 체인 구현 (RAG Chain Implementation)
이제 검색(Retrieval)과 생성(Generation)을 결합하는 RAG 체인을 구현합니다. 질문이 들어오면 벡터 스토어에서 관련 문서를 검색하고, 이 문서를 LLM에 전달하여 답변을 생성하도록 합니다.
# main.py 파일에 이어서 작성from langchain.chains import RetrievalQAfrom langchain.chat_models import ChatOpenAI# 4. RAG 체인 구현def setup_rag_chain(vector_store, api_key): # ChatOpenAI 모델 사용 (gpt-3.5-turbo 또는 gpt-4) llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0.7, openai_api_key=api_key) # 검색기(Retriever) 설정 # k=3은 가장 유사한 문서 3개를 가져오도록 설정 retriever = vector_store.as_retriever(search_kwargs={"k": 3}) # RetrievalQA 체인 설정 # chain_type은 "stuff" (모든 문서를 프롬프트에 넣음), "map_reduce" 등 선택 가능 qa_chain = RetrievalQA.from_chain_type( llm=llm, chain_type="stuff", retriever=retriever, return_source_documents=True # 답변 생성에 사용된 원본 문서 반환 여부 ) print("RAG 체인 설정 완료.") return qa_chainif __name__ == "__main__": # 이전 단계 코드... raw_documents = load_documents() text_chunks = split_documents(raw_documents) vector_store = create_vector_store(text_chunks, openai_api_key) qa_chain = setup_rag_chain(vector_store, openai_api_key) # 테스트 질문 (옵션) # query = "회사 복지 정책에 대해 알려줘." # result = qa_chain({"query": query}) # print(f"질문: {query}") # print(f"답변: {result['result']}") # print(f"출처: {result['source_documents']}")여기서 `ChatOpenAI`는 실제로 질문에 답변을 생성하는 LLM입니다. `retriever`는 벡터 스토어에서 질문과 관련된 문서를 찾아주는 역할을 하며, `RetrievalQA` 체인은 이 둘을 결합하여 질문에 대한 답변을 생성합니다. `return_source_documents=True`를 설정하면, AI가 어떤 문서를 참고하여 답변했는지 출처를 확인할 수 있어 신뢰도를 높일 수 있습니다.
5단계: 질문 답변 챗봇 인터페이스 구축 (Chatbot Interface)
이제 사용자가 질문을 입력하고 AI 챗봇이 답변을 제공하는 간단한 명령줄 인터페이스를 만듭니다.
# main.py 파일에 이어서 작성# 5. 챗봇 인터페이스def run_chatbot(qa_chain): print("\n사내 지식 챗봇이 준비되었습니다. '종료'를 입력하면 챗봇이 종료됩니다.") while True: query = input("\n질문을 입력하세요: ") if query.lower() == "종료": print("챗봇을 종료합니다.") break try: result = qa_chain({"query": query}) print("\n=== 답변 ===") print(result['result']) if result['source_documents']: print("\n=== 출처 문서 ===") for doc in result['source_documents']: print(f"- {doc.metadata.get('source', '알 수 없음')}, 페이지: {doc.metadata.get('page', '알 수 없음')}") print("==============") except Exception as e: print(f"오류 발생: {e}. API 키를 확인하거나 질문을 다시 시도해주세요.")if __name__ == "__main__": # 모든 설정 완료 후 챗봇 실행 raw_documents = load_documents() text_chunks = split_documents(raw_documents) vector_store = create_vector_store(text_chunks, openai_api_key) qa_chain = setup_rag_chain(vector_store, openai_api_key) run_chatbot(qa_chain)이 `run_chatbot` 함수는 무한 루프를 돌며 사용자 입력을 받습니다. 사용자가 ‘종료’를 입력하면 챗봇이 종료되고, 그 외의 질문에 대해서는 `qa_chain`을 호출하여 답변을 생성하고 출력합니다. 답변과 함께 사용된 출처 문서의 파일명과 페이지 번호를 보여주어 답변의 신뢰성을 높였습니다.
위 이미지는 여러분이 구현한 챗봇이 터미널에서 성공적으로 실행되어 질문에 답변하고 출처를 명확히 제시하는 모습을 보여줄 것입니다. 사용자가 질문을 입력하면, 챗봇이 내부 문서를 기반으로 정확하고 신뢰할 수 있는 답변을 제공합니다.
결과 확인
이제 여러분이 작성한 `main.py` 파일을 실행하여 사내 지식 챗봇을 테스트할 차례입니다.
# 가상 환경이 활성화되어 있는지 확인# Windows: .\venv\Scripts\activate# macOS/Linux: source venv/bin/activate# 파이썬 스크립트 실행python main.py스크립트가 실행되면, 문서 로드, 분할, 벡터 스토어 구축 과정이 콘솔에 출력되고, 최종적으로 챗봇 인터페이스가 나타날 것입니다. 준비한 `company_policy.pdf` (또는 여러분의 내부 자료)에 기반한 질문을 해보세요. 예를 들어, `company_policy.pdf`에 복지 정책 내용이 있다면 다음과 같이 질문할 수 있습니다.
- “회사 복지 정책에 대해 알려줘.”
- “휴가 규정은 어떻게 돼?”
- “인사 평가 기준은 뭐야?”
챗봇은 내부 문서를 검색하여 가장 관련성이 높은 정보를 찾아 LLM에 전달하고, LLM은 그 정보를 바탕으로 자연어 답변을 생성할 것입니다. 답변과 함께 어떤 문서의 어느 페이지에서 해당 정보가 왔는지 출처가 함께 표시되는 것을 확인할 수 있습니다. 만약 답변이 부정확하거나 관련성이 떨어진다면, `chunk_size`나 `chunk_overlap` 값을 조정하거나, `retriever`의 `k` 값(가져올 문서 개수)을 조절하여 성능을 개선할 수 있습니다.
이 실습을 통해 우리는 RAG 기반의 사내 지식 챗봇이 단순히 LLM을 사용하는 것을 넘어, 기업의 특정 도메인 지식을 활용하여 훨씬 더 정확하고 신뢰할 수 있는 답변을 제공할 수 있음을 확인했습니다. 이는 내부 자료의 활용도를 극대화하고, 직원들이 필요한 정보를 더 쉽고 빠르게 얻을 수 있도록 돕는 강력한 도구가 될 것입니다. 여러분은 이제 이 기본적인 RAG 시스템을 기반으로 웹 인터페이스를 추가하거나, 더 다양한 문서 형식을 지원하고, 검색 성능을 최적화하는 등 무궁무진한 확장을 시도할 수 있습니다. 다음 강의에서는 이 챗봇을 더욱 고도화하는 방안에 대해 심도 깊게 다뤄보도록 하겠습니다.
