
※ 개요
LangChain + ChromaDB + OpenAI GPT 조합으로, Markdown 문서 기반 지식베이스와 OpenAI GPT-4를 활용해 24시간 즉각적인 고객 응대가 가능한 시스템 구축
※ 시스템 아키텍처
프로젝트 구조
├── Django Backend
│ ├── services/
│ │ └── chatbot_service.py (벡터DB + 문서 처리)
│ ├── views/
│ │ └── chatbot_views.py (API 엔드포인트)
│ ├── docs/ (MD 파일 저장소)
│ │ ├── faq.md
│ │ ├── payment.md
│ │ ├── refund_policy.md
│ │ └── shipping.md
│ └── chroma_db/ (벡터 데이터베이스)
└── React Frontend
└── screens/
└── ChatbotScreen.js (대화형 UI)
※ 핵심 구현
1. 벡터DB 기반 문서 검색 시스템
# base/services/chatbot_service.py
import os
from pathlib import Path
from langchain.text_splitter import MarkdownHeaderTextSplitter
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain.schema import Document
from langchain_community.vectorstores.utils import filter_complex_metadata
from django.conf import settings
import logging
logger = logging.getLogger(__name__)
# 경로 설정
BASE_DIR = Path(settings.BASE_DIR)
DOCS_PATH = BASE_DIR / "docs"
DB_PATH = Path(os.getenv('CHROMA_DB_PATH', str(BASE_DIR / "chroma_db")))
# 전역 벡터DB 인스턴스 (메모리 기반 싱글톤)
_vectordb_instance = None
_is_initialized = False
핵심 설계 포인트:
1. 메모리 기반 벡터DB: Windows 파일 잠금 문제 회피
2. 싱글톤 패턴: 서버 전체에서 하나의 인스턴스만 유지
3. 환경변수 지원: 유연한 경로 설정
2. Markdown 문서 처리 및 임베딩 생성
# base/services/chatbot_service.py
def build_vector_db(force_rebuild=False):
"""
📌 .md 파일들을 읽어서 메모리 기반 벡터DB를 생성하는 함수
"""
global _vectordb_instance, _is_initialized
# docs 폴더 체크
folder_exists, md_files = check_docs_folder()
if not folder_exists:
raise FileNotFoundError(f"Docs 폴더를 찾을 수 없습니다: {DOCS_PATH}")
docs = []
splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=[
("#", "header1"),
("##", "header2"),
("###", "header3"),
]
)
# 각 MD 파일 처리
for file_path in md_files:
with open(file_path, "r", encoding="utf-8") as f:
text = f.read()
# 헤더 기준으로 문서 분할
split_docs = splitter.split_text(text)
for i, doc in enumerate(split_docs):
# 메타데이터 단순화 (Chroma 호환성)
metadata = {
"source": file_path.name,
"chunk_id": i
}
docs.append(Document(
page_content=content,
metadata=metadata
))
# OpenAI 임베딩으로 벡터화
embeddings = OpenAIEmbeddings(api_key=settings.OPENAI_API_KEY)
# 복잡한 메타데이터 필터링 후 벡터DB 생성
filtered_docs = filter_complex_metadata(docs)
_vectordb_instance = Chroma.from_documents(
documents=filtered_docs,
embedding=embeddings
# persist_directory 없음 - 메모리에만 저장
)
_is_initialized = True
logger.info(f"✅ 벡터DB 생성 완료: {len(docs)}개 문서")
return _vectordb_instance
문서 처리 전략:
1. 헤더 기반 분할: #, ##, ### 단위로 청크 생성
2. 메타데이터 관리: 출처 파일명과 청크 ID 저장
3. 임베딩 생성: OpenAI API로 벡터 변환
4. 메모리 저장: 파일 시스템 대신 RAM 사용
3. 지능형 질의응답 API
# base/views/chatbot_views.py
from rest_framework.decorators import api_view
from rest_framework.response import Response
from langchain.chains import RetrievalQA
from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate
# 커스텀 프롬프트 템플릿
CUSTOM_PROMPT = PromptTemplate(
template="""당신은 친절한 이커머스 고객 상담 챗봇입니다.
다음 문서 내용을 바탕으로 질문에 답변해주세요:
{context}
질문: {question}
답변 시 주의사항:
1. 문서에 있는 내용만을 바탕으로 정확하게 답변하세요
2. 문서에 없는 내용은 추측하지 마세요
3. 친절하고 이해하기 쉽게 설명하세요
4. 문서에 정보가 없다면 "죄송합니다. 해당 정보를 찾을 수 없습니다"라고 답변하세요
답변:""",
input_variables=["context", "question"]
)
@api_view(["POST"])
def chatbot_query(request):
"""
📌 상담 챗봇 API 엔드포인트
"""
question = request.data.get("question")
# 벡터DB에서 관련 문서 검색
vectordb = get_vector_db()
retriever = vectordb.as_retriever(
search_type="similarity",
search_kwargs={"k": 3} # 상위 3개 문서
)
# GPT-4 기반 답변 생성
llm = ChatOpenAI(
model="gpt-4o-mini",
temperature=0.3, # 일관된 답변
api_key=settings.OPENAI_API_KEY
)
# RetrievalQA 체인 실행
qa = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
return_source_documents=True,
chain_type_kwargs={"prompt": CUSTOM_PROMPT}
)
result = qa({"query": question})
# 참조 문서 정보 추가
sources = []
for doc in result.get("source_documents", []):
sources.append({
"source": doc.metadata.get("source"),
"content_preview": doc.page_content[:200] + "..."
})
return Response({
"question": question,
"answer": result.get("result"),
"sources": sources
})
RAG (Retrieval-Augmented Generation) 파이프라인:
사용자 질문 → 벡터 검색 → 관련 문서 추출 → GPT-4 답변 생성 → 출처 포함 응답
4. 시스템 상태 모니터링
# base/views/chatbot_views.py
@api_view(["GET"])
def chatbot_status(request):
"""
📌 챗봇 상태 확인 API
"""
from base.services.chatbot_service import DOCS_PATH, _is_initialized, _vectordb_instance
# docs 폴더 확인
folder_exists, md_files = check_docs_folder()
# 벡터DB 상태 확인
if _is_initialized and _vectordb_instance:
collection_count = _vectordb_instance._collection.count()
db_status = "정상 (메모리)"
else:
collection_count = 0
db_status = "초기화 필요"
return Response({
"status": "ok" if folder_exists and collection_count > 0 else "error",
"docs_folder": {
"exists": folder_exists,
"path": str(DOCS_PATH),
"md_files": [f.name for f in md_files]
},
"vector_db": {
"status": db_status,
"document_count": collection_count,
"type": "memory-based"
}
})
※ 실행 및 배포
1. 환경 설정
# .env 파일
OPENAI_API_KEY=your_api_key_here
CHROMA_DB_PATH=C:\temp\chroma_db # Windows 파일 잠금 회피
2. 패키지 설치
# Backend
pip install langchain langchain-chroma langchain-openai
pip install langchain-community chromadb
# Frontend
npm install axios react-bootstrap
3. 벡터DB 초기화
# Django Management Command
python manage.py shell
>>> from base.services.chatbot_service import build_vector_db
>>> vectordb = build_vector_db(force_rebuild=True)
>>> print(f"✅ {vectordb._collection.count()}개 문서 로드 완료")
4. 서버 실행
# Backend
python manage.py runserver
# Frontend
npm start
※ 실제 작동 예시
사용자 질문
"환불은 어떻게 하나요?"
시스템 처리 과정
# 1. 벡터 검색
관련 문서 검색: refund_policy.md (유사도 0.92)
# 2. 컨텍스트 추출
"환불 정책
- 구매 후 7일 이내 환불 가능
- 미사용 제품에 한함
- 배송비는 고객 부담..."
# 3. GPT-4 답변 생성
"환불은 구매 후 7일 이내에 가능합니다.
제품이 미사용 상태여야 하며, 반품 배송비는
고객님께서 부담하셔야 합니다..."
# 4. 응답 반환
{
"answer": "환불은 구매 후 7일 이내...",
"sources": [{
"source": "refund_policy.md",
"content_preview": "환불 정책..."
}]
}
※ 트러블 슈팅
1. Windows 파일 잠금 문제
문제: ChromaDB SQLite 파일 접근 시 WinError 32
# 오류 메시지
[WinError 32] 다른 프로세스가 파일을 사용 중...
해결: 메모리 기반 벡터DB로 전환
# persist_directory 제거 → 메모리만 사용
_vectordb_instance = Chroma.from_documents(
documents=docs,
embedding=embeddings
# persist_directory 없음
)
2. 메타데이터 딕셔너리 오류
문제: Chroma가 중첩된 딕셔너리 거부
# 오류 메시지
Expected metadata value to be str, int, float, bool, or None, got {}
해결: 메타데이터 평탄화
from langchain_community.vectorstores.utils import filter_complex_metadata
filtered_docs = filter_complex_metadata(docs)
3. 경로 설정 문제
문제: 상대 경로로 인한 docs 폴더 미발견
해결: Django BASE_DIR 기반 절대 경로
BASE_DIR = Path(settings.BASE_DIR)
DOCS_PATH = BASE_DIR / "docs" # 절대 경로
※ 성능 최적화
1. 싱글톤 패턴
# 전역 인스턴스 재사용
if _is_initialized and _vectordb_instance:
return _vectordb_instance
2. 검색 최적화
retriever = vectordb.as_retriever(
search_type="similarity",
search_kwargs={"k": 3} # 상위 3개만
)
3. 프롬프트 엔지니어링
temperature=0.3 # 낮은 temperature로 일관성 확보'Web Project > e-commerce' 카테고리의 다른 글
| AWS EC2에 프로젝트 배포 (0) | 2025.08.25 |
|---|---|
| 도커화 (Docker Compose + Nginx) (0) | 2025.08.24 |
| LLM 기반 개인 맞춤 상품 추천 시스템 구축 (0) | 2025.08.09 |
| 리뷰 수정/삭제 (0) | 2025.08.06 |
| Hugging Face를 활용한 리뷰 AI 분석 시스템 구축 (0) | 2025.08.03 |