6.4 개인 문서 기반 검색 챗봇 구축 실습
이 절에서는 OpenAI의 Embedding API를 활용하여 개인 문서를 검색할 수 있는 챗봇을 직접 구축해보겠습니다. 이 챗봇은 사용자의 질문에 대해 사전에 업로드한 문서들을 기반으로 의미론적으로 가장 관련성이 높은 내용을 검색하고, 이를 바탕으로 GPT 모델에게 답변을 생성하게 됩니다.
이를 통해 여러분은 다음과 같은 핵심 개념들을 실습으로 익히게 됩니다:
문서 임베딩 처리 및 벡터 저장
Vector DB (여기서는 FAISS 사용)를 활용한 유사도 검색
OpenAI Chat API를 통한 RAG(Retrieval-Augmented Generation) 파이프라인 구성
FastAPI 기반 REST 챗봇 서버 구축
이 실습은 많은 실제 서비스들이 활용하는 ‘문서 기반 Q&A 챗봇’의 기본 뼈대를 제공하며, SaaS, 내부 지식검색, 고객 FAQ 챗봇 등에 다양하게 확장 가능합니다.
사전 준비
사용 언어
Python 3.8 이상
주요 라이브러리
openai, faiss-cpu, tiktoken, langchain, fastapi, uvicorn
기타
OpenAI API 키, 텍스트 파일 형태 문서 2~3개
환경이 미리 구축되어 있지 않았다면 다음 명령어로 필요한 패키지를 설치합니다:
pip install openai faiss-cpu langchain fastapi uvicorn tiktoken python-dotenv
Step 1 - 개인 문서를 벡터로 변환하기
먼저 사용자 문서를 문단 단위로 나누고, 각 문단을 OpenAI의 Embedding API를 이용해 벡터화합니다.
📄 예시 문서: documents/guide_1.txt
, documents/faq.txt
from openai import OpenAI
import os
from dotenv import load_dotenv
import tiktoken
load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
# 문서를 문단 단위로 나누는 함수
def split_into_chunks(text, max_tokens=500):
tokenizer = tiktoken.get_encoding("cl100k_base")
words = text.split("\n\n")
chunks = []
for paragraph in words:
if len(tokenizer.encode(paragraph)) <= max_tokens:
chunks.append(paragraph)
return chunks
# 문서 로드 및 분할
def load_documents(folder_path):
chunks = []
sources = []
for filename in os.listdir(folder_path):
with open(os.path.join(folder_path, filename), "r", encoding="utf-8") as f:
text = f.read()
split_chunks = split_into_chunks(text)
for chunk in split_chunks:
chunks.append(chunk)
sources.append(filename)
return chunks, sources
# 임베딩 생성
def get_embeddings(text_chunks):
response = client.embeddings.create(
model="text-embedding-3-small",
input=text_chunks
)
return [data.embedding for data in response.data]
Step 2 - FAISS 인덱싱
FAISS는 Facebook에서 개발한 고성능 벡터 검색 라이브러리입니다. 위에서 생성한 벡터들을 FAISS에 저장해 유사도 검색을 수행할 수 있습니다.
import faiss
import numpy as np
import pickle
# 벡터 저장과 인덱스 구축
def build_faiss_index(embeddings):
dim = len(embeddings[0])
index = faiss.IndexFlatL2(dim)
index.add(np.array(embeddings).astype("float32"))
return index
# 인덱스와 메타데이터 저장
def save_index(index, chunks, sources, path="my_index"):
faiss.write_index(index, f"{path}/doc_index.faiss")
with open(f"{path}/metadata.pkl", "wb") as f:
pickle.dump({"texts": chunks, "sources": sources}, f)
# 실행
chunks, sources = load_documents("documents")
embeddings = get_embeddings(chunks)
index = build_faiss_index(embeddings)
save_index(index, chunks, sources)
Step 3 - 쿼리 기반 검색 및 GPT 응답 생성
입력된 사용자 질문을 임베딩 후 FAISS와 비교하여 유사한 문단을 찾고, 이를 기반으로 GPT Chat API에 전달합니다.
def search_similar_docs(query, k=3):
# 1. 쿼리 임베딩
query_embed = client.embeddings.create(
model="text-embedding-3-small",
input=[query]
).data[0].embedding
query_vector = np.array(query_embed).astype("float32").reshape(1, -1)
# 2. 인덱스 및 메타데이터 로드
index = faiss.read_index("my_index/doc_index.faiss")
with open("my_index/metadata.pkl", "rb") as f:
metadata = pickle.load(f)
# 3. 유사 문단 검색
D, I = index.search(query_vector, k)
matched_texts = [metadata["texts"][i] for i in I[0]]
matched_sources = [metadata["sources"][i] for i in I[0]]
return matched_texts, matched_sources
def generate_answer_with_context(question):
context_texts, sources = search_similar_docs(question)
context = "\n\n".join(context_texts)
system_prompt = """너는 사용자 문서 기반 전문 상담봇이야. 아래 참고사항에 기반해 반드시 질문에 정확히 답해줘."""
user_prompt = f"""[참고 내용]
{context}
[질문]
{question}
위 내용을 기반으로 답변을 해줘."""
chat_response = client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
],
temperature=0.3,
max_tokens=512
)
return chat_response.choices[0].message.content
Step 4 - FastAPI로 챗봇 서비스화
위 기능들을 REST API로 제공하면 다양한 프론트엔드와 연결할 수 있는 챗봇 API 서버가 완성됩니다.
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Query(BaseModel):
question: str
@app.post("/ask")
def ask_bot(query: Query):
answer = generate_answer_with_context(query.question)
return {"answer": answer}
실행:
uvicorn chatbot:app --reload --port 8000
예시 요청 (curl):
curl -X POST http://localhost:8000/ask -H "Content-Type: application/json" -d '{"question": "환불은 어떻게 하나요?"}'
결과 기대
이 챗봇은 질문을 받으면 사용자의 문서셋에서 가장 관련 있는 문단을 찾아 GPT에게 전달하고, GPT는 그 문맥 내에서 답변 생성합니다. 이를 통해 일반적인 "지식 없는 챗봇"이 아닌, 사용자의 특정 문서에 특화된 의미 기반 Q&A가 가능해집니다.
확장 아이디어
문단 스코어링 시 cosine similarity 적용
문서 업데이트 시의 벡터 재처리 자동화
사용자 인증을 추가한 문서 권한별 챗봇 제공
Gradio 또는 Streamlit으로 챗 UI 제작
LangChain 기반 RAG 체인으로 확장 (12장에서 다룸)
이처럼 Embedding API와 Vector DB, Chat API의 조합은 매우 강력한 문서기반 챗봇 구조를 가능케 합니다. 다음 절에서는 이를 기반으로 function calling, JSON mode 등과 조합하여 다기능 AI 에이전트를 설계하는 기법을 다룹니다.
Last updated