최저임금법 특화 벡터 스토어 구축하기
이제 분할된 최저임금법 청크들을 벡터로 변환해서 검색 가능한 데이터베이스로 만들어보겠습니다. 단순히 벡터 스토어를 만드는 것이 아니라 최저임금법의 특성을 활용한 메타데이터 필터링과 조문별 정확한 검색이 가능하도록 구축할 거예요.
벡터 스토어는 RAG 시스템의 핵심 구성 요소입니다. 사용자가 "최저임금 위반 시 처벌은 어떻게 되나?"라고 질문하면, 이 질문을 벡터로 변환해서 가장 유사한 법령 내용을 찾아내는 역할을 하죠. Chroma 벡터 데이터베이스를 사용해서 로컬 환경에서도 빠르고 정확한 검색이 가능한 시스템을 만들 어보겠습니다.
1. 임베딩 모델 설정 및 벡터 변환 준비
임베딩 모델의 중요성
최저임금법 내용을 정확하게 벡터화하기 위해 OpenAI의 임베딩 모델을 설정하고, 벡터 변환 과정을 준비하는 단계입니다. 법령 용어의 의미를 정확히 포착할 수 있도록 설정할 거예요.
import tiktoken # at the top of your script, 한 번만 추가하세요
print("=== 임베딩 모델 설정 및 벡터 변환 준비 ===")
# 임베딩 모델 재확인 및 설정
print(f"🤖 임베딩 모델명: {embeddings.model}")
# 청크 전처리 및 임베딩 준비
print(f"📊 총 청크 수: {len(all_chunks)}개")
# 메타데이터 최적화
optimized_chunks = []
for chunk in all_chunks:
optimized_metadata = {
"law_name": chunk.metadata.get("law_name"),
"page": chunk.metadata.get("page", 0),
"page_label": chunk.metadata.get("page_label"),
"article_number": chunk.metadata.get("article_number"),
"contains_article": chunk.metadata.get("contains_article", False),
"chunk_length": len(chunk.page_content),
"source": "최저임금법_PDF"
}
optimized_chunks.append(Document(
page_content=chunk.page_content,
metadata=optimized_metadata
))
print(f"✅ 메타데이터 최적화 완료: {len(optimized_chunks)}개 청크 준비")
# 토큰 수와 비용 계산
encoder = tiktoken.get_encoding("cl100k_base")
total_tokens = sum(len(encoder.encode(doc.page_content)) for doc in optimized_chunks)
cost_usd = total_tokens / 1_000_000 * 0.02 # text-embedding-3-small: $0.02 per 1M tokens
print(f"\n🔢 총 토큰 수: {total_tokens:,} tokens")
print(f"💰 예상 임베딩 비용: ${cost_usd:.4f} USD")
메타데이터 최적화의 의미
벡터 스토어를 만들기 전에 각 청크에 메타데이터를 설정해야 합니다. 이렇게 하는 이유는 나중에 검색 결과를 필터링하거나 더 정확한 정보를 제공하기 위함이거든요.
- law_name: 문서명 (최저임금법)
- page: 페이지 번호
- page_label: PDF 상의 페이지 표시
- article_number: 조문 번호 (사용자가 "제28조"라고 검색했을 때 매칭)
- contains_article: 해당 청크가 조문을 포함하는지 여부
- chunk_length: 청크의 길이 (너무 짧은 청크는 필터링 가능)
- source: 출처 표시
토큰 수와 비용 계산
tiktoken 라이브러리를 사용해서 전체 청크를 임베딩할 때 필요한 토큰 수를 미리 계산합니다. text-embedding-3-small 모델의 가격(100만 토큰당 $0.02)을 기준으로 예상 비용을 산출할 수 있어요.
2. Chroma 벡터 스토어 구축
Chroma 벡터 데이터베이스 생성
이제 실제로 벡터 스토어를 생성하는 단계입니다. 최저임금법 청크들을 벡터로 변환해서 Chroma 데이터베이스에 저장합니다.
print("=== Chroma 벡터 스토어 구축 ===")
import shutil
import os
# 기존 벡터 스토어 초기화
chroma_db_path = "./chroma_minimum_wage_law"
if os.path.exists(chroma_db_path):
shutil.rmtree(chroma_db_path)
print(f"🗑️ 기존 벡터 스토어 삭제: {chroma_db_path}")
# Chroma 벡터 스토어 생성
print(f"🚀 Chroma 벡터 스토어 생성 중...")
print(f" 컬렉션명: minimum_wage_law_collection")
print(f" 저장 경로: {chroma_db_path}")
vectorstore = Chroma.from_documents(
documents=optimized_chunks,
embedding=embeddings,
collection_name="minimum_wage_law_collection",
persist_directory=chroma_db_path
)
print(f"✅ 벡터 스토어 생성 완료!")
# 벡터 스토어 정보 확인
collection = vectorstore._collection
total_vectors = collection.count()
print(f"\n📊 벡터 스토어 정보:")
print(f" 저장된 벡터 수: {total_vectors}개")
print(f" 컬렉션 이름: {collection.name}")
print(f" 데이터베이스 크기: {os.path.getsize(chroma_db_path + '/chroma.sqlite3') / 1024 / 1024:.2f} MB" if os.path.exists(chroma_db_path + '/chroma.sqlite3') else "크기 확인 불가")
print(f"\n✅ 최저임금법 벡터 스토어 구축 완료!")
Chroma 벡터 스토어 구축 과정 이해하기
- shutil.rmtree(chroma_db_path): 기존에 생성된 벡터 스토어가 있다면 삭제해서 깨끗한 상태에서 새로 구축합니다.
- Chroma.from_documents(): 준비된 청크들을 사용해서 Chroma 벡터 스토어를 생성합니다. 모든 청크가 벡터로 변환되어 저장되어요.
- documents=optimized_chunks: 앞에서 준비한 최적화된 청크
- embedding=embeddings: OpenAI의 임베딩 모델
- collection_name="minimum_wage_law_collection: 최저임금법 전용 컬렉션으로 명명해서 다른 법령과 구분할 수 있습니다
- persist_directory=chroma_db_path: 벡터 스토어를 로컬 디스크에 영구 저장해서 자유용할 수 있도록 설정합니다
- vectorstore._collection: Chroma 벡터 스토어의 내부 컬렉션 객체에 접근해서 저장된 벡터의 개수와 컬렉션 이름을 확인할 수 있습니다
- os.path.getsize(): Chroma 데이터베이스 파일의 크기를 바이트 단위로 확인해서 MB로 변환해서 저장 공간 사용량을 파악합니다
3. 벡터 스토어 검색 테스트
간단한 검색으로 벡터 스토어 동작 확인
구축된 벡터 스토어가 최저임금법 내용을 제대로 검색할 수 있는지 간단히 테스트하는 과정입니다.
print("=== 벡터 스토어 검색 테스트 ===")
# 간단한 검색 테스트
test_query = "벌칙"
search_results = vectorstore.similarity_search(test_query, k=1)
print(f"🔍 검색 테스트:")
print(f" 검색어: '{test_query}'")
print(f" 가장 유사한 결과:")
if search_results:
result = search_results[0]
article_info = f" (제{result.metadata.get('article_number')}조)" if result.metadata.get('article_number') else ""
page_info = f"페이지 {result.metadata.get('page_label', '?')}"
print(f" 출처{article_info} - {page_info}")
# 내용 미리보기
content = result.page_content.replace('\n', ' ')
preview = content if len(content) > 200 else content
print(f" 내용: {preview}")
print(f"\n✅ 벡터 스토어 검색 기능 정상 동작! 고급 Retriever 시스템 구축 준비 완료")
검색 테스트 상세 설명
test_query = "벌칙": 최저임금법에서 명확한 답이 있는 간단한 검색어를 사용해서 벡터 검색 성능을 확인합니다. "벌칙"은 제28조에 명시되어 있는 핵심 키워드예요.
vectorstore.similarity_search(test_query, k=1): 벡터 유사도 기반으로 검색어와 가장 유사한 1개 청크를 검색합니다. k=1로 설정해서 가장 관련성 높은 결과만 확인합니다.
search_results[0]: 검색 결과 리스트에서 첫 번째(가장 유사한) 결과를 가져옵니다. 이 결과가 Document 객체 형태로 반환되어요.
result.metadata.get('article_number'): 검색된 청크의 메타데이터에서 조문 번호를 추출합니다. 어느 조문에서 나온 내용인지 사용자가 확인할 수 있어요.
result.metadata.get('page_label', '?'): 검색된 청크의 원본 PDF 페이지 정보를 추출합니다. 페이지 정보가 없으면 '?'로 표시합니다.
result.page_content.replace('\n', ' '): 검색된 청크의 실제 텍스트 내용에서 줄바꿈을 공백으로 바꿔서 가독성을 높입니다.
len(content) > 200 else content: 청크 내용이 200자를 초과하면 미리보기로 일부만 표시하고, 200자 이하면 전체를 표시합니다.
다음 단계
이제 최저임금법 벡터 스토어의 검색 기능이 정상적으로 자동한다는 것을 확인했습니다! 다음 단계에서는 이 벡터 스토어를 기반으로 더 정확하고 똑똑한 고급 Retriever 시스템을 구축해보겠습니다.
'Python > LangChain' 카테고리의 다른 글
| LangChain, RAG - 최저임금법 Q&A RAG 시스템: 완성 (0) | 2025.12.18 |
|---|---|
| LangChain, RAG - 최저임금법 Q&A RAG 시스템: 텍스트 분할 (0) | 2025.12.16 |
| LangChain, RAG - 최저임금법 Q&A RAG 시스템: 실제 최저임금법 조문 데이터 수집하기 (0) | 2025.12.15 |
| LangChain, RAG - 실전 프로젝트: 최저임금법 전문 챗봇 서비스 (0) | 2025.12.14 |
| LangChain, RAG - 실전 예제: 다양한 Retriever 전략 비교 실습 (1) | 2025.12.13 |