financial-rag-chatbot / scripts /quick_setup_test_db.py
Claude
Fix embedder method call in test DB creation script
6efbad0 unverified
"""
λΉ λ₯Έ ν…ŒμŠ€νŠΈμš© 벑터 DB 생성 슀크립트
κ°„λ‹¨ν•œ 금육 κ΄€λ ¨ μƒ˜ν”Œ λ¬Έμ„œλ‘œ 벑터 DB 생성 (1λΆ„ 이내)
"""
import sys
import os
from pathlib import Path
# ν”„λ‘œμ νŠΈ 루트λ₯Ό Python path에 μΆ”κ°€
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))
from services.vector_store import VectorStore
from services.embedder import Embedder
from loguru import logger
# μƒ˜ν”Œ 금육/경제 λ¬Έμ„œ (ν•œκ΅­μ–΄)
SAMPLE_DOCUMENTS = [
{
"text": "포트폴리였 λ‹€κ°ν™”λŠ” 투자 μœ„ν—˜μ„ λΆ„μ‚°μ‹œν‚€λŠ” μ€‘μš”ν•œ μ „λž΅μž…λ‹ˆλ‹€. μ„œλ‘œ λ‹€λ₯Έ μžμ‚° ν΄λž˜μŠ€μ— νˆ¬μžν•¨μœΌλ‘œμ¨ νŠΉμ • μžμ‚°μ˜ ν•˜λ½μ΄ 전체 ν¬νŠΈν΄λ¦¬μ˜€μ— λ―ΈμΉ˜λŠ” 영ν–₯을 μ΅œμ†Œν™”ν•  수 μžˆμŠ΅λ‹ˆλ‹€.",
"metadata": {"source": "portfolio_theory.pdf", "page": 1, "title": "포트폴리였 이둠"}
},
{
"text": "ν˜„λŒ€ 포트폴리였 이둠(MPT)에 λ”°λ₯΄λ©΄, 효율적 νˆ¬μžμ„ μ€ μ£Όμ–΄μ§„ μœ„ν—˜ μˆ˜μ€€μ—μ„œ μ΅œλŒ€ μˆ˜μ΅μ„ μ œκ³΅ν•˜λŠ” ν¬νŠΈν΄λ¦¬μ˜€λ“€μ˜ μ§‘ν•©μž…λ‹ˆλ‹€. λ§ˆμ½”μœ„μΈ λŠ” 이 이둠으둜 노벨 κ²½μ œν•™μƒμ„ μˆ˜μƒν–ˆμŠ΅λ‹ˆλ‹€.",
"metadata": {"source": "portfolio_theory.pdf", "page": 2, "title": "포트폴리였 이둠"}
},
{
"text": "κΈˆμœ΅μœ„κΈ°λŠ” λŒ€μ²΄λ‘œ κ³Όλ„ν•œ λ ˆλ²„λ¦¬μ§€, μžμ‚° 가격 κ±°ν’ˆ, μ‹œμŠ€ν…œμ  리슀크의 μΆ•μ μœΌλ‘œ 인해 λ°œμƒν•©λ‹ˆλ‹€. 2008λ…„ κΈ€λ‘œλ²Œ κΈˆμœ΅μœ„κΈ°λŠ” μ„œλΈŒν”„λΌμž„ λͺ¨κΈ°μ§€ μ‹œμž₯의 λΆ•κ΄΄μ—μ„œ μ‹œμž‘λ˜μ—ˆμŠ΅λ‹ˆλ‹€.",
"metadata": {"source": "financial_crisis.pdf", "page": 1, "title": "κΈˆμœ΅μœ„κΈ°μ˜ 원인"}
},
{
"text": "κΈˆμœ΅μœ„κΈ° μ˜ˆλ°©μ„ μœ„ν•΄μ„œλŠ” κ±΄μ „ν•œ 규제, μ μ ˆν•œ 자본 μš”κ΅¬μ‚¬ν•­, 그리고 μ‹œμŠ€ν…œμ  리슀크 λͺ¨λ‹ˆν„°λ§μ΄ ν•„μˆ˜μ μž…λ‹ˆλ‹€. λ°”μ €IIIλŠ” μ΄λŸ¬ν•œ 규제 κ°•ν™”μ˜ λŒ€ν‘œμ μΈ μ˜ˆμž…λ‹ˆλ‹€.",
"metadata": {"source": "financial_crisis.pdf", "page": 2, "title": "κΈˆμœ΅μœ„κΈ°μ˜ 원인"}
},
{
"text": "효율적 μ‹œμž₯ κ°€μ„€(EMH)은 μ£Όκ°€κ°€ λͺ¨λ“  κ°€μš© 정보λ₯Ό λ°˜μ˜ν•œλ‹€κ³  μ£Όμž₯ν•©λ‹ˆλ‹€. μ•½ν˜•, μ€€κ°•ν˜•, κ°•ν˜• νš¨μœ¨μ„±μ˜ μ„Έ κ°€μ§€ ν˜•νƒœλ‘œ κ΅¬λΆ„λ©λ‹ˆλ‹€.",
"metadata": {"source": "market_efficiency.pdf", "page": 1, "title": "μ‹œμž₯ νš¨μœ¨μ„±"}
},
{
"text": "ν–‰λ™μž¬λ¬΄ν•™μ€ νˆ¬μžμžλ“€μ˜ 비합리적 행동이 μ‹œμž₯에 λ―ΈμΉ˜λŠ” 영ν–₯을 μ—°κ΅¬ν•©λ‹ˆλ‹€. κ³Όμž‰λ°˜μ‘, κ³Όμ†Œλ°˜μ‘, ꡰ집행동 등이 λŒ€ν‘œμ μΈ ν˜„μƒμž…λ‹ˆλ‹€.",
"metadata": {"source": "behavioral_finance.pdf", "page": 1, "title": "ν–‰λ™μž¬λ¬΄ν•™"}
},
{
"text": "μžμ‚°κ°€κ²©κ²°μ •λͺ¨ν˜•(CAPM)은 κΈ°λŒ€μˆ˜μ΅λ₯ μ΄ μ‹œμž₯ μœ„ν—˜ 프리미엄과 λ² νƒ€μ˜ 곱에 λ¬΄μœ„ν—˜ 수읡λ₯ μ„ λ”ν•œ 값이라고 μ„€λͺ…ν•©λ‹ˆλ‹€. λ² νƒ€λŠ” μ‹œμž₯ λŒ€λΉ„ μžμ‚°μ˜ 변동성을 λ‚˜νƒ€λƒ…λ‹ˆλ‹€.",
"metadata": {"source": "asset_pricing.pdf", "page": 1, "title": "μžμ‚°κ°€κ²©κ²°μ •"}
},
{
"text": "μ˜΅μ…˜ κ°€κ²©κ²°μ •μ—λŠ” λΈ”λž™-μˆ„μ¦ˆ λͺ¨ν˜•이 널리 μ‚¬μš©λ©λ‹ˆλ‹€. 이 λͺ¨ν˜•은 μ£Όκ°€μ˜ λ‘œκ·Έμ •κ·œλΆ„ν¬ κ°€μ • ν•˜μ— μœ λŸ½ν˜• μ˜΅μ…˜μ˜ 이둠 가격을 κ³„μ‚°ν•©λ‹ˆλ‹€.",
"metadata": {"source": "derivatives.pdf", "page": 1, "title": "νŒŒμƒμƒν’ˆ"}
},
{
"text": "μœ„ν—˜ κ΄€λ¦¬μ—μ„œ VaR(Value at Risk)λŠ” νŠΉμ • μ‹ λ’°μˆ˜μ€€κ³Ό κΈ°κ°„μ—μ„œ μ˜ˆμƒλ˜λŠ” μ΅œλŒ€ 손싀을 μΈ‘μ •ν•©λ‹ˆλ‹€. κΈˆμœ΅κΈ°κ΄€μ˜ 리슀크 관리에 널리 μ‚¬μš©λ©λ‹ˆλ‹€.",
"metadata": {"source": "risk_management.pdf", "page": 1, "title": "μœ„ν—˜ 관리"}
},
{
"text": "μ€‘μ•™μ€ν–‰μ˜ 톡화정책은 금리 μ‘°μ •, κ³΅κ°œμ‹œμž₯운영, μ§€κΈ‰μ€€λΉ„μœ¨ λ³€κ²½ 등을 톡해 경제 μ•ˆμ •ν™”λ₯Ό μΆ”κ΅¬ν•©λ‹ˆλ‹€. μΈν”Œλ ˆμ΄μ…˜ νƒ€κ²ŸνŒ…μ΄ μ£Όμš” μ •μ±… ν”„λ ˆμž„μ›Œν¬μž…λ‹ˆλ‹€.",
"metadata": {"source": "monetary_policy.pdf", "page": 1, "title": "톡화정책"}
},
{
"text": "ESG νˆ¬μžλŠ” ν™˜κ²½(Environmental), μ‚¬νšŒ(Social), 지배ꡬ쑰(Governance) μš”μ†Œλ₯Ό κ³ λ €ν•˜λŠ” 투자 μ „λž΅μž…λ‹ˆλ‹€. 지속가λŠ₯ν•œ 투자의 μ€‘μš”μ„±μ΄ μ¦κ°€ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.",
"metadata": {"source": "esg_investing.pdf", "page": 1, "title": "ESG 투자"}
},
{
"text": "μ•”ν˜Έν™”νλŠ” 블둝체인 κΈ°μˆ μ„ 기반으둜 ν•œ λ””μ§€ν„Έ μžμ‚°μž…λ‹ˆλ‹€. λΉ„νŠΈμ½”μΈ, 이더리움 등이 λŒ€ν‘œμ μ΄λ©°, νƒˆμ€‘μ•™ν™” 금육(DeFi)의 기반이 되고 μžˆμŠ΅λ‹ˆλ‹€.",
"metadata": {"source": "cryptocurrency.pdf", "page": 1, "title": "μ•”ν˜Έν™”ν"}
},
{
"text": "μ‹ μš©ν‰κ°€λŠ” μ±„λ¬΄μžμ˜ 채무 이행 λŠ₯λ ₯을 ν‰κ°€ν•©λ‹ˆλ‹€. μ‹ μš©λ“±κΈ‰μ€ 투자 μ˜μ‚¬κ²°μ •κ³Ό 금리 결정에 μ€‘μš”ν•œ 영ν–₯을 λ―ΈμΉ©λ‹ˆλ‹€.",
"metadata": {"source": "credit_rating.pdf", "page": 1, "title": "μ‹ μš©ν‰κ°€"}
},
{
"text": "μ£Όμ‹μ‹œμž₯의 μ΄μƒν˜„μƒ(anomaly)μ—λŠ” 규λͺ¨νš¨κ³Ό, κ°€μΉ˜νš¨κ³Ό, λͺ¨λ©˜ν…€ 효과 등이 μžˆμŠ΅λ‹ˆλ‹€. μ΄λŠ” 효율적 μ‹œμž₯ 가섀에 λŒ€ν•œ λ°˜λ‘€λ‘œ μ œμ‹œλ©λ‹ˆλ‹€.",
"metadata": {"source": "market_anomalies.pdf", "page": 1, "title": "μ‹œμž₯ μ΄μƒν˜„μƒ"}
},
{
"text": "ꡬ쑰화 κΈˆμœ΅μ€ μžμ‚°μœ λ™ν™”μ¦κΆŒ(ABS), λ‹΄λ³΄λΆ€μ±„κΆŒ(CDO) λ“± λ³΅μž‘ν•œ κΈˆμœ΅μƒν’ˆμ„ ν¬ν•¨ν•©λ‹ˆλ‹€. 2008λ…„ κΈˆμœ΅μœ„κΈ°μ—μ„œ μ€‘μš”ν•œ 역할을 ν–ˆμŠ΅λ‹ˆλ‹€.",
"metadata": {"source": "structured_finance.pdf", "page": 1, "title": "ꡬ쑰화 금육"}
},
]
def main():
"""ν…ŒμŠ€νŠΈμš© 벑터 DB λΉ λ₯΄κ²Œ 생성"""
logger.info("=" * 80)
logger.info("ν…ŒμŠ€νŠΈμš© 벑터 DB 생성 μ‹œμž‘...")
logger.info("=" * 80)
try:
# 1. Embedder μ΄ˆκΈ°ν™” (무료 sentence-transformers μ‚¬μš©)
logger.info("1️⃣ Embedder μ΄ˆκΈ°ν™” 쀑...")
embedder = Embedder(
model_type="sentence-transformers",
model_name="all-MiniLM-L6-v2"
)
logger.info(f"βœ… Embedder μ€€λΉ„ μ™„λ£Œ ({embedder.get_embedding_dimension()}차원)")
# 2. Vector Store μ΄ˆκΈ°ν™”
logger.info("2️⃣ Vector Store μ΄ˆκΈ°ν™” 쀑...")
persist_dir = project_root / "data" / "chroma_db"
persist_dir.mkdir(parents=True, exist_ok=True)
vector_store = VectorStore(
persist_directory=str(persist_dir),
collection_name="financial_papers"
)
logger.info("βœ… Vector Store μ€€λΉ„ μ™„λ£Œ")
# 3. μƒ˜ν”Œ λ¬Έμ„œ μž„λ² λ”© 생성
logger.info(f"3️⃣ {len(SAMPLE_DOCUMENTS)}개 μƒ˜ν”Œ λ¬Έμ„œ μž„λ² λ”© 쀑...")
texts = [doc["text"] for doc in SAMPLE_DOCUMENTS]
embeddings = embedder.embed_batch(texts)
logger.info(f"βœ… μž„λ² λ”© 생성 μ™„λ£Œ ({len(embeddings)}개)")
# 4. Vector Store에 μΆ”κ°€ (chunks ν˜•μ‹μœΌλ‘œ λ³€ν™˜)
logger.info("4️⃣ Vector Store에 λ¬Έμ„œ μΆ”κ°€ 쀑...")
chunks = []
for i, doc in enumerate(SAMPLE_DOCUMENTS):
chunks.append({
'text': doc['text'],
'source_filename': doc['metadata']['source'],
'source_filepath': f"test_data/{doc['metadata']['source']}",
'chunk_id': i, # 고유 IDλ₯Ό μœ„ν•΄ 인덱슀 μ‚¬μš©
'total_chunks': 1,
'metadata': doc['metadata'],
'page_count': doc['metadata'].get('page', 1)
})
vector_store.add_documents(
chunks=chunks,
embeddings=embeddings
)
logger.info("βœ… λ¬Έμ„œ μΆ”κ°€ μ™„λ£Œ")
# 5. 검증
logger.info("5️⃣ μƒμ„±λœ DB 검증 쀑...")
count = vector_store.collection.count()
logger.info("=" * 80)
logger.info(f"✨ ν…ŒμŠ€νŠΈ 벑터 DB 생성 μ™„λ£Œ!")
logger.info(f"πŸ“Š 총 λ¬Έμ„œ: {count}개")
logger.info(f"πŸ“ μœ„μΉ˜: {persist_dir}")
logger.info(f"πŸ” μ»¬λ ‰μ…˜: financial_papers")
logger.info("=" * 80)
# 6. κ°„λ‹¨ν•œ ν…ŒμŠ€νŠΈ 검색 (선택사항)
try:
logger.info("\nπŸ§ͺ ν…ŒμŠ€νŠΈ 검색 μ‹€ν–‰ 쀑...")
test_query = "포트폴리였 λ‹€κ°ν™”λŠ” λ¬΄μ—‡μΈκ°€μš”?"
query_embedding = embedder.embed_batch([test_query])[0]
results = vector_store.search(
query_embedding=query_embedding,
top_k=3
)
logger.info(f"질문: {test_query}")
logger.info(f"검색 κ²°κ³Ό: {len(results['documents'])}개 λ¬Έμ„œ 발견")
for i, (doc, distance) in enumerate(zip(results['documents'], results['distances']), 1):
metadata = results['metadatas'][i-1] if i-1 < len(results['metadatas']) else {}
title = metadata.get('title', 'Unknown')
logger.info(f" [{i}] {title} (거리: {distance:.4f})")
except Exception as e:
logger.warning(f"ν…ŒμŠ€νŠΈ 검색 μ‹€νŒ¨ (λ¬΄μ‹œ κ°€λŠ₯): {str(e)}")
logger.info("\nβœ… DB 생성 μ™„λ£Œ! λ°±μ—”λ“œ μ„œλ²„λ₯Ό μ‹€ν–‰ν•  μ€€λΉ„κ°€ λ˜μ—ˆμŠ΅λ‹ˆλ‹€.")
except Exception as e:
logger.error(f"❌ 였λ₯˜ λ°œμƒ: {str(e)}")
import traceback
traceback.print_exc()
sys.exit(1)
if __name__ == "__main__":
main()