Spaces:
Running
Running
| import os | |
| import glob | |
| import torch | |
| from langchain_community.document_loaders import PyMuPDFLoader, UnstructuredWordDocumentLoader, TextLoader | |
| from langchain_text_splitters import RecursiveCharacterTextSplitter | |
| from langchain_huggingface import HuggingFaceEmbeddings | |
| # pyrefly: ignore [missing-import] | |
| from langchain_chroma import Chroma | |
| import warnings | |
| import pickle | |
| from langchain_community.retrievers import BM25Retriever | |
| from tokenizer import vi_tokenizer | |
| warnings.filterwarnings('ignore') | |
| def clean_vietnamese_text(text: str) -> str: | |
| if not text: | |
| return text | |
| text = text.replace('ƣ', 'ư').replace('Ƣ', 'Ư') | |
| return text | |
| def load_documents(data_dir): | |
| documents = [] | |
| md_files = glob.glob(os.path.join(data_dir, "*.md")) | |
| if md_files: | |
| print(f" -> Tìm thấy {len(md_files)} file Markdown (.md). Tiến hành nạp dữ liệu từ các file này...") | |
| for md_file in md_files: | |
| print(f" => Đang đọc file Markdown: {os.path.basename(md_file)}...") | |
| try: | |
| loader = TextLoader(md_file, encoding='utf-8') | |
| documents.extend(loader.load()) | |
| except Exception as e: | |
| print(f"Lỗi khi đọc file {md_file}: {e}") | |
| return documents | |
| pdf_files = glob.glob(os.path.join(data_dir, "*.pdf")) | |
| for pdf_file in pdf_files: | |
| print(f" -> Đang đọc file PDF: {os.path.basename(pdf_file)}...") | |
| try: | |
| loader = PyMuPDFLoader(pdf_file) | |
| loaded_docs = loader.load() | |
| for doc in loaded_docs: | |
| doc.page_content = clean_vietnamese_text(doc.page_content) | |
| documents.extend(loaded_docs) | |
| except Exception as e: | |
| print(f"Lỗi khi đọc file {pdf_file}: {e}") | |
| word_files = glob.glob(os.path.join(data_dir, "*.doc")) + glob.glob(os.path.join(data_dir, "*.docx")) | |
| for word_file in word_files: | |
| print(f" -> Đang đọc file Word: {os.path.basename(word_file)}...") | |
| try: | |
| loader = UnstructuredWordDocumentLoader(word_file) | |
| documents.extend(loader.load()) | |
| except Exception as e: | |
| print(f"Lỗi khi đọc file {word_file}: {e}") | |
| return documents | |
| def build_vector_db(): | |
| current_dir = os.path.dirname(os.path.abspath(__file__)) | |
| project_root = os.path.dirname(os.path.dirname(current_dir)) | |
| data_dir = os.path.join(project_root, "data_RAG") | |
| persist_dir = os.path.join(project_root, "src", "rag", "chroma_db") | |
| if not os.path.exists(data_dir): | |
| print(f"Lỗi: Không tìm thấy thư mục {data_dir}") | |
| return | |
| print("BƯỚC 1: Đang đọc tài liệu từ thư mục...") | |
| docs = load_documents(data_dir) | |
| if not docs: | |
| print("Không có tài liệu nào được đọc thành công. Vui lòng kiểm tra lại data_RAG.") | |
| return | |
| print(f" => Tổng số trang/đoạn tài liệu gốc thu được: {len(docs)}") | |
| print("\nBƯỚC 2: Phân mảnh tài liệu (Chunking)...") | |
| MARKDOWN_SEPARATORS = [ | |
| "\n#{1,6} ", | |
| "```\n", | |
| "\n\\*\\*\\*+\n", | |
| "\n---+\n", | |
| "\n___+\n", | |
| "\n\n", | |
| "\n", | |
| " ", | |
| "" | |
| ] | |
| text_splitter = RecursiveCharacterTextSplitter( | |
| chunk_size=1000, | |
| chunk_overlap=200, | |
| separators=MARKDOWN_SEPARATORS, | |
| is_separator_regex=True | |
| ) | |
| chunks = text_splitter.split_documents(docs) | |
| print(f" => Tổng số chunks tạo ra: {len(chunks)}") | |
| print("\nBƯỚC 3: Tạo chỉ mục Keyword Search (BM25)...") | |
| bm25_retriever = BM25Retriever.from_documents(chunks, preprocess_func=vi_tokenizer) | |
| os.makedirs(persist_dir, exist_ok=True) | |
| bm25_path = os.path.join(persist_dir, "bm25_retriever.pkl") | |
| with open(bm25_path, 'wb') as f: | |
| pickle.dump(bm25_retriever, f) | |
| print(f" => Đã lưu BM25 Index tại: {bm25_path}") | |
| print("\nBƯỚC 4: Tải Embedding Model (keepitreal/vietnamese-sbert)...") | |
| device = 'cuda' if torch.cuda.is_available() else 'cpu' | |
| print(f" => Đang sử dụng thiết bị: {device.upper()}") | |
| embeddings = HuggingFaceEmbeddings( | |
| model_name="keepitreal/vietnamese-sbert", | |
| model_kwargs={'device': device} | |
| ) | |
| print("\nBƯỚC 5: Tính toán Vector và lưu vào ChromaDB (Có thể mất vài phút)...") | |
| vector_db = Chroma.from_documents( | |
| documents=chunks, | |
| embedding=embeddings, | |
| persist_directory=persist_dir | |
| ) | |
| print(f"\n THÀNH CÔNG! Vector Database đã được lưu thành công tại: {persist_dir}") | |
| if __name__ == "__main__": | |
| build_vector_db() | |