Files
guangdong-red-base/generate_svgs.py
T

1341 lines
75 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
为用户交流技术实现方案.md中的架构图生成SVG并转为JPG
"""
import os
import subprocess
OUTPUT_DIR = os.path.join(os.path.dirname(__file__), "images")
os.makedirs(OUTPUT_DIR, exist_ok=True)
# ─── 颜色方案 ───
C_BG = "#ffffff"
C_TITLE = "#1a1a2e"
C_LAYER1 = "#e3f2fd" # 浅蓝
C_LAYER1_B = "#1565c0"
C_LAYER2 = "#e8f5e9" # 浅绿
C_LAYER2_B = "#2e7d32"
C_LAYER3 = "#fff3e0" # 浅橙
C_LAYER3_B = "#e65100"
C_LAYER4 = "#f3e5f5" # 浅紫
C_LAYER4_B = "#6a1b9a"
C_LAYER5 = "#fce4ec" # 浅粉
C_LAYER5_B = "#c62828"
C_LAYER6 = "#e0f7fa" # 浅青
C_LAYER6_B = "#00838f"
C_BOX = "#f5f5f5"
C_BOX_B = "#616161"
C_ARROW = "#424242"
C_HIGHLIGHT = "#ffd54f"
C_AI = "#e8eaf6"
C_AI_B = "#283593"
C_VR = "#ede7f6"
C_VR_B = "#4527a0"
C_SECURITY = "#ffebee"
C_SECURITY_B = "#b71c1c"
C_DEPLOY = "#e0f2f1"
C_DEPLOY_B = "#00695c"
FONT = "Microsoft YaHei, PingFang SC, Heiti SC, SimHei, sans-serif"
def svg_header(w, h):
return f'<svg xmlns="http://www.w3.org/2000/svg" width="{w}" height="{h}" viewBox="0 0 {w} {h}">'
def svg_text(x, y, text, size=14, color=C_TITLE, bold=False, anchor="middle", font=FONT):
weight = "bold" if bold else "normal"
return f'<text x="{x}" y="{y}" font-family="{font}" font-size="{size}" fill="{color}" font-weight="{weight}" text-anchor="{anchor}" dominant-baseline="middle">{text}</text>'
def svg_rect(x, y, w, h, fill=C_BOX, stroke=C_BOX_B, rx=8, sw=1.5, opacity=1.0):
return f'<rect x="{x}" y="{y}" width="{w}" height="{h}" rx="{rx}" ry="{rx}" fill="{fill}" stroke="{stroke}" stroke-width="{sw}" opacity="{opacity}"/>'
def svg_box(x, y, w, h, text, fill=C_BOX, stroke=C_BOX_B, ts=13, tc=C_TITLE, bold=False, rx=8):
cx = x + w/2
cy = y + h/2
return svg_rect(x, y, w, h, fill, stroke, rx) + svg_text(cx, cy, text, ts, tc, bold)
import math as _math
def svg_arrow(x1, y1, x2, y2, color=C_ARROW, sw=2, al=14, aw=0.55):
"""绘制带箭头的线段。箭头在(x2,y2)端。
al: 箭头长度, aw: 箭头半角(弧度)
"""
angle = _math.atan2(y2-y1, x2-x1)
# 箭头三角形的两个底点
bx = x2 - al * _math.cos(angle)
by = y2 - al * _math.sin(angle)
ax1 = bx + al * _math.sin(angle) * _math.tan(aw)
ay1 = by - al * _math.cos(angle) * _math.tan(aw)
ax2 = bx - al * _math.sin(angle) * _math.tan(aw)
ay2 = by + al * _math.cos(angle) * _math.tan(aw)
# 线段缩短到箭头底部
return (f'<line x1="{x1}" y1="{y1}" x2="{bx:.1f}" y2="{by:.1f}" stroke="{color}" stroke-width="{sw}" stroke-linecap="round"/>'
f'<polygon points="{x2},{y2} {ax1:.1f},{ay1:.1f} {ax2:.1f},{ay2:.1f}" fill="{color}"/>')
def svg_line(x1, y1, x2, y2, color=C_ARROW, sw=1.5, dash=None):
d = f' stroke-dasharray="{dash}"' if dash else ''
return f'<line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" stroke="{color}" stroke-width="{sw}"{d} stroke-linecap="round"/>'
def svg_arrow_dashed(x1, y1, x2, y2, color=C_ARROW, sw=1.5, dash='6,4', al=12, aw=0.5):
"""绘制带箭头的虚线段。"""
angle = _math.atan2(y2-y1, x2-x1)
bx = x2 - al * _math.cos(angle)
by = y2 - al * _math.sin(angle)
ax1 = bx + al * _math.sin(angle) * _math.tan(aw)
ay1 = by - al * _math.cos(angle) * _math.tan(aw)
ax2 = bx - al * _math.sin(angle) * _math.tan(aw)
ay2 = by + al * _math.cos(angle) * _math.tan(aw)
return (f'<line x1="{x1}" y1="{y1}" x2="{bx:.1f}" y2="{by:.1f}" stroke="{color}" stroke-width="{sw}" stroke-dasharray="{dash}" stroke-linecap="round"/>'
f'<polygon points="{x2},{y2} {ax1:.1f},{ay1:.1f} {ax2:.1f},{ay2:.1f}" fill="{color}"/>')
def svg_arrow_down(cx, y1, y2, color=C_ARROW, sw=2, al=14):
"""绘制垂直向下的箭头。"""
return svg_arrow(cx, y1, cx, y2, color, sw, al)
def svg_h_branch(x1, y1, x2_left, x2_right, y2, color=C_ARROW, sw=2):
"""从(x1,y1)向下到y2,然后水平分叉到x2_left和x2_right,各带箭头。"""
s = f'<line x1="{x1}" y1="{y1}" x2="{x1}" y2="{y2}" stroke="{color}" stroke-width="{sw}" stroke-linecap="round"/>'
s += f'<line x1="{x2_left}" y1="{y2}" x2="{x2_right}" y2="{y2}" stroke="{color}" stroke-width="{sw}" stroke-linecap="round"/>'
s += svg_arrow(x2_left, y2, x2_left, y2+18, color, sw)
s += svg_arrow(x2_right, y2, x2_right, y2+18, color, sw)
return s
def save_svg_and_jpg(svg_content, name):
svg_path = os.path.join(OUTPUT_DIR, f"{name}.svg")
png_path = os.path.join(OUTPUT_DIR, f"{name}.png")
jpg_path = os.path.join(OUTPUT_DIR, f"{name}.jpg")
if not svg_content.rstrip().endswith("</svg>"):
svg_content += "\n</svg>"
with open(svg_path, "w", encoding="utf-8") as f:
f.write(svg_content)
# SVG → PNG (rsvg-convert支持中文字体)
result = subprocess.run(
["/usr/local/bin/rsvg-convert", "-d", "200", "-p", "200",
"-f", "png", "-o", png_path, svg_path],
capture_output=True, text=True
)
if result.returncode != 0:
print(f"{name} rsvg-convert失败: {result.stderr[:200]}")
return
# PNG → JPG (sips是macOS自带工具)
result2 = subprocess.run(
["sips", "-s", "format", "jpeg", "-s", "formatOptions", "95",
png_path, "--out", jpg_path],
capture_output=True, text=True
)
if result2.returncode != 0:
print(f"{name} sips失败: {result2.stderr[:200]}")
return
os.remove(png_path)
print(f"{name}.jpg")
# ═══════════════════════════════════════════════════════════════
# 图1: 六层技术架构
# ═══════════════════════════════════════════════════════════════
def gen_01_six_layer():
W, H = 900, 620
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "六层技术架构", 22, C_TITLE, True)
layers = [
("用户触达层", "线下终端(触屏/VR/环幕/UWB/RFID) | 线上(Web/小程序/H5) | 节点终端", C_LAYER1, C_LAYER1_B),
("应用服务层", "F1自适应叙事 F2情境任务 F3文物数字生命 F4成长护照 F5云上共同体 F6青年共创 F7行动实验室 F8星火节点", C_LAYER2, C_LAYER2_B),
("公共平台层", "X1体验编排引擎 X2成长护照档案 X3内容治理中心 S9节点协同", C_LAYER3, C_LAYER3_B),
("AI能力层", "可信叙事引擎 学习成长引擎 编审助手引擎 策展助手引擎", C_LAYER4, C_LAYER4_B),
("数据资产层", "知识图谱 向量检索库 内容资源仓 业务数据库 时序分析库 实时缓存 消息总线", C_LAYER5, C_LAYER5_B),
("基础设施层", "私有云容器编排 GPU推理集群 5G专网 IoT网关 CDN 安防网络", C_LAYER6, C_LAYER6_B),
]
y = 60
lh = 80
for i, (title, content, fill, stroke) in enumerate(layers):
s += svg_rect(40, y, W-80, lh-10, fill, stroke, 10, 2)
s += svg_text(130, y+22, title, 15, stroke, True)
s += svg_text(W/2, y+50, content, 12, "#333")
if i < len(layers) - 1:
s += svg_arrow_down(W/2, y+lh-12, y+lh, "#bbb", 1.5, 8)
y += lh
save_svg_and_jpg(s, "01_six_layer_architecture")
# ═══════════════════════════════════════════════════════════════
# 图2: 系统部署拓扑
# ═══════════════════════════════════════════════════════════════
def gen_02_deployment():
W, H = 880, 560
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "系统部署拓扑", 22, C_TITLE, True)
# 公网入口
s += svg_box(300, 60, 280, 50, "公网入口 WAF + 负载均衡", C_SECURITY, C_SECURITY_B, 13, C_SECURITY_B, True)
# 分叉到两个区域 — 用h_branch
s += svg_h_branch(W/2, 110, 180, 700, 155, C_ARROW, 2)
# 线上云区
s += svg_box(60, 175, 240, 160, "", C_LAYER1, C_LAYER1_B, 13, C_LAYER1_B, True, 10)
s += svg_text(180, 200, "线上云区 (公有云)", 14, C_LAYER1_B, True)
s += svg_text(180, 230, "Web/小程序", 12, "#333")
s += svg_text(180, 255, "API网关", 12, "#333")
s += svg_text(180, 280, "CDN节点", 12, "#333")
s += svg_text(180, 305, "港澳加速", 12, "#333")
# 线下场馆区
s += svg_box(580, 175, 240, 160, "", C_LAYER2, C_LAYER2_B, 13, C_LAYER2_B, True, 10)
s += svg_text(700, 200, "线下场馆区 (私有云)", 14, C_LAYER2_B, True)
s += svg_text(700, 230, "AI推理GPU集群", 12, "#333")
s += svg_text(700, 255, "容器集群", 12, "#333")
s += svg_text(700, 280, "数据库集群", 12, "#333")
s += svg_text(700, 305, "知识图谱+向量库", 12, "#333")
# VPN连线 — 双向虚线箭头
s += svg_arrow_dashed(300, 255, 580, 255, "#888", 1.5, "6,4")
s += svg_arrow_dashed(580, 255, 300, 255, "#888", 1.5, "6,4")
s += svg_text(440, 245, "VPN专线互联", 11, "#888")
# 设备区 — 从线下场馆区底部出发
s += svg_box(350, 380, 200, 60, "IoT/安防/VR\n设备网络 (VLAN隔离)", C_LAYER6, C_LAYER6_B, 12, C_LAYER6_B, True)
s += svg_arrow(700, 335, 550, 380, C_ARROW, 2)
# 全国节点 — 从线上云区底部出发
s += svg_box(60, 380, 240, 60, "全国星火节点\n(远程组件包分发)", C_LAYER3, C_LAYER3_B, 12, C_LAYER3_B, True)
s += svg_arrow(180, 335, 180, 380, C_ARROW, 2)
s += svg_text(W/2, 490, "AI推理+核心数据部署在场馆私有云,保证安全与低延迟", 12, "#666")
s += svg_text(W/2, 515, "Web/小程序/云展馆部署在公有云,CDN加速港澳访问", 12, "#666")
s += svg_text(W/2, 540, "全国节点通过S9节点协同系统远程部署,断网不失能", 12, "#666")
save_svg_and_jpg(s, "02_deployment_topology")
# ═══════════════════════════════════════════════════════════════
# 图3: AI可信知识引擎架构
# ═══════════════════════════════════════════════════════════════
def gen_03_ai_engine():
W, H = 860, 580
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "AI可信知识引擎架构", 22, C_TITLE, True)
# 外框
s += svg_rect(30, 55, W-60, 490, "#fafafa", C_AI_B, 12, 2)
# 三列
cols = [
("知识图谱", "事件/人物/文物/\n概念/地点及关系\n\n史实锚点\n\n交叉验证", C_LAYER4, C_LAYER4_B),
("RAG检索增强", "用户提问\n → 语义检索\n → 召回史料\n → 生成回答\n\n读史料后回答\n非凭记忆回答", C_LAYER1, C_LAYER1_B),
("大模型微调", "延安精神\n专属语料\n → LoRA微调\n → 人群适配\n\n懂延安·懂党史·懂教育", C_LAYER3, C_LAYER3_B),
]
cw = 230
gap = 20
x0 = 50
for i, (title, content, fill, stroke) in enumerate(cols):
x = x0 + i * (cw + gap)
s += svg_rect(x, 80, cw, 280, fill, stroke, 10, 2)
s += svg_text(x + cw/2, 110, title, 16, stroke, True)
for j, line in enumerate(content.split("\n")):
s += svg_text(x + cw/2, 145 + j*22, line, 12, "#333")
if i < 2:
s += svg_arrow(x + cw, 220, x + cw + gap, 220, "#999", 2, 12)
# 汇聚箭头
s += svg_arrow_down(W/2, 360, 380, "#999", 2)
# 安全护栏
s += svg_rect(150, 380, W-300, 140, C_SECURITY, C_SECURITY_B, 10, 2)
s += svg_text(W/2, 410, "安全护栏层", 15, C_SECURITY_B, True)
steps = ["输入过滤", "事实校验", "输出过滤", "审计追溯"]
for i, step in enumerate(steps):
sx = 200 + i * 130
s += svg_rect(sx, 430, 110, 35, "#fff", C_SECURITY_B, 6, 1.5)
s += svg_text(sx + 55, 448, step, 12, C_SECURITY_B, True)
if i < 3:
s += svg_arrow(sx + 110, 448, sx + 130, 448, C_SECURITY_B, 2, 10)
save_svg_and_jpg(s, "03_ai_knowledge_engine")
# ═══════════════════════════════════════════════════════════════
# 图4: 可信叙事引擎
# ═══════════════════════════════════════════════════════════════
def gen_04_narrative_engine():
W, H = 860, 480
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "可信叙事引擎", 22, C_TITLE, True)
s += svg_rect(30, 55, W-60, 400, "#fafafa", C_LAYER3_B, 12, 2)
# 内容组件仓库
s += svg_rect(50, 80, 320, 220, C_LAYER3, C_LAYER3_B, 10, 2)
s += svg_text(210, 110, "内容组件仓库", 15, C_LAYER3_B, True)
items = ["已审核的文字片段", "影像素材", "3D模型", "互动任务", "AI讲解词", "每个组件标注: 适用人群/时长/审核状态"]
for i, item in enumerate(items):
s += svg_text(210, 140 + i*25, item, 12, "#333")
# 箭头 — 从内容仓库到叙事算法
s += svg_arrow(370, 190, 440, 190, C_ARROW, 2, 14)
# 叙事路线算法
s += svg_rect(440, 80, 360, 220, C_LAYER1, C_LAYER1_B, 10, 2)
s += svg_text(620, 110, "叙事路线算法", 15, C_LAYER1_B, True)
inputs = ["输入: 人群类型 + 学习问题", " 可用时长 + 语言偏好", " 实时客流状态"]
s += svg_text(620, 145, inputs[0], 12, "#333")
s += svg_text(620, 170, inputs[1], 12, "#333")
s += svg_text(620, 195, inputs[2], 12, "#333")
s += svg_text(620, 225, "输出: 节点序列 + 每节点内容", 12, C_LAYER1_B, True)
s += svg_text(620, 250, " 适配方案 + 时长估算", 12, C_LAYER1_B, True)
# 底部原则
s += svg_rect(50, 330, W-100, 50, C_HIGHLIGHT, "#f57f17", 8, 2)
s += svg_text(W/2, 355, "关键原则: 不生成新内容,只编排已审核组件 — 像乐高积木一样编排", 13, "#e65100", True)
s += svg_text(W/2, 420, "同一空间千人千面: 小学生看动画+游戏 | 党员干部看文献+分析 | 港澳青年看繁体+湾区视角", 12, "#666")
save_svg_and_jpg(s, "04_narrative_engine")
# ═══════════════════════════════════════════════════════════════
# 图5: 学习成长引擎
# ═══════════════════════════════════════════════════════════════
def gen_05_growth_engine():
W, H = 860, 420
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "学习成长引擎", 22, C_TITLE, True)
# 三个阶段
stages = [
("参观前导学", ["人群选择", "问题选择", "路线生成", "导学推送"], C_LAYER1, C_LAYER1_B),
("参观中记录", ["UWB自动记录位置", "任务完成记录", "AI问答记录", "理解表达记录"], C_LAYER2, C_LAYER2_B),
("离馆后回访", ["第7天回顾推送", "第30天进展反馈", "第90天成果提交", "行动评估+案例入库"], C_LAYER3, C_LAYER3_B),
]
sw = 240
gap = 20
x0 = 40
for i, (title, items, fill, stroke) in enumerate(stages):
x = x0 + i * (sw + gap)
s += svg_rect(x, 60, sw, 220, fill, stroke, 10, 2)
s += svg_text(x + sw/2, 90, title, 15, stroke, True)
for j, item in enumerate(items):
s += svg_text(x + sw/2, 125 + j*30, item, 12, "#333")
if i < 2:
s += svg_arrow(x + sw, 170, x + sw + gap, 170, "#999", 2, 14)
# 统一档案
s += svg_arrow_down(W/2, 280, 310, "#999", 2)
s += svg_rect(120, 310, W-240, 60, C_LAYER4, C_LAYER4_B, 10, 2)
s += svg_text(W/2, 332, "统一成长档案 (X2护照)", 15, C_LAYER4_B, True)
s += svg_text(W/2, 352, "跨系统同步 · 全程陪伴 · 闭环追踪 · 北极星指标可计算", 12, "#333")
save_svg_and_jpg(s, "05_growth_engine")
# ═══════════════════════════════════════════════════════════════
# 图6: 复制运营引擎
# ═══════════════════════════════════════════════════════════════
def gen_06_replication_engine():
W, H = 860, 520
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "复制运营引擎", 22, C_TITLE, True)
# 旗舰中心
s += svg_rect(180, 60, 500, 100, C_LAYER5, C_LAYER5_B, 10, 2)
s += svg_text(W/2, 90, "L4 旗舰中心 (13000㎡)", 15, C_LAYER5_B, True)
s += svg_text(W/2, 115, "权威内容生产 → 组件包打包 → 版本发布", 12, "#333")
s += svg_text(W/2, 138, "全国节点调度 → 远程监控 → 数据回收", 12, "#333")
# 分发线 — 从旗舰中心向下到水平分发线
s += svg_line(W/2, 160, W/2, 180, C_ARROW, 2)
s += svg_text(W/2 + 55, 175, "组件包分发", 10, "#999")
s += svg_line(130, 180, 730, 180, C_ARROW, 2)
# 四级节点
nodes = [
("L1 微节点", "一体机\n即开即用\n定期WiFi同步", C_LAYER6, C_LAYER6_B),
("L2 标准节点", "7天部署\n本地AI推理\nVR串流", C_LAYER1, C_LAYER1_B),
("L3 城市节点", "独立运营\n本地服务器\n内容本地化", C_LAYER2, C_LAYER2_B),
("L4 旗舰中心", "全量系统\n内容生产\n全国调度", C_LAYER5, C_LAYER5_B),
]
nw = 180
gap = 20
x0 = 40
for i, (title, desc, fill, stroke) in enumerate(nodes):
x = x0 + i * (nw + gap)
s += svg_arrow(x + nw/2, 180, x + nw/2, 200)
s += svg_rect(x, 200, nw, 130, fill, stroke, 10, 2)
s += svg_text(x + nw/2, 225, title, 13, stroke, True)
for j, line in enumerate(desc.split("\n")):
s += svg_text(x + nw/2, 250 + j*22, line, 11, "#333")
# 组件包内容
s += svg_rect(50, 370, W-100, 50, C_HIGHLIGHT, "#f57f17", 8, 2)
s += svg_text(W/2, 395, "组件包: 知识图谱子集 + 史料向量 + 课程 + 任务剧本 + VR场景 + 运营SOP", 12, "#e65100", True)
s += svg_text(W/2, 460, "断网不失能: 各节点本地有AI推理和内容缓存,网络恢复后自动同步", 12, "#666")
s += svg_text(W/2, 485, "内容可回流: 各节点优秀作品可提交旗舰中心审核后全国共享", 12, "#666")
save_svg_and_jpg(s, "06_replication_engine")
# ═══════════════════════════════════════════════════════════════
# 图7: F1自适应叙事空间 + X1编排引擎
# ═══════════════════════════════════════════════════════════════
def gen_07_f1_x1():
W, H = 900, 620
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "F1自适应叙事空间 + X1体验编排引擎", 20, C_TITLE, True)
# F1三模块
s += svg_rect(30, 55, W-60, 180, "#fafafa", C_LAYER1_B, 10, 2)
s += svg_text(W/2, 80, "F1 自适应叙事空间系统", 14, C_LAYER1_B, True)
modules = [
("入馆选择终端", "人群类型/学习问题\n可用时长/语言"),
("空间联动控制", "屏幕/灯光/音频\nAI/互动设备"),
("学习脉络生成", "路线节点/文物\n任务/AI问答/报告"),
]
mw = 250
for i, (title, desc) in enumerate(modules):
x = 50 + i * (mw + 15)
s += svg_rect(x, 95, mw, 120, C_LAYER1, C_LAYER1_B, 8, 1.5)
s += svg_text(x + mw/2, 120, title, 13, C_LAYER1_B, True)
for j, line in enumerate(desc.split("\n")):
s += svg_text(x + mw/2, 150 + j*22, line, 11, "#333")
# X1编排引擎
s += svg_arrow_down(W/2, 240, 275, C_LAYER3_B, 2, 14)
s += svg_text(W/2, 258, "X1全场景体验编排引擎", 14, C_LAYER3_B, True)
s += svg_rect(30, 275, W-60, 280, "#fafafa", C_LAYER3_B, 10, 2)
# 输入
s += svg_rect(50, 295, 200, 120, C_LAYER3, C_LAYER3_B, 8, 1.5)
s += svg_text(150, 320, "输入", 13, C_LAYER3_B, True)
inputs = ["用户路线", "UWB实时位置(10Hz)", "实时客流", "设备状态"]
for i, inp in enumerate(inputs):
s += svg_text(150, 345 + i*22, inp, 11, "#333")
# 箭头 — 输入到处理
s += svg_arrow(250, 355, 320, 355, C_ARROW, 2, 14)
# 处理
s += svg_rect(320, 295, 260, 120, C_HIGHLIGHT, "#f57f17", 8, 2)
s += svg_text(450, 320, "实时流式计算 (CEP)", 13, "#e65100", True)
s += svg_text(450, 350, "复杂事件处理引擎", 12, "#333")
s += svg_text(450, 375, "毫秒级复合条件判断", 12, "#333")
s += svg_text(450, 400, "规则 + AI编排决策", 12, "#333")
# 箭头 — 处理到输出
s += svg_arrow(580, 355, 650, 355, C_ARROW, 2, 14)
# 输出
s += svg_rect(650, 295, 200, 120, C_LAYER2, C_LAYER2_B, 8, 1.5)
s += svg_text(750, 320, "输出", 13, C_LAYER2_B, True)
outputs = ["设备控制指令", "用户学习记录", "客流调度信号"]
for i, out in enumerate(outputs):
s += svg_text(750, 345 + i*22, out, 11, "#333")
# 设备控制
s += svg_text(W/2, 445, "六类设备统一编排", 13, C_TITLE, True)
devices = ["LED屏幕", "灯光系统", "音频系统", "VR设备", "互动终端", "AI数字人"]
dw = 120
for i, dev in enumerate(devices):
x = 60 + i * (dw + 10)
s += svg_rect(x, 460, dw, 35, C_LAYER6, C_LAYER6_B, 6, 1.5)
s += svg_text(x + dw/2, 478, dev, 11, C_LAYER6_B, True)
s += svg_text(W/2, 530, "UWB厘米级室内定位 + 复杂事件处理 + 像指挥家指挥交响乐", 12, "#666")
s += svg_text(W/2, 555, "同一物理空间,数字内容随人而变", 12, C_LAYER3_B, True)
save_svg_and_jpg(s, "07_f1_x1_orchestration")
# ═══════════════════════════════════════════════════════════════
# 图8: F2情境任务剧场
# ═══════════════════════════════════════════════════════════════
def gen_08_f2_task():
W, H = 860, 400
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "F2 情境任务剧场系统", 22, C_TITLE, True)
modules = [
("任务引擎", "任务定义: 历史问题\n角色分配/资源约束\n证据集/历史事实\n评价维度\n状态机驱动(9状态)"),
("团队协作终端", "主屏: 情境展示\n分屏: 证据查看\n个人终端: 决策\n团队屏: 方案汇总\nVR模式: 虚拟窑洞讨论"),
("AI复盘引擎", "团队方案 vs 历史\nAI追问(史料引用)\n证据使用分析\n现实连接问题\n像历史导师一对一"),
]
mw = 250
for i, (title, desc) in enumerate(modules):
x = 30 + i * (mw + 15)
fill = [C_LAYER2, C_LAYER1, C_LAYER4][i]
stroke = [C_LAYER2_B, C_LAYER1_B, C_LAYER4_B][i]
s += svg_rect(x, 55, mw, 240, fill, stroke, 10, 2)
s += svg_text(x + mw/2, 82, title, 14, stroke, True)
for j, line in enumerate(desc.split("\n")):
s += svg_text(x + mw/2, 110 + j*22, line, 11, "#333")
if i < 2:
s += svg_arrow(x + mw, 175, x + mw + 15, 175, "#999", 2, 12)
# 底部
s += svg_rect(30, 320, W-60, 50, C_HIGHLIGHT, "#f57f17", 8, 2)
s += svg_text(W/2, 345, "VR与非VR双模式共享同一剧本: 同一任务定义/证据集/历史揭示/AI复盘/护照记录", 12, "#e65100", True)
save_svg_and_jpg(s, "08_f2_task_theater")
# ═══════════════════════════════════════════════════════════════
# 图9: F3文物数字生命
# ═══════════════════════════════════════════════════════════════
def gen_09_f3_artifact():
W, H = 860, 400
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "F3 文物数字生命系统", 22, C_TITLE, True)
modules = [
("数字采集管理", "3D扫描任务\n摄影测量任务\n质量检测\n元数据录入"),
("五层内容管理", "L1看见: 图像/3D\nL2知道: 元数据\nL3理解: 关联网络\nL4研究: 档案\nL5连接: 课程/任务"),
("关系星图引擎", "力导向图渲染\n交互式星图\n时间轴\n点击节点展开"),
]
mw = 250
for i, (title, desc) in enumerate(modules):
x = 30 + i * (mw + 15)
fill = [C_LAYER3, C_LAYER4, C_LAYER1][i]
stroke = [C_LAYER3_B, C_LAYER4_B, C_LAYER1_B][i]
s += svg_rect(x, 55, mw, 200, fill, stroke, 10, 2)
s += svg_text(x + mw/2, 82, title, 14, stroke, True)
for j, line in enumerate(desc.split("\n")):
s += svg_text(x + mw/2, 110 + j*22, line, 11, "#333")
if i < 2:
s += svg_arrow(x + mw, 155, x + mw + 15, 155, "#999", 2, 12)
# 五层递进
s += svg_text(W/2, 290, "一件文物 = 一个知识宇宙", 14, C_TITLE, True)
levels = ["看见", "知道", "理解", "研究", "连接"]
lw = 140
for i, lv in enumerate(levels):
x = 50 + i * (lw + 10)
fill = [C_LAYER6, C_LAYER1, C_LAYER2, C_LAYER3, C_LAYER4][i]
stroke = [C_LAYER6_B, C_LAYER1_B, C_LAYER2_B, C_LAYER3_B, C_LAYER4_B][i]
s += svg_rect(x, 305, lw, 35, fill, stroke, 6, 1.5)
s += svg_text(x + lw/2, 323, lv, 12, stroke, True)
if i < 4:
s += svg_arrow(x + lw, 323, x + lw + 10, 323, "#999", 1.5, 10)
s += svg_text(W/2, 370, "推测性复原标识: 有争议部分用不同颜色边框区分「史实确认」和「推测复原」", 11, "#666")
save_svg_and_jpg(s, "09_f3_artifact_life")
# ═══════════════════════════════════════════════════════════════
# 图10: F4成长护照
# ═══════════════════════════════════════════════════════════════
def gen_10_f4_passport():
W, H = 860, 380
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "F4 成长护照系统", 22, C_TITLE, True)
stages = [
("参观前导学", ["人群/问题选择", "团队目标创建", "参观建议生成", "导学材料推送"]),
("参观中记录", ["文物查看记录", "任务完成记录", "AI问答记录", "理解表达记录", "行动选择记录"]),
("离馆后回访", ["第7天推送回顾", "第30天进展反馈", "第90天成果提交", "后续课程推荐"]),
]
sw = 250
for i, (title, items) in enumerate(stages):
x = 30 + i * (sw + 10)
fill = [C_LAYER1, C_LAYER2, C_LAYER3][i]
stroke = [C_LAYER1_B, C_LAYER2_B, C_LAYER3_B][i]
s += svg_rect(x, 55, sw, 200, fill, stroke, 10, 2)
s += svg_text(x + sw/2, 82, title, 14, stroke, True)
for j, item in enumerate(items):
s += svg_text(x + sw/2, 110 + j*25, item, 11, "#333")
if i < 2:
s += svg_arrow(x + sw, 155, x + sw + 10, 155, "#999", 2, 12)
# X2统一档案
s += svg_arrow_down(W/2, 255, 275, "#999", 2)
s += svg_rect(100, 275, W-200, 50, C_LAYER4, C_LAYER4_B, 10, 2)
s += svg_text(W/2, 300, "X2 统一成长档案 — 跨系统数据同步 · GraphQL统一查询", 13, C_LAYER4_B, True)
s += svg_text(W/2, 350, "隐私保护是设计底线: 禁止采集人脸特征/情绪识别/政治倾向推断", 11, C_SECURITY_B)
save_svg_and_jpg(s, "10_f4_growth_passport")
# ═══════════════════════════════════════════════════════════════
# 图11: F5云上学习共同体
# ═══════════════════════════════════════════════════════════════
def gen_11_f5_cloud():
W, H = 860, 400
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "F5 云上学习共同体", 22, C_TITLE, True)
modules = [
("Web3D云展馆", "3D渲染引擎\n虚拟漫游\nAI导览\n文物3D展示"),
("云端课程平台", "视频点播/直播\n课程管理\n学时记录\n证书发放"),
("协作任务平台", "实时音视频协作\n多人同堂任务\n粤港澳三地同步\n在线研讨室"),
]
mw = 250
for i, (title, desc) in enumerate(modules):
x = 30 + i * (mw + 15)
fill = [C_LAYER1, C_LAYER2, C_LAYER3][i]
stroke = [C_LAYER1_B, C_LAYER2_B, C_LAYER3_B][i]
s += svg_rect(x, 55, mw, 160, fill, stroke, 10, 2)
s += svg_text(x + mw/2, 82, title, 14, stroke, True)
for j, line in enumerate(desc.split("\n")):
s += svg_text(x + mw/2, 110 + j*22, line, 11, "#333")
# 四种模式
s += svg_text(W/2, 245, "四种访问模式", 13, C_TITLE, True)
modes = [("8分钟精华\n轻量H5", C_LAYER6, C_LAYER6_B), ("一节课模式\n视频+互动", C_LAYER1, C_LAYER1_B),
("协作任务模式\n实时协作+白板", C_LAYER3, C_LAYER3_B), ("深度研究模式\n文献检索+图谱", C_LAYER4, C_LAYER4_B)]
mw2 = 190
for i, (title, fill, stroke) in enumerate(modes):
x = 30 + i * (mw2 + 10)
s += svg_rect(x, 265, mw2, 55, fill, stroke, 8, 1.5)
for j, line in enumerate(title.split("\n")):
s += svg_text(x + mw2/2, 283 + j*18, line, 11, stroke, j==0)
s += svg_text(W/2, 365, "三档渲染自适应: 高端(WebGPU+PBR) → 轻量(WebGL+减面) → 极简(全景图片热点)", 11, "#666")
save_svg_and_jpg(s, "11_f5_cloud_community")
# ═══════════════════════════════════════════════════════════════
# 图12: F6湾区青年共创
# ═══════════════════════════════════════════════════════════════
def gen_12_f6_cocreation():
W, H = 860, 350
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "F6 湾区青年共创平台", 22, C_TITLE, True)
modules = [
("共创项目管理", "年度主题发布\n混合小组组建\n专家工作坊预约\n进度跟踪\n资源库访问"),
("AI辅助创作工具集", "AI辅助写作(标注)\nAI辅助绘画\n短视频编辑\n声音档案录制\n互动网页制作"),
("三级审核与展示", "AI编审自动检查\n编辑复审(人工)\n专家终审\n线上专区展示\n优秀作品入临展"),
]
mw = 250
for i, (title, desc) in enumerate(modules):
x = 30 + i * (mw + 15)
fill = [C_LAYER2, C_LAYER4, C_LAYER3][i]
stroke = [C_LAYER2_B, C_LAYER4_B, C_LAYER3_B][i]
s += svg_rect(x, 55, mw, 200, fill, stroke, 10, 2)
s += svg_text(x + mw/2, 82, title, 14, stroke, True)
for j, line in enumerate(desc.split("\n")):
s += svg_text(x + mw/2, 110 + j*22, line, 11, "#333")
if i < 2:
s += svg_arrow(x + mw, 155, x + mw + 15, 155, "#999", 2, 12)
s += svg_rect(30, 280, W-60, 40, C_HIGHLIGHT, "#f57f17", 8, 2)
s += svg_text(W/2, 300, "AI编审四项自动检查: 史实校验 + 来源完整性 + 版权合规 + 政治合规", 12, "#e65100", True)
s += svg_text(W/2, 335, "AI是工具不是作者,人的创造力是核心 — 所有AI生成内容强制标注", 11, "#666")
save_svg_and_jpg(s, "12_f6_cocreation")
# ═══════════════════════════════════════════════════════════════
# 图13: F7行动实验室
# ═══════════════════════════════════════════════════════════════
def gen_13_f7_action():
W, H = 860, 350
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "F7 现实行动实验室", 22, C_TITLE, True)
modules = [
("行动选择终端", "行动方向展示\n智能推荐(基于参观记录)\n行动模板选择\n自定义行动\n团队行动创建"),
("行动跟踪系统", "行动卡片生成\n7/30/90天跟踪\n进展提醒推送\n反馈表单"),
("成果回流系统", "成果提交入口\n成果墙展示\n课程案例转化\n年度优秀评选"),
]
mw = 250
for i, (title, desc) in enumerate(modules):
x = 30 + i * (mw + 15)
fill = [C_LAYER3, C_LAYER1, C_LAYER2][i]
stroke = [C_LAYER3_B, C_LAYER1_B, C_LAYER2_B][i]
s += svg_rect(x, 55, mw, 200, fill, stroke, 10, 2)
s += svg_text(x + mw/2, 82, title, 14, stroke, True)
for j, line in enumerate(desc.split("\n")):
s += svg_text(x + mw/2, 110 + j*22, line, 11, "#333")
if i < 2:
s += svg_arrow(x + mw, 155, x + mw + 15, 155, "#999", 2, 12)
s += svg_rect(30, 280, W-60, 40, C_HIGHLIGHT, "#f57f17", 8, 2)
s += svg_text(W/2, 300, "把「一次性参观」变成「持续性行动」 — 优秀成果成为系统内容资产", 12, "#e65100", True)
s += svg_text(W/2, 335, "三类行动: 个人7天(实事求是调查员) | 团队30天(南泥湾式创新) | 湾区90天(红色故事传播者)", 11, "#666")
save_svg_and_jpg(s, "13_f7_action_lab")
# ═══════════════════════════════════════════════════════════════
# 图14: F8星火节点网络
# ═══════════════════════════════════════════════════════════════
def gen_14_f8_nodes():
W, H = 860, 520
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "F8 星火节点全国复制网络", 22, C_TITLE, True)
nodes = [
("L4 旗舰中心", "13000㎡", "完整私有云 + GPU集群\n完整知识图谱+向量库+内容仓\n全国节点调度中心\n权威内容生产与发布", C_LAYER5, C_LAYER5_B),
("L3 城市节点", "院校/区县基地", "本地服务器 + 完整展陈\n知识图谱子集 + 向量库子集\n独立运营 + 数据上报\n云端连线能力", C_LAYER2, C_LAYER2_B),
("L2 标准节点", "高校/企事业单位", "触屏终端 + 边缘AI + VR\n本地内容缓存 + 云端AI增强\n成长护照接入\n7天标准化部署", C_LAYER1, C_LAYER1_B),
("L1 微节点", "一体机", "一块触屏交互一体机\n离线内容包 + AI轻量版\n定期WiFi/4G同步更新", C_LAYER6, C_LAYER6_B),
]
nh = 90
for i, (title, scale, desc, fill, stroke) in enumerate(nodes):
y = 55 + i * (nh + 10)
s += svg_rect(40, y, W-80, nh, fill, stroke, 10, 2)
s += svg_text(160, y + 25, title, 14, stroke, True)
s += svg_text(160, y + 48, scale, 11, "#666")
for j, line in enumerate(desc.split("\n")):
s += svg_text(450, y + 20 + j*18, line, 11, "#333")
if i < 3:
s += svg_arrow_down(W/2, y + nh, y + nh + 10, "#999", 2, 10)
s += svg_text(W/2, 490, "组件包标准化分发 — 像App Store更新一样简单", 12, C_LAYER3_B, True)
save_svg_and_jpg(s, "14_f8_spark_nodes")
# ═══════════════════════════════════════════════════════════════
# 图15: VR/XR整体架构
# ═══════════════════════════════════════════════════════════════
def gen_15_vr_architecture():
W, H = 900, 420
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "VR/XR 沉浸体验系统架构", 22, C_TITLE, True)
layers = [
("内容生产层", ["Unity场景工程", "3D资产管线", "交互逻辑脚本", "AI NPC驱动", "场景烘焙渲染"], C_VR, C_VR_B),
("运行时引擎", ["场景管理器", "多人同步引擎", "编排对接层", "安全监控守护", "性能监控"], C_LAYER4, C_LAYER4_B),
("设备与体感层", ["VR头显(Pico/Meta)", "6DOF动态座椅", "手部追踪", "触觉反馈背心", "风效/热效/气味"], C_LAYER3, C_LAYER3_B),
]
lh = 100
for i, (title, items, fill, stroke) in enumerate(layers):
y = 55 + i * (lh + 10)
s += svg_rect(30, y, W-60, lh, fill, stroke, 10, 2)
s += svg_text(130, y + 25, title, 14, stroke, True)
iw = 140
for j, item in enumerate(items):
x = 240 + j * (iw + 10)
s += svg_rect(x, y + 45, iw, 35, "#fff", stroke, 6, 1)
s += svg_text(x + iw/2, y + 63, item, 10, "#333")
if i < 2:
s += svg_arrow_down(W/2, y + lh, y + lh + 10, "#999", 2, 10)
s += svg_text(W/2, 390, "Unity + XR Interaction Toolkit — 一次开发多设备运行 | 6DOF体感+五感同步沉浸", 12, C_VR_B, True)
save_svg_and_jpg(s, "15_vr_xr_architecture")
# ═══════════════════════════════════════════════════════════════
# 图16: 多人协同VR架构
# ═══════════════════════════════════════════════════════════════
def gen_16_vr_multiplayer():
W, H = 700, 520
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "多人协同VR架构", 22, C_TITLE, True)
# 4个VR座位
seats = ["VR座位1\n(队长)", "VR座位2\n(队员)", "VR座位3\n(队员)", "VR座位4\n(队员)"]
for i, label in enumerate(seats):
x = 60 + i * 150
s += svg_rect(x, 60, 130, 60, C_VR, C_VR_B, 8, 2)
for j, line in enumerate(label.split("\n")):
s += svg_text(x + 65, 78 + j*18, line, 11, C_VR_B, j==0)
# 从座位底部到状态同步服务器顶部,用箭头
s += svg_arrow(x + 65, 120, 350, 170, C_ARROW, 1.5, 10)
# 状态同步服务器
s += svg_rect(200, 170, 300, 60, C_LAYER4, C_LAYER4_B, 10, 2)
s += svg_text(W/2, 195, "状态同步服务器", 14, C_LAYER4_B, True)
s += svg_text(W/2, 215, "Photon Fusion — 头像/手部/任务进度实时同步", 11, "#333")
s += svg_arrow_down(W/2, 230, 250, C_ARROW, 2, 12)
# 房间管理
s += svg_rect(200, 250, 300, 50, C_LAYER3, C_LAYER3_B, 10, 2)
s += svg_text(W/2, 275, "房间管理服务 — 4人同场,场次调度", 12, C_LAYER3_B, True)
s += svg_arrow_down(W/2, 300, 320, C_ARROW, 2, 12)
# X1编排
s += svg_rect(200, 320, 300, 50, C_LAYER1, C_LAYER1_B, 10, 2)
s += svg_text(W/2, 345, "X1编排引擎 — 任务剧本推送", 12, C_LAYER1_B, True)
# 底部说明
s += svg_rect(50, 400, W-100, 90, C_HIGHLIGHT, "#f57f17", 8, 2)
s += svg_text(W/2, 425, "技术亮点", 13, "#e65100", True)
s += svg_text(W/2, 448, "• Photon Fusion状态同步: 4人虚拟化身和手势实时可见", 11, "#333")
s += svg_text(W/2, 468, "• 角色与F2任务对应: 各有专属资源和约束,真正的团队协作", 11, "#333")
s += svg_text(W/2, 488, "• VR内AI NPC: 由AI可信知识引擎驱动,可对话的AI角色", 11, "#333")
save_svg_and_jpg(s, "16_vr_multiplayer")
# ═══════════════════════════════════════════════════════════════
# 图17: 云端VR串流
# ═══════════════════════════════════════════════════════════════
def gen_17_vr_cloud_streaming():
W, H = 860, 400
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "云端VR串流方案", 22, C_TITLE, True)
# 左侧: 节点
s += svg_rect(40, 70, 300, 200, C_LAYER1, C_LAYER1_B, 10, 2)
s += svg_text(190, 100, "L2/L3节点 (轻量硬件)", 14, C_LAYER1_B, True)
items_l = ["VR头显", "WiFi/5G传输", "解码显示", "手柄/头显数据"]
for i, item in enumerate(items_l):
s += svg_text(190, 130 + i*30, item, 12, "#333")
if i < 3:
s += svg_arrow_down(190, 140 + i*30, 150 + i*30, "#bbb", 1.5, 8)
# 中间: 数据流 — 双向箭头
s += svg_arrow(340, 130, 460, 130, C_VR_B, 2, 12)
s += svg_arrow(460, 130, 340, 130, C_VR_B, 2, 12)
s += svg_text(400, 118, "视频流", 10, C_VR_B, True)
s += svg_arrow(340, 160, 460, 160, C_VR_B, 2, 12)
s += svg_arrow(460, 160, 340, 160, C_VR_B, 2, 12)
s += svg_text(400, 148, "音频流", 10, C_VR_B, True)
s += svg_arrow(340, 190, 460, 190, C_VR_B, 2, 12)
s += svg_text(400, 178, "输入流", 10, C_VR_B, True)
s += svg_text(400, 220, "H.265超低延迟传输", 11, "#999")
# 右侧: 云端
s += svg_rect(520, 70, 300, 200, C_LAYER5, C_LAYER5_B, 10, 2)
s += svg_text(670, 100, "旗舰中心/区域云端 (GPU集群)", 14, C_LAYER5_B, True)
items_r = ["云端渲染服务器", "Unity应用实例", "串流编码H.265", "超低延迟传输"]
for i, item in enumerate(items_r):
s += svg_text(670, 130 + i*30, item, 12, "#333")
# 方案表
s += svg_text(W/2, 310, "四种方案对比", 13, C_TITLE, True)
schemes = [("CloudXR\n15-30ms\nL3城市节点", C_LAYER2, C_LAYER2_B),
("ALVR开源\n20-40ms\nL2标准节点", C_LAYER1, C_LAYER1_B),
("本地渲染\n&lt;10ms\nL4旗舰中心", C_LAYER5, C_LAYER5_B),
("WebXR\n30-80ms\nF5云展馆", C_LAYER6, C_LAYER6_B)]
sw = 190
for i, (label, fill, stroke) in enumerate(schemes):
x = 30 + i * (sw + 10)
s += svg_rect(x, 325, sw, 55, fill, stroke, 8, 1.5)
for j, line in enumerate(label.split("\n")):
s += svg_text(x + sw/2, 343 + j*16, line, 10, stroke, j==0)
save_svg_and_jpg(s, "17_vr_cloud_streaming")
# ═══════════════════════════════════════════════════════════════
# 图18: X1编排数据流
# ═══════════════════════════════════════════════════════════════
def gen_18_x1_dataflow():
W, H = 860, 320
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "X1体验编排引擎 — 数据流", 22, C_TITLE, True)
# 事件源
sources = ["UWB位置流\n(10Hz)", "客流计数\n(实时)", "设备状态\n(实时)", "用户路线\n(API)", "任务状态\n(API)"]
s += svg_text(120, 75, "事件源", 13, C_TITLE, True)
for i, src in enumerate(sources):
y = 90 + i * 38
s += svg_rect(40, y, 160, 32, C_LAYER1, C_LAYER1_B, 6, 1.5)
s += svg_text(120, y + 16, src.replace("\n", " "), 10, C_LAYER1_B, True)
# 从每个事件源到处理引擎的箭头
s += svg_arrow(200, y + 16, 280, 175, C_ARROW, 1.5, 10)
# 处理引擎
s += svg_rect(280, 130, 260, 90, C_HIGHLIGHT, "#f57f17", 10, 2)
s += svg_text(410, 160, "实时流式计算", 14, "#e65100", True)
s += svg_text(410, 185, "复杂事件处理(CEP)", 12, "#333")
s += svg_text(410, 205, "规则 + AI编排决策", 12, "#333")
s += svg_arrow(540, 175, 620, 175)
# 输出
s += svg_text(700, 75, "输出目标", 13, C_TITLE, True)
outputs = ["设备控制指令\n(实时消息推送)", "用户学习记录\n(写入成长护照)", "客流调度信号\n(反馈智慧场馆)"]
for i, out in enumerate(outputs):
y = 90 + i * 55
s += svg_rect(620, y, 200, 42, C_LAYER2, C_LAYER2_B, 6, 1.5)
for j, line in enumerate(out.split("\n")):
s += svg_text(720, y + 14 + j*16, line, 10, C_LAYER2_B, True)
# 从处理引擎到每个输出目标的箭头
s += svg_arrow(540, 175, 620, y + 21, C_ARROW, 1.5, 10)
s += svg_text(W/2, 290, "五路数据流实时汇入 — 系统对全馆状态有「上帝视角」", 12, "#666")
save_svg_and_jpg(s, "18_x1_dataflow")
# ═══════════════════════════════════════════════════════════════
# 图19: X2跨系统同步
# ═══════════════════════════════════════════════════════════════
def gen_19_x2_sync():
W, H = 700, 380
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "X2 成长护照 — 跨系统同步", 22, C_TITLE, True)
# 数据源
sources = ["票务系统", "教研系统", "港澳专项", "数据中台", "线下终端"]
for i, src in enumerate(sources):
y = 70 + i * 45
s += svg_rect(40, y, 160, 35, C_LAYER1, C_LAYER1_B, 8, 1.5)
s += svg_text(120, y + 18, src, 12, C_LAYER1_B, True)
# 从数据源到中心API的箭头
s += svg_arrow(200, y + 17, 280, 200, C_ARROW, 1.5, 10)
# 中心
s += svg_rect(280, 165, 200, 70, C_LAYER4, C_LAYER4_B, 10, 2)
s += svg_text(380, 190, "X2成长护照统一API", 13, C_LAYER4_B, True)
s += svg_text(380, 215, "GraphQL统一查询", 12, "#333")
# 输出
s += svg_rect(520, 165, 140, 70, C_LAYER2, C_LAYER2_B, 10, 2)
s += svg_text(590, 190, "统一学习档案", 13, C_LAYER2_B, True)
s += svg_text(590, 215, "全生命周期", 12, "#333")
s += svg_arrow(480, 200, 520, 200, C_ARROW, 2, 14)
# 底部
s += svg_text(W/2, 300, "从第一次预约到第90天行动成果,一个人的完整学习旅程在一本护照里", 12, "#666")
s += svg_text(W/2, 325, "对前端来说,所有数据来自一个「虚拟数据库」", 12, C_LAYER4_B, True)
s += svg_text(W/2, 355, "北极星指标可计算: 完成核心学习路径+理解验证+形成行动+产生反馈的完整闭环人数", 11, "#e65100")
save_svg_and_jpg(s, "19_x2_sync")
# ═══════════════════════════════════════════════════════════════
# 图20: X3审核工作流
# ═══════════════════════════════════════════════════════════════
def gen_20_x3_review():
W, H = 700, 480
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "X3 权威内容治理中心 — 审核工作流", 20, C_TITLE, True)
steps = [
("内容创建/修改", C_LAYER6, C_LAYER6_B),
("AI编审助手自动检查\n事实校验·来源检查·合规检查·版权检查", C_LAYER4, C_LAYER4_B),
("编辑复审 (人工)", C_LAYER3, C_LAYER3_B),
("专家终审 (关键内容)", C_SECURITY, C_SECURITY_B),
("版本发布 (带版本号,可回滚)", C_LAYER2, C_LAYER2_B),
("同步到: 展陈/云展馆/课程平台/全国节点", C_LAYER1, C_LAYER1_B),
]
for i, (step, fill, stroke) in enumerate(steps):
y = 65 + i * 60
s += svg_rect(100, y, W-200, 45, fill, stroke, 8, 2)
s += svg_text(W/2, y + 23, step, 12, stroke, True)
if i < len(steps) - 1:
s += svg_arrow_down(W/2, y + 45, y + 60, "#999", 2, 12)
# 底部
s += svg_rect(50, 440, W-100, 30, C_HIGHLIGHT, "#f57f17", 6, 1.5)
s += svg_text(W/2, 456, "AI初审自动化把审核效率提升10倍,人工只做最关键的终审", 11, "#e65100", True)
save_svg_and_jpg(s, "20_x3_review_workflow")
# ═══════════════════════════════════════════════════════════════
# 图21: 网络安全架构
# ═══════════════════════════════════════════════════════════════
def gen_21_security():
W, H = 700, 420
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "网络安全架构", 22, C_TITLE, True)
zones = [
("互联网", "WAF (Web应用防火墙)", C_SECURITY, C_SECURITY_B),
("DMZ区", "负载均衡 + API网关", C_LAYER3, C_LAYER3_B),
("应用区 (容器集群)", "微服务集群 (服务网格 + 自动扩缩容)", C_LAYER1, C_LAYER1_B),
("数据区 (隔离)", "业务数据库 | 知识图谱 | 向量库 | 内容仓 | GPU推理集群", C_LAYER4, C_LAYER4_B),
("设备区 (VLAN隔离)", "IoT/安防/VR/灯光音频 (独立VLAN)", C_LAYER6, C_LAYER6_B),
]
for i, (zone, content, fill, stroke) in enumerate(zones):
y = 60 + i * 65
s += svg_rect(50, y, W-100, 55, fill, stroke, 10, 2)
s += svg_text(180, y + 20, zone, 13, stroke, True)
s += svg_text(W/2, y + 40, content, 11, "#333")
if i < len(zones) - 1:
s += svg_arrow_down(W/2, y + 55, y + 65, "#999", 2, 12)
save_svg_and_jpg(s, "21_security_architecture")
# ═══════════════════════════════════════════════════════════════
# 图22: AI安全三层护栏
# ═══════════════════════════════════════════════════════════════
def gen_22_ai_guardrail():
W, H = 600, 480
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "AI安全三层护栏", 22, C_TITLE, True)
# 用户输入
s += svg_rect(200, 60, 200, 40, C_LAYER6, C_LAYER6_B, 8, 2)
s += svg_text(W/2, 80, "用户输入", 14, C_LAYER6_B, True)
s += svg_arrow_down(W/2, 100, 125, "#999", 2, 12)
# 第一层
s += svg_rect(150, 125, 300, 50, C_SECURITY, C_SECURITY_B, 10, 2)
s += svg_text(W/2, 145, "第一层: 输入过滤", 13, C_SECURITY_B, True)
s += svg_text(W/2, 162, "拦截不当提问", 11, "#333")
s += svg_arrow_down(W/2, 175, 195, "#999", 2, 12)
# 第二层
s += svg_rect(150, 195, 300, 50, C_SECURITY, C_SECURITY_B, 10, 2)
s += svg_text(W/2, 215, "第二层: 事实校验", 13, C_SECURITY_B, True)
s += svg_text(W/2, 232, "AI回答与知识图谱比对,发现错误则拦截", 11, "#333")
s += svg_arrow_down(W/2, 245, 265, "#999", 2, 12)
# 第三层
s += svg_rect(150, 265, 300, 50, C_SECURITY, C_SECURITY_B, 10, 2)
s += svg_text(W/2, 285, "第三层: 输出过滤", 13, C_SECURITY_B, True)
s += svg_text(W/2, 302, "拦截不当表述", 11, "#333")
s += svg_arrow_down(W/2, 315, 335, "#999", 2, 12)
# 输出
s += svg_rect(100, 335, 400, 50, C_LAYER2, C_LAYER2_B, 10, 2)
s += svg_text(W/2, 355, "安全回答 + 史料来源 + 审计日志", 13, C_LAYER2_B, True)
s += svg_text(W/2, 372, "100%不当内容拦截率", 11, "#333")
# 底部
s += svg_text(W/2, 420, "NPC禁止模拟革命领袖发表新言论,仅复述已审核史料", 11, "#666")
s += svg_text(W/2, 445, "全部对话可审计追溯,支持质量回查", 11, "#666")
save_svg_and_jpg(s, "22_ai_guardrail")
# ═══════════════════════════════════════════════════════════════
# 图23: 多语言与多人群适配流程
# ═══════════════════════════════════════════════════════════════
def gen_23_multilang_flow():
W, H = 520, 620
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "多语言与多人群适配流程", 20, C_TITLE, True)
steps = [
("用户提问(繁体中文)", C_LAYER6, C_LAYER6_B),
("意图识别 + 人群类型判断(港澳中学生)", C_LAYER1, C_LAYER1_B),
("RAG检索(史料库语义检索,召回相关文献)", C_LAYER4, C_LAYER4_B),
("大模型生成(适配港澳中学生理解水平,繁体中文输出)", C_LAYER3, C_LAYER3_B),
("安全护栏(事实校验 + 输出过滤)", C_SECURITY, C_SECURITY_B),
("TTS语音合成(粤语发音可选)", C_LAYER2, C_LAYER2_B),
("回答 + 史料来源 + 追问建议", C_LAYER5, C_LAYER5_B),
]
bw = 420
bh = 50
x0 = (W - bw) // 2
y0 = 55
gap = 22
for i, (step, fill, stroke) in enumerate(steps):
y = y0 + i * (bh + gap)
s += svg_rect(x0, y, bw, bh, fill, stroke, 10, 2)
s += svg_text(W/2, y + bh/2 + 5, step, 12, stroke, True)
if i < len(steps) - 1:
s += svg_arrow_down(W/2, y + bh, y + bh + gap, "#999", 2, 14)
s += svg_text(W/2, H - 20, "同一问题 · 不同人群 · 不同语言 · 不同深度", 11, "#666")
save_svg_and_jpg(s, "23_multilang_flow")
# ═══════════════════════════════════════════════════════════════
# 图24: X1体验编排引擎详细架构
# ═══════════════════════════════════════════════════════════════
def gen_24_x1_detail():
W, H = 860, 680
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "X1 体验编排引擎 — 详细架构", 20, C_TITLE, True)
# 外框
s += svg_rect(30, 50, W-60, H-70, "#fafafa", C_LAYER3_B, 12, 2)
# ── 输入区 ──
s += svg_text(60, 78, "输入", 14, C_LAYER3_B, True)
inputs = [
("用户路线", "来自可信叙事引擎"),
("实时位置", "UWB定位 10Hz更新"),
("实时客流", "摄像头+IoT传感器"),
("设备状态", "监控系统"),
]
iw = 180
ih = 55
ix0 = 50
for i, (title, desc) in enumerate(inputs):
x = ix0 + i * (iw + 15)
s += svg_rect(x, 88, iw, ih, C_LAYER3, C_LAYER3_B, 8, 1.5)
s += svg_text(x + iw/2, 108, title, 12, C_LAYER3_B, True)
s += svg_text(x + iw/2, 128, desc, 10, "#666")
# 汇聚箭头 → 编排逻辑
for i in range(4):
x = ix0 + i * (iw + 15) + iw/2
s += svg_arrow(x, 143, W/2, 175, C_ARROW, 1.5, 10)
# ── 编排逻辑区 ──
s += svg_text(W/2, 168, "编排逻辑 (实时流式计算)", 14, "#e65100", True)
s += svg_rect(50, 175, W-100, 250, C_HIGHLIGHT, "#f57f17", 10, 2)
steps = [
("1. 用户进入区域", "匹配路线节点"),
("2. 查询内容组件", "该节点对应的内容组件"),
("3. 下发指令到设备", "屏幕/灯光/音频/AI终端/互动设备"),
("4. 客流拥挤", "切换备选节点 → 重新下发指令"),
]
sy = 190
for i, (title, desc) in enumerate(steps):
y = sy + i * 55
s += svg_rect(70, y, W-140, 42, "#fff", "#f57f17", 8, 1.5)
s += svg_text(90, y + 26, title, 12, "#e65100", True)
s += svg_text(280, y + 26, desc, 11, "#333")
if i < 3:
s += svg_arrow_down(W/2, y + 42, y + 55, "#f57f17", 1.5, 10)
# 设备指令细分 (第3步展开)
dev_cmds = ["屏幕: 播放对应内容", "灯光: 切换场景灯光", "音频: 播放对应导览", "AI终端: 加载讲解上下文", "互动设备: 激活对应任务"]
s += svg_text(W/2, 415, "第3步设备指令明细", 11, "#e65100", True)
dcw = 150
for i, cmd in enumerate(dev_cmds):
x = 55 + i * (dcw + 5)
s += svg_rect(x, 425, dcw, 30, "#fff", "#f57f17", 6, 1)
s += svg_text(x + dcw/2, 444, cmd, 9, "#333")
# ── 输出区 ──
s += svg_arrow_down(W/2, 460, 475, C_ARROW, 2, 14)
s += svg_text(W/2, 470, "输出", 14, C_LAYER2_B, True)
outputs = [
("设备控制指令", "实时消息推送"),
("用户学习记录", "写入成长护照"),
("客流调度信号", "反馈智慧场馆"),
]
ow = 220
ox0 = (W - 3*ow - 2*20) // 2
for i, (title, desc) in enumerate(outputs):
x = ox0 + i * (ow + 20)
s += svg_rect(x, 480, ow, 55, C_LAYER2, C_LAYER2_B, 8, 2)
s += svg_text(x + ow/2, 500, title, 12, C_LAYER2_B, True)
s += svg_text(x + ow/2, 520, desc, 10, "#666")
s += svg_text(W/2, H - 25, "实时事件驱动 · 毫秒级响应 · 同一空间千人千面", 12, C_LAYER3_B, True)
save_svg_and_jpg(s, "24_x1_detail")
# ═══════════════════════════════════════════════════════════════
# 图25: F3文物扫码体验流程
# ═══════════════════════════════════════════════════════════════
def gen_25_f3_scan_flow():
W, H = 540, 660
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "F3 文物扫码体验流程", 20, C_TITLE, True)
steps = [
("展柜前扫描二维码", C_LAYER6, C_LAYER6_B),
("手机加载文物星图H5", C_LAYER1, C_LAYER1_B),
("L1 看见: 3D模型 + 基本信息", C_LAYER6, C_LAYER6_B),
("上滑 → L2 知道: 元数据卡片", C_LAYER1, C_LAYER1_B),
("点击星图节点 → L3 理解: 关联网络可视化", C_LAYER2, C_LAYER2_B),
('点击"深度研究" → L4 研究: 档案文献', C_LAYER3, C_LAYER3_B),
('点击"相关课程" → L5 连接: 课程推荐', C_LAYER4, C_LAYER4_B),
('点击"做一做" → 跳转F7行动选择', C_LAYER5, C_LAYER5_B),
]
bw = 440
bh = 48
x0 = (W - bw) // 2
y0 = 55
gap = 26
for i, (step, fill, stroke) in enumerate(steps):
y = y0 + i * (bh + gap)
s += svg_rect(x0, y, bw, bh, fill, stroke, 10, 2)
s += svg_text(W/2, y + bh/2 + 5, step, 12, stroke, True)
if i < len(steps) - 1:
s += svg_arrow_down(W/2, y + bh, y + bh + gap, "#999", 2, 14)
s += svg_text(W/2, H - 20, "一件文物 = 一个知识宇宙 · 从看见到行动", 11, "#666")
save_svg_and_jpg(s, "25_f3_scan_flow")
# ═══════════════════════════════════════════════════════════════
# 图26: F2情境任务剧场 VR/非VR双模式共享架构
# ═══════════════════════════════════════════════════════════════
def gen_26_f2_vr_share():
W, H = 720, 560
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "F2 情境任务剧场 — VR/非VR双模式共享架构", 18, C_TITLE, True)
# 顶部: F2
s += svg_rect(W/2-130, 50, 260, 45, C_LAYER3, C_LAYER3_B, 10, 2)
s += svg_text(W/2, 78, "F2 情境任务剧场", 15, C_LAYER3_B, True)
# 分叉箭头到两个模式
s += svg_arrow(W/2, 95, W/2-160, 130, C_ARROW, 2, 12)
s += svg_arrow(W/2, 95, W/2+160, 130, C_ARROW, 2, 12)
# 左: 非VR模式
s += svg_rect(60, 130, 280, 160, C_LAYER1, C_LAYER1_B, 10, 2)
s += svg_text(200, 155, "非VR模式", 14, C_LAYER1_B, True)
non_vr = ["多屏互动终端", "AI复盘"]
for i, item in enumerate(non_vr):
s += svg_rect(90, 170+i*50, 220, 38, "#fff", C_LAYER1_B, 8, 1.5)
s += svg_text(200, 193+i*50, item, 12, "#333")
# 右: VR模式
s += svg_rect(380, 130, 280, 160, C_VR, C_VR_B, 10, 2)
s += svg_text(520, 155, "VR模式", 14, C_VR_B, True)
vr_items = ["沉浸场景", "体感设备", "多人协同", "AI复盘"]
for i, item in enumerate(vr_items):
s += svg_rect(410, 170+i*32, 220, 26, "#fff", C_VR_B, 6, 1.2)
s += svg_text(520, 188+i*32, item, 11, "#333")
# 汇聚箭头到共享层
s += svg_arrow(200, 290, W/2, 330, C_ARROW, 2, 12)
s += svg_arrow(520, 290, W/2, 330, C_ARROW, 2, 12)
# 共享层
s += svg_rect(80, 330, W-160, 200, C_LAYER5, C_LAYER5_B, 10, 2)
s += svg_text(W/2, 358, "共享层", 14, C_LAYER5_B, True)
shared = [
"同一任务定义",
"同一证据集(从知识图谱检索)",
"同一历史事实揭示",
"同一AI学习教练复盘",
"同一成长护照记录",
]
for i, item in enumerate(shared):
y = 375 + i * 28
s += svg_rect(120, y, W-240, 22, "#fff", C_LAYER5_B, 6, 1)
s += svg_text(W/2, y+15, item, 11, "#333")
s += svg_text(W/2, H - 20, "不同体验通道 · 同一学习内核", 12, C_LAYER5_B, True)
save_svg_and_jpg(s, "26_f2_vr_share")
# ═══════════════════════════════════════════════════════════════
# 图27: VR体验全流程
# ═══════════════════════════════════════════════════════════════
def gen_27_vr_experience_flow():
W, H = 720, 780
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "VR 体验全流程", 20, C_TITLE, True)
phases = [
("排队区(3分钟)", C_LAYER6, C_LAYER6_B, [
"观看历史背景视频",
"查看分配到的角色和资源约束",
"学习VR手柄操作和交互方式",
"了解安全须知和紧急退出方法",
"查看排队位置和预计等待时间",
]),
("进入VR工位", C_LAYER1, C_LAYER1_B, [
"佩戴VR头显 + 触觉背心",
"系统加载场景(按人群类型适配内容)",
"4人同步进入同一历史场景",
]),
("沉浸体验(8-15分钟)", C_VR, C_VR_B, [
"在虚拟场景中执行任务",
"抓取文物、查看证据",
"与AI NPC对话(可提问,NPC用史料回答)",
"4人语音讨论 + 手势交互",
"体感设备同步场景事件",
"团队做出决策",
]),
("退出VR", C_LAYER5, C_LAYER5_B, [
"AI复盘(学习教练分析方案vs历史)",
"现实连接问题",
"学习记录写入成长护照",
"设备复位 + 消毒(3分钟)",
]),
]
pw = 560
x0 = (W - pw) // 2
y = 55
for pi, (title, fill, stroke, items) in enumerate(phases):
ph = 40 + len(items) * 26 + 10
s += svg_rect(x0, y, pw, ph, fill, stroke, 10, 2)
s += svg_rect(x0, y, pw, 32, fill, stroke, 10, 2)
s += svg_text(W/2, y + 21, title, 14, stroke, True)
for i, item in enumerate(items):
iy = y + 42 + i * 26
s += svg_text(x0 + 20, iy + 16, "" + item, 11, "#333")
y += ph
if pi < len(phases) - 1:
s += svg_arrow_down(W/2, y, y + 20, C_ARROW, 2, 14)
y += 20
s += svg_text(W/2, H - 18, "从排队到离场 · 全流程数据记录 · 无缝衔接成长护照", 11, "#666")
save_svg_and_jpg(s, "27_vr_experience_flow")
# ═══════════════════════════════════════════════════════════════
# 图28: 最小可行原型组成
# ═══════════════════════════════════════════════════════════════
def gen_28_mvp():
W, H = 860, 420
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "最小可行原型 (MVP)", 20, C_TITLE, True)
# 左侧: 最小原型标签
s += svg_rect(40, 140, 130, 140, C_LAYER3, C_LAYER3_B, 10, 2)
s += svg_text(105, 195, "最小原型", 16, C_LAYER3_B, True)
s += svg_text(105, 220, "MVP", 13, C_LAYER3_B)
s += svg_text(105, 250, "6个核心模块", 10, "#666")
# 等号
s += svg_text(195, 210, "=", 28, C_LAYER3_B, True)
# 6个组件 (2行×3列)
comps = [
("文物3D展示", "1件", C_LAYER6, C_LAYER6_B),
("情境任务", "1个", C_LAYER1, C_LAYER1_B),
("AI问答入口", "1个", C_LAYER4, C_LAYER4_B),
("成长护照H5", "1个", C_LAYER2, C_LAYER2_B),
("行动选择页", "1个", C_LAYER5, C_LAYER5_B),
("30天回访推送", "1个", C_VR, C_VR_B),
]
cw = 175
ch = 80
cx0 = 235
cy0 = 100
gap_x = 20
gap_y = 30
for i, (title, qty, fill, stroke) in enumerate(comps):
col = i % 3
row = i // 3
x = cx0 + col * (cw + gap_x)
y = cy0 + row * (ch + gap_y)
s += svg_rect(x, y, cw, ch, fill, stroke, 10, 2)
s += svg_text(x + cw/2, y + 28, qty, 12, stroke, True)
s += svg_text(x + cw/2, y + 55, title, 14, stroke, True)
# + 号连接 (同行内)
if col < 2:
s += svg_text(x + cw + gap_x/2 - 5, y + ch/2 + 5, "+", 20, "#999", True)
# 第一行末到第二行首的连接箭头
if i == 2:
s += svg_arrow(x + cw/2, y + ch, x + cw/2, y + ch + 10, "#999", 1.5, 10)
s += svg_arrow(x + cw/2, y + ch + 10, cx0 + cw/2, y + ch + gap_y, "#999", 1.5, 10)
s += svg_text(x + cw/2 + 15, y + ch + 12, "+", 16, "#999", True)
# 底部说明
s += svg_text(W/2, H - 25, "6个模块 = 1条完整体验链路 · 从看到文物到行动反馈", 12, "#666")
save_svg_and_jpg(s, "28_mvp")
# ═══════════════════════════════════════════════════════════════
# 图29: MVP用户体验流程
# ═══════════════════════════════════════════════════════════════
def gen_29_mvp_flow():
W, H = 860, 580
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "MVP 用户体验流程", 20, C_TITLE, True)
steps = [
("触屏终端开始", "用户入场", C_LAYER6, C_LAYER6_B),
("查看文物3D模型", "可旋转缩放", C_LAYER1, C_LAYER1_B),
("参与情境任务", "阅读证据 → 团队讨论 → 提交方案 → 历史揭示 → AI复盘", C_LAYER4, C_LAYER4_B),
("向AI提问", "获得带史料引用的回答", C_LAYER3, C_LAYER3_B),
("打开手机H5查看成长护照", "离馆后继续", C_LAYER2, C_LAYER2_B),
("选择一个行动方向", "F7现实行动", C_LAYER5, C_LAYER5_B),
("30天后收到微信推送", "提交行动反馈", C_VR, C_VR_B),
]
bw = 680
bh = 52
x0 = (W - bw) // 2
y0 = 55
gap = 22
for i, (title, desc, fill, stroke) in enumerate(steps):
y = y0 + i * (bh + gap)
s += svg_rect(x0, y, bw, bh, fill, stroke, 10, 2)
s += svg_text(x0 + 30, y + bh/2 + 5, title, 13, stroke, True)
s += svg_text(x0 + bw - 20, y + bh/2 + 5, desc, 11, "#666")
if i < len(steps) - 1:
s += svg_arrow_down(W/2, y + bh, y + bh + gap, C_ARROW, 2, 14)
s += svg_text(W/2, H - 18, "从入场到离馆后30天 · 一条完整的体验闭环", 11, "#666")
save_svg_and_jpg(s, "29_mvp_flow")
# ═══════════════════════════════════════════════════════════════
# 图30: MVP原型部署方案
# ═══════════════════════════════════════════════════════════════
def gen_30_mvp_deploy():
W, H = 760, 460
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "MVP 原型部署方案", 20, C_TITLE, True)
s += svg_text(W/2, 55, "单服务器部署(原型阶段)", 13, "#666")
# 外框
s += svg_rect(40, 75, W-80, 340, "#fafafa", C_LAYER6_B, 12, 2)
servers = [
("GPU服务器", "×1台", "AI推理 + 向量检索", C_LAYER3, C_LAYER3_B),
("应用服务器", "×1台", "业务服务 + 缓存", C_LAYER1, C_LAYER1_B),
("前端服务器", "×1台", "3D展示 + 小程序后端", C_LAYER4, C_LAYER4_B),
("互动终端", "×1台", "情境任务 + AI问答界面", C_LAYER2, C_LAYER2_B),
]
bw = 300
bh = 60
x0 = (W - bw) // 2
y0 = 95
gap = 20
for i, (name, qty, desc, fill, stroke) in enumerate(servers):
y = y0 + i * (bh + gap)
s += svg_rect(x0, y, bw, bh, fill, stroke, 10, 2)
s += svg_text(x0 + 20, y + 25, name, 14, stroke, True)
s += svg_text(x0 + bw - 20, y + 25, qty, 12, stroke, True)
s += svg_text(x0 + 20, y + 48, desc, 11, "#333")
if i < len(servers) - 1:
s += svg_arrow_down(W/2, y + bh, y + bh + gap, "#bbb", 1.5, 10)
s += svg_text(W/2, H - 20, "4台设备 · 最小成本验证完整体验闭环", 12, C_LAYER6_B, True)
save_svg_and_jpg(s, "30_mvp_deploy")
# ═══════════════════════════════════════════════════════════════
# 图31: 核心体验闭环
# ═══════════════════════════════════════════════════════════════
def gen_31_core_loop():
import math as _m
W, H = 760, 560
s = svg_header(W, H)
s += f'<rect width="{W}" height="{H}" fill="{C_BG}"/>'
s += svg_text(W/2, 30, "核心体验闭环", 20, C_TITLE, True)
stages = [
("提问", "参观前选择学习问题\nAI全程可问答", C_LAYER3, C_LAYER3_B),
("体验", "自适应路线 + 沉浸VR\n情境任务 + 文物星图", C_VR, C_VR_B),
("理解", "AI复盘 + 理解表达\n离馆报告", C_LAYER4, C_LAYER4_B),
("行动", "行动选择\n7/30/90天跟踪", C_LAYER2, C_LAYER2_B),
("反馈", "进展提交 + 成果回流\n优秀案例入库", C_LAYER5, C_LAYER5_B),
("再传播", "青年共创 + 云展馆\n全国节点复制", C_LAYER1, C_LAYER1_B),
]
n = len(stages)
cx, cy = W/2, 310
r = 180
bw, bh = 130, 60
# 计算每个节点的位置
pts = []
for i in range(n):
angle = -_m.pi/2 + i * 2 * _m.pi / n # 从顶部开始顺时针
x = cx + r * _m.cos(angle)
y = cy + r * _m.sin(angle)
pts.append((x, y, angle))
# 先画弧形箭头连接 (相邻节点之间)
for i in range(n):
x1, y1, a1 = pts[i]
x2, y2, a2 = pts[(i+1) % n]
# 在圆弧上取中点偏移,画弧线
mid_angle = (a1 + a2) / 2 if i < n-1 else (a1 + a2 + 2*_m.pi) / 2
arc_r = r - 5
mx = cx + arc_r * _m.cos(mid_angle)
my = cy + arc_r * _m.sin(mid_angle)
# 画曲线箭头
s += f'<path d="M {x1:.0f} {y1:.0f} Q {mx:.0f} {my:.0f} {x2:.0f} {y2:.0f}" fill="none" stroke="{C_ARROW}" stroke-width="2" stroke-linecap="round"/>'
# 箭头头在终点
da = mid_angle - a2 if i < n-1 else mid_angle - a2 + 2*_m.pi
bx = x2 - 12 * _m.cos(a2 + _m.pi/2)
by = y2 - 12 * _m.sin(a2 + _m.pi/2)
s += svg_arrow(bx, by, x2, y2, C_ARROW, 2, 12)
# 画节点框和文字 (覆盖在线之上)
for i, (title, desc, fill, stroke) in enumerate(stages):
x, y, angle = pts[i]
rx = x - bw/2
ry = y - bh/2
s += svg_rect(rx, ry, bw, bh, fill, stroke, 10, 2)
s += svg_text(x, y - 5, title, 15, stroke, True)
# desc 多行
lines = desc.split("\\n")
for j, line in enumerate(lines):
s += svg_text(x, y + 15 + j*14, line, 9, "#333")
# 中心文字
s += svg_text(cx, cy - 5, "核心体验", 16, C_TITLE, True)
s += svg_text(cx, cy + 15, "闭环", 16, C_TITLE, True)
s += svg_text(cx, cy + 35, "一件文物 → 一次行动", 10, "#999")
save_svg_and_jpg(s, "31_core_loop")
# ═══════════════════════════════════════════════════════════════
# 主程序
# ═══════════════════════════════════════════════════════════════
if __name__ == "__main__":
print("开始生成SVG → JPG...")
gen_01_six_layer()
gen_02_deployment()
gen_03_ai_engine()
gen_04_narrative_engine()
gen_05_growth_engine()
gen_06_replication_engine()
gen_07_f1_x1()
gen_08_f2_task()
gen_09_f3_artifact()
gen_10_f4_passport()
gen_11_f5_cloud()
gen_12_f6_cocreation()
gen_13_f7_action()
gen_14_f8_nodes()
gen_15_vr_architecture()
gen_16_vr_multiplayer()
gen_17_vr_cloud_streaming()
gen_18_x1_dataflow()
gen_19_x2_sync()
gen_20_x3_review()
gen_21_security()
gen_22_ai_guardrail()
gen_23_multilang_flow()
gen_24_x1_detail()
gen_25_f3_scan_flow()
gen_26_f2_vr_share()
gen_27_vr_experience_flow()
gen_28_mvp()
gen_29_mvp_flow()
gen_30_mvp_deploy()
gen_31_core_loop()
print(f"\n全部完成! 共生成31张JPG,保存在: {OUTPUT_DIR}")