Skill: wujiaming88/svg-architecture-diagram (version 1.2.0)

--- name: svg-architecture-diagram description: >- Create professional, publication-quality technical architecture diagrams using pure SVG in HTML, then screenshot via Playwright. Produces crisp, pixel-perfect diagrams with precise connection lines, color-coded modules, and clear text at any resolution. Use when: (1) user asks for a system architecture diagram, (2) user wants a technical component diagram or flow chart, (3) user needs a data flow or pipeline visualization, (4) any diagram requiring accurate text labels and precise connecting lines. Triggers: "architecture diagram", "架构图", "技术架构", "system diagram", "component diagram", "flow diagram", "数据流图", "模块图", "draw architecture", "画架构图", "technical diagram". Prefer this over AI image generation for any diagram with text labels. --- # SVG Architecture Diagram Create professional technical architecture diagrams using pure SVG, rendered to high-res PNG via Playwright. ## Why SVG (not CSS positioning or AI image generation) | Approach | Lines/Arrows | Text Quality | Precision | |----------|-------------|-------------|-----------| | **SVG (this skill)** | ✅ Perfect: `<line>`, `<path>`, `<marker>` | ✅ Crisp at any size | ✅ Pixel-perfect | | CSS absolute positioning | ❌ Hacky: borders, pseudo-elements | ✅ OK | ❌ Hard to align | | AI image generation | ❌ No control | ❌ Garbled text | ❌ No precision | ## Quick Start ### Step 1: Plan the diagram Identify: - **Modules** — group related components (color-coded) - **Hierarchy** — top-to-bottom flow (user → core → subsystems → output) - **Connections** — data flow (solid lines), feedback (dashed lines) ### Step 2: Create the HTML file Write a single HTML file with an inline SVG. Standard canvas: **1600×1000px**. ```html <!DOCTYPE html> <html><head> <meta charset="UTF-8"> <style> @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); * { margin: 0; padding: 0; box-sizing: border-box; } body { width: 1600px; height: 1000px; background: #fafafa; overflow: hidden; } </style> </head><body> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1000" width="1600" height="1000"> <defs> <!-- Arrow markers — one per color --> <marker id="arr-indigo" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"> <path d="M0,0 L8,3 L0,6 Z" fill="#6366f1"/> </marker> <!-- Shadow filter --> <filter id="shadow" x="-4%" y="-4%" width="108%" height="108%"> <feDropShadow dx="0" dy="2" stdDeviation="4" flood-color="#000" flood-opacity="0.08"/> </filter> </defs> <!-- Diagram content here --> </svg> </body></html> ``` ### Step 3: Build the diagram using these SVG patterns **Filled header card** (module title): ```svg <rect x="X" y="Y" width="W" height="40" rx="10" fill="#6366f1" filter="url(#shadow)"/> <text x="CENTER" y="Y+25" text-anchor="middle" font-size="13" font-weight="700" fill="#fff">🔄 Module Name</text> ``` **Outlined detail card** (sub-component): ```svg <rect x="X" y="Y" width="W" height="65" rx="10" fill="#fff" stroke="#6366f1" stroke-width="2" filter="url(#shadow)"/> <text x="X+20" y="Y+22" font-size="12" font-weight="700" fill="#6366f1">Component Title</text> <text x="X+20" y="Y+40" font-size="11" fill="#6b7280">Description line 1</text> <text x="X+20" y="Y+55" font-size="10" fill="#9ca3af">Metadata / specs</text> ``` **Connection line** (with arrow): ```svg <line x1="FROM_X" y1="FROM_Y" x2="TO_X" y2="TO_Y" stroke="#6366f1" stroke-width="2" marker-end="url(#arr-indigo)"/> ``` **Curved connection** (L-shape or bend): ```svg <path d="M startX,startY L midX,midY L endX,endY" stroke="#6366f1" stroke-width="2" fill="none" marker-end="url(#arr-indigo)"/> ``` **Dashed feedback line**: ```svg <path d="M x1,y1 L x2,y2" stroke="#8b5cf6" stroke-width="2" fill="none" stroke-dasharray="6,4" marker-end="url(#arr-purple)"/> ``` **Connection label**: ```svg <text x="MID_X" y="MID_Y-5" font-size="10" fill="#6366f1" font-weight="500">label text</text> ``` ### Step 4: Screenshot with Playwright ```python from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.launch() page = browser.new_page( viewport={"width": 1600, "height": 1000}, device_scale_factor=4, # 4x ultra-high res (default) ) page.goto("file:///path/to/diagram.html", wait_until="networkidle") page.wait_for_timeout(1500) page.screenshot(path="diagram.png", full_page=True) browser.close() ``` Or use the bundled script: `scripts/screenshot.py <input.html> [output.png]` ## Design System See `references/design-system.md` for the complete color palette, card styles, arrow markers, and text sizing rules. ## Critical Rules (prevent common issues) ### Text Overflow Prevention 1. **Max characters per line** at font-size 11px ≈ 7px/char: - 300px container → max 37 chars - 340px container → max 43 chars - 440px container → max 57 chars 2. **Long text → split into multiple `<text>` elements** with Y offset +15px each 3. **Always leave 20px padding** on each side of text inside cards 4. **Test at 1x scale** before generating final 4x screenshot ### Connection Line Rules 1. **Never use CSS for connections** — always SVG `<line>` or `<path>` 2. **One `<marker>` per color** — define in `<defs>`, reference with `marker-end` 3. **Straight lines** when possible; use `<path>` L-segments for bends 4. **Avoid crossing lines** — rearrange layout if lines would cross 5. **Label every connection** — brief verb/noun near the midpoint 6. **⚠️ Minimum 20px gap between vertically stacked cards** — Arrow markers are 8px long. If the gap between cards is less than 20px, the arrow will completely cover the line, making it look like "arrow only, no line". Use card height 34px + gap 22px = 56px per step. 7. **Connection line length must be at least 17px** — This ensures 9px visible line + 8px arrow marker. Example: card bottom at y=324, next card top at y=346, line from y1=324 to y2=343 (19px). 8. **Never make line length < marker size (8px)** — The line will be invisible. ### Layout Rules 1. **Top-to-bottom** primary flow (input at top, output at bottom/right) 2. **Left-right symmetry** when possible 3. **Group related modules** vertically (e.g., memory layers stacked) 4. **Minimum 20px gap** between vertically stacked cards (see Connection Line Rules) 5. **Color-code by function** — see design system for standard palette 6. **Include a legend** (bottom-right corner) explaining colors and line types 7. **Include a title** (top center) and source attribution (bottom center) ### Font Rules 1. **Font family**: `font-family="Inter, 'PingFang SC', 'Microsoft YaHei', sans-serif"` — set on root `<svg>` or first `<text>` 2. Load Inter via Google Fonts in `<style>` block 3. **Chinese text**: use `PingFang SC` / `Microsoft YaHei` fallback 4. **Font sizes**: titles 13-14px, descriptions 10-11px, metadata 9-10px ## Examples Two complete working examples are included: - `references/example-hermes.html` — Hermes Agent architecture (6 modules, medium complexity) - `references/example-openclaw.html` — OpenClaw platform architecture (12 modules, high complexity, demonstrates proper vertical card spacing for Agent Loop steps) ## Delivery Output `MEDIA:<path>` for inline delivery, or `openclaw message send --channel telegram --target <id> --media <path> --force-document` for Telegram. If PNG exceeds ~1MB for Telegram delivery, convert to JPEG (quality=95): ```python from PIL import Image img = Image.open("diagram.png") img.save("diagram.jpg", "JPEG", quality=95, optimize=True) ``` Default is 4x (6400×4000px for 1600×1000 canvas). Always use maximum resolution.

path: references/design-system.md
content: # SVG Architecture Diagram — Color Palette Reference

## Standard Module Colors

