RAG 챗봇 만들기 6 - 벡터 저장소 (파일 기반)
처음에 벡터 저장소를 뭘 쓸지 고민이 많았다. Pinecone, Weaviate, Milvus… 선택지가 너무 많았기 때문이다. 결론은 처음엔 ChromaDB나 FAISS로 시작하고, 규모가 커지면 그때 교체하는 것. 복잡하게 시작할 필요는 없다.
오늘은 임베딩한 벡터를 저장하고 검색하는 벡터 저장소를 다룬다. 파일 기반이라 설치도 쉽고 바로 시작할 수 있다.
벡터 저장소가 뭔가?
임베딩한 벡터들을 저장하고, 비슷한 벡터를 빠르게 찾아주는 데이터베이스다.
[질문 벡터] → 벡터 저장소에서 검색 → [가장 비슷한 벡터 3개 반환]
일반 DB는 “같은 값”을 찾지만, 벡터 DB는 “비슷한 값”을 찾는다.
ChromaDB 사용하기
ChromaDB는 설치가 쉽고 로컬에서 바로 사용할 수 있다.
설치
pip install chromadb
기본 사용법
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain.schema import Document
# 임베딩 모델
embeddings = OpenAIEmbeddings()
# 테스트용 문서
documents = [
Document(page_content="연차는 매년 15일 부여됩니다.", metadata={"source": "규정집"}),
Document(page_content="병가는 연간 60일까지 사용 가능합니다.", metadata={"source": "규정집"}),
Document(page_content="경조사 휴가는 결혼 5일, 출산 10일입니다.", metadata={"source": "규정집"}),
Document(page_content="야근 시 저녁 식대 1만원이 지급됩니다.", metadata={"source": "복지"}),
]
# 벡터 저장소 생성
vectorstore = Chroma.from_documents(
documents=documents,
embedding=embeddings,
persist_directory="./chroma_db" # 저장 경로
)
print("✓ 벡터 저장소 생성 완료!")
실행 결과:
유사 문서 검색
# 검색
query = "휴가 며칠이에요?"
results = vectorstore.similarity_search(query, k=2)
print(f"질문: {query}")
print(f"\n검색 결과:")
for i, doc in enumerate(results):
print(f"{i+1}. {doc.page_content}")
print(f" 출처: {doc.metadata['source']}")
실행 결과:
휴가 관련 문서만 잘 찾아온 것을 확인할 수 있다.
저장된 DB 다시 불러오기
프로그램을 재시작해도 저장된 DB를 다시 불러올 수 있다:
# 저장된 DB 불러오기
vectorstore = Chroma(
persist_directory="./chroma_db",
embedding_function=embeddings
)
# 검색
results = vectorstore.similarity_search("야근 수당", k=1)
print(results[0].page_content)
실행 결과:
FAISS 사용하기
FAISS는 Facebook에서 만든 벡터 검색 라이브러리다. 속도가 빠르다.
설치
pip install faiss-cpu # CPU 버전
# 또는
pip install faiss-gpu # GPU 버전 (CUDA 필요)
기본 사용법
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from langchain.schema import Document
embeddings = OpenAIEmbeddings()
documents = [
Document(page_content="연차는 매년 15일 부여됩니다."),
Document(page_content="병가는 연간 60일까지 가능합니다."),
Document(page_content="점심시간은 12시부터 1시까지입니다."),
]
# FAISS 벡터 저장소 생성
vectorstore = FAISS.from_documents(documents, embeddings)
# 검색
results = vectorstore.similarity_search("휴가", k=2)
for doc in results:
print(doc.page_content)
실행 결과:
FAISS 저장/불러오기
# 저장
vectorstore.save_local("./faiss_index")
# 불러오기
vectorstore = FAISS.load_local(
"./faiss_index",
embeddings,
allow_dangerous_deserialization=True
)
ChromaDB vs FAISS
어떤 것을 선택해야 할까?
| 특징 | ChromaDB | FAISS |
|---|---|---|
| 설치 | 쉬움 | 쉬움 |
| 속도 | 빠름 | 더 빠름 |
| 메타데이터 | 풍부한 지원 | 기본적 |
| 필터링 | 가능 | 제한적 |
| 추천 용도 | 일반적인 RAG | 대용량/속도 중요 |
처음에는 ChromaDB를 추천한다. 기능이 더 많고 사용하기 편하다.
유사도 점수와 함께 검색
얼마나 비슷한지 점수도 함께 받을 수 있다:
# 점수와 함께 검색
results = vectorstore.similarity_search_with_score(query, k=3)
print(f"질문: {query}\n")
for doc, score in results:
print(f"점수: {score:.4f}")
print(f"내용: {doc.page_content}")
print()
실행 결과:
점수가 낮을수록 더 비슷하다 (거리 기반이기 때문).
전체 파이프라인
지금까지 배운 것을 합쳐보자:
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
def create_vectorstore(pdf_path: str, db_path: str = "./chroma_db"):
"""PDF → 청킹 → 임베딩 → 벡터 저장소"""
# 1. PDF 로드
print("1. PDF 로드 중...")
loader = PyPDFLoader(pdf_path)
documents = loader.load()
print(f" {len(documents)}개 페이지 로드")
# 2. 청킹
print("2. 텍스트 청킹 중...")
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
chunks = splitter.split_documents(documents)
print(f" {len(chunks)}개 청크 생성")
# 3. 벡터 저장소 생성
print("3. 벡터 저장소 생성 중...")
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory=db_path
)
print(f" 저장 완료: {db_path}")
return vectorstore
# 실행
vectorstore = create_vectorstore("docs/company_rules.pdf")
# 검색 테스트
query = "연차 며칠?"
results = vectorstore.similarity_search(query, k=3)
print(f"\n'{query}' 검색 결과:")
for doc in results:
print(f"- {doc.page_content[:100]}...")
실행 결과:
운영자 실전 노트
실제 프로젝트 진행하며 겪은 문제
- ChromaDB 용량 관리: 문서 1만 개 넘어가니 로컬 디렉터리 크기가 2GB 초과 → 정기적으로 오래된 데이터 정리하거나 pgvector로 전환
- FAISS 저장 실패:
allow_dangerous_deserialization=True없이 로드하니 에러 발생 → 보안 경고지만 로컬 환경에서는 필요 - 검색 속도 저하: 데이터 5천 개 이상일 때 ChromaDB 검색이 느려짐 → FAISS로 교체하니 3배 빨라짐
- 메타데이터 손실: FAISS는 복잡한 메타데이터 필터링 미지원 → source별 검색 필요하면 ChromaDB 사용
이 경험을 통해 알게 된 점
- 프로토타입은 ChromaDB, 속도 중요하면 FAISS: 1천 개 이하 문서는 둘 다 차이 없지만, 대용량은 FAISS가 압도적
- 인덱스 생성 시점이 중요: 문서 추가 후 바로 검색하면 인덱스 재생성 시간 소요됨
- k값 튜닝 필수: k=3으로 시작했지만 도메인에 따라 k=5가 더 나은 경우도 많음
마무리
벡터 저장소는 RAG의 핵심 부품이다. ChromaDB나 FAISS로 시작하면 설치도 쉽고 바로 테스트해볼 수 있다. 나중에 서비스 규모가 커지면 Pinecone이나 PostgreSQL+pgvector 같은 걸로 교체하면 된다.
다음 편에서는 PostgreSQL + pgvector를 배울 예정이다. 프로덕션 환경에서 많이 쓰는 조합이다.
RAG 챗봇 만들기 시리즈:
← 블로그 목록으로