Files
llm_wiki/raw/教育AI研究/tools/update_knowledge_graph.py
T
hehaiguang1123 a6f05ab2d5 Phase 0-2: Schema cleanup, typed relations, event-driven automation
- Phase 0: AGENTS.md cleanup (dedup quotes, renumber sections, merge qmd)
- Phase 1: typed relations (manage-relations.py, graph-search.py, check-staleness.py, detect-conflicts.py)
- Phase 2: frontmatter validator, weekly lint, knowledge promotion, git hooks
- Fix .gitignore to track tools/ and .githooks/
- Fix git remote URL (remove plaintext token)
- New wiki pages: 504 pages, 34 raw sources
2026-07-01 08:05:43 +08:00

324 lines
11 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import io
import json
import datetime
from pathlib import Path
from typing import Dict, List, Any, Optional
import random
if sys.platform == "win32":
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
class KnowledgeGraphUpdater:
def __init__(self, canvas_path: Path, extracted_data_path: Path):
self.canvas_path = canvas_path
self.data_path = extracted_data_path
self.canvas_data = self.load_canvas()
self.extracted_data = self.load_extracted_data()
def load_canvas(self) -> Dict[str, Any]:
try:
with open(self.canvas_path, "r", encoding="utf-8") as f:
return json.load(f)
except Exception as e:
print(f"Error loading canvas: {str(e)}")
return {}
def load_extracted_data(self) -> Dict[str, Any]:
try:
with open(self.data_path, "r", encoding="utf-8") as f:
return json.load(f)
except Exception as e:
print(f"Error loading extracted data: {str(e)}")
return {}
def get_existing_institutions(self) -> set:
institutions = set()
for node in self.canvas_data.get("nodes", []):
if node.get("type") == "text" and "机构档案" in node.get("id", ""):
institutions.add(node["id"])
return institutions
def generate_node_id(self, prefix: str, name: str) -> str:
safe_name = name.replace(" ", "_").replace("/", "_").replace("\\", "_")[:20]
return (
f"{prefix}_{safe_name}_{datetime.datetime.now().strftime('%Y%m%d%H%M%S')}"
)
def create_institution_node(
self, inst: Dict[str, Any], x: int, y: int
) -> Dict[str, Any]:
inst_name = inst.get("name", "Unknown")
inst_type = inst.get("type", "Unknown")
quality_score = inst.get("quality_score", 0)
key_data = inst.get("key_data", "")
node_id = self.generate_node_id("node", inst_name)
node = {
"id": node_id,
"type": "text",
"x": x + random.randint(-50, 50),
"y": y + random.randint(-50, 50),
"width": 300,
"height": 120,
"color": "1" if quality_score >= 85 else "2",
"text": f"## {inst_name}\n**类型**: {inst_type}\n**质量分**: {quality_score:.1f}\n**核心数据**: {key_data[:50]}...",
}
return node_id, node
def create_edge(
self, source_id: str, target_id: str, label: str = ""
) -> Dict[str, Any]:
edge = {
"id": f"edge_{source_id}_{target_id}",
"from": source_id,
"to": target_id,
"label": label,
}
return edge
def add_new_institutions(self):
new_nodes = []
new_edges = []
institutions = self.extracted_data.get("reports", [])
existing_institutions = self.get_existing_institutions()
group_positions = {
"教师工具": {"x": 1500, "y": -850, "group": "group_edtech000001"},
"协作辅导": {"x": 1850, "y": -750, "group": "group_edtech000001"},
"K-12平台": {"x": 1500, "y": -650, "group": "group_edtech000001"},
}
for report in institutions:
for inst in report.get("institutions", []):
inst_name = inst.get("name", "")
inst_type = inst.get("type", "")
if inst_name and inst_name not in existing_institutions:
position = group_positions.get(
inst_type, {"x": 1200, "y": -500, "group": "group_reports00001"}
)
node_id, node = self.create_institution_node(
inst, position["x"], position["y"]
)
new_nodes.append(node)
existing_institutions.add(inst_name)
if position.get("group"):
edge = self.create_edge(
position["group"], node_id, f"属于{inst_type}"
)
new_edges.append(edge)
if new_nodes:
self.canvas_data["nodes"].extend(new_nodes)
print(f"✅ Added {len(new_nodes)} new institution nodes")
if new_edges:
existing_edges = self.canvas_data.get("edges", [])
existing_edges.extend(new_edges)
self.canvas_data["edges"] = existing_edges
print(f"✅ Added {len(new_edges)} new edges")
return len(new_nodes), len(new_edges)
def update_metadata(self):
today = datetime.date.today()
week_number = today.isocalendar()[1]
total_institutions = sum(
len(report.get("institutions", []))
for report in self.extracted_data.get("reports", [])
)
for node in self.canvas_data.get("nodes", []):
if node.get("id") == "title001canvasv12":
node["text"] = (
f"# 全球教育AI知识图谱\n更新日期:{today} | W{week_number} | 机构总计:{total_institutions}"
)
print(f"✅ Updated metadata in title node")
break
def add_research_report_nodes(self):
new_nodes = []
reports = self.extracted_data.get("reports", [])
existing_reports = set()
for node in self.canvas_data.get("nodes", []):
if node.get("type") == "text" and "研究计划" in node.get("text", ""):
existing_reports.add(node["id"])
report_count = 0
base_y = -300
base_x = 1600
for report in reports:
theme = report.get("theme", "")
week_num = report.get("week_number", "")
if theme and f"W{week_num}" not in existing_reports and report_count < 5:
node_id = f"report_W{week_num}"
y_pos = base_y + (report_count * 150)
node = {
"id": node_id,
"type": "text",
"x": base_x + random.randint(-50, 50),
"y": y_pos,
"width": 400,
"height": 100,
"color": "6",
"text": f"**W{week_num}**: {theme[:40]}...",
}
new_nodes.append(node)
existing_reports.add(f"W{week_num}")
report_count += 1
if new_nodes:
self.canvas_data["nodes"].extend(new_nodes)
print(f"✅ Added {len(new_nodes)} research report nodes")
reports_group_id = "group_reports00001"
for node_id in [node["id"] for node in new_nodes]:
edge = self.create_edge(reports_group_id, node_id, "研究")
existing_edges = self.canvas_data.get("edges", [])
existing_edges.append(edge)
self.canvas_data["edges"] = existing_edges
print(f"✅ Added {len(new_nodes)} edges to research reports group")
return len(new_nodes)
def save_updated_canvas(self):
output_path = (
self.canvas_path.parent
/ f"全球教育AI机构关系图谱_v{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.canvas"
)
with open(output_path, "w", encoding="utf-8") as f:
json.dump(self.canvas_data, f, ensure_ascii=False, indent=2)
print(f"✅ Updated canvas saved to: {output_path}")
return output_path
def generate_update_report(self) -> str:
update_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
report = f"""# 知识图谱更新报告
## 基本信息
- **更新时间**: {update_time}
- **源文件**: {self.canvas_path.name}
- **数据来源**: {self.data_path.name}
- **新版本**: v{datetime.datetime.now().strftime("%Y%m%d")}
## 更新内容
### 新增节点
- **机构档案节点**: 基于每周报告提取的新机构
- **研究报告节点**: 近期研究计划/报告主题
### 更新的元数据
- **标题节点**: 更新日期和机构总数
- **版本号**: 自动递增
## 图谱统计
- **总节点数**: {len(self.canvas_data.get("nodes", []))}
- **总边数**: {len(self.canvas_data.get("edges", []))}
- **分组数**: {len([n for n in self.canvas_data.get("nodes", []) if n.get("type") == "group"])}
## 下一步建议
1. 手动检查节点布局,调整重叠
2. 添加更多关系边以增强连接性
3. 定期更新以保持图谱时效性
---
*更新工具: Knowledge Graph Updater*
*自动生成时间: {update_time}*
"""
return report
def save_update_report(self, report_path: Path):
report_content = self.generate_update_report()
report_path.parent.mkdir(parents=True, exist_ok=True)
with open(report_path, "w", encoding="utf-8") as f:
f.write(report_content)
print(f"✅ Update report saved to: {report_path}")
def main():
print("=" * 60)
print("Knowledge Graph Visualization Update Tool")
print("=" * 60)
print(f"Execution time: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print()
project_dir = Path(__file__).parent.parent
canvas_path = project_dir / "全球教育AI机构关系图谱.canvas"
extracted_data_path = (
project_dir / "outputs" / "weekly_reports_extraction_2026-04-16.json"
)
if not canvas_path.exists():
print(f"❌ Canvas file not found: {canvas_path}")
return
if not extracted_data_path.exists():
print(f"❌ Extracted data file not found: {extracted_data_path}")
print("Please run extract_weekly_reports.py first")
return
updater = KnowledgeGraphUpdater(canvas_path, extracted_data_path)
print("Step 1: Adding new institution nodes...")
new_insts, new_edges = updater.add_new_institutions()
print("\nStep 2: Adding research report nodes...")
new_reports = updater.add_research_report_nodes()
print("\nStep 3: Updating metadata...")
updater.update_metadata()
print("\nStep 4: Saving updated canvas...")
output_canvas = updater.save_updated_canvas()
print("\nStep 5: Generating update report...")
report_path = (
output_canvas.parent
/ "outputs"
/ f"knowledge_graph_update_report_{datetime.date.today()}.md"
)
updater.save_update_report(report_path)
print()
print("=" * 60)
print("Update Complete")
print("=" * 60)
print(f"New institutions: {new_insts}")
print(f"New edges: {new_edges}")
print(f"New research reports: {new_reports}")
print(f"Output canvas: {output_canvas.name}")
print(f"Update report: {report_path.name}")
print("=" * 60)
if __name__ == "__main__":
main()