| Module Type | Fill Color | Border | Text | Use For |
|-------------|-----------|--------|------|---------|
| User / External | `#ec4899` (pink) | — | white | User input, LLM, external services |
| Core Engine | `#6366f1` (indigo) | — | white | Main loop, core processing, orchestration |
| Storage / Memory | `#10b981` (green) | `#10b981` | green/gray | Database, cache, memory layers |
| Plugin / Extension | `#f59e0b` (amber) | `#f59e0b` | amber/gray | Skills, plugins, extensions |
| AI / Learning | `#8b5cf6` (purple) | `#8b5cf6` | purple/gray | Self-evolution, ML, optimization |
| Infrastructure | `#64748b` (slate) | `#94a3b8` | slate/gray | Tools, shell, file system |
| Security / Guard | `#ef4444` (red) | `#ef4444` | red | Security, validation, guard |
| Output / Result | `#10b981` (green) | — | white | Output, delivery, response |

## Card Styles

### Filled Card (header/title blocks)
```svg
<rect x="X" y="Y" width="W" height="40" rx="10" fill="#6366f1" filter="url(#shadow)"/>
<text x="CENTER" y="Y+25" text-anchor="middle" font-size="14" font-weight="700" fill="#fff">Title</text>
```

### Outlined Card (detail blocks)
```svg
<rect x="X" y="Y" width="W" height="H" rx="10" fill="#fff" stroke="#6366f1" stroke-width="2" filter="url(#shadow)"/>
<text x="X+20" y="Y+22" font-size="12" font-weight="700" fill="#6366f1">Title</text>
<text x="X+20" y="Y+40" font-size="11" fill="#6b7280">Description</text>
<text x="X+20" y="Y+55" font-size="10" fill="#9ca3af">Metadata</text>
```

### Highlight Card (warnings/special)
```svg
<rect x="X" y="Y" width="W" height="H" rx="10" fill="#fffbeb" stroke="#f59e0b" stroke-width="1"/>
```

## Arrow Markers

```svg
<marker id="arr-indigo" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
  <path d="M0,0 L8,3 L0,6 Z" fill="#6366f1"/>
</marker>
```

## Connection Lines

| Type | Style | Use |
|------|-------|-----|
| Data flow | `stroke-width="2"` solid + arrow | Primary data movement |
| Feedback | `stroke-width="2" stroke-dasharray="6,4"` + arrow | Feedback loops, optimization |
| Internal | `stroke-width="1.5"` solid + arrow | Within-module connections |
| Label | `font-size="10" font-weight="500"` | Connection description |

## Text Sizing Rules

| Element | Font Size | Weight | Anchor |
|---------|-----------|--------|--------|
| Page title | 22px | 700 | middle |
| Subtitle | 12px | 400 | middle |
| Module header | 13-14px | 700 | middle |
| Card title | 11-12px | 700 | left |
| Description | 10-11px | 400 | left |
| Metadata | 9-10px | 400 | left |
| Connection label | 10px | 500 | left |
| Legend | 11px | 400 | left |

## Anti-Overflow Rules

1. **Max text width** = container width - 40px (20px padding each side)
2. At 11px font, ~7px per character → max ~43 chars in 300px container
3. Long text → split into two `<text>` lines (Y offset +16px)
4. Use `text-anchor="middle"` for centered headers
5. Use left-aligned for descriptions in outlined cards

## Card Spacing Rules (CRITICAL)

1. **Vertical stacked cards**: card height 34px + gap 22px = **56px per step**
2. **Connection line**: start at card bottom (y + height), end at next card top (y) minus 3px
3. **Minimum line length**: 17px (9px visible line + 8px arrow marker)
4. **Arrow marker size**: 8px — if line < 8px, arrow covers entire line (invisible)
5. **Example**: card at y=290 h=34 → bottom=324. Next card y=346. Line: y1=324 y2=343 (19px ✓)


