File size: 6,638 Bytes
b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d bf7ec12 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d b91b0a5 c429a2d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
"""Script build ChromaDB từ markdown files với incremental update."""
import sys
import argparse
from pathlib import Path
from dotenv import find_dotenv, load_dotenv
load_dotenv(find_dotenv(usecwd=True))
REPO_ROOT = Path(__file__).resolve().parents[1]
if str(REPO_ROOT) not in sys.path:
sys.path.insert(0, str(REPO_ROOT))
from core.rag.chunk import chunk_markdown_file
from core.rag.embedding_model import EmbeddingConfig, QwenEmbeddings
from core.rag.vector_store import ChromaConfig, ChromaVectorDB
from core.hash_file.hash_file import HashProcessor
_hasher = HashProcessor(verbose=False)
def get_db_file_info(db: ChromaVectorDB) -> dict:
"""Lấy thông tin files đã có trong DB (IDs và hash)."""
docs = db.get_all_documents()
file_to_ids = {}
file_to_hash = {}
for d in docs:
meta = d.get("metadata", {})
source = meta.get("source_basename") or meta.get("source_file")
doc_id = d.get("id")
content_hash = meta.get("content_hash", "")
if source and doc_id:
if source not in file_to_ids:
file_to_ids[source] = set()
file_to_ids[source].add(doc_id)
# Lưu hash đầu tiên tìm thấy cho file
if source not in file_to_hash and content_hash:
file_to_hash[source] = content_hash
return {"ids": file_to_ids, "hashes": file_to_hash}
def main():
parser = argparse.ArgumentParser(description="Build ChromaDB từ markdown files")
parser.add_argument("--force", action="store_true", help="Build lại tất cả files")
parser.add_argument("--no-delete", action="store_true", help="Không xóa docs orphaned")
args = parser.parse_args()
print("=" * 60)
print("BUILD HUST RAG DATABASE")
print("=" * 60)
# Bước 1: Khởi tạo embedder
print("\n[1/5] Khởi tạo embedder...")
emb_cfg = EmbeddingConfig()
emb = QwenEmbeddings(emb_cfg)
print(f" Model: {emb_cfg.model}")
print(f" API: {emb_cfg.api_base_url}")
# Bước 2: Khởi tạo ChromaDB
print("\n[2/5] Khởi tạo ChromaDB...")
db_cfg = ChromaConfig()
db = ChromaVectorDB(embedder=emb, config=db_cfg)
old_count = db.count()
print(f" Collection: {db_cfg.collection_name}")
print(f" Số docs hiện tại: {old_count}")
# Lấy trạng thái hiện tại của DB
db_info = {"ids": {}, "hashes": {}}
if not args.force and old_count > 0:
print("\n Đang quét documents trong DB...")
db_info = get_db_file_info(db)
print(f" Tìm thấy {len(db_info['ids'])} source files trong DB")
# Bước 3: Quét markdown files
print("\n[3/5] Quét markdown files...")
root = REPO_ROOT / "data" / "data_process"
md_files = sorted(root.rglob("*.md"))
print(f" Tìm thấy {len(md_files)} markdown files trên disk")
# So sánh files trên disk vs trong DB
current_files = {f.name for f in md_files}
db_files = set(db_info["ids"].keys())
# Tìm files cần xóa (có trong DB nhưng không có trên disk)
files_to_delete = db_files - current_files
# Bước 4: Xóa docs orphaned
deleted_count = 0
if files_to_delete and not args.no_delete:
print(f"\n[4/5] Dọn dẹp {len(files_to_delete)} files đã xóa...")
for filename in files_to_delete:
doc_ids = list(db_info["ids"].get(filename, []))
if doc_ids:
db.delete_documents(doc_ids)
deleted_count += len(doc_ids)
print(f" Đã xóa: {filename} ({len(doc_ids)} chunks)")
else:
print("\n[4/5] Không có files cần xóa")
# Bước 5: Xử lý markdown files (thêm mới, cập nhật)
print("\n[5/5] Xử lý markdown files...")
total_added = 0
total_updated = 0
skipped = 0
for i, f in enumerate(md_files, 1):
file_hash = _hasher.get_file_hash(str(f))
db_hash = db_info["hashes"].get(f.name, "")
existing_ids = db_info["ids"].get(f.name, set())
# Bỏ qua nếu hash khớp (file không thay đổi)
if not args.force and db_hash == file_hash:
print(f" [{i}/{len(md_files)}] {f.name}: BỎ QUA (không đổi)")
skipped += 1
continue
# Nếu file thay đổi, xóa chunks cũ trước
if existing_ids and not args.force:
db.delete_documents(list(existing_ids))
print(f" [{i}/{len(md_files)}] {f.name}: CẬP NHẬT (xóa {len(existing_ids)} chunks cũ)")
is_update = True
else:
is_update = False
try:
docs = chunk_markdown_file(f)
if docs:
# Thêm hash vào metadata để phát hiện thay đổi lần sau
for doc in docs:
if hasattr(doc, 'metadata'):
doc.metadata["content_hash"] = file_hash
elif isinstance(doc, dict) and "metadata" in doc:
doc["metadata"]["content_hash"] = file_hash
n = db.upsert_documents(docs)
if is_update:
total_updated += n
print(f" [{i}/{len(md_files)}] {f.name}: +{n} chunks mới")
else:
total_added += n
print(f" [{i}/{len(md_files)}] {f.name}: {n} chunks")
else:
print(f" [{i}/{len(md_files)}] {f.name}: BỎ QUA (không có chunks)")
except Exception as e:
print(f" [{i}/{len(md_files)}] {f.name}: LỖI - {e}")
# Tổng kết
new_count = db.count()
has_changes = deleted_count > 0 or total_updated > 0 or total_added > 0
# Xóa BM25 cache nếu có thay đổi (vì BM25 không hỗ trợ incremental update)
if has_changes:
bm25_cache = REPO_ROOT / "data" / "chroma" / "bm25_cache.pkl"
if bm25_cache.exists():
bm25_cache.unlink()
print("\n[!] Đã xóa BM25 cache (sẽ tự rebuild khi query)")
print(f"\n{'=' * 60}")
print("TỔNG KẾT")
print("=" * 60)
print(f" Đã xóa (orphaned): {deleted_count} chunks")
print(f" Đã cập nhật: {total_updated} chunks")
print(f" Đã thêm mới: {total_added} chunks")
print(f" Đã bỏ qua: {skipped} files")
print(f" Số docs trong DB: {old_count} -> {new_count} ({new_count - old_count:+d})")
print("\nHOÀN TẤT!")
if __name__ == "__main__":
main() |