mirror of
https://github.com/hwanny1128/HGZero.git
synced 2026-06-13 02:29:12 +00:00
feat: init rag service
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
"""
|
||||
RAG Minutes 테이블 초기화 스크립트
|
||||
"""
|
||||
import psycopg2
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 프로젝트 루트를 Python 경로에 추가
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from src.utils.config import load_config, get_database_url
|
||||
|
||||
|
||||
def init_rag_minutes_table(db_url: str, migration_file: str):
|
||||
"""
|
||||
RAG Minutes 테이블 초기화
|
||||
|
||||
Args:
|
||||
db_url: 데이터베이스 연결 URL
|
||||
migration_file: 마이그레이션 SQL 파일 경로
|
||||
"""
|
||||
try:
|
||||
print(f"데이터베이스 연결 중...")
|
||||
conn = psycopg2.connect(db_url)
|
||||
cur = conn.cursor()
|
||||
|
||||
print(f"마이그레이션 파일 읽기: {migration_file}")
|
||||
with open(migration_file, 'r', encoding='utf-8') as f:
|
||||
sql = f.read()
|
||||
|
||||
print("마이그레이션 실행 중...")
|
||||
cur.execute(sql)
|
||||
conn.commit()
|
||||
|
||||
print("✓ RAG Minutes 테이블 초기화 완료")
|
||||
|
||||
# 테이블 확인
|
||||
cur.execute("""
|
||||
SELECT COUNT(*)
|
||||
FROM information_schema.tables
|
||||
WHERE table_name = 'rag_minutes'
|
||||
""")
|
||||
count = cur.fetchone()[0]
|
||||
|
||||
if count > 0:
|
||||
print(f"✓ rag_minutes 테이블이 생성되었습니다")
|
||||
|
||||
# 인덱스 확인
|
||||
cur.execute("""
|
||||
SELECT indexname
|
||||
FROM pg_indexes
|
||||
WHERE tablename = 'rag_minutes'
|
||||
""")
|
||||
indexes = cur.fetchall()
|
||||
print(f"✓ 생성된 인덱스: {len(indexes)}개")
|
||||
for idx in indexes:
|
||||
print(f" - {idx[0]}")
|
||||
else:
|
||||
print("✗ 테이블 생성 실패")
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
except Exception as e:
|
||||
print(f"✗ 에러 발생: {str(e)}")
|
||||
raise
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 설정 로드
|
||||
config_path = Path(__file__).parent.parent / "config.yaml"
|
||||
config = load_config(str(config_path))
|
||||
db_url = get_database_url(config)
|
||||
db_url = "postgresql://hgzerouser:Hi5Jessica!@4.217.133.186:5432/ragdb"
|
||||
|
||||
# 마이그레이션 파일 경로
|
||||
migration_file = Path(__file__).parent.parent / "migrations" / "V1__create_rag_minutes_table.sql"
|
||||
|
||||
if not migration_file.exists():
|
||||
print(f"✗ 마이그레이션 파일을 찾을 수 없습니다: {migration_file}")
|
||||
sys.exit(1)
|
||||
|
||||
# 초기화 실행
|
||||
init_rag_minutes_table(db_url, str(migration_file))
|
||||
@@ -0,0 +1,246 @@
|
||||
"""
|
||||
관련자료 데이터 로딩 스크립트
|
||||
"""
|
||||
import sys
|
||||
import json
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
from datetime import datetime
|
||||
|
||||
# 프로젝트 루트 디렉토리를 Python 경로에 추가
|
||||
project_root = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
from src.models.document import Document, DocumentChunk, DocumentMetadata
|
||||
from src.db.azure_search import AzureAISearchDB
|
||||
from src.utils.config import load_config
|
||||
from src.utils.embedding import EmbeddingGenerator
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def load_documents_from_json(file_path: Path) -> List[Document]:
|
||||
"""
|
||||
JSON 파일에서 문서 데이터 로드
|
||||
|
||||
Args:
|
||||
file_path: JSON 파일 경로
|
||||
|
||||
Returns:
|
||||
문서 리스트
|
||||
"""
|
||||
logger.info(f"JSON 파일 로딩: {file_path}")
|
||||
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
|
||||
documents = []
|
||||
|
||||
# 업무 도메인별 데이터 처리
|
||||
for domain, doc_types in data.get("sample_data", {}).items():
|
||||
for doc_type, docs in doc_types.items():
|
||||
for doc_data in docs:
|
||||
# Metadata 파싱
|
||||
metadata = None
|
||||
if "metadata" in doc_data:
|
||||
metadata = DocumentMetadata(**doc_data["metadata"])
|
||||
|
||||
# Document 객체 생성
|
||||
doc = Document(
|
||||
document_id=doc_data["document_id"],
|
||||
document_type=doc_data["document_type"],
|
||||
business_domain=doc_data.get("business_domain"),
|
||||
title=doc_data["title"],
|
||||
content=doc_data["content"],
|
||||
summary=doc_data["summary"],
|
||||
keywords=doc_data.get("keywords", []),
|
||||
created_date=doc_data.get("created_date"),
|
||||
participants=doc_data.get("participants", []),
|
||||
metadata=metadata,
|
||||
embedding=None # 나중에 생성
|
||||
)
|
||||
|
||||
documents.append(doc)
|
||||
|
||||
logger.info(f" → {len(documents)}개 문서 로드 완료")
|
||||
return documents
|
||||
|
||||
|
||||
def create_chunks(
|
||||
document: Document,
|
||||
embedding_gen: EmbeddingGenerator,
|
||||
max_tokens: int = 2000
|
||||
) -> List[DocumentChunk]:
|
||||
"""
|
||||
문서를 청크로 분할 및 임베딩 생성
|
||||
|
||||
Args:
|
||||
document: 문서
|
||||
embedding_gen: 임베딩 생성기
|
||||
max_tokens: 최대 토큰 수
|
||||
|
||||
Returns:
|
||||
문서 청크 리스트
|
||||
"""
|
||||
chunks = []
|
||||
|
||||
# 전체 문서를 하나의 청크로 처리 (간단한 구현)
|
||||
# 실제로는 안건 단위로 분할해야 함
|
||||
content = document.content
|
||||
token_count = embedding_gen.get_token_count(content)
|
||||
|
||||
if token_count > max_tokens:
|
||||
# 간단한 분할: 문단 단위
|
||||
paragraphs = content.split("\n\n")
|
||||
current_chunk = ""
|
||||
chunk_index = 0
|
||||
|
||||
for para in paragraphs:
|
||||
test_chunk = current_chunk + "\n\n" + para if current_chunk else para
|
||||
if embedding_gen.get_token_count(test_chunk) > max_tokens:
|
||||
# 현재 청크 저장
|
||||
if current_chunk:
|
||||
chunks.append({
|
||||
"content": current_chunk,
|
||||
"chunk_index": chunk_index
|
||||
})
|
||||
chunk_index += 1
|
||||
|
||||
current_chunk = para
|
||||
else:
|
||||
current_chunk = test_chunk
|
||||
|
||||
# 마지막 청크 저장
|
||||
if current_chunk:
|
||||
chunks.append({
|
||||
"content": current_chunk,
|
||||
"chunk_index": chunk_index
|
||||
})
|
||||
|
||||
else:
|
||||
# 토큰 수가 적으면 하나의 청크로
|
||||
chunks.append({
|
||||
"content": content,
|
||||
"chunk_index": 0
|
||||
})
|
||||
|
||||
# 임베딩 생성
|
||||
chunk_texts = [chunk["content"] for chunk in chunks]
|
||||
embeddings = embedding_gen.generate_embeddings_batch(chunk_texts)
|
||||
|
||||
# DocumentChunk 객체 생성
|
||||
document_chunks = []
|
||||
for chunk_data, embedding in zip(chunks, embeddings):
|
||||
chunk = DocumentChunk(
|
||||
id=f"{document.document_id}_chunk_{chunk_data['chunk_index']}",
|
||||
document_id=document.document_id,
|
||||
document_type=document.document_type,
|
||||
title=document.title,
|
||||
folder=document.metadata.folder if document.metadata else None,
|
||||
created_date=document.created_date,
|
||||
participants=document.participants,
|
||||
keywords=document.keywords,
|
||||
agenda_id=None, # 간단한 구현에서는 None
|
||||
agenda_title=None,
|
||||
chunk_index=chunk_data["chunk_index"],
|
||||
content=chunk_data["content"],
|
||||
content_vector=embedding,
|
||||
token_count=embedding_gen.get_token_count(chunk_data["content"])
|
||||
)
|
||||
document_chunks.append(chunk)
|
||||
|
||||
return document_chunks
|
||||
|
||||
|
||||
def main():
|
||||
"""메인 함수"""
|
||||
logger.info("=" * 60)
|
||||
logger.info("관련자료 데이터 로딩 시작")
|
||||
logger.info("=" * 60)
|
||||
|
||||
# 1. 설정 로드
|
||||
config = load_config(str(project_root / "config.yaml"))
|
||||
logger.info("✓ 설정 로드 완료")
|
||||
|
||||
# 2. Azure AI Search 연결
|
||||
azure_search = config["azure_search"]
|
||||
search_db = AzureAISearchDB(
|
||||
endpoint=azure_search["endpoint"],
|
||||
api_key=azure_search["api_key"],
|
||||
index_name=azure_search["index_name"],
|
||||
api_version=azure_search["api_version"]
|
||||
)
|
||||
logger.info("✓ Azure AI Search 연결 완료")
|
||||
|
||||
# 3. 인덱스 생성
|
||||
search_db.create_index()
|
||||
logger.info("✓ 인덱스 생성 완료")
|
||||
|
||||
# 4. 임베딩 생성기 초기화
|
||||
azure_openai = config["azure_openai"]
|
||||
embedding_gen = EmbeddingGenerator(
|
||||
api_key=azure_openai["api_key"],
|
||||
endpoint=azure_openai["endpoint"],
|
||||
model=azure_openai["embedding_model"],
|
||||
dimension=azure_openai["embedding_dimension"],
|
||||
api_version=azure_openai["api_version"]
|
||||
)
|
||||
logger.info("✓ 임베딩 생성기 초기화 완료")
|
||||
|
||||
# 5. 문서 데이터 로딩
|
||||
data_file = project_root.parent / config["data"]["documents_file"]
|
||||
if not data_file.exists():
|
||||
logger.error(f"❌ 파일 없음: {data_file}")
|
||||
sys.exit(1)
|
||||
|
||||
documents = load_documents_from_json(data_file)
|
||||
logger.info(f"✓ 총 {len(documents)}개 문서 로드 완료")
|
||||
|
||||
# 6. 청킹 및 임베딩 생성
|
||||
logger.info("청킹 및 임베딩 생성 시작")
|
||||
all_chunks = []
|
||||
|
||||
for i, doc in enumerate(documents, 1):
|
||||
logger.info(f" 처리 중: {i}/{len(documents)} - {doc.title}")
|
||||
chunks = create_chunks(doc, embedding_gen)
|
||||
all_chunks.extend(chunks)
|
||||
|
||||
logger.info(f"✓ 총 {len(all_chunks)}개 청크 생성 완료")
|
||||
|
||||
# 7. Azure AI Search에 업로드
|
||||
logger.info("Azure AI Search 업로드 시작")
|
||||
success = search_db.upload_documents(all_chunks)
|
||||
|
||||
if success:
|
||||
logger.info(f"✓ {len(all_chunks)}개 청크 업로드 완료")
|
||||
else:
|
||||
logger.error("❌ 업로드 실패")
|
||||
sys.exit(1)
|
||||
|
||||
# 8. 통계 조회
|
||||
stats = search_db.get_stats()
|
||||
logger.info("=" * 60)
|
||||
logger.info("관련자료 통계")
|
||||
logger.info("=" * 60)
|
||||
logger.info(f"전체 문서: {stats['total_documents']}개")
|
||||
logger.info(f"전체 청크: {stats['total_chunks']}개")
|
||||
logger.info("\n문서 타입별 통계:")
|
||||
for doc_type, count in sorted(stats['by_type'].items(), key=lambda x: x[1], reverse=True):
|
||||
logger.info(f" - {doc_type}: {count}개")
|
||||
|
||||
logger.info("=" * 60)
|
||||
logger.info("관련자료 데이터 로딩 완료")
|
||||
logger.info("=" * 60)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except Exception as e:
|
||||
logger.error(f"오류 발생: {str(e)}", exc_info=True)
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,196 @@
|
||||
"""
|
||||
용어집 데이터 로딩 스크립트
|
||||
"""
|
||||
import sys
|
||||
import json
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
# 프로젝트 루트 디렉토리를 Python 경로에 추가
|
||||
project_root = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
from src.models.term import Term, DocumentSource
|
||||
from src.db.postgres_vector import PostgresVectorDB
|
||||
from src.utils.config import load_config, get_database_url
|
||||
from src.utils.embedding import EmbeddingGenerator
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def load_terms_from_json(file_path: Path) -> List[Term]:
|
||||
"""
|
||||
JSON 파일에서 용어 데이터 로드
|
||||
|
||||
Args:
|
||||
file_path: JSON 파일 경로
|
||||
|
||||
Returns:
|
||||
용어 리스트
|
||||
"""
|
||||
logger.info(f"JSON 파일 로딩: {file_path}")
|
||||
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
|
||||
terms = []
|
||||
|
||||
# 도메인별 데이터 처리
|
||||
for domain_data in data.get("terms", []):
|
||||
domain = domain_data.get("domain")
|
||||
source_type = domain_data.get("source_type")
|
||||
|
||||
for term_data in domain_data.get("data", []):
|
||||
# DocumentSource 파싱
|
||||
doc_source = None
|
||||
if "document_source" in term_data:
|
||||
doc_source = DocumentSource(**term_data["document_source"])
|
||||
|
||||
# Term 객체 생성
|
||||
term = Term(
|
||||
term_id=term_data["term_id"],
|
||||
term_name=term_data["term_name"],
|
||||
normalized_name=term_data["normalized_name"],
|
||||
category=term_data["category"],
|
||||
definition=term_data["definition"],
|
||||
context=term_data.get("context", ""),
|
||||
synonyms=term_data.get("synonyms", []),
|
||||
related_terms=term_data.get("related_terms", []),
|
||||
document_source=doc_source,
|
||||
confidence_score=term_data.get("confidence_score", 0.0),
|
||||
usage_count=term_data.get("usage_count", 0),
|
||||
last_updated=term_data.get("last_updated"),
|
||||
embedding=None # 나중에 생성
|
||||
)
|
||||
|
||||
terms.append(term)
|
||||
|
||||
logger.info(f" → {len(terms)}개 용어 로드 완료")
|
||||
return terms
|
||||
|
||||
|
||||
def generate_embeddings(
|
||||
terms: List[Term],
|
||||
embedding_gen: EmbeddingGenerator
|
||||
) -> List[Term]:
|
||||
"""
|
||||
용어 임베딩 생성
|
||||
|
||||
Args:
|
||||
terms: 용어 리스트
|
||||
embedding_gen: 임베딩 생성기
|
||||
|
||||
Returns:
|
||||
임베딩이 추가된 용어 리스트
|
||||
"""
|
||||
logger.info(f"임베딩 생성 시작: {len(terms)}개 용어")
|
||||
|
||||
# 임베딩 텍스트 준비 (용어명 + 정의 + 맥락)
|
||||
texts = []
|
||||
for term in terms:
|
||||
text = f"{term.term_name}\n{term.definition}"
|
||||
if term.context:
|
||||
text += f"\n{term.context}"
|
||||
texts.append(text)
|
||||
|
||||
# 배치 임베딩 생성
|
||||
embeddings = embedding_gen.generate_embeddings_batch(texts, batch_size=50)
|
||||
|
||||
# 임베딩 추가
|
||||
for term, embedding in zip(terms, embeddings):
|
||||
term.embedding = embedding
|
||||
|
||||
logger.info(f" → 임베딩 생성 완료")
|
||||
return terms
|
||||
|
||||
|
||||
def main():
|
||||
"""메인 함수"""
|
||||
logger.info("=" * 60)
|
||||
logger.info("용어집 데이터 로딩 시작")
|
||||
logger.info("=" * 60)
|
||||
|
||||
# 1. 설정 로드
|
||||
config = load_config(str(project_root / "config.yaml"))
|
||||
logger.info("✓ 설정 로드 완료")
|
||||
|
||||
# 2. PostgreSQL 연결
|
||||
db_url = get_database_url(config)
|
||||
db = PostgresVectorDB(db_url)
|
||||
logger.info("✓ PostgreSQL 연결 완료")
|
||||
|
||||
# 3. 데이터베이스 초기화
|
||||
db.init_database()
|
||||
logger.info("✓ 데이터베이스 초기화 완료")
|
||||
|
||||
# 4. 임베딩 생성기 초기화
|
||||
azure_openai = config["azure_openai"]
|
||||
embedding_gen = EmbeddingGenerator(
|
||||
api_key=azure_openai["api_key"],
|
||||
endpoint=azure_openai["endpoint"],
|
||||
model=azure_openai["embedding_model"],
|
||||
dimension=azure_openai["embedding_dimension"],
|
||||
api_version=azure_openai["api_version"]
|
||||
)
|
||||
logger.info("✓ 임베딩 생성기 초기화 완료")
|
||||
|
||||
# 5. 용어 데이터 로딩
|
||||
all_terms = []
|
||||
data_dir = project_root.parent / config["data"]["terms_dir"]
|
||||
|
||||
for filename in config["data"]["terms_files"]:
|
||||
file_path = data_dir / filename
|
||||
if file_path.exists():
|
||||
terms = load_terms_from_json(file_path)
|
||||
all_terms.extend(terms)
|
||||
else:
|
||||
logger.warning(f"⚠ 파일 없음: {file_path}")
|
||||
|
||||
logger.info(f"✓ 총 {len(all_terms)}개 용어 로드 완료")
|
||||
|
||||
# 6. 임베딩 생성
|
||||
all_terms = generate_embeddings(all_terms, embedding_gen)
|
||||
|
||||
# 7. 데이터베이스에 삽입
|
||||
logger.info(f"데이터베이스 삽입 시작: {len(all_terms)}개 용어")
|
||||
success_count = 0
|
||||
fail_count = 0
|
||||
|
||||
for i, term in enumerate(all_terms, 1):
|
||||
if db.insert_term(term):
|
||||
success_count += 1
|
||||
else:
|
||||
fail_count += 1
|
||||
|
||||
if i % 100 == 0:
|
||||
logger.info(f" 진행: {i}/{len(all_terms)} (성공: {success_count}, 실패: {fail_count})")
|
||||
|
||||
logger.info(f"✓ 삽입 완료: 성공 {success_count}, 실패 {fail_count}")
|
||||
|
||||
# 8. 통계 조회
|
||||
stats = db.get_stats()
|
||||
logger.info("=" * 60)
|
||||
logger.info("용어집 통계")
|
||||
logger.info("=" * 60)
|
||||
logger.info(f"전체 용어: {stats['total_terms']}개")
|
||||
logger.info(f"평균 신뢰도: {stats['avg_confidence']:.2f}")
|
||||
logger.info("\n카테고리별 통계:")
|
||||
for category, count in sorted(stats['by_category'].items(), key=lambda x: x[1], reverse=True):
|
||||
logger.info(f" - {category}: {count}개")
|
||||
|
||||
logger.info("=" * 60)
|
||||
logger.info("용어집 데이터 로딩 완료")
|
||||
logger.info("=" * 60)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except Exception as e:
|
||||
logger.error(f"오류 발생: {str(e)}", exc_info=True)
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,245 @@
|
||||
"""
|
||||
Vector DB 통합 시스템 설정 검증 스크립트
|
||||
|
||||
사용법: python scripts/validate_setup.py
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import List, Tuple
|
||||
|
||||
# 프로젝트 루트 디렉토리
|
||||
project_root = Path(__file__).parent.parent
|
||||
|
||||
|
||||
def check_file_exists(file_path: Path, description: str) -> bool:
|
||||
"""파일 존재 여부 확인"""
|
||||
exists = file_path.exists()
|
||||
status = "✓" if exists else "✗"
|
||||
print(f" {status} {description}: {file_path.name}")
|
||||
return exists
|
||||
|
||||
|
||||
def check_directory_exists(dir_path: Path, description: str) -> bool:
|
||||
"""디렉토리 존재 여부 확인"""
|
||||
exists = dir_path.exists() and dir_path.is_dir()
|
||||
status = "✓" if exists else "✗"
|
||||
print(f" {status} {description}: {dir_path.name}/")
|
||||
return exists
|
||||
|
||||
|
||||
def check_python_version() -> bool:
|
||||
"""Python 버전 확인"""
|
||||
version = sys.version_info
|
||||
is_valid = version.major == 3 and version.minor >= 9
|
||||
status = "✓" if is_valid else "✗"
|
||||
print(f" {status} Python 버전: {version.major}.{version.minor}.{version.micro}")
|
||||
if not is_valid:
|
||||
print(f" → Python 3.9 이상이 필요합니다")
|
||||
return is_valid
|
||||
|
||||
|
||||
def check_dependencies() -> bool:
|
||||
"""필수 패키지 설치 확인"""
|
||||
required_packages = [
|
||||
"fastapi",
|
||||
"uvicorn",
|
||||
"psycopg2",
|
||||
"openai",
|
||||
"anthropic",
|
||||
"azure.search.documents",
|
||||
"pydantic",
|
||||
"pyyaml",
|
||||
"tenacity"
|
||||
]
|
||||
|
||||
missing_packages = []
|
||||
for package in required_packages:
|
||||
try:
|
||||
__import__(package.replace("-", "_").split(".")[0])
|
||||
print(f" ✓ {package}")
|
||||
except ImportError:
|
||||
print(f" ✗ {package}")
|
||||
missing_packages.append(package)
|
||||
|
||||
if missing_packages:
|
||||
print(f"\n → 누락된 패키지를 설치하세요: pip install {' '.join(missing_packages)}")
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def check_env_variables() -> Tuple[bool, List[str]]:
|
||||
"""환경 변수 설정 확인"""
|
||||
required_vars = [
|
||||
"POSTGRES_HOST",
|
||||
"POSTGRES_PORT",
|
||||
"POSTGRES_DATABASE",
|
||||
"POSTGRES_USER",
|
||||
"POSTGRES_PASSWORD",
|
||||
"AZURE_OPENAI_API_KEY",
|
||||
"AZURE_OPENAI_ENDPOINT",
|
||||
"AZURE_SEARCH_ENDPOINT",
|
||||
"AZURE_SEARCH_API_KEY",
|
||||
"CLAUDE_API_KEY"
|
||||
]
|
||||
|
||||
# .env 파일 확인
|
||||
env_file = project_root / ".env"
|
||||
if env_file.exists():
|
||||
print(f" ✓ .env 파일 존재")
|
||||
# .env 파일 로드 시뮬레이션
|
||||
with open(env_file, 'r', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if '=' in line and not line.startswith('#'):
|
||||
key, value = line.split('=', 1)
|
||||
if value and value != f"your_{key.lower()}_here":
|
||||
os.environ[key] = value
|
||||
else:
|
||||
print(f" ✗ .env 파일 없음")
|
||||
print(f" → .env.example을 .env로 복사하고 실제 값으로 수정하세요")
|
||||
|
||||
missing_vars = []
|
||||
for var in required_vars:
|
||||
value = os.environ.get(var, "")
|
||||
has_value = bool(value) and not value.startswith("your_")
|
||||
|
||||
if has_value:
|
||||
# API 키는 앞 4자리만 표시
|
||||
if "KEY" in var or "PASSWORD" in var:
|
||||
display_value = value[:4] + "..." if len(value) > 4 else "***"
|
||||
else:
|
||||
display_value = value
|
||||
print(f" ✓ {var}: {display_value}")
|
||||
else:
|
||||
print(f" ✗ {var}: 설정 필요")
|
||||
missing_vars.append(var)
|
||||
|
||||
return len(missing_vars) == 0, missing_vars
|
||||
|
||||
|
||||
def check_data_files() -> bool:
|
||||
"""샘플 데이터 파일 확인"""
|
||||
data_dir = project_root.parent / "design/aidata"
|
||||
meet_ref_file = project_root.parent / "design/meet-ref.json"
|
||||
|
||||
all_exists = True
|
||||
|
||||
# 용어 데이터 파일
|
||||
term_files = ["terms-01.json", "terms-02.json", "terms-03.json", "terms-04.json"]
|
||||
for filename in term_files:
|
||||
file_path = data_dir / filename
|
||||
exists = file_path.exists()
|
||||
status = "✓" if exists else "✗"
|
||||
print(f" {status} {filename}")
|
||||
all_exists = all_exists and exists
|
||||
|
||||
# 관련 문서 데이터 파일
|
||||
exists = meet_ref_file.exists()
|
||||
status = "✓" if exists else "✗"
|
||||
print(f" {status} meet-ref.json")
|
||||
all_exists = all_exists and exists
|
||||
|
||||
if not all_exists:
|
||||
print(f"\n → 데이터 파일이 design/ 디렉토리에 있는지 확인하세요")
|
||||
|
||||
return all_exists
|
||||
|
||||
|
||||
def main():
|
||||
"""메인 검증 함수"""
|
||||
print("\n" + "=" * 70)
|
||||
print("Vector DB 통합 시스템 설정 검증")
|
||||
print("=" * 70 + "\n")
|
||||
|
||||
results = {}
|
||||
|
||||
# 1. Python 버전 확인
|
||||
print("1. Python 버전 확인")
|
||||
results["python"] = check_python_version()
|
||||
print()
|
||||
|
||||
# 2. 프로젝트 구조 확인
|
||||
print("2. 프로젝트 구조 확인")
|
||||
structure_checks = [
|
||||
(project_root / "config.yaml", "설정 파일"),
|
||||
(project_root / "requirements.txt", "의존성 파일"),
|
||||
(project_root / "README.md", "문서"),
|
||||
(project_root / "src", "소스 디렉토리"),
|
||||
(project_root / "src/models", "모델 디렉토리"),
|
||||
(project_root / "src/db", "DB 디렉토리"),
|
||||
(project_root / "src/services", "서비스 디렉토리"),
|
||||
(project_root / "src/api", "API 디렉토리"),
|
||||
(project_root / "scripts", "스크립트 디렉토리"),
|
||||
(project_root / "tests", "테스트 디렉토리")
|
||||
]
|
||||
|
||||
structure_ok = True
|
||||
for path, desc in structure_checks:
|
||||
if path.is_dir():
|
||||
structure_ok = check_directory_exists(path, desc) and structure_ok
|
||||
else:
|
||||
structure_ok = check_file_exists(path, desc) and structure_ok
|
||||
|
||||
results["structure"] = structure_ok
|
||||
print()
|
||||
|
||||
# 3. 의존성 패키지 확인
|
||||
print("3. 의존성 패키지 확인")
|
||||
results["dependencies"] = check_dependencies()
|
||||
print()
|
||||
|
||||
# 4. 환경 변수 확인
|
||||
print("4. 환경 변수 확인")
|
||||
env_ok, missing_vars = check_env_variables()
|
||||
results["environment"] = env_ok
|
||||
print()
|
||||
|
||||
# 5. 데이터 파일 확인
|
||||
print("5. 샘플 데이터 파일 확인")
|
||||
results["data_files"] = check_data_files()
|
||||
print()
|
||||
|
||||
# 결과 요약
|
||||
print("=" * 70)
|
||||
print("검증 결과 요약")
|
||||
print("=" * 70)
|
||||
|
||||
all_passed = all(results.values())
|
||||
|
||||
for category, passed in results.items():
|
||||
status = "✓ 통과" if passed else "✗ 실패"
|
||||
print(f" {status}: {category}")
|
||||
|
||||
print()
|
||||
|
||||
if all_passed:
|
||||
print("🎉 모든 검증을 통과했습니다!")
|
||||
print()
|
||||
print("다음 단계:")
|
||||
print(" 1. 데이터베이스 초기화: python scripts/load_terms.py")
|
||||
print(" 2. 관련자료 로딩: python scripts/load_documents.py")
|
||||
print(" 3. API 서버 실행: python -m src.api.main")
|
||||
print(" 4. API 문서 확인: http://localhost:8000/docs")
|
||||
else:
|
||||
print("⚠️ 일부 검증에 실패했습니다.")
|
||||
print()
|
||||
print("실패한 항목을 확인하고 수정한 후 다시 실행하세요.")
|
||||
|
||||
if not results["dependencies"]:
|
||||
print("\n의존성 설치 명령:")
|
||||
print(" pip install -r requirements.txt")
|
||||
|
||||
if not results["environment"]:
|
||||
print("\n환경 변수 설정 방법:")
|
||||
print(" 1. .env.example을 .env로 복사")
|
||||
print(" 2. .env 파일을 열어 실제 값으로 수정")
|
||||
|
||||
print()
|
||||
print("=" * 70)
|
||||
|
||||
return 0 if all_passed else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user