Spaces:
Sleeping
Sleeping
| import os | |
| import json | |
| import numpy as np | |
| from sentence_transformers import SentenceTransformer | |
| import chromadb | |
| from chromadb.config import Settings | |
| import logging | |
| os.makedirs("logs", exist_ok=True) | |
| logging.basicConfig(filename='logs/vector_db.log', level=logging.INFO, | |
| format='%(asctime)s - %(levelname)s - %(message)s') | |
| class KCSCVectorDB: | |
| def __init__(self, data_dir="data", db_dir="vector_db"): | |
| self.data_dir = data_dir | |
| self.db_dir = db_dir | |
| os.makedirs(self.db_dir, exist_ok=True) | |
| # ์๋ฒ ๋ฉ ๋ชจ๋ธ ๋ก๋ (๋ค๊ตญ์ด ๋ชจ๋ธ ์ฌ์ฉ) | |
| self.model = SentenceTransformer('jhgan/ko-sroberta-multitask') | |
| # Chroma DB ํด๋ผ์ด์ธํธ ์ด๊ธฐํ | |
| self.client = chromadb.PersistentClient(path=self.db_dir, settings=Settings(allow_reset=True)) | |
| # ๊ฐ ๋ฌธ์ ํ์ ๋ณ ์ปฌ๋ ์ ์์ฑ | |
| self.collections = {} | |
| for doc_type in ["KDS", "KCS"]: | |
| try: | |
| self.collections[doc_type] = self.client.get_or_create_collection( | |
| name=doc_type, | |
| metadata={"description": f"{doc_type} ์ค๊ณ๊ธฐ์ค ๋ฌธ์"} | |
| ) | |
| except Exception as e: | |
| logging.error(f"{doc_type} ์ปฌ๋ ์ ์์ฑ ์ค ์ค๋ฅ: {str(e)}") | |
| def load_processed_docs(self): | |
| """์ฒ๋ฆฌ๋ ๋ฌธ์ ๋ฐ์ดํฐ ๋ก๋""" | |
| try: | |
| file_path = os.path.join(self.data_dir, "processed_docs.json") | |
| with open(file_path, 'r', encoding='utf-8') as f: | |
| return json.load(f) | |
| except Exception as e: | |
| logging.error(f"์ฒ๋ฆฌ๋ ๋ฌธ์ ๋ก๋ ์ค ์ค๋ฅ: {str(e)}") | |
| return [] | |
| def create_document_chunks(self, docs, chunk_size=1000, overlap=200): | |
| """๋ฌธ์๋ฅผ ์ฒญํฌ๋ก ๋ถํ """ | |
| chunks = [] | |
| for doc in docs: | |
| content = doc.get('content', '') | |
| if not content or len(content) <= chunk_size: | |
| # ๋ด์ฉ์ด ์๊ฑฐ๋ ์ฒญํฌ ํฌ๊ธฐ๋ณด๋ค ์์ ๊ฒฝ์ฐ ๊ทธ๋๋ก ์ฌ์ฉ | |
| chunk_doc = doc.copy() | |
| chunk_doc['chunk_id'] = f"{doc['id']}-0" | |
| chunks.append(chunk_doc) | |
| continue | |
| # ๊ธด ๋ฌธ์๋ฅผ ์ฒญํฌ๋ก ๋ถํ | |
| for i in range(0, len(content), chunk_size - overlap): | |
| chunk_content = content[i:i + chunk_size] | |
| if len(chunk_content) < 100: # ๋๋ฌด ์์ ์ฒญํฌ๋ ๊ฑด๋๋ฐ๊ธฐ | |
| continue | |
| chunk_doc = doc.copy() | |
| chunk_doc['content'] = chunk_content | |
| chunk_doc['chunk_id'] = f"{doc['id']}-{i//(chunk_size-overlap)}" | |
| chunks.append(chunk_doc) | |
| logging.info(f"์ด {len(docs)}๊ฐ ๋ฌธ์์์ {len(chunks)}๊ฐ์ ์ฒญํฌ ์์ฑ๋จ") | |
| return chunks | |
| def build_index(self): | |
| """๋ฒกํฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ตฌ์ถ""" | |
| docs = self.load_processed_docs() | |
| if not docs: | |
| logging.error("๋ฌธ์๋ฅผ ๋ก๋ํ ์ ์์ต๋๋ค.") | |
| return False | |
| # ๋ฌธ์๋ฅผ ์ฒญํฌ๋ก ๋ถํ | |
| chunks = self.create_document_chunks(docs) | |
| # ๋ฌธ์ ํ์ ๋ณ๋ก ๊ทธ๋ฃนํ | |
| doc_type_groups = {} | |
| for chunk in chunks: | |
| doc_type = chunk.get('doc_type', 'unknown') | |
| if doc_type not in doc_type_groups: | |
| doc_type_groups[doc_type] = [] | |
| doc_type_groups[doc_type].append(chunk) | |
| # ๊ฐ ๋ฌธ์ ํ์ ๋ณ๋ก ์ธ๋ฑ์ฑ | |
| for doc_type, type_chunks in doc_type_groups.items(): | |
| if doc_type not in self.collections: | |
| logging.warning(f"{doc_type} ์ปฌ๋ ์ ์ด ์์ต๋๋ค. ๊ฑด๋๋๋๋ค.") | |
| continue | |
| collection = self.collections[doc_type] | |
| # ๊ธฐ์กด ๋ฐ์ดํฐ ํ์ธ ๋ฐ ํ์์ ์ด๊ธฐํ | |
| if collection.count() > 0: | |
| logging.info(f"{doc_type} ์ปฌ๋ ์ ์ด๊ธฐํ (๊ธฐ์กด {collection.count()}๊ฐ ํญ๋ชฉ)") | |
| collection.delete(where={}) | |
| # ์ฒญํฌ ๋ฐ์ดํฐ ์ค๋น | |
| ids = [] | |
| documents = [] | |
| metadatas = [] | |
| embeddings = [] | |
| for chunk in type_chunks: | |
| # ํ ์คํธ ์ค๋น (์ ๋ชฉ + ๋ด์ฉ) | |
| text_for_embedding = f"{chunk.get('name', '')} {chunk.get('title', '')}: {chunk.get('content', '')}" | |
| # ํ ์คํธ๊ฐ ๋น์ด์์ผ๋ฉด ๊ฑด๋๋ | |
| if not text_for_embedding.strip(): | |
| continue | |
| # ์๋ฒ ๋ฉ ์์ฑ | |
| embedding = self.model.encode(text_for_embedding) | |
| ids.append(chunk['chunk_id']) | |
| documents.append(text_for_embedding) | |
| # ๋ฉํ๋ฐ์ดํฐ ์ค๋น (๊ฒ์ ๊ฒฐ๊ณผ์์ ํ์ํ ์ ๋ณด) | |
| metadata = { | |
| "id": chunk['id'], | |
| "code": chunk.get('code', ''), | |
| "full_code": chunk.get('full_code', ''), | |
| "name": chunk.get('name', ''), | |
| "title": chunk.get('title', ''), | |
| "doc_type": doc_type, | |
| "version": chunk.get('version', '') | |
| } | |
| metadatas.append(metadata) | |
| embeddings.append(embedding.tolist()) | |
| # ๋ฐ์ดํฐ ์ถ๊ฐ | |
| if ids: | |
| collection.add( | |
| ids=ids, | |
| documents=documents, | |
| metadatas=metadatas, | |
| embeddings=embeddings | |
| ) | |
| logging.info(f"{doc_type} ์ปฌ๋ ์ ์ {len(ids)}๊ฐ ํญ๋ชฉ ์ถ๊ฐ๋จ") | |
| return True | |
| def search(self, query, doc_types=None, limit=5): | |
| """์์ฐ์ด ์ฟผ๋ฆฌ๋ก ๊ด๋ จ ๋ฌธ์ ๊ฒ์""" | |
| if not doc_types: | |
| doc_types = list(self.collections.keys()) | |
| # ์ฟผ๋ฆฌ ์๋ฒ ๋ฉ | |
| query_embedding = self.model.encode(query) | |
| all_results = [] | |
| # ๊ฐ ์ปฌ๋ ์ ์์ ๊ฒ์ | |
| for doc_type in doc_types: | |
| if doc_type not in self.collections: | |
| continue | |
| collection = self.collections[doc_type] | |
| results = collection.query( | |
| query_embeddings=[query_embedding.tolist()], | |
| n_results=limit | |
| ) | |
| # ๊ฒฐ๊ณผ ํ์ ๋ณํ | |
| for i in range(len(results['ids'][0])): | |
| result = { | |
| "id": results['ids'][0][i], | |
| "text": results['documents'][0][i], | |
| "metadata": results['metadatas'][0][i], | |
| "distance": float(results['distances'][0][i]) | |
| } | |
| all_results.append(result) | |
| # ์ ์ฒด ๊ฒฐ๊ณผ๋ฅผ ๊ฑฐ๋ฆฌ(์ ์ฌ๋)๋ฅผ ๊ธฐ์ค์ผ๋ก ์ ๋ ฌ | |
| all_results = sorted(all_results, key=lambda x: x['distance']) | |
| return all_results[:limit] | |
| # ์ฌ์ฉ ์์ | |
| if __name__ == "__main__": | |
| vector_db = KCSCVectorDB() | |
| # ๋ฒกํฐ DB ๊ตฌ์ถ | |
| vector_db.build_index() | |
| # ๊ฒ์ ํ ์คํธ | |
| results = vector_db.search("์ฒ ๊ทผ์ฝํฌ๋ฆฌํธ ๊ธฐ๋ฅ์ ์ค๊ณ ๋ฐฉ๋ฒ") | |
| for i, result in enumerate(results): | |
| print(f"\n--- ๊ฒฐ๊ณผ {i+1} ---") | |
| print(f"๋ฌธ์: {result['metadata']['name']} ({result['metadata']['code']})") | |
| print(f"์ ์ฌ๋: {1 - result['distance']:.4f}") | |
| print(f"๋ด์ฉ ๋ฏธ๋ฆฌ๋ณด๊ธฐ: {result['text'][:200]}...") | |