| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368 |
- """
- lambdagentpaas 系统架构图 — PPT 优化版 v3
- 修复:
- - 卡片标题与正文留足间距 (title_gap 参数)
- - 显式计算文字实际占位, 防止与 va 基准点的计算误差
- - 增大所有卡片高度, 给正文足够空间
- - L1/L2 层高度相应增加
- - 所有尺寸支持 SCALE 参数统一缩放
- 用法:
- python3 docs/assets/draw_architecture.py
- python3 docs/assets/draw_architecture.py --scale 1.5 # 放大 1.5 倍
- python3 docs/assets/draw_architecture.py --scale 0.8 # 缩小到 0.8 倍
- """
- import argparse
- import os
- import matplotlib
- matplotlib.use("Agg")
- import matplotlib.pyplot as plt
- from matplotlib.patches import FancyBboxPatch, Rectangle
- plt.rcParams["font.sans-serif"] = [
- "PingFang HK", "PingFang SC", "Hiragino Sans GB",
- "STHeiti", "Heiti TC", "Songti SC", "Arial Unicode MS",
- ]
- plt.rcParams["axes.unicode_minus"] = False
- # ═══════════════════════════════════════════════════════════
- # 配色
- # ═══════════════════════════════════════════════════════════
- C = {
- "L3_bg": "#FFF4E0", "L3_edge": "#E67E22", "L3_header": "#F39C12",
- "L3_card": "#FFD79A",
- "L2_bg": "#E3F2FD", "L2_edge": "#1976D2", "L2_header": "#1E88E5",
- "L2_card": "#A8D0F2",
- "L2_safety_bg": "#FFEBEE", "L2_safety_edge": "#C62828",
- "L2_safety_header": "#E53935", "L2_safety_card": "#FFCDD2",
- "L1_bg": "#F3E5F5", "L1_edge": "#7B1FA2", "L1_header": "#8E24AA",
- "L1_card": "#D7A8E0", "L1_paper": "#C789D3",
- "L0_bg": "#E8F5E9", "L0_edge": "#388E3C", "L0_header": "#43A047",
- "L0_card": "#9FD9A5",
- "text": "#1A1A1A",
- "text_mid": "#37474F",
- "text_light":"#FFFFFF",
- "arrow": "#263238",
- }
- # ═══════════════════════════════════════════════════════════
- # 绘图辅助
- # ═══════════════════════════════════════════════════════════
- class Scaler:
- """统一缩放器 — 一个参数控制所有尺寸。"""
- def __init__(self, scale: float = 1.0):
- self.s = scale
- def fs(self, size: int) -> float:
- """字号缩放"""
- return size * self.s
- def lw(self, width: float) -> float:
- """线宽缩放"""
- return width * self.s
- # 数据单位的坐标 / 尺寸保持不变 (ax 坐标系固定 0-20, 0-15.5)
- # 只需要让 figsize 按 scale 变化即可让输出像素 = scale × 原始像素
- def figsize(self, w: float, h: float):
- return (w * self.s, h * self.s)
- def layer(ax, s: Scaler, x, y, w, h, header_h, label, bg, edge, header_color):
- """层: 外框 + 顶部 header 带"""
- ax.add_patch(FancyBboxPatch(
- (x, y), w, h,
- boxstyle="round,pad=0.08,rounding_size=0.18",
- facecolor=bg, edgecolor=edge, linewidth=s.lw(2.5), alpha=0.5, zorder=1,
- ))
- header_y = y + h - header_h
- ax.add_patch(Rectangle(
- (x + 0.1, header_y + 0.05), w - 0.2, header_h - 0.1,
- facecolor=header_color, edgecolor="none", alpha=0.92, zorder=2,
- ))
- ax.text(
- x + w / 2, header_y + header_h / 2, label,
- ha="center", va="center",
- fontsize=s.fs(17), fontweight="bold",
- color=C["text_light"], zorder=5,
- )
- def card(ax, s: Scaler, x, y, w, h, title, lines, face, edge,
- title_size=15, body_size=12,
- pad_top=0.22, title_gap=0.28):
- """
- 组件卡片 — 正确的文字布局
- 如果有 lines (正文):
- - 标题 va='top', 放在卡片顶部 (距顶 pad_top)
- - 正文 va='top', 在标题下方 title_gap + 标题高度 处
- 否则:
- - 标题居中显示
- """
- ax.add_patch(FancyBboxPatch(
- (x, y), w, h,
- boxstyle="round,pad=0.06,rounding_size=0.12",
- facecolor=face, edgecolor=edge, linewidth=s.lw(1.8), zorder=3,
- ))
- if lines:
- title_h = title_size / 72.0
- title_top_y = y + h - pad_top
- ax.text(
- x + w / 2, title_top_y, title,
- ha="center", va="top",
- fontsize=s.fs(title_size), fontweight="bold",
- color=C["text"], zorder=4,
- )
- body_top_y = title_top_y - title_h - title_gap
- body = "\n".join(lines)
- ax.text(
- x + w / 2, body_top_y, body,
- ha="center", va="top",
- fontsize=s.fs(body_size), color=C["text_mid"],
- linespacing=1.55, zorder=4,
- )
- else:
- ax.text(
- x + w / 2, y + h / 2, title,
- ha="center", va="center",
- fontsize=s.fs(title_size), fontweight="bold",
- color=C["text"], zorder=4,
- )
- def arrow_down(ax, s: Scaler, x, y_top, y_bot, color=None, lw=3):
- ax.annotate(
- "", xy=(x, y_bot), xytext=(x, y_top),
- arrowprops=dict(arrowstyle="-|>", color=color or C["arrow"],
- lw=s.lw(lw), mutation_scale=25),
- zorder=2,
- )
- # ═══════════════════════════════════════════════════════════
- # 主绘图
- # ═══════════════════════════════════════════════════════════
- def draw(output: str, scale: float = 1.0):
- """
- 绘制架构图。
- Args:
- output: 输出 PNG 路径
- scale: 全局缩放比例 (1.0 = 默认; 1.5 = 放大; 0.8 = 缩小)
- 所有字号、线宽、figsize 按此比例缩放
- """
- s = Scaler(scale)
- BASE_W, BASE_H = 20, 15.5 # 基础画布尺寸 (数据单位)
- fig, ax = plt.subplots(figsize=s.figsize(BASE_W, BASE_H))
- ax.set_xlim(0, BASE_W)
- ax.set_ylim(0, BASE_H)
- ax.axis("off")
- # ━━━ 顶部标题 ━━━
- ax.text(10, 15.0, "lambdagentpaas 系统架构",
- ha="center", va="center",
- fontsize=s.fs(28), fontweight="bold", color=C["text"])
- ax.text(10, 14.45, "形式化 λA 演算驱动的智能体编程语言 + PaaS 平台",
- ha="center", va="center",
- fontsize=s.fs(15), color=C["text_mid"], style="italic")
- # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- # L3 应用层 y=11.3 ~ 13.95 h=2.65 header 0.6
- # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- layer(ax, s, 0.3, 11.3, 19.4, 2.65, 0.6,
- "L3 应用层 — 真实案例",
- C["L3_bg"], C["L3_edge"], C["L3_header"])
- card(ax, s, 0.9, 11.45, 5.9, 1.85,
- "qaagent67wiki",
- ["海事法规问答", "1326 源文件 / 2989 wiki 页"],
- C["L3_card"], C["L3_edge"], title_size=18, body_size=13)
- card(ax, s, 7.05, 11.45, 5.9, 1.85,
- "agent67v2",
- ["多智能体编程助手", "42 工具 / 5 个子智能体"],
- C["L3_card"], C["L3_edge"], title_size=18, body_size=13)
- card(ax, s, 13.2, 11.45, 5.9, 1.85,
- "research67",
- ["科研助手 (IDEA → 论文)", "8 个子智能体 / 迭代优化"],
- C["L3_card"], C["L3_edge"], title_size=18, body_size=13)
- # L3 → L2
- for x in (3.85, 10, 16.15):
- arrow_down(ax, s, x, 11.25, 10.8)
- # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- # L2 工程实现 y=5.0 ~ 10.75 h=5.75 header 0.55
- # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- layer(ax, s, 0.3, 5.0, 19.4, 5.75, 0.55,
- "L2 工程实现 (agentpaas)",
- C["L2_bg"], C["L2_edge"], C["L2_header"])
- card(ax, s, 0.9, 9.05, 18.2, 1.0,
- "REST API (28 端点) + MCP Server (5 工具) + GitHub Action",
- [], C["L2_card"], C["L2_edge"], title_size=16)
- card(ax, s, 0.9, 7.2, 5.9, 1.7,
- "Instance 机制",
- ["template / instance", "YAML 深度合并 + workspace"],
- C["L2_card"], C["L2_edge"], title_size=15, body_size=12)
- card(ax, s, 7.05, 7.2, 5.9, 1.7,
- "双引擎执行",
- ["Recursive / CEK / Adaptive", "Python 栈 vs CEK Machine"],
- C["L2_card"], C["L2_edge"], title_size=15, body_size=12)
- card(ax, s, 13.2, 7.2, 5.9, 1.7,
- "多租户 + 安全",
- ["RBAC + Quota + Rate Limit", "审计日志 + API Key"],
- C["L2_card"], C["L2_edge"], title_size=15, body_size=12)
- # ── Safety Net ──
- safety_x, safety_y, safety_w, safety_h = 0.9, 5.15, 18.2, 1.9
- ax.add_patch(FancyBboxPatch(
- (safety_x, safety_y), safety_w, safety_h,
- boxstyle="round,pad=0.06,rounding_size=0.15",
- facecolor=C["L2_safety_bg"], edgecolor=C["L2_safety_edge"],
- linewidth=s.lw(2), zorder=3,
- ))
- safety_header_h = 0.5
- ax.add_patch(Rectangle(
- (safety_x + 0.1, safety_y + safety_h - safety_header_h - 0.05),
- safety_w - 0.2, safety_header_h - 0.05,
- facecolor=C["L2_safety_header"], edgecolor="none",
- alpha=0.92, zorder=4,
- ))
- ax.text(
- 10, safety_y + safety_h - safety_header_h / 2 - 0.05,
- "编译时四道安全网 Static Analysis",
- ha="center", va="center",
- fontsize=s.fs(15), fontweight="bold",
- color=C["text_light"], zorder=5,
- )
- checks = [
- ("Lint\n26 条规则", 2.0),
- ("Type Check\nT-Compose", 6.1),
- ("Cost Predict\n分级类型", 10.2),
- ("Store Indep.\nPair 汇合", 14.3),
- ]
- check_y, check_h = 5.25, 1.1
- for label, cx in checks:
- ax.add_patch(FancyBboxPatch(
- (cx, check_y), 3.0, check_h,
- boxstyle="round,pad=0.04,rounding_size=0.1",
- facecolor=C["L2_safety_card"],
- edgecolor=C["L2_safety_edge"],
- linewidth=s.lw(1.5), zorder=5,
- ))
- ax.text(
- cx + 1.5, check_y + check_h / 2, label,
- ha="center", va="center",
- fontsize=s.fs(13), fontweight="bold",
- color=C["text"], zorder=6, linespacing=1.5,
- )
- mid_y = check_y + check_h / 2
- for i in range(3):
- x1 = checks[i][1] + 3.0
- x2 = checks[i+1][1]
- ax.annotate(
- "", xy=(x2, mid_y), xytext=(x1, mid_y),
- arrowprops=dict(arrowstyle="->",
- color=C["L2_safety_edge"], lw=s.lw(2.5)),
- zorder=6,
- )
- ax.annotate(
- "", xy=(18.0, mid_y), xytext=(17.3, mid_y),
- arrowprops=dict(arrowstyle="->",
- color=C["L2_safety_edge"], lw=s.lw(2.5)),
- zorder=6,
- )
- ax.text(18.05, mid_y, " PASS", ha="left", va="center",
- fontsize=s.fs(14), fontweight="bold", color="#2E7D32", zorder=7)
- # L2 → L1
- for x in (5, 10, 15):
- arrow_down(ax, s, x, 4.95, 4.55)
- # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- # L1 理论技术 y=1.5 ~ 4.5 h=3.0 header 0.55
- # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- layer(ax, s, 0.3, 1.5, 19.4, 3.0, 0.55,
- "L1 理论技术 (lambdagent / λA 演算)",
- C["L1_bg"], C["L1_edge"], C["L1_header"])
- card(ax, s, 0.9, 2.55, 5.9, 1.35,
- "三层编程模型",
- ["Skill → Pattern → Orchestration"],
- C["L1_card"], C["L1_edge"], title_size=15, body_size=12)
- card(ax, s, 7.05, 2.55, 5.9, 1.35,
- "λA 构造子 11 + 5",
- ["Lam / Tool / Compose / Loop / Route / Pair / ..."],
- C["L1_card"], C["L1_edge"], title_size=15, body_size=12)
- card(ax, s, 13.2, 2.55, 5.9, 1.35,
- "6 个协作模式",
- ["review / fan_out / pipeline / escalation / ..."],
- C["L1_card"], C["L1_edge"], title_size=15, body_size=12)
- card(ax, s, 0.9, 1.6, 18.2, 0.9,
- "形式化理论基础 — 三篇论文",
- [],
- C["L1_paper"], C["L1_edge"], title_size=15)
- ax.text(
- 10, 1.78,
- "Paper I λA 类型系统 | Paper II 操作语义 + CEK | Paper III 类型与效果 + 分级类型",
- ha="center", va="center",
- fontsize=s.fs(12), color=C["text_mid"], style="italic", zorder=5,
- )
- # L1 → L0
- for x in (5, 10, 15):
- arrow_down(ax, s, x, 1.45, 1.15)
- # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- # L0 基础设施 y=0.1 ~ 1.1 h=1.0 header 0.4
- # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- layer(ax, s, 0.3, 0.1, 19.4, 1.0, 0.4,
- "L0 基础设施",
- C["L0_bg"], C["L0_edge"], C["L0_header"])
- card(ax, s, 0.9, 0.2, 5.9, 0.48, "LLM Providers",
- [], C["L0_card"], C["L0_edge"], title_size=14)
- card(ax, s, 7.05, 0.2, 5.9, 0.48, "Storage",
- [], C["L0_card"], C["L0_edge"], title_size=14)
- card(ax, s, 13.2, 0.2, 5.9, 0.48, "Integration",
- [], C["L0_card"], C["L0_edge"], title_size=14)
- # 保存
- os.makedirs(os.path.dirname(output), exist_ok=True)
- plt.savefig(output, dpi=180, bbox_inches="tight",
- facecolor="white", edgecolor="none")
- plt.close(fig)
- print(f"✅ Saved: {output} (scale={scale})")
- if __name__ == "__main__":
- parser = argparse.ArgumentParser(description="Draw lambdagentpaas architecture diagram")
- parser.add_argument("--scale", type=float, default=1.0,
- help="全局缩放比例 (1.0=默认, 1.5=放大, 0.8=缩小)")
- parser.add_argument("--output", type=str, default=None,
- help="输出路径 (默认: docs/assets/lambdagentpaas-architecture.png)")
- args = parser.parse_args()
- here = os.path.dirname(os.path.abspath(__file__))
- output = args.output or os.path.join(here, "lambdagentpaas-architecture.png")
- draw(output, scale=args.scale)
|