path: references/example-hermes.html
content: <!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
  width: 1600px; height: 1000px;
  background: #fafafa;
  font-family: 'Inter', 'PingFang SC', 'Microsoft YaHei', sans-serif;
  overflow: hidden;
}
</style>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1000" width="1600" height="1000">
  <defs>
    <!-- 箭头标记 -->
    <marker id="arr-indigo" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
      <path d="M0,0 L8,3 L0,6 Z" fill="#6366f1"/>
    </marker>
    <marker id="arr-green" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
      <path d="M0,0 L8,3 L0,6 Z" fill="#10b981"/>
    </marker>
    <marker id="arr-amber" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
      <path d="M0,0 L8,3 L0,6 Z" fill="#f59e0b"/>
    </marker>
    <marker id="arr-purple" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
      <path d="M0,0 L8,3 L0,6 Z" fill="#8b5cf6"/>
    </marker>
    <marker id="arr-slate" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
      <path d="M0,0 L8,3 L0,6 Z" fill="#64748b"/>
    </marker>
    <marker id="arr-pink" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
      <path d="M0,0 L8,3 L0,6 Z" fill="#ec4899"/>
    </marker>
    <!-- 圆角矩形 filter -->
    <filter id="shadow" x="-4%" y="-4%" width="108%" height="108%">
      <feDropShadow dx="0" dy="2" stdDeviation="4" flood-color="#000" flood-opacity="0.08"/>
    </filter>
  </defs>

  <!-- ========== 背景标题 ========== -->
  <text x="800" y="40" text-anchor="middle" font-size="22" font-weight="700" fill="#1f2937" font-family="Inter, sans-serif">Hermes Agent 技术架构图</text>
  <text x="800" y="62" text-anchor="middle" font-size="12" fill="#9ca3af" font-family="Inter, sans-serif">Nous Research · MIT License · 95,600+ GitHub Stars</text>

  <!-- ========== 顶层：用户 → Agent Loop → LLM ========== -->
  
  <!-- 用户输入 -->
  <rect x="80" y="90" width="180" height="60" rx="12" fill="#ec4899" filter="url(#shadow)"/>
  <text x="170" y="117" text-anchor="middle" font-size="14" font-weight="700" fill="#fff">👤 User Input</text>
  <text x="170" y="134" text-anchor="middle" font-size="11" fill="rgba(255,255,255,0.85)">用户输入 / 任务指令</text>

  <!-- Agent Loop -->
  <rect x="580" y="85" width="440" height="70" rx="14" fill="#6366f1" filter="url(#shadow)"/>
  <text x="800" y="115" text-anchor="middle" font-size="16" font-weight="700" fill="#fff">🔄 Agent Loop（核心循环）</text>
  <text x="800" y="135" text-anchor="middle" font-size="10" fill="rgba(255,255,255,0.85)">构建 Prompt → 调用 LLM → 执行工具 → 返回结果 → Periodic Nudge</text>

  <!-- LLM -->
  <rect x="1340" y="90" width="180" height="60" rx="12" fill="#ec4899" filter="url(#shadow)"/>
  <text x="1430" y="117" text-anchor="middle" font-size="14" font-weight="700" fill="#fff">🧠 LLM</text>
  <text x="1430" y="134" text-anchor="middle" font-size="11" fill="rgba(255,255,255,0.85)">Claude / GPT / Qwen</text>

  <!-- 连线：用户 → Agent Loop -->
  <line x1="260" y1="120" x2="575" y2="120" stroke="#ec4899" stroke-width="2.5" marker-end="url(#arr-pink)"/>
  <!-- 连线：Agent Loop → LLM -->
  <line x1="1020" y1="115" x2="1335" y2="115" stroke="#ec4899" stroke-width="2.5" marker-end="url(#arr-pink)"/>
  <!-- 连线：LLM → Agent Loop (返回) -->
  <line x1="1335" y1="130" x2="1020" y2="130" stroke="#6366f1" stroke-width="2" stroke-dasharray="6,4" marker-end="url(#arr-indigo)"/>

  <!-- ========== 中部左：三层记忆架构 ========== -->
  
  <!-- 记忆系统标题 -->
  <rect x="60" y="195" width="340" height="40" rx="10" fill="#10b981" filter="url(#shadow)"/>
  <text x="230" y="220" text-anchor="middle" font-size="13" font-weight="700" fill="#fff">🧠 三层记忆架构</text>

  <!-- L1 -->
  <rect x="60" y="250" width="340" height="65" rx="10" fill="#fff" stroke="#10b981" stroke-width="2" filter="url(#shadow)"/>
  <text x="80" y="272" font-size="12" font-weight="700" fill="#10b981">L1: Session Context</text>
  <text x="80" y="290" font-size="11" fill="#6b7280">当前对话工作记忆 · 内存存储</text>
  <text x="80" y="305" font-size="10" fill="#9ca3af">容量: 模型上下文窗口 · 检索: 即时</text>

  <!-- L2 -->
  <rect x="60" y="328" width="340" height="65" rx="10" fill="#fff" stroke="#10b981" stroke-width="2" filter="url(#shadow)"/>
  <text x="80" y="350" font-size="12" font-weight="700" fill="#10b981">L2: Persistent Store</text>
  <text x="80" y="368" font-size="10" fill="#6b7280">SQLite + FTS5 全文搜索 · Skills · 记忆</text>
  <text x="80" y="383" font-size="10" fill="#9ca3af">容量: 无限 · 检索: &lt;10ms</text>

  <!-- L3 -->
  <rect x="60" y="406" width="340" height="65" rx="10" fill="#fff" stroke="#10b981" stroke-width="2" filter="url(#shadow)"/>
  <text x="80" y="428" font-size="12" font-weight="700" fill="#10b981">L3: External Knowledge</text>
  <text x="80" y="446" font-size="11" fill="#6b7280">MCP 服务器 · 外部知识库 · RAG</text>
  <text x="80" y="461" font-size="10" fill="#9ca3af">容量: 无限 · 检索: 按需</text>

  <!-- 记忆层之间的连线 -->
  <line x1="230" y1="315" x2="230" y2="325" stroke="#10b981" stroke-width="1.5" marker-end="url(#arr-green)"/>
  <line x1="230" y1="393" x2="230" y2="403" stroke="#10b981" stroke-width="1.5" marker-end="url(#arr-green)"/>

  <!-- 连线：Agent Loop ↔ 记忆系统 -->
  <path d="M580,140 L450,140 L450,270 L400,270" stroke="#10b981" stroke-width="2" fill="none" marker-end="url(#arr-green)"/>
  <text x="455" y="200" font-size="10" fill="#10b981" font-weight="500">读取/写入记忆</text>

  <!-- ========== 中部中：Agent Loop 详细流程 ========== -->
  
  <!-- 流程步骤 -->
  <rect x="545" y="195" width="155" height="42" rx="8" fill="#eef2ff" stroke="#6366f1" stroke-width="1.5"/>
  <text x="622" y="213" text-anchor="middle" font-size="11" font-weight="600" fill="#6366f1">① 构建 System Prompt</text>
  <text x="622" y="228" text-anchor="middle" font-size="9" fill="#9ca3af">注入 Skills 索引+记忆</text>

  <rect x="545" y="250" width="155" height="42" rx="8" fill="#eef2ff" stroke="#6366f1" stroke-width="1.5"/>
  <text x="622" y="268" text-anchor="middle" font-size="11" font-weight="600" fill="#6366f1">② LLM 推理</text>
  <text x="622" y="283" text-anchor="middle" font-size="9" fill="#9ca3af">生成回复或工具调用</text>

  <rect x="545" y="305" width="155" height="42" rx="8" fill="#eef2ff" stroke="#6366f1" stroke-width="1.5"/>
  <text x="622" y="323" text-anchor="middle" font-size="11" font-weight="600" fill="#6366f1">③ 执行工具</text>
  <text x="622" y="338" text-anchor="middle" font-size="9" fill="#9ca3af">Shell/文件/搜索/Skill</text>

  <rect x="545" y="360" width="155" height="42" rx="8" fill="#eef2ff" stroke="#6366f1" stroke-width="1.5"/>
  <text x="622" y="378" text-anchor="middle" font-size="11" font-weight="600" fill="#6366f1">④ 返回结果</text>
  <text x="622" y="393" text-anchor="middle" font-size="9" fill="#9ca3af">输出给用户或继续循环</text>

  <rect x="545" y="415" width="155" height="42" rx="8" fill="#fef3c7" stroke="#f59e0b" stroke-width="1.5"/>
  <text x="622" y="433" text-anchor="middle" font-size="11" font-weight="600" fill="#d97706">⑤ Periodic Nudge</text>
  <text x="622" y="448" text-anchor="middle" font-size="9" fill="#9ca3af">每10-15 turns 自省</text>

  <!-- 流程步骤连线 -->
  <line x1="622" y1="237" x2="622" y2="247" stroke="#6366f1" stroke-width="1.5" marker-end="url(#arr-indigo)"/>
  <line x1="622" y1="292" x2="622" y2="302" stroke="#6366f1" stroke-width="1.5" marker-end="url(#arr-indigo)"/>
  <line x1="622" y1="347" x2="622" y2="357" stroke="#6366f1" stroke-width="1.5" marker-end="url(#arr-indigo)"/>
  <line x1="622" y1="402" x2="622" y2="412" stroke="#f59e0b" stroke-width="1.5" marker-end="url(#arr-amber)"/>
  <!-- 循环回到顶部 -->
  <path d="M545,430 L520,430 L520,215 L542,215" stroke="#6366f1" stroke-width="1.5" fill="none" stroke-dasharray="5,3" marker-end="url(#arr-indigo)"/>
  <text x="510" y="320" font-size="9" fill="#6366f1" transform="rotate(-90, 510, 320)" text-anchor="middle">循环</text>

  <!-- ========== 中部右：Skill 系统 ========== -->
  
  <!-- Skill 系统标题 -->
  <rect x="780" y="195" width="340" height="40" rx="10" fill="#f59e0b" filter="url(#shadow)"/>
  <text x="950" y="220" text-anchor="middle" font-size="14" font-weight="700" fill="#fff">⚡ Skill 系统 Skills System</text>

  <!-- skill_manage 工具 -->
  <rect x="780" y="250" width="340" height="55" rx="10" fill="#fff" stroke="#f59e0b" stroke-width="2" filter="url(#shadow)"/>
  <text x="800" y="272" font-size="12" font-weight="700" fill="#d97706">skill_manage 工具</text>
  <text x="800" y="290" font-size="11" fill="#6b7280">create / read / update / delete / list</text>

  <!-- Pattern Extraction -->
  <rect x="780" y="318" width="340" height="55" rx="10" fill="#fff" stroke="#f59e0b" stroke-width="2" filter="url(#shadow)"/>
  <text x="800" y="340" font-size="11" font-weight="700" fill="#d97706">Pattern Extraction</text>
  <text x="800" y="358" font-size="10" fill="#6b7280">从执行轨迹提取可复用工作流</text>

  <!-- Skill 存储 -->
  <rect x="780" y="386" width="165" height="55" rx="10" fill="#fff" stroke="#f59e0b" stroke-width="2" filter="url(#shadow)"/>
  <text x="800" y="408" font-size="12" font-weight="700" fill="#d97706">📁 Skill 存储</text>
  <text x="800" y="426" font-size="10" fill="#6b7280">~/.hermes/skills/</text>

  <!-- Skills Guard -->
  <rect x="955" y="386" width="165" height="55" rx="10" fill="#fff" stroke="#ef4444" stroke-width="2" filter="url(#shadow)"/>
  <text x="975" y="408" font-size="11" font-weight="700" fill="#ef4444">🛡️ Guard</text>
  <text x="975" y="426" font-size="9" fill="#6b7280">安全扫描·防注入</text>

  <!-- Skill 格式说明 -->
  <rect x="780" y="454" width="340" height="60" rx="10" fill="#fffbeb" stroke="#f59e0b" stroke-width="1"/>
  <text x="800" y="474" font-size="11" font-weight="600" fill="#d97706">SKILL.md 格式:</text>
  <text x="800" y="491" font-size="10" fill="#6b7280">When to Use → Procedure → Pitfalls</text>
  <text x="800" y="505" font-size="9" fill="#9ca3af">触发: 5+ 工具调用 · 错误恢复 · 用户纠正</text>

  <!-- Skill 系统内部连线 -->
  <line x1="950" y1="305" x2="950" y2="315" stroke="#f59e0b" stroke-width="1.5" marker-end="url(#arr-amber)"/>
  <line x1="950" y1="373" x2="950" y2="383" stroke="#f59e0b" stroke-width="1.5" marker-end="url(#arr-amber)"/>
  <line x1="950" y1="441" x2="950" y2="451" stroke="#f59e0b" stroke-width="1.5" marker-end="url(#arr-amber)"/>

  <!-- 连线：Agent Loop ↔ Skill 系统 -->
  <path d="M700,320 L775,320" stroke="#f59e0b" stroke-width="2" fill="none" marker-end="url(#arr-amber)"/>
  <text x="730" y="312" font-size="10" fill="#d97706" font-weight="500">调用</text>

  <!-- ========== 底部左：Self-Evolution ========== -->
  
  <rect x="60" y="530" width="480" height="45" rx="10" fill="#8b5cf6" filter="url(#shadow)"/>
  <text x="300" y="558" text-anchor="middle" font-size="14" font-weight="700" fill="#fff">🔮 Self-Evolution 自进化系统</text>

  <!-- DSPy -->
  <rect x="60" y="590" width="230" height="75" rx="10" fill="#fff" stroke="#8b5cf6" stroke-width="2" filter="url(#shadow)"/>
  <text x="80" y="612" font-size="12" font-weight="700" fill="#7c3aed">DSPy 优化器</text>
  <text x="80" y="630" font-size="11" fill="#6b7280">自动优化 System Prompt</text>
  <text x="80" y="648" font-size="10" fill="#9ca3af">基于任务结果反馈迭代提升</text>

  <!-- GEPA -->
  <rect x="310" y="590" width="230" height="75" rx="10" fill="#fff" stroke="#8b5cf6" stroke-width="2" filter="url(#shadow)"/>
  <text x="330" y="612" font-size="12" font-weight="700" fill="#7c3aed">GEPA 循环</text>
  <text x="330" y="630" font-size="10" fill="#6b7280">Generate → Evaluate → Prune</text>
  <text x="330" y="648" font-size="10" fill="#9ca3af">→ Augment 循环精炼</text>

  <!-- GEPA 循环箭头 -->
  <path d="M425,668 Q425,690 370,690 Q315,690 315,668" stroke="#8b5cf6" stroke-width="1.5" fill="none" stroke-dasharray="4,3" marker-end="url(#arr-purple)"/>
  <text x="370" y="700" text-anchor="middle" font-size="9" fill="#8b5cf6">循环精炼</text>

  <!-- 连线：Self-Evolution ↔ Skill 系统 -->
  <path d="M425,575 L425,530 Q425,515 500,515 L780,400" stroke="#8b5cf6" stroke-width="2" fill="none" stroke-dasharray="6,4" marker-end="url(#arr-purple)"/>
  <text x="600" y="505" font-size="10" fill="#7c3aed" font-weight="500">优化 Skill 质量</text>

  <!-- 连线：Self-Evolution ↔ Agent Loop -->
  <path d="M300,530 L300,470 L545,440" stroke="#8b5cf6" stroke-width="2" fill="none" stroke-dasharray="6,4" marker-end="url(#arr-purple)"/>
  <text x="370" y="480" font-size="10" fill="#7c3aed" font-weight="500">优化 Prompt</text>

  <!-- ========== 底部右：工具系统 ========== -->
  
  <rect x="620" y="555" width="500" height="40" rx="10" fill="#64748b" filter="url(#shadow)"/>
  <text x="870" y="580" text-anchor="middle" font-size="14" font-weight="700" fill="#fff">🛠️ 工具系统 Tool System</text>

  <!-- 工具卡片 -->
  <rect x="620" y="608" width="115" height="52" rx="8" fill="#fff" stroke="#94a3b8" stroke-width="1.5"/>
  <text x="677" y="628" text-anchor="middle" font-size="11" font-weight="600" fill="#475569">📂 文件操作</text>
  <text x="677" y="645" text-anchor="middle" font-size="9" fill="#9ca3af">read/write/edit</text>

  <rect x="745" y="608" width="115" height="52" rx="8" fill="#fff" stroke="#94a3b8" stroke-width="1.5"/>
  <text x="802" y="628" text-anchor="middle" font-size="11" font-weight="600" fill="#475569">💻 Shell 执行</text>
  <text x="802" y="645" text-anchor="middle" font-size="9" fill="#9ca3af">bash / python</text>

  <rect x="870" y="608" width="115" height="52" rx="8" fill="#fff" stroke="#94a3b8" stroke-width="1.5"/>
  <text x="927" y="628" text-anchor="middle" font-size="11" font-weight="600" fill="#475569">🌐 Web 搜索</text>
  <text x="927" y="645" text-anchor="middle" font-size="9" fill="#9ca3af">search / fetch</text>

  <rect x="995" y="608" width="125" height="52" rx="8" fill="#fff" stroke="#f59e0b" stroke-width="1.5"/>
  <text x="1057" y="628" text-anchor="middle" font-size="11" font-weight="600" fill="#d97706">⚡ skill_manage</text>
  <text x="1057" y="645" text-anchor="middle" font-size="9" fill="#9ca3af">Skill CRUD</text>

  <!-- 连线：Agent Loop → 工具系统 -->
  <path d="M622,457 L622,480 Q622,500 700,500 L870,552" stroke="#64748b" stroke-width="2" fill="none" marker-end="url(#arr-slate)"/>
  <text x="750" y="495" font-size="10" fill="#64748b" font-weight="500">工具调用</text>

  <!-- ========== 右侧：输出 ========== -->
  <rect x="1200" y="195" width="180" height="60" rx="12" fill="#10b981" filter="url(#shadow)"/>
  <text x="1290" y="222" text-anchor="middle" font-size="14" font-weight="700" fill="#fff">📤 输出</text>
  <text x="1290" y="239" text-anchor="middle" font-size="11" fill="rgba(255,255,255,0.85)">回复用户 / 执行结果</text>

  <!-- 连线：Agent Loop → 输出 -->
  <path d="M700,380 Q750,380 750,260 L750,225 L1195,225" stroke="#10b981" stroke-width="2" fill="none" marker-end="url(#arr-green)"/>

  <!-- ========== 质量指标 ========== -->
  <rect x="1200" y="300" width="340" height="160" rx="12" fill="#fff" stroke="#e5e7eb" stroke-width="1.5" filter="url(#shadow)"/>
  <text x="1220" y="325" font-size="13" font-weight="700" fill="#1f2937">📊 关键指标 Key Metrics</text>
  <line x1="1220" y1="335" x2="1520" y2="335" stroke="#e5e7eb" stroke-width="1"/>
  <text x="1220" y="358" font-size="11" fill="#6b7280">• GitHub Stars: 95,600+</text>
  <text x="1220" y="378" font-size="11" fill="#6b7280">• 开源协议: MIT</text>
  <text x="1220" y="398" font-size="11" fill="#6b7280">• 记忆检索: &lt;10ms (FTS5)</text>
  <text x="1220" y="418" font-size="11" fill="#6b7280">• Skill 触发: 5+ 工具调用</text>
  <text x="1220" y="438" font-size="11" fill="#6b7280">• 自省频率: 每 10-15 turns</text>

  <!-- ========== 图例 ========== -->
  <rect x="1200" y="490" width="340" height="130" rx="10" fill="#fff" stroke="#e5e7eb" stroke-width="1"/>
  <text x="1220" y="515" font-size="12" font-weight="700" fill="#1f2937">图例 Legend</text>
  <rect x="1220" y="528" width="14" height="14" rx="3" fill="#ec4899"/>
  <text x="1244" y="540" font-size="11" fill="#6b7280">用户 / LLM</text>
  <rect x="1340" y="528" width="14" height="14" rx="3" fill="#6366f1"/>
  <text x="1364" y="540" font-size="11" fill="#6b7280">Agent Loop</text>
  <rect x="1220" y="552" width="14" height="14" rx="3" fill="#10b981"/>
  <text x="1244" y="564" font-size="11" fill="#6b7280">记忆系统</text>
  <rect x="1340" y="552" width="14" height="14" rx="3" fill="#f59e0b"/>
  <text x="1364" y="564" font-size="11" fill="#6b7280">Skill 系统</text>
  <rect x="1220" y="576" width="14" height="14" rx="3" fill="#8b5cf6"/>
  <text x="1244" y="588" font-size="11" fill="#6b7280">Self-Evolution</text>
  <rect x="1340" y="576" width="14" height="14" rx="3" fill="#64748b"/>
  <text x="1364" y="588" font-size="11" fill="#6b7280">工具系统</text>
  <line x1="1220" y1="604" x2="1260" y2="604" stroke="#6366f1" stroke-width="2"/>
  <text x="1270" y="608" font-size="10" fill="#6b7280">数据流</text>
  <line x1="1340" y1="604" x2="1380" y2="604" stroke="#8b5cf6" stroke-width="2" stroke-dasharray="5,3"/>
  <text x="1390" y="608" font-size="10" fill="#6b7280">反馈/优化</text>

  <!-- 底部来源 -->
  <text x="800" y="985" text-anchor="middle" font-size="10" fill="#d1d5db">基于 Hermes Agent 源码 + Nous Research 官方文档 + 社区评测 · W.ai Deep Tech Research · 2026-04</text>

