资讯动态

AI自动生成draw.io架构图:从自然语言到可编辑XML的实践

发布时间:2026/10/21 17:43:51 来源:尧图企业网站定制
讲个真实感受每次画架构图打开画布之后脑子就开始放空。摆放方块、拉线、调对齐、改字体一个小时过去图倒是能看但心里的烦躁早盖过了成就感。我见过太多人卡在这一步——不是不懂系统设计而是把大量时间耗在了“画”这件事上。后来我自己折腾了一个小项目代号 next-ai-draw-io核心就一句话把架构描述丢给 AI让 AI 直接生成 draw.io 能打开的架构图文件。这篇文章把我从思路到踩坑的完整过程都写下来给想做类似工具的开发者一个可复现的参考。这个项目解决的痛点非常明确人们不缺想法缺的是把想法迅速变成图的能力。架构图本质上是一种沟通语言它服务的对象是后续的评审、汇报、开发和维护。如果 AI 能理解一段文字描述并直接吐出一个标准格式的图文件那整个前置环节就能被压缩到几十秒。适合看这篇文章的人有三类第一类是正在做架构设计但厌恶手工绘图的开发者和技术负责人第二类是想给团队搭建内部工具、减少重复劳动的开发人员第三类是纯粹对“自然语言生成图形”这个技术方向感兴趣的爱好者。我会把思路拆解、数据格式设计、代码实现和常见问题都摊开来讲。1. 项目整体思路为什么是“文字进文件出”1.1 先想清楚一个关键问题AI 输出的终点是什么做这个项目之前我踩过一次弯路。最初的想法是让 AI 直接生成一张 PNG 图片或者一段 HTML 页面甚至用 Canvas 画出来。试了一圈后发现这条路的问题非常大AI 直接生成图片结果不可编辑稍微改一个模块名就得重新生成AI 生成 HTML虽然好看但和团队现有的协作工具、评审流程完全脱节。后来我换了个思路AI 图形的终点不应该是一张图片而应该是一个可编辑的中间格式文件。这个思路直接决定了后面所有的技术选型。以 draw.io 为例它的源文件本质是 XML包含一个个mxCell节点。只要我能生成合法的 XML 结构任何安装了 draw.io 的人都能直接打开、编辑、导出还能接入 Git 做版本管理。所以项目的核心链路变成了三段式自然语言描述 → 结构化中间表示 → draw.io 文件。这条链路的好处是每一段都可控、可调试、可替换。如果 AI 生成的中间表示有问题我可以单独修正如果 draw.io 格式规范更新了我也只需要改最后一段的序列化逻辑。1.2 读懂 draw.io 文件格式一切的基础想要让 AI 生成 draw.io 文件第一步是自己彻底理解这个格式。draw.io 的 XML 结构其实不复杂核心就是mxGraphModel里面嵌套root然后挂一堆mxCell。每个 cell 要么是一个节点vertex要么是一条连线edge。mxGraphModel dx800 dy600 grid1 gridSize10 guides1 tooltips1 connect1 arrows1 fold1 page1 pageScale1 pageWidth1169 pageHeight826 math0 shadow0 root mxCell id0 / mxCell id1 parent0 / mxCell idnode-1 value用户端 stylerounded1;whiteSpacewrap;html1;fillColor#dae8fc;strokeColor#6c8ebf; vertex1 parent1 mxGeometry x160 y60 width160 height60 asgeometry / /mxCell mxCell idedge-1 styleedgeStyleorthogonalEdgeStyle;rounded0;html1;exitX1;exitY0.5;entryX0;entryY0.5; edge1 parent1 sourcenode-1 targetnode-2 mxGeometry relative1 asgeometry / /mxCell /root /mxGraphModel这段 XML 里最关键的信息有三个value是节点显示出来的文字mxGeometry决定了节点的坐标和尺寸source和target属性决定了连线的两端。搞懂这三个东西整个项目就等于破冰了。剩下的问题就是怎么让 AI 输出一种我能稳定解析、又能轻松转成这套 XML 的中间格式。1.3 为什么选择 JSON 作为“中间语言”一开始我想直接让 AI 生成 XML但很快发现不现实。XML 的标签配对非常严格AI 偶尔会漏掉闭合标签或者把属性值写错一旦出错draw.io 直接打不开文件。更麻烦的是XML 本身不适合描述“这个节点左边是谁、右边是谁”这种关系逻辑。于是我在中间加了一层 JSON。JSON 的结构天然适合表达树形和网状关系AI 对 JSON 的输出稳定性也远高于 XML。我设计了一个高度约束的 JSON Schema节点和边分开描述{ nodes: [ { id: auth, label: 认证服务, layer: 应用层 }, { id: db-user, label: 用户库, layer: 数据层 } ], edges: [ { source: auth, target: db-user, label: 读写 } ] }这套结构做了三个重要的约束第一节点必须带layer字段用于分层布局第二边只允许引用已存在的节点 id第三label长度限制在合理范围内。这些约束是血的教训换来的——如果不限制AI 会创造出各种奇怪的字段名解析程序会变得无限复杂。2. 提示词工程让 AI 稳定输出结构化数据的核心方法2.1 给 AI 立规矩人话加模板双管齐下整个项目里提示词的分量比代码还重。我调试了很久才找到一个稳定输出的公式先讲清楚任务背景再给一个严格的 JSON 模板最后给一个错误示例。这三板斧缺一不可。系统中实际使用的系统提示词System Prompt核心部分如下你是一个软件架构图生成助手。用户会用文字描述一个系统的组成和关系你需要理解后输出 JSON。 输出必须严格遵守以下模板不得输出任何其他内容 { nodes: [ { id: 短横线命名, label: 中文字符串, layer: 接入层/应用层/数据层 } ], edges: [ { source: 节点id, target: 节点id, label: 关系说明 } ] } 节点 id 只能包含小写字母、数字和短横线。 如果用户描述中涉及两个节点之间的关系必须在 edges 中体现。关键不在于提示词写得有多花哨而在于模板约束得有多死。我给 AI 的模板里全是严格要求它就没有多少自由发挥的空间。这也解决了 AI 输出不稳定的最大问题——发散性。2.2 万能型指令把用户的一句话变成图用户输入通常很口语化比如“我有一个前端调用后端接口后端连数据库”。这种句子丢给 AI它需要做一层实体抽取。为了提高准确率我要求 AI 输出前先做一次自我检查确认每个节点都有归属层确认每条边两端的节点都存在。我用的用户指令模板是这样一段以下是我的系统描述{{用户输入}} 请理解后按系统提示词中的 JSON 模板输出不要输出多余的解释。这段指令配合系统提示词能覆盖绝大多数场景。我实测下来只要用户输入里包含明确的系统组件名词和动作关系词调用、连接、读写、发送AI 的抽取准确率都很高。如果用户输入特别模糊比如只有“帮我画一个网站架构”AI 会靠常识补充默认层级虽然不够个性化但至少能产出一张合格的基础图。2.3 提示词迭代实录我是怎么从“输出乱码”调到“一次成功”的这里写一段真实的迭代过程供大家参考。最初版本的系统提示词只有一句话“请你理解需求并输出架构图 JSON”。结果 AI 输出的 JSON 里出现了description、title、relationship等一堆自定义字段解析器完全无法处理。第一次修改规定字段名必须用nodes、edges节点必须含id、label、layer。第二次修改加了一个“错误示例”专门展示 AI 上次犯的错误。第三次修改增加“edges 必须引用已存在节点 id”的明确校验逻辑。三次迭代之后AI 的输出基本稳定成功率从最初的 40% 提升到了 95% 以上。这个经验可以抽象成一句话不要让 AI 去理解你的代码要让 AI 去理解你的模板。提示词里给的信息越具体AI 的发挥空间越小输出就越可控。3. 核心实现从 JSON 到 XML 的映射与布局计算3.1 节点和连线的映射规则拿到结构化 JSON 之后下一步就是把它转换成 draw.io 的 XML。这一步的原理不复杂遍历 JSON 数组为每个节点生成一个mxCell为每条边生成一个edge类型的mxCell。节点的映射规则我总结成了三条id直接作为 XML 的id属性但要加一个前缀避免与 draw.io 内部的0、1两个保留 id 冲突。value必须是 HTML 转义后的文本否则中文引号、尖括号会导致 XML 解析失败。style字符串由 layer 字段决定每一层有独立的配色视觉上分层更清晰。连线的映射规则则要特别注意source和target属性必须指向已经生成的节点 id否则 draw.io 打开文件会报错。连线上的文字标签也存在value属性里配合style里的exitX、entryX控制连接点方位。3.2 分层布局算法不用排队直接算坐标draw.io 本身不会自动布局所有节点坐标都必须预先算好。我实现的是一种简化版分层布局按layer分组每个层对应一列层内节点垂直排列。坐标计算的核心逻辑如下function computeLayout(nodes) { const layers {}; nodes.forEach(node { if (!layers[node.layer]) layers[node.layer] []; layers[node.layer].push(node); }); const layerOrder Object.keys(layers); const layerWidth 220; const nodeHeight 60; const verticalGap 40; const horizontalGap 80; const positions {}; layerOrder.forEach((layerName, layerIndex) { const layerNodes layers[layerName]; const totalHeight layerNodes.length * nodeHeight (layerNodes.length - 1) * verticalGap; let startY (600 - totalHeight) / 2 40; layerNodes.forEach(node { positions[node.id] { x: 80 layerIndex * (layerWidth horizontalGap), y: startY, width: layerWidth, height: nodeHeight }; startY nodeHeight verticalGap; }); }); return positions; }这段代码的思路很直白先统计每个层有多少个节点算出总高度然后让节点在画布垂直方向居中。水平位置由层序号决定垂直位置由层内序号决定。效果是左到右依次排列接入层、应用层、数据层看起来和手工画的标准多层架构图几乎一样。实际使用时如果一层的节点特别多画布高度会被撑得很大所以我还加了一个动态调整画布尺寸的逻辑根据最大层节点数乘以单位高度设置pageHeight。这个细节很多人会忽略但如果不处理节点多了之后会超出画布边界导出 PDF 时会被人为截断。3.3 连线正交化让图看起来“专业”普通的直线连接在节点层级多、交错频繁时非常丑。为了让最终效果接近人肉绘制我用了 draw.io 内置的正交连线样式styleedgeStyleorthogonalEdgeStyle;rounded1;html1;exitX1;exitY0.5;entryX0;entryY0.5;orthogonalEdgeStyle的作用是让连线变成横平竖直的折线这是架构图最常见的风格。exitX1表示连线从节点右侧中点出去entryX0表示连到目标节点左侧中点。这套组合几乎能覆盖所有“左到右”的图看起来很整齐。不过我后来也发现当同一层有两个节点都连接到右层同一个节点时正交连线会重叠。这个问题到后面“常见问题”部分我会专门讲怎么解决。4. 实操记录从零跑通整个流程4.1 项目启动环境准备和文件结构我没有用复杂的后端框架整个项目就是一个 Node.js 命令行工具加上一个极简的 Web 界面。Node 版本用的 18 以上HTTP 服务用的是内置的http模块AI 调用用的是官方的 OpenAI SDK 兼容接口。文件结构如下next-ai-draw-io/ ├── package.json ├── index.js ├── src/ │ ├── ai.js │ ├── parser.js │ ├── layout.js │ └── xml.js └── prompt/ └── system.txtai.js负责调用大模型接口parser.js负责把 AI 返回的文本解析成 JSONlayout.js负责坐标计算xml.js负责生成 draw.io 的 XML。四个模块各干各的互相之间只通过普通对象传递数据调试起来非常顺畅。4.2 代码实现关键环节的逐步拆解第一步调用 AI 接口。这个环节要特别注意把系统提示词和用户输入拼在一起temperature 设置为 0。为什么是 0因为画架构图是确定性任务我不希望 AI 每次生成的结果都不一样temperature 越低输出越稳定。async function generateGraph(userInput) { const response await client.chat.completions.create({ model: gpt-4o-mini, temperature: 0, messages: [ { role: system, content: systemPrompt }, { role: user, content: userInput } ] }); return response.choices[0].message.content; }第二步解析 AI 输出。AI 偶尔会在 JSON 前后加一些 json 之类的标记所以需要先做一轮清理把代码块标记剥掉再用 JSON.parse 解析。如果解析失败就用正则把 JSON 部分先截出来再试一次。function parseAIResponse(text) { let jsonStr text.trim(); const fenceMatch jsonStr.match(/json\s*([\s\S]*?)\s*/); if (fenceMatch) jsonStr fenceMatch[1]; return JSON.parse(jsonStr); }第三步校验结构。解析后的 JSON 必须经过一次完整性校验。主要检查三件事nodes是否为空所有edges引用的 id 是否都在nodes里每个节点是否有layer字段。这套校验帮我拦截了大量脏数据宁可让用户重新输入也不能拿着错误的 JSON 往下走。4.3 实际效果一份真实的输入输出对比我用一段实际的描述测了一轮输入是这样的用户通过浏览器访问前端页面前端通过 HTTP 调用后端接口网关 网关转发给订单服务订单服务连接 MySQL 数据库。AI 输出的 JSON 大致长这样{ nodes: [ { id: browser, label: 浏览器, layer: 接入层 }, { id: frontend, label: 前端页面, layer: 接入层 }, { id: gateway, label: 接口网关, layer: 应用层 }, { id: order, label: 订单服务, layer: 应用层 }, { id: mysql, label: MySQL, layer: 数据层 } ], edges: [ { source: browser, target: frontend, label: 访问 }, { source: frontend, target: gateway, label: HTTP }, { source: gateway, target: order, label: 转发 }, { source: order, target: mysql, label: 读写 } ] }生成的 draw.io 文件打开之后五个节点分三层排开横平竖直连线正交带箭头基本达到了“可直接用于评审”的水平。整个过程从输入文字到文件落地大约 20 秒。5. 常见问题与排查技巧实录5.1 AI 输出的 JSON 偶发不合法怎么降低概率我最初遇到最多的就是 AI 输出 JSON 时多了一个逗号、或者少了一个引号。后来我做了两层防护第一层是在提示词里反复强调“严格输出合法 JSON不要输出任何其他内容”第二层是在解析器里做容错处理用正则截取大括号片段再尝试修复明显的尾逗号问题。最有效的一招其实是在提示词末尾追加一句“请最后检查你输出的 JSON 是否可以被 JSON.parse 直接解析”。加了这句话之后AI 的自我纠错能力被激活成功率肉眼可见地提高了。5.2 图形重叠和连线交叉布局算法的两个“烂摊子”节点文字太长导致重叠是第一个会遇到的问题。中文环境下一个字符大约占用 14 像素宽度节点宽度 220 像素大概能容纳 14 个汉字。如果 label 超过这个长度在 draw.io 里就会自动换行节点高度不够就会显示不全。解决办法有两个要么在提示词里限制 label 最多不超过 12 个字要么在布局时根据 label 长度动态调整节点宽度。我最后选择了前者简单有效。连线交叉的问题则稍微复杂当多个节点同时指向同一个节点时四条正交连线会挤在一起。我最后的处理是如果目标节点有多个入边就把它们的entryY在 0.2 到 0.8 之间均匀分配虽然不能完全消除交叉但视觉上会好很多。5.3 打开文件报错90% 都是 ID 冲突或 XML 转义问题draw.io 对 XML 的要求很严格一个未转义的多字节字符就可能导致整个文件打不开。我遇到过一个典型的坑用户在一个模块描述里写了一个 符号AI 原样输出到 labelXML 直接解析失败。排查手段是拿到生成的 XML 后先用浏览器直接打开观察报错位置再用文本编辑器检查附近是否有未转义的特殊符号。修复方式很简单写一个escapeXml函数把、、、、统一转义。这个函数必须加在 AI 输出之后、写入文件之前顺序不能乱。5.4 一张排错速查表症状常见原因解决方式draw.io 提示文件损坏XML 标签未闭合或存在非法字符检查转义函数确保特殊符号已转义连线没有箭头style 中缺少endArrowblock在 edge 的 style 里追加endArrowblock;节点显示为英文标签AI 将中文 label 解析失败检查 AI 返回的 JSON确认 label 字段非空所有节点挤在左上角布局计算未执行检查是否有调用computeLayout并写入坐标打开后节点全部重叠坐标计算未考虑节点尺寸检查布局代码中是否设置了 width 和 heightAI 输出被截断输出 token 超限调大max_tokens或缩小描述内容6. 项目扩展方向从架构图到更宽的自动化边界draw.io 这个项目跑通之后我意识到它的架构完全是可以复用的。JSON 作为中间层的思路其实可以套用到任何“从文字到图形”的场景。比如时序图只需要把节点换成“参与者”把边换成“消息箭头”再比如部署拓扑图把 layer 换成“机房/集群/节点”即可。我还计划把系统接入团队的 CI/CD 流程当开发者在代码仓库里更新架构描述文档时自动重新生成最新的架构图并作为附件上传到文档平台。这样架构图永远和文字描述同步再也不用担心“图和代码脱节”这个老大难问题。另外有两点心得可以分享。第一AI 工具的实际可用性不在模型能力而在约束设计。你给 AI 的自由度越低拿到的结果就越可用。第二中间表示层的设计决定了整个系统的上限不要试图让 AI 一步到位生成最终文件多一层结构化的数据就多一份可控性。这个项目的核心价值在于把“画图”变成了“写文字”。作为开发者我终于可以把更多精力放在系统设计本身而不是被绘图工具绑架。如果你也在做架构设计或者想给团队做一个提效工具我强烈建议沿着这个思路自己跑一遍成本极低收益却非常可观。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