#!/usr/bin/env python3 """ Script sửa lỗi: cập nhật tóm tắt 28 nguồn GOV_ trong master_tourism_vn.md Sử dụng ID field để định vị entry, load LOCAL9R_KEY từ .env """ import json, os, re, subprocess, sys, urllib.request, time from pathlib import Path NLM_BIN = "/opt/ai-os/products/ceo/integrations/notebooklm-mcp-cli/.venv/bin/nlm" MASTER_FILE = "/opt/ai-os/products/ceo/content/research/domains/tourism/master_tourism_vn.md" LOCAL9R_URL = "http://127.0.0.1:20128/v1/chat/completions" NOTEBOOK_ID = "a0f8646b-90b1-4a65-bddc-07280aa06a99" # Load LOCAL9R_KEY từ .env env_path = "/root/.hermes/.env" key = "" with open(env_path) as f: for line in f: if line.startswith("LOCAL9R_KEY="): key = line.split("=", 1)[1].strip() break LOCAL9R_KEY = key if not LOCAL9R_KEY: print("ERROR: LOCAL9R_KEY not found in /root/.hermes/.env") sys.exit(1) print(f"LOCAL9R_KEY loaded: {len(LOCAL9R_KEY)} chars") # 28 GOV sources verified from NotebookLM source list GOV_SOURCES = [ ("c7923829-7313-43db-9aa1-2db1b631c17e", "GOV_2017_Nghị quyết số 08-NQTW phát triển du lịch trở thành ngành kinh tế mũi nhọn 2017.md"), ("656da2d2-2f72-4384-84b3-4ceee8e53543", "GOV_2019_Quyết định 147QĐ-TTg 2020 phê duyệt Chiến lược phát triển du lịch Việt Nam đến năm 2030.md"), ("107d6263-a22a-4461-a72c-b8796f9c20e4", "GOV_2020_Luật Đầu tư theo phương thức đối tác công tư 2020 số 64/2020/QH14 áp dụng năm 2024 mới nhất"), ("d09889b7-cf01-40f9-88d9-caaae92e9c2f", "GOV_2020_Phê duyệt Chiến lược phát triển du lịch Việt Nam đến năm 2030"), ("516f7148-436d-4b20-84ca-ae24d4b5959b", "GOV_2020_Quyết định 147/QĐ-TTg chiến lược phát triển du lịch Việt Nam đến năm 2030"), ("e71e6594-a92e-42e4-899d-470dff3e2ec6", "GOV_2021_Quyết định 1658/QĐ-TTg 2021 Chiến lược quốc gia về tăng trưởng xanh giai đoạn 2021-2030, tầm nhìn 2050"), ("18528a87-bb0e-4277-a67f-24571c5cc70e", "GOV_2021_Thông tư 11/2021/TT-BVHTTDL Hệ thống chỉ tiêu thống kê ngành văn hóa thể thao và du lịch mới nhất"), ("0aa0c6a5-f1e1-4fba-8fa2-a4dfb59032f2", "GOV_2022_Quyết định 754/QĐ-UBND TP.HCM 2022 phân bổ chi tiết kế hoạch đầu tư công"), ("a7a7b1be-8ddf-4e8f-a7ec-1b9fc70cd35f", "GOV_2023_Nghị quyết 82_NQ-CP 2023 giải pháp đẩy nhanh phục hồi tăng tốc phát triển du lịch hiệu quả bền vững.md"), ("3cd8373e-407a-4d08-8250-c070360a107c", "GOV_2023_Nghị quyết 81/2023/QH15 Quy hoạch tổng thể quốc gia thời kỳ 2021 2030"), ("d669d629-188d-4c5f-be79-4200c68d7613", "GOV_2023_QD440_Phê duyệt Chiến lược Marketing du lịch Việt Nam đến 2030"), ("88a8e5c4-b5e6-47b6-8399-0c7f87b4dffd", "GOV_2024_509qd-ttg-Quy hoạch hệ thống du lịch thời kỳ 2021-2030, tầm nhìn đến năm 2045.pdf"), ("2e760930-9640-4704-bd61-449a8e59ddc5", "GOV_2024_Chỉ thị 08/CT-TTg năm 2024 phát triển du lịch toàn diện, nhanh và bền vững thời gian tới do Thủ tướng Chính phủ ban hành"), ("83f4e69c-4528-438c-aed3-bdac0d85d460", "GOV_2024_Quy hoạch hệ thống du lịch thời kỳ 2021-2030, tầm nhìn đến năm 2045..pdf"), ("9fd3b5cc-7f24-439d-a70c-0e1740c66082", "GOV_2024_Quyết định 1704/QĐ-BKHĐT 2024 Phương án Điều tra chi tiêu khách du lịch đến Việt Nam"), ("ac03012e-4b2f-4e2f-8f63-1fa2e394d2ed", "GOV_2024_Quyết định 509/QÐ-TTg năm 2024 phê duyệt Quy hoạch hệ thống du lịch thời kỳ 2021-2030, tầm nhìn đến năm 2045 do Thủ tướng Chính phủ ban hành"), ("fb97a91e-5130-4f16-ae3a-d598cf7a86d1", "GOV_2024_TOÀN VĂN: Nghị quyết 57-NQ/TW về đột phá phát triển khoa học, công nghệ, đổi mới sáng tạo và chuyển đổi số quốc gia_Mục tiêu 2045"), ("3acf33a8-7b06-4a37-8fcd-16b9881c3c43", "GOV_2025_47 BC-KTNN.pdf"), ("68e5815b-3eb2-4cf6-9764-ba858834b93e", "GOV_2025_Nghị quyết 68-NQTW 2025 về phát triển kinh tế tư nhân_Mục tiêu 2045.md"), ("df7eeecf-0424-418f-b41d-e80ce367529a", "GOV_2025_Nghị quyết 252/2025/QH15 sửa đổi Nghị quyết 81/2023/QH15 Quy hoạch tổng thể quốc gia 2021 2030"), ("ff916eaf-5612-44bf-9f54-02a80a14187d", "GOV_2025_Nghị quyết 62/2025/NQ-HĐND thu hút đoàn khách du lịch tham dự hội nghị Hồ Chí Minh"), ("03537aa1-7217-4b0e-871d-3d5a11c3ec2e", "GOV_2025_Nghị quyết 66-NQ/TW về đổi mới công tác xây dựng và thi hành pháp luật_Mục tiêu 2045"), ("bd20af3e-93dd-49c6-a8c8-faa3f1a5da87", "GOV_2025_Nghị quyết số 59-NQ/TW của Bộ Chính trị khóa XIII: Bước phát triển mới trong tư duy của Đảng về đối ngoại và hội nhập quốc tế - Tạp chí Cộng sản_Mục tiêu 2045"), ("5ace7d69-7204-4959-b264-e381a4c4e682", "GOV_2025_Văn bản hợp nhất 123/VBHN-VPQH 2025 Luật Đầu tư theo phương thức đối tác công tư"), ("12edefe7-fbb0-4c8d-9dbf-e04f7a948593", "GOV_2026_18-KL/TW_Thúc đẩy du lịch quốc tế gắn với các dịch vụ chăm sóc sức khoẻ, mua sắm hàng hoá"), ("1ff804a3-74e4-4932-83e0-d076308a1f4d", "GOV_2026_Phê duyệt điều chỉnh Quy hoạch hệ thống du lịch thời kỳ 2021-2030, tầm nhìn đến năm 2045"), ("86a7aefe-d144-4fea-8be8-635fb1d6f745", "GOV_2026_QĐ374-dc_qhhtdl.pdf"), ("193d80c7-e166-4010-b3b7-728f098b7ad4", "GOV_2026_Đánh giá việc thực hiện chính sách, pháp luật lĩnh vực văn hóa, thể thao và du lịch"), ] def get_nlm_content(source_id, retries=2): """Lấy nội dung từ NotebookLM với retry.""" for attempt in range(1 + retries): try: result = subprocess.run( [NLM_BIN, "content", "source", source_id], capture_output=True, text=True, timeout=120, cwd="/opt/ai-os/products/ceo", ) if result.returncode != 0: # Nếu lỗi auth thì refresh if "Authentication" in result.stderr: print(f" ⚠️ Auth error, running refresh...") subprocess.run(["python3", "scripts/notebooklm_refresh.py"], capture_output=True, timeout=30, cwd="/opt/ai-os/products/ceo") continue print(f" ERROR nlm attempt {attempt+1}: {result.stderr[:150]}") continue try: data = json.loads(result.stdout) return data.get("content", "") except json.JSONDecodeError: if len(result.stdout.strip()) > 100: return result.stdout continue except subprocess.TimeoutExpired: print(f" TIMEOUT source {source_id}, attempt {attempt+1}") continue except Exception as e: print(f" ERROR attempt {attempt+1}: {e}") continue return None def call_9router(content_text, source_title): """Gọi 9router local để tóm tắt.""" prompt = f"""You are a strict RAG summarizer. Summarize the following Vietnamese government policy document on tourism. SOURCE TITLE: {source_title} RULES: 1. ONLY summarize what is actually stated. NEVER fabricate. 2. If the document does NOT mention bottlenecks or challenges, do NOT invent them. 3. Focus on: policy orientation, measurable targets, responsible agencies, key measures, explicitly mentioned bottlenecks. 4. Output format: first EN summary then VN summary. 5. For VN: ABSOLUTELY NO DASHES in prose. Use asterisks or numbers for lists. 6. 3-6 sentences each. Third person. Precise and content-focused. DOCUMENT CONTENT: {content_text[:12000]} OUTPUT exactly (no extra text): - **Tóm tắt:** ** [English summary, strict RAG, 3-6 sentences] - **Tóm tắt (VN):** ** [Vietnamese summary, strict RAG, 3-6 sentences, NO DASHES in prose]""" payload = { "model": "default", "messages": [{"role": "user", "content": prompt}], "max_tokens": 1024, "temperature": 0.1, "stream": False, } headers = { "Content-Type": "application/json", "Authorization": f"Bearer {LOCAL9R_KEY}", } try: req = urllib.request.Request(LOCAL9R_URL, data=json.dumps(payload).encode(), headers=headers, method="POST") with urllib.request.urlopen(req, timeout=180) as resp: result = json.loads(resp.read()) return result["choices"][0]["message"]["content"] except Exception as e: print(f" ERROR 9router: {e}") return None def update_entry_by_id(source_id, new_summary_block): """Thay thế phần tóm tắt của entry dựa trên ID field.""" path = Path(MASTER_FILE) content = path.read_text(encoding='utf-8') # Tìm entry chứa ID này # Pattern: từ dòng có ID cho đến khi gặp "### ." hoặc hết file pattern = rf'(##? \d+\.\s*.*?)(?=\n##? \d+\.|\Z)' entries = list(re.finditer(pattern, content, re.DOTALL)) target_entry = None for e in entries: if f"`{source_id}`" in e.group(0): target_entry = e break if not target_entry: print(f" ❌ Entry with ID {source_id[:20]}... NOT FOUND in master file") return False entry_text = target_entry.group(0) # Tìm vị trí của - **Tóm tắt:** ** và thay thế đến hết - **Tóm tắt (VN):** **... summary_pattern = re.compile( r'(- \*\*Tóm tắt:\*\* \*\* .*?)(?=\n### \d+\.|\n## \d+\.|\Z)', re.DOTALL ) sum_match = summary_pattern.search(entry_text) if not sum_match: # Không tìm thấy summary cũ (gốc hoặc bị xóa), append vào cuối entry print(f" ⚠️ No Tóm tắt found in entry, appending") new_entry = entry_text.rstrip('\n') + '\n' + new_summary_block + '\n' else: old_summary = sum_match.group(0) new_entry = entry_text.replace(old_summary, new_summary_block.rstrip('\n')) content = content.replace(entry_text, new_entry, 1) if content == path.read_text(encoding='utf-8'): print(f" ⚠️ No changes detected after replacement") return False path.write_text(content, encoding='utf-8') return True def main(): print(f"=== 🔨 Cập nhật {len(GOV_SOURCES)} nguồn GOV_ ===\n") success = fail = skip = 0 for idx, (source_id, title) in enumerate(GOV_SOURCES, 1): display = title[:65] print(f"[{idx:2d}/{len(GOV_SOURCES)}] {display}...") # Bước 1: Fetch nội dung từ NotebookLM print(" 📥 Fetching from NotebookLM...") raw = get_nlm_content(source_id) if not raw or len(raw.strip()) < 50: print(f" ⏭️ SKIP: Content empty for {source_id[:20]}") skip += 1 continue print(f" 📄 Content: {len(raw)} chars") # Bước 2: Tóm tắt qua 9router print(" 🧠 Calling 9router...") summary = call_9router(raw, title) if not summary: print(f" ❌ FAIL: 9router error for source {idx}") fail += 1 continue # Validate định dạng if "- **Tóm tắt:**" not in summary or "- **Tóm tắt (VN):**" not in summary: print(f" ❌ FAIL: Invalid format. Response: {summary[:200]}") fail += 1 continue # Bước 3: Update master file theo ID print(" ✏️ Updating master file...") ok = update_entry_by_id(source_id, summary.strip()) if ok: success += 1 print(f" ✅ Updated") else: fail += 1 print(f" ❌ FAIL to update in master file") if idx < len(GOV_SOURCES) and idx % 4 == 0: print(" --- Chờ 2s ---") time.sleep(2) print(f"\n=== 🏁 Kết thúc: {success} thành công, {fail} thất bại, {skip} bỏ qua ===") if __name__ == "__main__": main()