</svg>
</body>
</html>


path: references/example-openclaw.html
content: <!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
  width: 1600px; height: 1100px;
  background: #fafafa;
  font-family: 'Inter', 'PingFang SC', 'Microsoft YaHei', sans-serif;
  overflow: hidden;
}
</style>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1100" width="1600" height="1100">
  <defs>
    <marker id="arr-indigo" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
      <path d="M0,0 L8,3 L0,6 Z" fill="#6366f1"/>
    </marker>
    <marker id="arr-green" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
      <path d="M0,0 L8,3 L0,6 Z" fill="#10b981"/>
    </marker>
    <marker id="arr-amber" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
      <path d="M0,0 L8,3 L0,6 Z" fill="#f59e0b"/>
    </marker>
    <marker id="arr-purple" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
      <path d="M0,0 L8,3 L0,6 Z" fill="#8b5cf6"/>
    </marker>
    <marker id="arr-slate" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
      <path d="M0,0 L8,3 L0,6 Z" fill="#64748b"/>
    </marker>
    <marker id="arr-pink" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
      <path d="M0,0 L8,3 L0,6 Z" fill="#ec4899"/>
    </marker>
    <marker id="arr-red" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
      <path d="M0,0 L8,3 L0,6 Z" fill="#ef4444"/>
    </marker>
    <filter id="shadow" x="-4%" y="-4%" width="108%" height="108%">
      <feDropShadow dx="0" dy="2" stdDeviation="4" flood-color="#000" flood-opacity="0.08"/>
    </filter>
  </defs>

  <!-- ========== 标题 ========== -->
  <text x="800" y="38" text-anchor="middle" font-size="22" font-weight="700" fill="#1f2937">OpenClaw 完整技术架构图</text>
  <text x="800" y="58" text-anchor="middle" font-size="12" fill="#9ca3af">Multi-Channel AI Agent Platform · Gateway → Sessions → Agent Loop → Tools</text>

  <!-- ========== 第一层：消息渠道 Channels ========== -->
  <rect x="195" y="80" width="105" height="48" rx="8" fill="#ec4899" filter="url(#shadow)"/>
  <text x="247" y="101" text-anchor="middle" font-size="11" font-weight="700" fill="#fff">Telegram</text>
  <text x="247" y="118" text-anchor="middle" font-size="9" fill="rgba(255,255,255,0.8)">grammY</text>

  <rect x="310" y="80" width="105" height="48" rx="8" fill="#ec4899" filter="url(#shadow)"/>
  <text x="362" y="101" text-anchor="middle" font-size="11" font-weight="700" fill="#fff">WhatsApp</text>
  <text x="362" y="118" text-anchor="middle" font-size="9" fill="rgba(255,255,255,0.8)">Baileys</text>

  <rect x="425" y="80" width="105" height="48" rx="8" fill="#ec4899" filter="url(#shadow)"/>
  <text x="477" y="106" text-anchor="middle" font-size="11" font-weight="700" fill="#fff">Discord</text>

  <rect x="540" y="80" width="105" height="48" rx="8" fill="#ec4899" filter="url(#shadow)"/>
  <text x="592" y="106" text-anchor="middle" font-size="11" font-weight="700" fill="#fff">Slack</text>

  <rect x="655" y="80" width="105" height="48" rx="8" fill="#ec4899" filter="url(#shadow)"/>
  <text x="707" y="106" text-anchor="middle" font-size="11" font-weight="700" fill="#fff">Signal</text>

  <rect x="770" y="80" width="105" height="48" rx="8" fill="#ec4899" filter="url(#shadow)"/>
  <text x="822" y="106" text-anchor="middle" font-size="11" font-weight="700" fill="#fff">iMessage</text>

  <rect x="885" y="80" width="105" height="48" rx="8" fill="#ec4899" filter="url(#shadow)"/>
  <text x="937" y="106" text-anchor="middle" font-size="11" font-weight="700" fill="#fff">WebChat</text>

  <rect x="1000" y="80" width="105" height="48" rx="8" fill="#ec4899" filter="url(#shadow)"/>
  <text x="1052" y="106" text-anchor="middle" font-size="11" font-weight="700" fill="#fff">CLI</text>

  <!-- ========== 第二层：Gateway ========== -->
  <rect x="195" y="155" width="910" height="55" rx="12" fill="#6366f1" filter="url(#shadow)"/>
  <text x="650" y="180" text-anchor="middle" font-size="16" font-weight="700" fill="#fff">🌐 Gateway（单一守护进程 · WebSocket 127.0.0.1:18789）</text>
  <text x="650" y="198" text-anchor="middle" font-size="10" fill="rgba(255,255,255,0.85)">消息路由 · 认证 · 设备配对 · JSON Schema 协议 · Canvas Host · HTTP API + WS API</text>

  <!-- 渠道 → Gateway 连线 -->
  <line x1="362" y1="128" x2="362" y2="152" stroke="#ec4899" stroke-width="2" marker-end="url(#arr-pink)"/>
  <line x1="592" y1="128" x2="592" y2="152" stroke="#ec4899" stroke-width="2" marker-end="url(#arr-pink)"/>
  <line x1="822" y1="128" x2="822" y2="152" stroke="#ec4899" stroke-width="2" marker-end="url(#arr-pink)"/>
  <line x1="1052" y1="128" x2="1052" y2="152" stroke="#ec4899" stroke-width="2" marker-end="url(#arr-pink)"/>

  <!-- ========== 第三层左：Agent Loop ========== -->
  <rect x="60" y="240" width="280" height="40" rx="10" fill="#6366f1" filter="url(#shadow)"/>
  <text x="200" y="265" text-anchor="middle" font-size="13" font-weight="700" fill="#fff">🔄 Agent Loop 核心循环</text>

  <rect x="60" y="290" width="280" height="34" rx="8" fill="#eef2ff" stroke="#6366f1" stroke-width="1.5"/>
  <text x="200" y="311" text-anchor="middle" font-size="11" font-weight="600" fill="#6366f1">① RPC 接收 → 解析 Session</text>

  <rect x="60" y="346" width="280" height="34" rx="8" fill="#eef2ff" stroke="#6366f1" stroke-width="1.5"/>
  <text x="200" y="367" text-anchor="middle" font-size="11" font-weight="600" fill="#6366f1">② 会话队列串行化</text>

  <rect x="60" y="402" width="280" height="34" rx="8" fill="#eef2ff" stroke="#6366f1" stroke-width="1.5"/>
  <text x="200" y="423" text-anchor="middle" font-size="11" font-weight="600" fill="#6366f1">③ Prompt 组装</text>

  <rect x="60" y="458" width="280" height="34" rx="8" fill="#eef2ff" stroke="#6366f1" stroke-width="1.5"/>
  <text x="200" y="479" text-anchor="middle" font-size="11" font-weight="600" fill="#6366f1">④ 模型推理（流式输出）</text>

  <rect x="60" y="514" width="280" height="34" rx="8" fill="#eef2ff" stroke="#6366f1" stroke-width="1.5"/>
  <text x="200" y="535" text-anchor="middle" font-size="11" font-weight="600" fill="#6366f1">⑤ 工具执行</text>

  <rect x="60" y="570" width="280" height="34" rx="8" fill="#eef2ff" stroke="#6366f1" stroke-width="1.5"/>
  <text x="200" y="591" text-anchor="middle" font-size="11" font-weight="600" fill="#6366f1">⑥ Transcript 持久化</text>

  <rect x="60" y="626" width="280" height="34" rx="8" fill="#fef3c7" stroke="#f59e0b" stroke-width="1.5"/>
  <text x="200" y="647" text-anchor="middle" font-size="11" font-weight="600" fill="#d97706">⑦ 生命周期 (end/error)</text>

  <!-- Agent Loop 步骤连线 (22px gap, line+arrow clearly visible) -->
  <line x1="200" y1="324" x2="200" y2="343" stroke="#6366f1" stroke-width="2" marker-end="url(#arr-indigo)"/>
  <line x1="200" y1="380" x2="200" y2="399" stroke="#6366f1" stroke-width="2" marker-end="url(#arr-indigo)"/>
  <line x1="200" y1="436" x2="200" y2="455" stroke="#6366f1" stroke-width="2" marker-end="url(#arr-indigo)"/>
  <line x1="200" y1="492" x2="200" y2="511" stroke="#6366f1" stroke-width="2" marker-end="url(#arr-indigo)"/>
  <line x1="200" y1="548" x2="200" y2="567" stroke="#6366f1" stroke-width="2" marker-end="url(#arr-indigo)"/>
  <line x1="200" y1="604" x2="200" y2="623" stroke="#6366f1" stroke-width="2" marker-end="url(#arr-indigo)"/>

  <!-- 循环箭头 -->
  <path d="M60,645 L35,645 L35,298 L57,298" stroke="#6366f1" stroke-width="1.5" fill="none" stroke-dasharray="5,3" marker-end="url(#arr-indigo)"/>
  <text x="30" y="440" font-size="9" fill="#6366f1" transform="rotate(-90, 30, 440)" text-anchor="middle">循环</text>

  <!-- Gateway → Agent Loop -->
  <path d="M400,210 L200,210 L200,237" stroke="#6366f1" stroke-width="2" fill="none" marker-end="url(#arr-indigo)"/>

  <!-- ========== 第三层中左：Context Engine ========== -->
  <rect x="380" y="240" width="280" height="40" rx="10" fill="#10b981" filter="url(#shadow)"/>
  <text x="520" y="265" text-anchor="middle" font-size="13" font-weight="700" fill="#fff">🧠 Context Engine 上下文引擎</text>

  <rect x="380" y="292" width="280" height="42" rx="10" fill="#fff" stroke="#10b981" stroke-width="2" filter="url(#shadow)"/>
  <text x="400" y="312" font-size="11" font-weight="700" fill="#10b981">System Prompt Builder</text>
  <text x="400" y="327" font-size="10" fill="#9ca3af">组装系统提示 + 注入上下文</text>

  <rect x="380" y="344" width="280" height="42" rx="10" fill="#fff" stroke="#10b981" stroke-width="2" filter="url(#shadow)"/>
  <text x="400" y="364" font-size="11" font-weight="700" fill="#10b981">Skills Loader</text>
  <text x="400" y="379" font-size="10" fill="#9ca3af">扫描 + 注入技能描述到 Prompt</text>

  <rect x="380" y="396" width="280" height="42" rx="10" fill="#fff" stroke="#10b981" stroke-width="2" filter="url(#shadow)"/>
  <text x="400" y="416" font-size="11" font-weight="700" fill="#10b981">Memory System</text>
  <text x="400" y="431" font-size="10" fill="#9ca3af">MEMORY.md + memory/*.md + lossless-claw</text>

  <rect x="380" y="448" width="280" height="42" rx="10" fill="#fff" stroke="#10b981" stroke-width="2" filter="url(#shadow)"/>
  <text x="400" y="468" font-size="11" font-weight="700" fill="#10b981">Bootstrap Files</text>
  <text x="400" y="483" font-size="10" fill="#9ca3af">SOUL / IDENTITY / USER / TOOLS / AGENTS</text>

  <rect x="380" y="500" width="280" height="36" rx="10" fill="#fff" stroke="#10b981" stroke-width="2" filter="url(#shadow)"/>
  <text x="400" y="522" font-size="11" font-weight="700" fill="#10b981">Transcript Repair</text>

  <!-- Agent Loop → Context Engine -->
  <line x1="340" y1="395" x2="377" y2="395" stroke="#10b981" stroke-width="2" marker-end="url(#arr-green)"/>
  <text x="348" y="388" font-size="9" fill="#10b981" font-weight="500">上下文</text>

  <!-- ========== 第三层中右：Sessions + Cron ========== -->
  <rect x="700" y="240" width="240" height="40" rx="10" fill="#10b981" filter="url(#shadow)"/>
  <text x="820" y="265" text-anchor="middle" font-size="13" font-weight="700" fill="#fff">📋 Sessions 会话管理</text>

  <rect x="700" y="292" width="240" height="100" rx="10" fill="#fff" stroke="#10b981" stroke-width="2" filter="url(#shadow)"/>
  <text x="720" y="312" font-size="11" font-weight="700" fill="#10b981">会话管理</text>
  <text x="720" y="330" font-size="10" fill="#6b7280">• Session Key 解析</text>
  <text x="720" y="346" font-size="10" fill="#6b7280">• Transcript 存储 + 写锁</text>
  <text x="720" y="362" font-size="10" fill="#6b7280">• Model Overrides</text>
  <text x="720" y="378" font-size="10" fill="#6b7280">• Sub-agent 编排</text>

  <!-- Cron -->
  <rect x="700" y="408" width="240" height="40" rx="10" fill="#f59e0b" filter="url(#shadow)"/>
  <text x="820" y="433" text-anchor="middle" font-size="13" font-weight="700" fill="#fff">⏰ Cron 定时任务</text>

  <rect x="700" y="458" width="240" height="80" rx="10" fill="#fff" stroke="#f59e0b" stroke-width="2" filter="url(#shadow)"/>
  <text x="720" y="478" font-size="11" font-weight="700" fill="#d97706">Schedule Types</text>
  <text x="720" y="496" font-size="10" fill="#6b7280">at / every / cron expression</text>
  <text x="720" y="512" font-size="10" fill="#6b7280">Payload: systemEvent / agentTurn</text>
  <text x="720" y="528" font-size="10" fill="#6b7280">Delivery: announce / webhook</text>

  <!-- Gateway → Sessions -->
  <path d="M700,210 L820,210 L820,237" stroke="#10b981" stroke-width="2" fill="none" marker-end="url(#arr-green)"/>

  <!-- Gateway → Cron -->
  <path d="M900,210 L950,210 L950,425 L940,425" stroke="#f59e0b" stroke-width="2" fill="none" stroke-dasharray="6,4" marker-end="url(#arr-amber)"/>

  <!-- ========== 第三层右：LLM Providers ========== -->
  <rect x="980" y="240" width="230" height="40" rx="10" fill="#8b5cf6" filter="url(#shadow)"/>
  <text x="1095" y="265" text-anchor="middle" font-size="13" font-weight="700" fill="#fff">🧠 LLM Providers 模型层</text>

  <rect x="980" y="292" width="230" height="42" rx="10" fill="#fff" stroke="#8b5cf6" stroke-width="2" filter="url(#shadow)"/>
  <text x="1000" y="312" font-size="11" font-weight="700" fill="#8b5cf6">Anthropic</text>
  <text x="1000" y="327" font-size="10" fill="#9ca3af">Claude Opus / Sonnet / Haiku</text>

  <rect x="980" y="344" width="230" height="42" rx="10" fill="#fff" stroke="#8b5cf6" stroke-width="2" filter="url(#shadow)"/>
  <text x="1000" y="364" font-size="11" font-weight="700" fill="#8b5cf6">AWS Bedrock</text>
  <text x="1000" y="379" font-size="10" fill="#9ca3af">Anthropic / Amazon / Stability</text>

  <rect x="980" y="396" width="230" height="42" rx="10" fill="#fff" stroke="#8b5cf6" stroke-width="2" filter="url(#shadow)"/>
  <text x="1000" y="416" font-size="11" font-weight="700" fill="#8b5cf6">OpenAI</text>
  <text x="1000" y="431" font-size="10" fill="#9ca3af">GPT-4o / o1 / o3</text>

  <rect x="980" y="448" width="110" height="42" rx="10" fill="#fff" stroke="#8b5cf6" stroke-width="2" filter="url(#shadow)"/>
  <text x="1000" y="468" font-size="11" font-weight="700" fill="#8b5cf6">Google</text>
  <text x="1000" y="483" font-size="10" fill="#9ca3af">Gemini</text>

  <rect x="1100" y="448" width="110" height="42" rx="10" fill="#fff" stroke="#8b5cf6" stroke-width="2" filter="url(#shadow)"/>
  <text x="1120" y="468" font-size="11" font-weight="700" fill="#8b5cf6">Mistral</text>
  <text x="1120" y="483" font-size="10" fill="#9ca3af">Large / Medium</text>

  <rect x="980" y="500" width="230" height="36" rx="10" fill="#fff" stroke="#8b5cf6" stroke-width="2" filter="url(#shadow)"/>
  <text x="1000" y="522" font-size="11" font-weight="700" fill="#8b5cf6">本地模型 Ollama / vLLM</text>

  <!-- Agent Loop → LLM -->
  <path d="M340,445 Q360,445 360,360 L360,260 L650,210 L1000,210 L1095,210 L1095,237" stroke="#8b5cf6" stroke-width="2" fill="none" marker-end="url(#arr-purple)"/>

  <!-- ========== 右侧上：Nodes ========== -->
  <rect x="1260" y="240" width="280" height="40" rx="10" fill="#ec4899" filter="url(#shadow)"/>
  <text x="1400" y="265" text-anchor="middle" font-size="13" font-weight="700" fill="#fff">📱 Nodes 伴侣设备</text>

  <rect x="1260" y="292" width="135" height="42" rx="10" fill="#fff" stroke="#ec4899" stroke-width="2" filter="url(#shadow)"/>
  <text x="1327" y="312" text-anchor="middle" font-size="11" font-weight="700" fill="#ec4899">macOS App</text>
  <text x="1327" y="327" text-anchor="middle" font-size="9" fill="#9ca3af">camera/screen</text>

  <rect x="1405" y="292" width="135" height="42" rx="10" fill="#fff" stroke="#ec4899" stroke-width="2" filter="url(#shadow)"/>
  <text x="1472" y="312" text-anchor="middle" font-size="11" font-weight="700" fill="#ec4899">iOS App</text>
  <text x="1472" y="327" text-anchor="middle" font-size="9" fill="#9ca3af">location/canvas</text>

  <rect x="1260" y="344" width="135" height="42" rx="10" fill="#fff" stroke="#ec4899" stroke-width="2" filter="url(#shadow)"/>
  <text x="1327" y="364" text-anchor="middle" font-size="11" font-weight="700" fill="#ec4899">Android App</text>
  <text x="1327" y="379" text-anchor="middle" font-size="9" fill="#9ca3af">camera/screen</text>

  <rect x="1405" y="344" width="135" height="42" rx="10" fill="#fff" stroke="#ec4899" stroke-width="2" filter="url(#shadow)"/>
  <text x="1472" y="364" text-anchor="middle" font-size="11" font-weight="700" fill="#ec4899">Headless Node</text>
  <text x="1472" y="379" text-anchor="middle" font-size="9" fill="#9ca3af">server mode</text>

  <!-- Gateway → Nodes -->
  <path d="M1105,182 L1200,182 L1400,182 L1400,237" stroke="#ec4899" stroke-width="2" fill="none" stroke-dasharray="6,4" marker-end="url(#arr-pink)"/>
  <text x="1250" y="175" font-size="10" fill="#ec4899" font-weight="500">WebSocket</text>

  <!-- ========== 右侧中：Skills 系统 ========== -->
  <rect x="1260" y="408" width="280" height="40" rx="10" fill="#f59e0b" filter="url(#shadow)"/>
  <text x="1400" y="433" text-anchor="middle" font-size="13" font-weight="700" fill="#fff">⚡ Skills 技能系统</text>

  <rect x="1260" y="458" width="280" height="80" rx="10" fill="#fff" stroke="#f59e0b" stroke-width="2" filter="url(#shadow)"/>
  <text x="1280" y="478" font-size="11" font-weight="700" fill="#d97706">Skill Sources</text>
  <text x="1280" y="496" font-size="10" fill="#6b7280">• 本地 Skills (~/.openclaw/skills/)</text>
  <text x="1280" y="512" font-size="10" fill="#6b7280">• ClawHub（社区 Skill 市场）</text>
  <text x="1280" y="528" font-size="10" fill="#6b7280">• Workspace Skills · 安全扫描</text>

  <!-- Skills → Context Engine -->
  <path d="M1260,480 L680,420 L663,420" stroke="#f59e0b" stroke-width="2" fill="none" stroke-dasharray="6,4" marker-end="url(#arr-amber)"/>
  <text x="950" y="445" font-size="10" fill="#d97706" font-weight="500">注入 Skill 描述</text>

  <!-- ========== 第四层：Tool System ========== -->
  <rect x="60" y="625" width="860" height="40" rx="10" fill="#64748b" filter="url(#shadow)"/>
  <text x="490" y="650" text-anchor="middle" font-size="14" font-weight="700" fill="#fff">🛠️ Tool System 工具系统</text>

  <rect x="60" y="678" width="120" height="48" rx="8" fill="#fff" stroke="#94a3b8" stroke-width="1.5"/>
  <text x="120" y="698" text-anchor="middle" font-size="11" font-weight="600" fill="#475569">exec</text>
  <text x="120" y="715" text-anchor="middle" font-size="9" fill="#9ca3af">Shell 执行</text>

  <rect x="190" y="678" width="120" height="48" rx="8" fill="#fff" stroke="#94a3b8" stroke-width="1.5"/>
  <text x="250" y="698" text-anchor="middle" font-size="11" font-weight="600" fill="#475569">read/write/edit</text>
  <text x="250" y="715" text-anchor="middle" font-size="9" fill="#9ca3af">文件操作</text>

  <rect x="320" y="678" width="120" height="48" rx="8" fill="#fff" stroke="#94a3b8" stroke-width="1.5"/>
  <text x="380" y="698" text-anchor="middle" font-size="11" font-weight="600" fill="#475569">web_search</text>
  <text x="380" y="715" text-anchor="middle" font-size="9" fill="#9ca3af">网络搜索+抓取</text>

  <rect x="450" y="678" width="120" height="48" rx="8" fill="#fff" stroke="#94a3b8" stroke-width="1.5"/>
  <text x="510" y="698" text-anchor="middle" font-size="11" font-weight="600" fill="#475569">sessions</text>
  <text x="510" y="715" text-anchor="middle" font-size="9" fill="#9ca3af">子Agent编排</text>

  <rect x="580" y="678" width="120" height="48" rx="8" fill="#fff" stroke="#94a3b8" stroke-width="1.5"/>
  <text x="640" y="698" text-anchor="middle" font-size="11" font-weight="600" fill="#475569">memory</text>
  <text x="640" y="715" text-anchor="middle" font-size="9" fill="#9ca3af">记忆检索</text>

  <rect x="710" y="678" width="100" height="48" rx="8" fill="#fff" stroke="#94a3b8" stroke-width="1.5"/>
  <text x="760" y="698" text-anchor="middle" font-size="11" font-weight="600" fill="#475569">image</text>
  <text x="760" y="715" text-anchor="middle" font-size="9" fill="#9ca3af">图像分析</text>

  <rect x="820" y="678" width="100" height="48" rx="8" fill="#fff" stroke="#94a3b8" stroke-width="1.5"/>
  <text x="870" y="698" text-anchor="middle" font-size="11" font-weight="600" fill="#475569">MCP</text>
  <text x="870" y="715" text-anchor="middle" font-size="9" fill="#9ca3af">外部工具协议</text>

  <!-- Agent Loop → Tools -->
  <path d="M200,592 L200,622" stroke="#64748b" stroke-width="2" fill="none" marker-end="url(#arr-slate)"/>
  <text x="210" y="612" font-size="9" fill="#64748b" font-weight="500">工具调用</text>

  <!-- ========== 第四层右：Plugin System ========== -->
  <rect x="960" y="625" width="280" height="40" rx="10" fill="#f59e0b" filter="url(#shadow)"/>
  <text x="1100" y="650" text-anchor="middle" font-size="13" font-weight="700" fill="#fff">🔌 Plugin System 插件系统</text>

  <rect x="960" y="678" width="280" height="80" rx="10" fill="#fff" stroke="#f59e0b" stroke-width="2" filter="url(#shadow)"/>
  <text x="980" y="698" font-size="11" font-weight="700" fill="#d97706">Plugin Architecture</text>
  <text x="980" y="716" font-size="10" fill="#6b7280">• Activation Planner · Plugin SDK</text>
  <text x="980" y="732" font-size="10" fill="#6b7280">• Hook: tool lifecycle + gateway pipeline</text>
  <text x="980" y="748" font-size="10" fill="#6b7280">• lossless-claw · clawguard-bench ...</text>

  <!-- Plugin ↔ Agent Loop -->
  <path d="M960,645 L400,530" stroke="#f59e0b" stroke-width="2" fill="none" stroke-dasharray="6,4" marker-end="url(#arr-amber)"/>
  <text x="680" y="578" font-size="10" fill="#d97706" font-weight="500">插件钩子</text>

  <!-- ========== 底部右：Security ========== -->
  <rect x="1260" y="625" width="280" height="40" rx="10" fill="#ef4444" filter="url(#shadow)"/>
  <text x="1400" y="650" text-anchor="middle" font-size="13" font-weight="700" fill="#fff">🛡️ Security 安全层</text>

  <rect x="1260" y="678" width="280" height="80" rx="10" fill="#fff" stroke="#ef4444" stroke-width="2" filter="url(#shadow)"/>
  <text x="1280" y="698" font-size="11" font-weight="700" fill="#ef4444">Security Controls</text>
  <text x="1280" y="716" font-size="10" fill="#6b7280">• Exec Approvals（执行审批）</text>
  <text x="1280" y="732" font-size="10" fill="#6b7280">• Device Pairing（设备配对）</text>
  <text x="1280" y="748" font-size="10" fill="#6b7280">• Auth: shared-secret / tailscale / ...</text>

  <!-- Security → Gateway (渗透全局) -->
  <path d="M1400,625 L1400,580 L1150,210 L1110,210" stroke="#ef4444" stroke-width="1.5" fill="none" stroke-dasharray="6,4" marker-end="url(#arr-red)"/>
  <text x="1300" y="560" font-size="9" fill="#ef4444" font-weight="500">安全策略</text>

  <!-- ========== 图例 ========== -->
  <rect x="60" y="790" width="1480" height="100" rx="10" fill="#fff" stroke="#e5e7eb" stroke-width="1"/>
  <text x="100" y="818" font-size="13" font-weight="700" fill="#1f2937">图例 Legend</text>

  <!-- 颜色图例 -->
  <rect x="100" y="832" width="14" height="14" rx="3" fill="#ec4899"/>
  <text x="122" y="844" font-size="11" fill="#6b7280">Channels / Nodes</text>

  <rect x="260" y="832" width="14" height="14" rx="3" fill="#6366f1"/>
  <text x="282" y="844" font-size="11" fill="#6b7280">Gateway / Agent Loop</text>

  <rect x="450" y="832" width="14" height="14" rx="3" fill="#10b981"/>
  <text x="472" y="844" font-size="11" fill="#6b7280">Context / Sessions</text>

  <rect x="630" y="832" width="14" height="14" rx="3" fill="#8b5cf6"/>
  <text x="652" y="844" font-size="11" fill="#6b7280">LLM Providers</text>

  <rect x="790" y="832" width="14" height="14" rx="3" fill="#f59e0b"/>
  <text x="812" y="844" font-size="11" fill="#6b7280">Skills / Plugins / Cron</text>

  <rect x="980" y="832" width="14" height="14" rx="3" fill="#64748b"/>
  <text x="1002" y="844" font-size="11" fill="#6b7280">Tool System</text>

  <rect x="1120" y="832" width="14" height="14" rx="3" fill="#ef4444"/>
  <text x="1142" y="844" font-size="11" fill="#6b7280">Security</text>

  <!-- 线条图例 -->
  <line x1="100" y1="866" x2="140" y2="866" stroke="#6366f1" stroke-width="2"/>
  <text x="150" y="870" font-size="10" fill="#6b7280">数据流</text>
  <line x1="260" y1="866" x2="300" y2="866" stroke="#8b5cf6" stroke-width="2" stroke-dasharray="5,3"/>
  <text x="310" y="870" font-size="10" fill="#6b7280">反馈/异步</text>

  <!-- 底部来源 -->
  <text x="800" y="920" text-anchor="middle" font-size="10" fill="#d1d5db">基于 OpenClaw 源码架构分析 · 2026-04-25</text>

</svg>
</body>
</html>


path: scripts/screenshot.py
content: #!/usr/bin/env python3
"""
SVG Architecture Diagram → High-res PNG screenshot via Playwright.

Usage:
    python3 screenshot.py <input.html> [output.png] [--scale 2] [--width 1600] [--height 1000]

Defaults optimized for architecture diagrams:
    - scale: 2 (high-res, good balance of quality and file size)
    - width: 1600
    - height: 1000
"""

import argparse
import sys
import os


def main():
    parser = argparse.ArgumentParser(description="SVG diagram screenshot via Playwright")
    parser.add_argument("html", help="Path to HTML file containing SVG diagram")
    parser.add_argument("output", nargs="?", default=None, help="Output PNG path")
    parser.add_argument("--scale", type=int, default=4, help="Device scale factor (default: 4)")
    parser.add_argument("--width", type=int, default=1600, help="Viewport width (default: 1600)")
    parser.add_argument("--height", type=int, default=1000, help="Viewport height (default: 1000)")
    parser.add_argument("--wait", type=int, default=1500, help="Wait ms after load (default: 1500)")

    args = parser.parse_args()

    html_path = args.html
    if not html_path.startswith(("http://", "https://", "file://")):
        abs_path = os.path.abspath(html_path)
        if not os.path.exists(abs_path):
            print(f"Error: File not found: {abs_path}", file=sys.stderr)
            sys.exit(1)
        html_path = f"file://{abs_path}"

    if args.output:
        output_path = args.output
    else:
        from pathlib import Path
        output_path = f"{Path(args.html).stem}.png"

    try:
        from playwright.sync_api import sync_playwright
    except ImportError:
        print("Error: playwright not installed. Run: pip install playwright && playwright install chromium", file=sys.stderr)
        sys.exit(1)

    print(f"Rendering: {html_path}")
    print(f"Viewport: {args.width}x{args.height} @ {args.scale}x")

    with sync_playwright() as p:
        browser = p.chromium.launch()
        page = browser.new_page(
            viewport={"width": args.width, "height": args.height},
            device_scale_factor=args.scale,
        )
        page.goto(html_path, wait_until="networkidle")
        if args.wait > 0:
            page.wait_for_timeout(args.wait)
        page.screenshot(path=output_path, full_page=True)
        browser.close()

    file_size = os.path.getsize(output_path)
    size_str = f"{file_size / 1024:.0f}KB" if file_size < 1024 * 1024 else f"{file_size / (1024 * 1024):.1f}MB"

    try:
        from PIL import Image
        img = Image.open(output_path)
        print(f"✅ {output_path} ({img.size[0]}x{img.size[1]}, {size_str})")
    except ImportError:
        print(f"✅ {output_path} ({size_str})")


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)