先说明一个背景我在做 AI Agent 绘图时最常遇到的痛点不是“模型不懂 SVG”而是“模型画出来的图完全不可控”。自动布局看似聪明但一旦节点一多图形就开始乱跑想微调某个框的位置只能整个重画。后来我接触到SVG-diagram这个思路——它不是用自动布局引擎而是让 Agent 像人一样“手工摆放”每个节点和连线坐标自己算、位置自己定。这种方式在工控组态、架构图、流程图中非常实用。本文就围绕SVG-diagram 这个 agent skill展开讲清楚它是什么、和普通 Agent 指令有什么区别、底层怎么实现坐标定位并给出一个完整的可落地示例。无论你是想自己做 Agent 技能包还是只想让 AI 帮你画出满意的架构图这篇文章都会很有用。1. SVG-diagram 是干什么的1.1 它能解决什么问题SVG-diagram 是一个agent skill作用是让 AI Agent 以“手工摆放坐标”的方式绘制 SVG 图表。传统画图工具一般依赖自动布局算法比如 Graphviz、Mermaid而 SVG-diagram 的核心逻辑是由 Agent 拿到节点关系后自己计算出每个节点在画布上的 x、y 坐标再根据坐标生成对应的rect、circle、path、text等 SVG 元素通过坐标数值的精确控制让每个图形元素都被放在预期位置。这种方式的优点很明显对比项自动布局Mermaid 等手放坐标SVG-diagram布局可预测性一般高对复杂图表的支持一般强修改单个元素位置困难容易对长文本的适配一般可控落地到实际 SVG 文件需转换直接输出1.2 它适合用在哪些场景我在实际使用中总结了几个典型场景架构图。系统模块多、依赖关系复杂自动布局往往把图拉得很长手放坐标能把同类模块归拢到一起。流程图。需要精确控制分支走向或者希望流程图贴在文档里的固定位置。工控组态图。工控领域经常要绘制“设备—管道—阀门”关系图这类图对点位位置要求极高必须手放坐标。教学示意图。比如在学习 SVG 时手放坐标能直观看到每个元素的布局逻辑。1.3 它和普通 SVG 生成有什么不同普通生成 SVG 的 Prompt 往往是帮我画一个系统架构图包含用户端、网关、服务端。模型产出的结果随机性很大位置全靠“感觉”。但 SVG-diagram 提供了一套约束它给 Agent 定义了一个“坐标画布”规则要求每个元素都显式声明x、y、width、height并且由 Agent 自己计算节点坐标和连线路径。这样生成结果稳定、可微调、可复用。2. Agent Skill 是什么和 Agent 有什么区别2.1 Skill 的概念要理解 SVG-diagram必须先理解agent skill。Agent Skill 可以翻译为“智能体技能包”本质是一套结构化的能力模块。它通常包含三部分能力描述告诉 Agent 这个技能是做什么的、在什么场景下调用。使用流程定义调用该技能时的步骤和约束。工具或模板代码提供可直接使用的函数、模板、示例文件。以 SVG-diagram 为例这个 skill 可能是这样定义的技能名称SVG-diagram 功能根据给定的节点和关系生成手放坐标的SVG图表 调用条件用户请求绘制架构图、流程图、拓扑图、组态图 输出格式标准SVG代码坐标必须由技能规则自行计算2.2 Skill 与 Agent 的区别这是很多初学者容易混淆的地方我做一个简洁区分维度AgentSkill概念层级完整智能体能自主决策智能体内部的一类能力模块是否独立运行是否需要被 Agent 调用职责范围感知、规划、执行、记忆执行某一类具体子任务类比一个员工员工的某项工作技能可以这样理解Agent 是一个整体它负责“判断现在该干什么”Skill 是一个功能包它负责“这件事具体怎么干”。SVG-diagram 本身不运行成一个 Agent而是作为技能嵌入到 Agent 中当 Agent 需要画图时被激活。2.3 为什么把 SVG 画图能力设计成 Skill 而不是独立 Agent设计成 Skill 的主要原因有三个复用性。多个 Agent 可以共享同一个 SVG-diagram 技能不需要重复开发。职责单一。画图只负责画图Agent 的规划和记忆能力不需要参与绘图细节。可维护性。当需要调整画图规则时只改 Skill 里的配置文件即可不需要改整个 Agent 逻辑。3. 核心原理什么是手放坐标式 SVG3.1 先理解 SVG 坐标系SVG 的坐标系和我们初中数学里的坐标系不一样。它的原点(0, 0)位于画布左上角x 轴向右为正y 轴向下为正。比如一个矩形svg width400 height300 xmlnshttp://www.w3.org/2000/svg rect x50 y60 width120 height80 fill#e0f7fa stroke#00796b / /svg这个矩形的左上角位于距离画布左边界 50 像素、上边界 60 像素的位置。(0,0) ---------------------------------- x 轴正向 | (50,60) | -------------- | | | | | | | -------------- ↓ y 轴正向所以“手放坐标”中的“坐标”指的就是这种基于像素或用户单位的 x、y 数值。3.2 自动布局与手放坐标的底层差异Mermaid 之类的工具使用的是自动布局引擎比如dagre、elk它们会根据节点大小和边关系计算出布局位置好处是省事坏处是节点多时布局不可控想微调某个节点的位置很难输出是 Mermaid 语法还需要额外渲染成图。而 SVG-diagram 让 Agent 自己承担“计算坐标”的工作。这样 Agent 可以按照用户描述的布局意图把节点一个一个摆上去。例如用户说“把用户端放左上角网关放中间服务端放右侧”Agent 就会按这个方位去计算坐标。3.3 一个简单的手放坐标示例比如我们想让 Agent 画一个两节点的简单图节点 A 在左边节点 B 在右边中间一条连线。手放坐标的生成逻辑是画布宽度 600高度 200。节点 A 的矩形左上角(30, 60)宽 120高 80。节点 B 的矩形左上角(400, 60)宽 120高 80。连线从 A 的右边缘中点(150, 100)连到 B 的左边缘中点(400, 100)。生成 SVGsvg width600 height200 xmlnshttp://www.w3.org/2000/svg rect x30 y60 width120 height80 fill#bbdefb stroke#1565c0 stroke-width2 / text x90 y105 text-anchormiddle font-size16Node A/text rect x400 y60 width120 height80 fill#c8e6c9 stroke#2e7d32 stroke-width2 / text x460 y105 text-anchormiddle font-size16Node B/text line x1150 y1100 x2400 y2100 stroke#546e7f stroke-width2 / /svg可以看到每个元素都可以精确控制位置。这就是 SVG-diagram 的核心思想。4. 环境准备与项目结构4.1 环境说明编写 SVG-diagram 这个 skill 不需要复杂的开发环境我建议按以下方式准备Agent 运行时支持 Skill 机制的 Agent 框架例如 Claude Agent SDK、自研 LLM Agent 等。本文以通用 Skill 结构为例。语言Python 3.9用于编写坐标计算与 SVG 生成辅助函数。浏览器用于预览生成的 SVG 文件Chrome、Edge 均可。编辑器VS Code 或任意文本编辑器。版本不是固定要求核心是掌握 Skill 的组织方式。4.2 Skill 项目目录结构我建议这样组织svg-diagram技能包svg-diagram/ ├── SKILL.md # 技能描述给 Agent 看的说明书 ├── templates/ # SVG 模板 │ ├── basic_rect.svg # 基础矩形模板 │ ├── flow_node.svg # 流程节点模板 │ └── curved_path.svg # 曲线路径模板 ├── scripts/ │ ├── generate_svg.py # 根据节点数据生成 SVG │ ├── coordinate_utils.py # 坐标计算工具 │ └── validate_svg.py # SVG 合法性检查 ├── examples/ │ ├── architecture.json # 架构图输入示例 │ └── architecture.svg # 架构图输出示例 └── README.md # 使用说明这样的结构有几点好处SKILL.md是 Agent 优先读取的入口文件templates存放常用图形模板方便 Agent 直接复用scripts存放计算和生成逻辑让 Agent 不用凭空手算examples提供输入输出样例帮助 Agent 理解任务。4.3 安装依赖如果只用 Python 标准库生成 SVG不需要额外依赖。如果希望后续做更复杂的布局可以安装lxml用于 XML 解析pip install lxml不过下面的示例代码只使用标准库xml.etree.ElementTree所以不装也能运行。5. 编写 SKILL.md 技能说明书5.1 SKILL.md 的作用SKILL.md 是整个技能包的核心。它的作用不是给程序员看而是给 Agent 的 LLM 上下文看。所以写法要有别于普通开发文档要强调“触发条件、执行步骤、行为约束、输出格式”。5.2 完整示例# SVG-diagram ## 功能描述 根据用户提供的节点关系描述生成坐标精确、排版可控的 SVG 图表。 本技能适用于架构图、流程图、网络拓扑图、工控组态示意图。 不适用于照片处理、位图编辑、复杂 3D 图形。 ## 触发条件 当用户提出以下请求时必须调用本技能 - “画一张架构图” - “生成流程图” - “用 SVG 画拓扑图” - “画一个组态图” ## 执行步骤 1. 解析用户输入提取节点列表和连接关系。 2. 根据节点数量确定画布尺寸默认宽度 800高度按节点行数自适应。 3. 使用 coordinate_utils.py 中的函数计算节点坐标。 4. 从 templates/ 中选择合适的模板。 5. 生成 SVG 字符串并确保每个元素带有 x、y 属性。 6. 返回完整 SVG 代码并附上画布尺寸说明。 ## 行为约束 - 必须手放坐标禁止使用自动布局占位。 - 每个矩形节点必须声明 x、y、width、height。 - 连线必须基于节点边缘坐标计算不要使用随机坐标。 - 文字如果超出矩形范围需要重新调整矩形宽度。 - 输出必须是纯 SVG不能附带 Mermaid 或 HTML 包装。这里的关键是“行为约束”部分。如果没有这段Agent 很可能又按惯性输出普通 SVG失去 hand-placed 的意义。6. 坐标计算工具函数6.1 为什么要写工具函数Agent 直接计算坐标时经常出现“算错了”的情况。把坐标计算抽成 Python 函数有两个好处Agent 可以调用函数获得准确坐标不用心算坐标逻辑统一生成的图样式更稳定。6.2 坐标计算工具代码# 文件路径svg-diagram/scripts/coordinate_utils.py def calc_node_rect(index, node_width140, node_height70, gap_x60, gap_y50, offset_x40, offset_y40): 根据节点序号计算矩形左上角坐标。 采用两列布局序号 0、1 在第一行2、3 在第二行以此类推。 col index % 2 row index // 2 x offset_x col * (node_width gap_x) y offset_y row * (node_height gap_y) return { x: x, y: y, width: node_width, height: node_height, cx: x node_width / 2, cy: y node_height / 2 } def calc_edge(start_rect, end_rect, directionright): 计算两个节点之间的连线坐标。 direction 控制连线从哪个方向连接到终点。 if direction right: x1 start_rect[x] start_rect[width] y1 start_rect[cy] x2 end_rect[x] y2 end_rect[cy] elif direction down: x1 start_rect[cx] y1 start_rect[y] start_rect[height] x2 end_rect[cx] y2 end_rect[y] else: raise ValueError(暂不支持该方向: %s % direction) return {x1: x1, y1: y1, x2: x2, y2: y2}在这个工具里我们使用了“两列布局”。这是最基础的手放布局策略适合大多数图表场景。6.3 生成 SVG 的脚本接下来通过 Python 生成完整 SVG# 文件路径svg-diagram/scripts/generate_svg.py import json import sys from xml.etree.ElementTree import Element, SubElement, tostring from xml.dom import minidom from coordinate_utils import calc_node_rect, calc_edge def prettify(elem): rough_string tostring(elem, encodingunicode) reparsed minidom.parseString(rough_string) return reparsed.toprettyxml(indent ) def generate_svg(data: dict) - str: nodes data[nodes] edges data[edges] svg Element(svg, { xmlns: http://www.w3.org/2000/svg, width: str(data.get(width, 800)), height: str(data.get(height, 320)), viewBox: 0 0 %s %s % (data.get(width, 800), data.get(height, 320)) }) node_rect_map {} for idx, node in enumerate(nodes): rect_info calc_node_rect(idx) node_rect_map[node[id]] rect_info # 绘制矩形 rect SubElement(svg, rect, { x: str(rect_info[x]), y: str(rect_info[y]), width: str(rect_info[width]), height: str(rect_info[height]), rx: 8, ry: 8, fill: node.get(fill, #e3f2fd), stroke: node.get(stroke, #1565c0), stroke-width: 2 }) rect.text # 绘制文字 text SubElement(svg, text, { x: str(rect_info[cx]), y: str(rect_info[cy]), text-anchor: middle, dominant-baseline: middle, font-size: 14, font-family: Arial, sans-serif, fill: #212121 }) text.text node[label] # 绘制连线 for edge in edges: start_rect node_rect_map[edge[from]] end_rect node_rect_map[edge[to]] line_info calc_edge(start_rect, end_rect, directionedge.get(direction, right)) line SubElement(svg, line, { x1: str(line_info[x1]), y1: str(line_info[y1]), x2: str(line_info[x2]), y2: str(line_info[y2]), stroke: #455a64, stroke-width: 2, marker-end: url(#arrow) }) return prettify(svg) if __name__ __main__: input_file sys.argv[1] if len(sys.argv) 1 else ../examples/architecture.json output_file sys.argv[2] if len(sys.argv) 2 else ../examples/architecture.svg with open(input_file, r, encodingutf-8) as f: data json.load(f) svg_str generate_svg(data) with open(output_file, w, encodingutf-8) as f: f.write(svg_str) print(已生成 SVG 文件, output_file)注意这里generate_svg函数把节点循环和连线圈分开了结构清晰便于 Agent 在出错时定位问题。7. 完整实战让 Agent 调用 SVG-diagram 画架构图7.1 准备输入数据我准备了一个简单的架构图输入文件// 文件路径svg-diagram/examples/architecture.json { width: 800, height: 320, nodes: [ { id: client, label: 客户端, fill: #bbdefb, stroke: #1565c0 }, { id: gateway, label: API 网关, fill: #fff9c4, stroke: #f9a825 }, { id: service, label: 订单服务, fill: #c8e6c9, stroke: #2e7d32 }, { id: database, label: 数据库, fill: #ffccbc, stroke: #d84315 } ], edges: [ { from: client, to: gateway, direction: right }, { from: gateway, to: service, direction: right }, { from: service, to: database, direction: right } ] }这组数据表达的意思是客户端 → 网关 → 订单服务 → 数据库典型的线性架构。7.2 运行生成脚本在命令行执行cd svg-diagram/scripts python generate_svg.py ../examples/architecture.json ../examples/architecture.svg预期输出已生成 SVG 文件 ../examples/architecture.svg7.3 查看生成的 SVG用浏览器打开architecture.svg显示的图形类似[客户端] ──── [API网关] ──── [订单服务] ──── [数据库]虽然是一个线性图但每个节点的坐标都是通过“两列布局 右向连线”精确生成的。你可以直接在文本编辑器里修改任意节点的 x、y 值不会影响其他节点。7.4 在 Agent 中调用 Skill当 Agent 框架支持 Skill 调用时用户的请求流程如下用户输入“画一个订单系统架构图包含客户端、网关、订单服务、数据库。”Agent 识别到绘图意图加载SKILL.md。Agent 调用coordinate_utils.py的calc_node_rect计算坐标。Agent 调用generate_svg.py生成 SVG。Agent 返回 SVG 代码并展示给用户。这里的关键是Agent 并不“自由发挥”画图而是通过 Skill 定义好的工具函数完成计算这样得到的结果稳定可控。8. 进阶如何让 Agent 支持自定义布局8.1 允许用户指定行列规则默认情况下我们使用两列布局。但实际业务中用户往往有自己的布局偏好。比如用户说“客户端放左上角其他服务放右边一列”这时候 Agent 需要在生成 SVG 前调整坐标计算参数。可以在 SKILL.md 中增加一条规则## 布局规则 - 如果用户指定了位置左上、右上、中间等优先按用户指定的方位调整坐标。 - 如果没有指定默认使用两列布局。8.2 支持任意节点坐标覆盖更灵活的方式是允许输入数据直接包含坐标{ id: client, label: 客户端, x: 40, y: 120 }在generate_svg.py中增加判断if x in node and y in node: rect_info { x: node[x], y: node[y], width: node.get(width, 140), height: node.get(height, 70), cx: node[x] node.get(width, 140) / 2, cy: node[y] node.get(height, 70) / 2 } else: rect_info calc_node_rect(idx)这样一个 Skill 就同时支持“自动手放坐标”和“用户指定坐标”两种模式灵活性大幅提升。9. 常见问题与排查思路9.1 常见问题表问题现象常见原因解决思路Agent 没有调用 Skill直接输出普通 SVGSKILL.md 中触发条件不明确检查SKILL.md中的触发条件和功能描述生成的矩形重叠节点太多但画布高度不足根据节点数量动态计算画布高度文字超出矩形框文字宽度大于矩形宽度按文字长度计算矩形最小宽度连线方向不对节点排列方向与连线方向不一致统一使用calc_edge的方向参数SVG 在浏览器中不显示缺少 xmlns 或 XML 格式错误用浏览器开发者工具检查 SVG 根属性Agent 坐标计算错误让 Agent 心算而非调用函数强制要求 Agent 调用coordinate_utils.py中的函数9.2 文字溢出问题详解文字溢出是手放坐标 SVG 最常见的问题。解决方案有两种根据字符数估算文字宽度动态调整矩形宽度。在 SVG 中加入foreignObject让 HTML 自动换行。第一种方式更通用我通常会在工具函数里加一个估算函数def calc_text_width(text, font_size14): # 中文字符按 1.2 倍字号宽度估算 zh_count sum(1 for ch in text if \u4e00 ch \u9fff) en_count len(text) - zh_count return int(zh_count * font_size * 1.2 en_count * font_size * 0.6)使用这个函数Agent 在计算矩形宽度时就能预留足够空间。9.3 如何验证 SVG 合法性在validate_svg.py中可以加入基础检查# 文件路径svg-diagram/scripts/validate_svg.py import sys from xml.etree.ElementTree import parse def validate_svg(path): try: tree parse(path) root tree.getroot() if svg not in root.tag: return False, 不是 SVG 文件 if root.get(xmlns) ! http://www.w3.org/2000/svg: return False, 缺少 xmlns 命名空间 return True, SVG 校验通过 except Exception as e: return False, f解析失败: {e} if __name__ __main__: ok, message validate_svg(sys.argv[1]) print(message)在 Agent 生成 SVG 后强制调用这个脚本能第一时间发现 XML 结构错误。10. 最佳实践与工程建议10.1 坐标计算的工程化手放坐标的核心原则是不要让 Agent 心算坐标。在实际项目中我强烈建议把所有坐标计算逻辑下沉为函数让 Agent 直接调用。这样做至少有三个好处减少 LLM 的算术错误方便统一修改布局策略代码可以被单测覆盖。10.2 图层与分组管理当图表比较复杂时可以按语义对 SVG 元素分组g idlayer-background rect x0 y0 width800 height320 fill#fafafa / /g g idlayer-nodes rect x40 y40 width140 height70 fill#bbdefb / text x110 y75 text-anchormiddle客户端/text /g g idlayer-edges line x1180 y175 x2320 y275 stroke#455a64 / /g这样做的优势是背景、节点、连线三个层可以独立调整生成代码时也更容易排查问题。10.3 响应式 SVG生产环境中SVG 常常需要放到网页或文档里。建议在 SVG 根元素中同时设置width、height和viewBoxsvg width800 height320 viewBox0 0 800 320 xmlnshttp://www.w3.org/2000/svg这样SVG 在不同屏幕下可以等比缩放不会因为父容器变化而变形。10.4 Skill 的 Prompt 工程SVG-diagram 作为 Agent Skill它的表现很大程度上取决于SKILL.md的质量。我建议遵循以下规范用“触发条件”明确定义适用场景避免 Agent 在无关场景中误用。用“行为约束”限制 Agent 的随意性列明必须遵守的生成规则。用“执行步骤”给出可操作流程减少 Agent 的推理负担。提供“输入输出示例”时尽量给出正反例对。10.5 安全与授权边界SVG 本质上是 XML 文件在 Web 环境中解析时要注意不要直接执行从不可信来源获取的 SVG 内容中的脚本虽然 SVG 中的 script 在现代浏览器中被限制仍需警惕。如果需要用户上传 SVG建议在服务端校验 XML 结构并移除script、foreignObject等高风险元素。在包含敏感业务架构图时注意权限控制不要把所有图都暴露给所有用户。11. 总结与下一步学习建议到这里我们已经完整介绍了 SVG-diagram 这个 agent skill 的核心思路与落地方式。它的本质并不是什么高深算法而是把“人类手工排版”的习惯转化成 Agent 能理解和执行的坐标计算规则。通过这种方式AI 画出的 SVG 图不再是“开盲盒”每次输出的节点位置、连线走向、尺寸比例都是可预期、可修改的。接下来如果你想继续深入可以考虑从以下几个方向扩展增加更多布局策略比如树形布局、环形布局、分层布局。把生成的 SVG 转为 PNG、PDF方便文档输出。扩展 Skill 的输入方式支持从 JSON、YAML、Markdown 描述中自动提取节点关系。将 SVG-diagram 嵌入到你的 Agent 工作流中让画图成为整个自动化流程的一环。下图是本文核心内容的关系回顾用户描述 —— Agent 识别意图 —— 调用 SVG-diagram Skill | v coordinate_utils.py 计算坐标 | v 生成手放坐标 SVG 文件 | v 浏览器预览 / 网页嵌入如果你正在做 Agent 技能开发或者一直觉得 AI 画图不可控可以照着本文的思路实现一个自己的 SVG-diagram。动手试一次你会对手放坐标的优势有更直观的体会。本文提到的示例代码可以直接复制运行建议在自己的机器上完整演练一遍。