"""
灵感点分析结果可视化脚本
读取 how/灵感点 目录下的分析结果,结合作者历史帖子详情,生成可视化HTML页面
"""
import json
from pathlib import Path
from typing import Dict, Any, List, Optional
from datetime import datetime
import html as html_module
def load_inspiration_points_data(inspiration_dir: str) -> List[Dict[str, Any]]:
"""
加载所有灵感点的分析结果(包含 step1 和 step3)
Args:
inspiration_dir: 灵感点目录路径
Returns:
灵感点分析结果列表
"""
inspiration_path = Path(inspiration_dir)
results = []
# 遍历所有子目录
for subdir in inspiration_path.iterdir():
if subdir.is_dir():
# 查找 step1 文件
step1_files = list(subdir.glob("all_step1_*.json"))
# 查找 step3 文件
step3_files = list(subdir.glob("all_step3_*.json"))
if step1_files:
try:
# 读取 step1
with open(step1_files[0], 'r', encoding='utf-8') as f:
step1_data = json.load(f)
# 尝试读取 step3
step3_data = None
if step3_files:
try:
with open(step3_files[0], 'r', encoding='utf-8') as f:
step3_data = json.load(f)
except Exception as e:
print(f"警告: 读取 {step3_files[0]} 失败: {e}")
results.append({
"step1": step1_data,
"step3": step3_data,
"inspiration_name": subdir.name
})
except Exception as e:
print(f"警告: 读取 {step1_files[0]} 失败: {e}")
return results
def load_posts_data(posts_dir: str) -> Dict[str, Dict[str, Any]]:
"""
加载所有帖子详情数据
Args:
posts_dir: 帖子目录路径
Returns:
帖子ID到帖子详情的映射
"""
posts_path = Path(posts_dir)
posts_map = {}
for post_file in posts_path.glob("*.json"):
try:
with open(post_file, 'r', encoding='utf-8') as f:
post_data = json.load(f)
post_id = post_data.get("channel_content_id")
if post_id:
posts_map[post_id] = post_data
except Exception as e:
print(f"警告: 读取 {post_file} 失败: {e}")
return posts_map
def generate_inspiration_card_html(inspiration_data: Dict[str, Any]) -> str:
"""
生成单个灵感点的卡片HTML
Args:
inspiration_data: 灵感点数据
Returns:
HTML字符串
"""
step1 = inspiration_data.get("step1", {})
inspiration_name = inspiration_data.get("inspiration_name", "未知灵感")
# 从step1中获取top1匹配分数
step1_matches = step1.get("匹配结果列表", []) if step1 else []
step1_score = 0
step1_match_element = ""
if step1_matches:
top_match = step1_matches[0]
match_result = top_match.get("匹配结果", {})
step1_score = match_result.get("score", 0)
input_info = top_match.get("输入信息", {})
step1_match_element = input_info.get("A_名称", "")
# 确定卡片颜色(基于Step1分数)
if step1_score >= 0.7:
border_color = "#10b981"
step1_color = "#10b981"
elif step1_score >= 0.5:
border_color = "#f59e0b"
step1_color = "#f59e0b"
elif step1_score >= 0.3:
border_color = "#3b82f6"
step1_color = "#3b82f6"
else:
border_color = "#ef4444"
step1_color = "#ef4444"
# 转义HTML
inspiration_name_escaped = html_module.escape(inspiration_name)
step1_match_element_escaped = html_module.escape(step1_match_element)
# 获取Step1匹配结果(简要展示Top3)
step1_match_preview = ""
if step1_matches:
preview_items = []
for idx, match in enumerate(step1_matches[:3]):
input_info = match.get("输入信息", {})
match_result = match.get("匹配结果", {})
element_name = input_info.get("A_名称", "")
match_score = match_result.get("score", 0)
# 确定排名标记
rank_emoji = ["🥇", "🥈", "🥉"][idx]
preview_items.append(f'''
{rank_emoji}
{html_module.escape(element_name)}
{match_score:.2f}
''')
step1_match_preview = f'''
'''
# 获取 Step3 生成的灵感点(简要展示)
step3_preview = ""
step3 = inspiration_data.get("step3")
if step3:
step3_inspirations = step3.get("灵感点列表", [])
if step3_inspirations:
preview_items = []
for idx, item in enumerate(step3_inspirations[:3]):
path = item.get("推理路径", "")
insp_point = item.get("灵感点", "")
preview_items.append(f'''
💡 {html_module.escape(insp_point)}
{html_module.escape(path)}
''')
step3_preview = f'''
'''
# 准备详细数据用于弹窗
detail_data_json = json.dumps(inspiration_data, ensure_ascii=False)
detail_data_json_escaped = html_module.escape(detail_data_json)
# 生成详细HTML并进行HTML转义(保留兼容性)
detail_html = generate_detail_html(inspiration_data)
detail_html_escaped = html_module.escape(detail_html)
# 生成 step1 和 step3 独立详情
step1_detail_html = generate_step1_detail_html(inspiration_data)
step1_detail_html_escaped = html_module.escape(step1_detail_html)
step3_detail_html = generate_step3_detail_html(inspiration_data)
step3_detail_html_escaped = html_module.escape(step3_detail_html)
html = f'''
Top1 分数
{step1_score:.3f}
🎯 匹配要素
{step1_match_element_escaped}
{step1_match_preview}
{step3_preview}
{'' if step3 else ''}
'''
return html
def generate_step1_detail_html(inspiration_data: Dict[str, Any]) -> str:
"""
生成 step1 匹配详情的HTML
Args:
inspiration_data: 灵感点数据
Returns:
step1 详细信息的HTML字符串
"""
import html as html_module
step1 = inspiration_data.get("step1", {})
inspiration_name = inspiration_data.get("inspiration_name", "未知灵感")
content = f'''
'''
# 获取元数据
metadata = step1.get("元数据", {})
# Step1 详细信息
if step1 and step1.get("灵感"):
inspiration = step1.get("灵感", "")
matches = step1.get("匹配结果列表", [])
content += f'''
🎯 灵感人设匹配分析
💡 灵感点内容:
{html_module.escape(inspiration)}
'''
# 显示匹配结果(显示Top5)
if matches:
content += f'''
匹配结果 (Top {min(len(matches), 5)}):
'''
for index, match in enumerate(matches[:5]):
input_info = match.get("输入信息", {})
match_result = match.get("匹配结果", {})
business_info = match.get("业务信息", {})
element_name = input_info.get("A_名称", "")
element_def = input_info.get("A_定义", "")
context_a = input_info.get("A_Context", "")
score = match_result.get("score", 0)
score_explain = match_result.get("score说明", "")
# 获取匹配关系
match_relations = match_result.get("匹配关系", [])
# 添加排名样式类
rank_class = ""
if index == 0:
rank_class = "top1"
elif index == 1:
rank_class = "top2"
elif index == 2:
rank_class = "top3"
content += f'''
'''
if element_def:
content += f'
📖 定义: {html_module.escape(element_def)}
'
if context_a:
content += f'
📍 所属: {html_module.escape(context_a)}
'
# 显示B语义分析、匹配关系、A语义分析(三列布局)
b_semantic = match_result.get("B语义分析", {})
a_semantic = match_result.get("A语义分析", {})
if b_semantic or a_semantic or match_relations:
content += '
'
# B语义分析
if b_semantic:
content += '''
'''
b_substance = b_semantic.get("实质", {})
b_form = b_semantic.get("形式", {})
if b_substance:
content += '
实质:
'
for key, value in b_substance.items():
content += f'''
{html_module.escape(key)}:
{html_module.escape(value)}
'''
if b_form:
content += '
形式:
'
for key, value in b_form.items():
content += f'''
{html_module.escape(key)}:
{html_module.escape(value)}
'''
content += '''
'''
# 显示匹配关系(中间)
if match_relations:
content += '''
'''
for idx, rel in enumerate(match_relations):
b_sem = rel.get("B语义", "")
a_sem = rel.get("A语义", "")
relation = rel.get("关系", "")
explanation = rel.get("说明", "")
distance_score = rel.get("距离分数", None)
# 构建中间关系文本
relation_middle = f'{html_module.escape(relation)}'
if distance_score is not None:
relation_middle += f' (分数: {distance_score:.2f})'
# 为每个关系项生成唯一ID,用于连接线定位
rel_id = f'rel_{index}_{idx}'
b_sem_id = f'b_sem_{index}_{idx}'
a_sem_id = f'a_sem_{index}_{idx}'
content += f'''
{relation_middle}
B: {html_module.escape(b_sem)}
→
A: {html_module.escape(a_sem)}
{html_module.escape(explanation)}
'''
content += '''
'''
# A语义分析
if a_semantic:
content += '''
'''
a_substance = a_semantic.get("实质", {})
a_form = a_semantic.get("形式", {})
if a_substance:
content += '
实质:
'
for key, value in a_substance.items():
content += f'''
{html_module.escape(key)}:
{html_module.escape(value)}
'''
if a_form:
content += '
形式:
'
for key, value in a_form.items():
content += f'''
{html_module.escape(key)}:
{html_module.escape(value)}
'''
content += '''
'''
content += '
' # end semantic-analysis-with-relations
# 显示分数详情(放在最后)
rule_score = match_result.get("规则分数", 0)
rule_score_explain = match_result.get("规则分数说明", "")
relevance_score = match_result.get("相关性分数", 0)
relevance_explain = match_result.get("相关性说明", "")
score_explain = match_result.get("score说明", "")
if rule_score or relevance_score or score_explain:
content += '
'
if rule_score or relevance_score:
content += '
'
if rule_score:
content += f'''
{f'
{html_module.escape(rule_score_explain)}
' if rule_score_explain else ''}
'''
if relevance_score:
content += f'''
{f'
{html_module.escape(relevance_explain)}
' if relevance_explain else ''}
'''
content += '
'
if score_explain:
content += f'
🎲 最终分数计算: {html_module.escape(score_explain)}
'
content += '
'
content += '''
'''
content += '''
'''
content += '''
'''
# 日志链接
if metadata.get("log_url"):
content += f'''
'''
return content
def generate_step3_detail_html(inspiration_data: Dict[str, Any]) -> str:
"""
生成 step3 生成灵感的详情HTML
Args:
inspiration_data: 灵感点数据
Returns:
step3 详细信息的HTML字符串
"""
import html as html_module
inspiration_name = inspiration_data.get("inspiration_name", "未知灵感")
step3 = inspiration_data.get("step3")
content = f'''
'''
if not step3:
content += '''
暂无生成的灵感点数据
'''
return content
# 获取元数据
metadata = step3.get("元数据", {})
anchor_info = step3.get("锚点信息", {})
step3_inspirations = step3.get("灵感点列表", [])
anchor_category = anchor_info.get("锚点分类", "")
category_def = anchor_info.get("分类定义", "")
category_context = anchor_info.get("分类上下文", "")
content += f'''
🎯 锚点信息
🎯 锚点分类:
{html_module.escape(anchor_category)}
'''
if category_def:
content += f'''
📖 分类定义:
{html_module.escape(category_def)}
'''
if category_context:
content += f'''
📍 分类上下文:
{html_module.escape(category_context)}
'''
content += '''
'''
# 生成的灵感点列表
if step3_inspirations:
content += f'''
✨ 生成的灵感点 (共 {len(step3_inspirations)} 个)
'''
for idx, item in enumerate(step3_inspirations):
path = item.get("推理路径", "")
insp_point = item.get("灵感点", "")
description = item.get("描述", "")
content += f'''
推理路径: {html_module.escape(path)}
描述: {html_module.escape(description)}
'''
content += '''
'''
# 日志链接
if metadata.get("log_url"):
content += f'''
'''
return content
def generate_detail_html(inspiration_data: Dict[str, Any]) -> str:
"""
生成灵感点的详细信息HTML
Args:
inspiration_data: 灵感点数据
Returns:
详细信息的HTML字符串
"""
import html as html_module
step1 = inspiration_data.get("step1", {})
inspiration_name = inspiration_data.get("inspiration_name", "未知灵感")
content = f'''
'''
# 获取元数据,用于后面的日志链接
metadata = step1.get("元数据", {})
# Step1 详细信息
if step1 and step1.get("灵感"):
inspiration = step1.get("灵感", "")
matches = step1.get("匹配结果列表", [])
content += f'''
🎯 灵感人设匹配分析
💡 灵感点内容:
{html_module.escape(inspiration)}
'''
# 显示匹配结果(显示Top5)
if matches:
content += f'''
匹配结果 (Top {min(len(matches), 5)}):
'''
for index, match in enumerate(matches[:5]):
input_info = match.get("输入信息", {})
match_result = match.get("匹配结果", {})
business_info = match.get("业务信息", {})
element_name = input_info.get("A_名称", "")
element_def = input_info.get("A_定义", "")
context_a = input_info.get("A_Context", "")
score = match_result.get("score", 0)
score_explain = match_result.get("score说明", "")
# 获取匹配关系
match_relations = match_result.get("匹配关系", [])
# 添加排名样式类
rank_class = ""
if index == 0:
rank_class = "top1"
elif index == 1:
rank_class = "top2"
elif index == 2:
rank_class = "top3"
content += f'''
'''
if element_def:
content += f'
📖 定义: {html_module.escape(element_def)}
'
if context_a:
content += f'
📍 所属: {html_module.escape(context_a)}
'
# 显示B语义分析、匹配关系、A语义分析(三列布局)
b_semantic = match_result.get("B语义分析", {})
a_semantic = match_result.get("A语义分析", {})
if b_semantic or a_semantic or match_relations:
content += '
'
# B语义分析
if b_semantic:
content += '''
'''
b_substance = b_semantic.get("实质", {})
b_form = b_semantic.get("形式", {})
if b_substance:
content += '
实质:
'
for key, value in b_substance.items():
content += f'''
{html_module.escape(key)}:
{html_module.escape(value)}
'''
if b_form:
content += '
形式:
'
for key, value in b_form.items():
content += f'''
{html_module.escape(key)}:
{html_module.escape(value)}
'''
content += '''
'''
# 显示匹配关系(中间)
if match_relations:
content += '''
'''
for idx, rel in enumerate(match_relations):
b_sem = rel.get("B语义", "")
a_sem = rel.get("A语义", "")
relation = rel.get("关系", "")
explanation = rel.get("说明", "")
distance_score = rel.get("距离分数", None)
# 构建中间关系文本
relation_middle = f'{html_module.escape(relation)}'
if distance_score is not None:
relation_middle += f' (分数: {distance_score:.2f})'
# 为每个关系项生成唯一ID,用于连接线定位
rel_id = f'rel_{index}_{idx}'
b_sem_id = f'b_sem_{index}_{idx}'
a_sem_id = f'a_sem_{index}_{idx}'
content += f'''
{relation_middle}
B: {html_module.escape(b_sem)}
→
A: {html_module.escape(a_sem)}
{html_module.escape(explanation)}
'''
content += '''
'''
# A语义分析
if a_semantic:
content += '''
'''
a_substance = a_semantic.get("实质", {})
a_form = a_semantic.get("形式", {})
if a_substance:
content += '
实质:
'
for key, value in a_substance.items():
content += f'''
{html_module.escape(key)}:
{html_module.escape(value)}
'''
if a_form:
content += '
形式:
'
for key, value in a_form.items():
content += f'''
{html_module.escape(key)}:
{html_module.escape(value)}
'''
content += '''
'''
content += '
' # end semantic-analysis-with-relations
# 显示分数详情(放在最后)
rule_score = match_result.get("规则分数", 0)
rule_score_explain = match_result.get("规则分数说明", "")
relevance_score = match_result.get("相关性分数", 0)
relevance_explain = match_result.get("相关性说明", "")
score_explain = match_result.get("score说明", "")
if rule_score or relevance_score or score_explain:
content += '
'
if rule_score or relevance_score:
content += '
'
if rule_score:
content += f'''
{f'
{html_module.escape(rule_score_explain)}
' if rule_score_explain else ''}
'''
if relevance_score:
content += f'''
{f'
{html_module.escape(relevance_explain)}
' if relevance_explain else ''}
'''
content += '
'
if score_explain:
content += f'
🎲 最终分数计算: {html_module.escape(score_explain)}
'
content += '
'
content += '''
'''
content += '''
'''
content += '''
'''
# Step3 详细信息
step3 = inspiration_data.get("step3")
if step3:
anchor_info = step3.get("锚点信息", {})
step3_inspirations = step3.get("灵感点列表", [])
anchor_category = anchor_info.get("锚点分类", "")
category_def = anchor_info.get("分类定义", "")
content += f'''
✨ Step3: 生成的灵感点
🎯 锚点分类:
{html_module.escape(anchor_category)}
'''
if category_def:
content += f'''
📖 分类定义:
{html_module.escape(category_def)}
'''
if step3_inspirations:
content += f'''
生成的灵感点列表 (共 {len(step3_inspirations)} 个):
'''
for idx, item in enumerate(step3_inspirations):
path = item.get("推理路径", "")
insp_point = item.get("灵感点", "")
description = item.get("描述", "")
content += f'''
推理路径: {html_module.escape(path)}
描述: {html_module.escape(description)}
'''
content += '''
'''
content += '''
'''
# 日志链接
if metadata.get("log_url"):
content += f'''
'''
return content
def generate_detail_modal_content_js() -> str:
"""
生成详情弹窗内容的JavaScript函数
Returns:
JavaScript代码字符串
"""
return '''
// Tab切换功能
function switchTab(event, tabId) {
// 移除所有tab的active状态
const tabButtons = document.querySelectorAll('.tab-button');
tabButtons.forEach(button => {
button.classList.remove('active');
});
// 隐藏所有tab内容
const tabContents = document.querySelectorAll('.tab-content');
tabContents.forEach(content => {
content.classList.remove('active');
});
// 激活当前tab
event.currentTarget.classList.add('active');
document.getElementById(tabId).classList.add('active');
}
function showInspirationDetail(element) {
const detailHtml = element.dataset.detailHtml;
const modal = document.getElementById('detailModal');
const modalBody = document.getElementById('modalBody');
modalBody.innerHTML = detailHtml;
modal.classList.add('active');
document.body.style.overflow = 'hidden';
}
function showStep1Detail(element) {
const step1DetailHtml = element.dataset.step1DetailHtml;
const modal = document.getElementById('detailModal');
const modalBody = document.getElementById('modalBody');
modalBody.innerHTML = step1DetailHtml;
modal.classList.add('active');
document.body.style.overflow = 'hidden';
}
function showStep3Detail(element) {
const step3DetailHtml = element.dataset.step3DetailHtml;
const modal = document.getElementById('detailModal');
const modalBody = document.getElementById('modalBody');
modalBody.innerHTML = step3DetailHtml;
modal.classList.add('active');
document.body.style.overflow = 'hidden';
}
function closeModal() {
const modal = document.getElementById('detailModal');
modal.classList.remove('active');
document.body.style.overflow = '';
}
function closeModalOnOverlay(event) {
if (event.target.id === 'detailModal') {
closeModal();
}
}
// ESC键关闭Modal
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape') {
closeModal();
}
});
// 搜索和过滤功能
function filterInspirations() {
const searchInput = document.getElementById('searchInput').value.toLowerCase();
const sortSelect = document.getElementById('sortSelect').value;
const cards = document.querySelectorAll('.inspiration-card');
let visibleCards = Array.from(cards);
// 搜索过滤
visibleCards.forEach(card => {
const name = card.dataset.inspirationName.toLowerCase();
if (name.includes(searchInput)) {
card.style.display = '';
} else {
card.style.display = 'none';
}
});
// 获取可见的卡片
visibleCards = Array.from(cards).filter(card => card.style.display !== 'none');
// 排序
if (sortSelect === 'score-desc' || sortSelect === 'score-asc') {
visibleCards.sort((a, b) => {
const scoreA = parseFloat(a.dataset.step1Score) || 0;
const scoreB = parseFloat(b.dataset.step1Score) || 0;
return sortSelect === 'score-desc' ? scoreB - scoreA : scoreA - scoreB;
});
} else if (sortSelect === 'name-asc' || sortSelect === 'name-desc') {
visibleCards.sort((a, b) => {
const nameA = a.dataset.inspirationName;
const nameB = b.dataset.inspirationName;
return sortSelect === 'name-asc' ? nameA.localeCompare(nameB) : nameB.localeCompare(nameA);
});
}
// 重新排列卡片
const container = document.querySelector('.inspirations-grid');
visibleCards.forEach(card => {
container.appendChild(card);
});
// 更新统计
updateStats();
}
function updateStats() {
const cards = document.querySelectorAll('.inspiration-card');
const visibleCards = Array.from(cards).filter(card => card.style.display !== 'none');
document.getElementById('totalCount').textContent = visibleCards.length;
let excellentCount = 0;
let goodCount = 0;
let normalCount = 0;
let needOptCount = 0;
let totalScore = 0;
visibleCards.forEach(card => {
const score = parseFloat(card.dataset.step1Score) || 0;
totalScore += score;
// 分数统计
if (score >= 0.7) excellentCount++;
else if (score >= 0.5) goodCount++;
else if (score >= 0.3) normalCount++;
else needOptCount++;
});
document.getElementById('top1ExcellentCount').textContent = excellentCount;
document.getElementById('top1GoodCount').textContent = goodCount;
document.getElementById('top1NormalCount').textContent = normalCount;
document.getElementById('top1NeedOptCount').textContent = needOptCount;
const avgScore = visibleCards.length > 0 ? (totalScore / visibleCards.length).toFixed(3) : '0.000';
document.getElementById('avgTop1Score').textContent = avgScore;
}
'''
def generate_persona_structure_html(persona_data: Dict[str, Any]) -> str:
"""
生成人设结构的树状HTML
Args:
persona_data: 人设数据
Returns:
人设结构的HTML字符串
"""
if not persona_data:
return '暂无人设数据
'
inspiration_list = persona_data.get("灵感点列表", [])
if not inspiration_list:
return '暂无灵感点列表数据
'
html_parts = ['']
for perspective_idx, perspective in enumerate(inspiration_list):
perspective_name = perspective.get("视角名称", "未知视角")
perspective_desc = perspective.get("视角描述", "")
pattern_list = perspective.get("模式列表", [])
# 一级节点:视角
html_parts.append(f'''
')
html_parts.append('
')
return ''.join(html_parts)
def generate_html(
inspirations_data: List[Dict[str, Any]],
posts_map: Dict[str, Dict[str, Any]],
persona_data: Dict[str, Any],
output_path: str
) -> str:
"""
生成完整的可视化HTML
Args:
inspirations_data: 灵感点数据列表
posts_map: 帖子数据映射
persona_data: 人设数据
output_path: 输出文件路径
Returns:
输出文件路径
"""
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# 统计信息
total_count = len(inspirations_data)
# 获取所有分数用于统计
def get_top1_score(d):
step1 = d.get("step1", {})
matches = step1.get("匹配结果列表", [])
if matches:
return matches[0].get("匹配结果", {}).get("score", 0)
return 0
# Top1分数统计
top1_excellent_count = sum(1 for d in inspirations_data if get_top1_score(d) >= 0.7)
top1_good_count = sum(1 for d in inspirations_data if 0.5 <= get_top1_score(d) < 0.7)
top1_normal_count = sum(1 for d in inspirations_data if 0.3 <= get_top1_score(d) < 0.5)
top1_need_opt_count = sum(1 for d in inspirations_data if get_top1_score(d) < 0.3)
# 平均分数
total_top1_score = sum(get_top1_score(d) for d in inspirations_data)
avg_top1_score = total_top1_score / total_count if total_count > 0 else 0
# 按Top1分数排序
inspirations_data_sorted = sorted(
inspirations_data,
key=lambda x: get_top1_score(x),
reverse=True
)
# 生成卡片HTML
cards_html = [generate_inspiration_card_html(data) for data in inspirations_data_sorted]
cards_html_str = '\n'.join(cards_html)
# 生成人设结构HTML
persona_structure_html = generate_persona_structure_html(persona_data)
# 生成JavaScript
detail_modal_js = generate_detail_modal_content_js()
# 完整HTML
html_content = f'''
灵感点分析可视化
📚 人设结构
{persona_structure_html}
生成时间: {timestamp}
'''
# 写入文件
output_file = Path(output_path)
output_file.parent.mkdir(parents=True, exist_ok=True)
with open(output_file, 'w', encoding='utf-8') as f:
f.write(html_content)
return str(output_file.absolute())
def load_persona_data(persona_path: str) -> Dict[str, Any]:
"""
加载人设数据
Args:
persona_path: 人设JSON文件路径
Returns:
人设数据字典
"""
try:
with open(persona_path, 'r', encoding='utf-8') as f:
return json.load(f)
except Exception as e:
print(f"警告: 读取人设文件失败: {e}")
return {}
def main():
"""主函数"""
import sys
# 配置路径
inspiration_dir = "/Users/semsevens/Desktop/workspace/aaa/dev_3/data/阿里多多酱/out/人设_1110/how/灵感点"
posts_dir = "/Users/semsevens/Desktop/workspace/aaa/dev_3/data/阿里多多酱/作者历史帖子"
persona_path = "/Users/semsevens/Desktop/workspace/aaa/dev_3/data/阿里多多酱/out/人设_1110/人设.json"
output_path = "/Users/semsevens/Desktop/workspace/aaa/dev_3/data/阿里多多酱/out/人设_1110/how/灵感点可视化.html"
print("=" * 60)
print("灵感点分析可视化脚本")
print("=" * 60)
# 加载数据
print("\n📂 正在加载灵感点数据...")
inspirations_data = load_inspiration_points_data(inspiration_dir)
print(f"✅ 成功加载 {len(inspirations_data)} 个灵感点")
print("\n📂 正在加载帖子数据...")
posts_map = load_posts_data(posts_dir)
print(f"✅ 成功加载 {len(posts_map)} 个帖子")
print("\n📂 正在加载人设数据...")
persona_data = load_persona_data(persona_path)
print(f"✅ 成功加载人设数据")
# 生成HTML
print("\n🎨 正在生成可视化HTML...")
result_path = generate_html(inspirations_data, posts_map, persona_data, output_path)
print(f"\n✅ 可视化文件已生成!")
print(f"📄 文件路径: {result_path}")
print(f"\n💡 在浏览器中打开该文件即可查看可视化结果")
print("=" * 60)
if __name__ == "__main__":
main()