#!/usr/bin/env python3 """ Script: update_gov_notebooklm.py Cập nhật tóm tắt cho các nguồn GOV_ VN còn lại bằng real IDs từ NotebookLM. Chỉ chạy cho các nguồn GOV_ chưa được cập nhật. """ import subprocess, json, re, time NLM_BIN = "/opt/ai-os/products/ceo/integrations/notebooklm-mcp-cli/.venv/bin/nlm" NOTEBOOK_ID = "a0f8646b-90b1-4a65-bddc-07280aa06a99" MASTER_PATH = "/opt/ai-os/products/ceo/content/research/domains/tourism/master_tourism_vn.md" # Real GOV IDs từ NotebookLM (đã verify) GOV_REAL = [ ("a7a7b1be-8ddf-4e8f-a7ec-1b9fc70cd35f", "GOV_2023_82-NQ-CP"), ("3cd8373e-407a-4d08-8250-c070360a107c", "GOV_2023_81-QH15"), ("bc44aefa-d149-444e-a498-ccf0d42adf59", "GOV_2023_QD440_Marketing"), ("88a8e5c4-b5e6-47b6-8399-0c7f87b4dffd", "GOV_2024_509qd-ttg"), ("2e760930-9640-4704-bd61-449a8e59ddc5", "GOV_2024_Chi thi 08"), ("83f4e69c-4528-438c-aed3-bdac0d85d460", "GOV_2024_Quy hoach du lich"), ("9fd3b5cc-7f24-439d-a70c-0e1740c66082", "GOV_2024_1704"), ("ac03012e-4b2f-4e2f-8f63-1fa2e394d2ed", "GOV_2024_509-QD-TTg"), ("fb97a91e-5130-4f16-ae3a-d598cf7a86d1", "GOV_2024_57-NQ-TW"), ("3acf33a8-7b06-4a37-8fcd-16b9881c3c43", "GOV_2025_47-BC-KTNN"), ("68e5815b-3eb2-4cf6-9764-ba858834b93e", "GOV_2025_68-NQTW"), ("df7eeecf-0424-418f-b41d-e80ce367529a", "GOV_2025_252-QH15"), ("ff916eaf-5612-44bf-9f54-02a80a14187d", "GOV_2025_62-NQ-HDND"), ("03537aa1-7217-4b0e-871d-3d5a11c3ec2e", "GOV_2025_66-NQTW"), ("bd20af3e-93dd-49c6-a8c8-faa3f1a5da87", "GOV_2025_59-NQTW"), ("5ace7d69-7204-4959-b264-e381a4c4e682", "GOV_2025_123-VBHN"), ("12edefe7-fbb0-4c8d-9dbf-e04f7a948593", "GOV_2026_18-KL-TW"), ("1ff804a3-74e4-4932-83e0-d076308a1f4d", "GOV_2026_Quy hoach dieu chinh"), ("86a7aefe-d144-4fea-8be8-635fb1d6f745", "GOV_2026_QD374"), ("193d80c7-e166-4010-b3b7-728f098b7ad4", "GOV_2026_Danh gia chinh sach"), ] def refresh_auth(): subprocess.run(["python3", "scripts/notebooklm_refresh.py"], cwd="/opt/ai-os/products/ceo") def query_vn(source_id): cmd = [NLM_BIN, "notebook", "query", "--source-ids", source_id, "--timeout", "180", NOTEBOOK_ID, "Hãy tóm tắt chi tiết văn bản này theo từng chương/phần. Liệt kê các mục tiêu cụ thể, số liệu benchmark và giải pháp nếu có. Chỉ dựa trên nội dung tài liệu, KHÔNG bịa thêm thông tin. TUYỆT ĐỐI KHÔNG dùng dấu gạch nối trong văn xuôi tiếng Việt."] try: res = subprocess.run(cmd, capture_output=True, text=True, timeout=200) if res.returncode == 0: data = json.loads(res.stdout) return data.get("answer", "") if "Authentication" in res.stderr: refresh_auth() res = subprocess.run(cmd, capture_output=True, text=True, timeout=200) if res.returncode == 0: data = json.loads(res.stdout) return data.get("answer", "") except: pass return None def get_en_summary(source_id): cmd = [NLM_BIN, "source", "describe", "--json", source_id] res = subprocess.run(cmd, capture_output=True, text=True, timeout=60) if res.returncode == 0: return json.loads(res.stdout).get("summary", "") return "" def update_master(source_id, vn_summary, en_summary=""): with open(MASTER_PATH, 'r') as f: master = f.read() entries = list(re.finditer(r'(##? \d+\.\s*.*?)(?=\n##? \d+\.|\Z)', master, re.DOTALL)) target = next((e for e in entries if f"`{source_id}`" in e.group(0)), None) if not target: print(f" ❌ Source {source_id[:12]} NOT FOUND in master") return False etext = target.group(0) new_summary = f"- **Tóm tắt:** ** {en_summary}\n\n- **Tóm tắt (VN):** ** {vn_summary}" if en_summary else f"- **Tóm tắt (VN):** ** {vn_summary}" # Replace existing summary block from "**Tóm tắt:**" to next entry sum_match = re.search(r'(- \*\*Tóm tắt:\*\* \*\* .*?)(?=\n### \d+\.|\n## \d+\.|\Z)', etext, re.DOTALL) if sum_match: master = master.replace(sum_match.group(0), new_summary) else: master = master.replace(etext, etext.rstrip() + '\n' + new_summary + '\n') with open(MASTER_PATH, 'w') as f: f.write(master) return True print("=== UPDATE GOV SOURCES ===") already_updated = ["c7923829", "656da2d2", "107d6263", "d09889b7", "516f7148"] for idx, (sid, title) in enumerate(GOV_REAL, 6): if sid[:12] in already_updated: print(f"[{idx}] SKIP {title[:40]} (already updated)") continue print(f"[{idx}] {title[:50]}...", end=" ", flush=True) vn = query_vn(sid) if not vn: print("❌ Query failed") continue en = get_en_summary(sid) length = len(vn) if update_master(sid, vn.split("***")[0].strip(), en): print(f"✅ OK ({length} chars)") else: print("❌ Not found in master") time.sleep(4) print("=== DONE ===")