ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

一句话生成可交互架构图:AI Agent + JSON + SVG 全流程实战

一句话生成可交互架构图:AI Agent + JSON + SVG 全流程实战 1. 从一句话到一张图这个项目到底在解决什么问题画架构图这件事做过的人都知道有多折磨。产品经理丢过来一段需求描述说“帮我画个系统架构图”你打开 Visio 或者 draw.io拖方块、连箭头、调对齐、改配色半小时过去了图还没成型。更别提改需求的时候——加一个模块所有连线要重排布局全乱。我见过太多团队架构图永远停留在第一版因为维护成本太高没人愿意动。这个项目的核心思路很直接你只需要说一句话比如“画一个电商系统的微服务架构图包含网关、用户服务、订单服务、支付服务、库存服务和消息队列”系统自动生成一张可以在浏览器里打开、可以拖拽、可以点击查看详情的交互式架构图。整个链路是自然语言输入 → AI Agent 解析语义 → 输出结构化 JSON → 渲染成 HTML 可交互页面。听起来简单但每一步都有不少门道。适合谁来参考这篇内容如果你是后端开发、架构师、技术负责人想快速把脑子里的架构想法可视化如果你是前端开发想了解怎么用 JSON 驱动动态图形渲染如果你对 AI Agent 应用开发感兴趣想找一个完整的、有实际产出的练手项目——这个方向都值得花时间研究。它不依赖什么高深技术核心就是JSON 数据结构设计 HTML/SVG 渲染 AI Agent 编排但组合起来能解决一个真实存在的效率问题。我实际跑过几轮之后最大的感受是关键不在于 AI 有多聪明而在于你怎么设计 JSON Schema 和渲染逻辑。AI 只负责把自然语言翻译成结构化数据剩下的交给确定性的渲染引擎。这个分工思路是整件事能稳定跑通的前提。2. 整体架构设计为什么选 JSON 做中间层2.1 三层解耦输入层、转换层、渲染层整个系统我把它拆成三层每层职责单一互不干扰。输入层就是用户的一句话描述可能很粗糙比如“帮我画个三层架构图”也可能很详细比如“画一个包含 CDN、负载均衡、应用服务器集群、Redis 缓存、MySQL 主从的 Web 架构”。输入层不需要做任何预处理直接把原始文本传给下一层。转换层是 AI Agent 的核心工作区。它接收自然语言输出一个严格符合预定义 Schema 的 JSON 对象。这个 JSON 里包含节点nodes和边edges两个核心数组每个节点有 id、label、type、group 等字段每条边有 source、target、label 等字段。为什么用 JSON 而不是直接让 AI 输出 HTML因为 JSON 是结构化的、可校验的、可程序化处理的而 HTML 是一坨字符串AI 生成的 HTML 布局几乎不可控改起来也麻烦。渲染层拿到 JSON 之后用 JavaScript 动态生成 SVG 或 Canvas 图形绑定交互事件。这一层完全不依赖 AI是纯确定性的代码逻辑。你给同样的 JSON永远得到同样的图。提示三层解耦的最大好处是你可以单独替换任何一层。比如换个更强的 AI 模型或者把渲染层从 SVG 换成 Canvas其他层不受影响。2.2 为什么 JSON 是最合适的中间格式有人可能会问为什么不让 AI 直接生成 Mermaid 或者 PlantUML 代码那些也是结构化的文本格式渲染起来也方便。我试过 Mermaid 方案问题在于Mermaid 的布局引擎是黑盒你很难精确控制节点位置和连线走向。而且 Mermaid 的交互能力有限想实现“点击节点弹出详情面板”这种功能基本做不到。PlantUML 更偏向静态图交互性更弱。JSON 的好处在于它把“图的结构”和“图的呈现”彻底分开了。结构就是 nodes 和 edges呈现就是渲染层的事。你可以在 JSON 里给每个节点加任意自定义字段比如description、tech_stack、owner渲染层根据这些字段决定要不要显示 tooltip、要不要变色、要不要加图标。这种灵活性是 Mermaid 和 PlantUML 给不了的。另外JSON 的校验非常成熟。你可以用 JSON Schema 定义一套规则AI 输出的结果直接跑一遍校验不合格就重试。这比校验一段 Mermaid 代码容易多了。2.3 AI Agent 在链路中的角色定位这里要澄清一个概念AI Agent 不是万能的它只做它擅长的事。在这个项目里AI 擅长的是“理解自然语言描述的系统结构”不擅长的是“精确计算坐标和布局”。所以我把 AI 的角色严格限定在“自然语言 → JSON”这一步后面的布局计算、渲染、交互全部交给代码。具体来说AI Agent 的工作流程是这样的接收用户输入的自然语言描述。识别出系统中有哪些组件节点以及组件之间的调用关系边。给每个节点分配一个合理的 type比如 service、database、queue、gateway。按照预定义的 JSON Schema 输出结构化数据。如果输出不符合 Schema自动重试或报错。这个流程里Prompt 的设计非常关键。我后面会详细讲怎么写出让 AI 稳定输出合格 JSON 的 Prompt。3. JSON Schema 设计让 AI 输出可控的结构化数据3.1 节点和边的字段定义先看我实际用的一套 Schema简化版长这样{ title: 电商系统微服务架构, description: 包含网关、用户服务、订单服务、支付服务、库存服务和消息队列, nodes: [ { id: gateway, label: API 网关, type: gateway, group: 接入层, description: 负责路由转发、鉴权、限流 }, { id: user-service, label: 用户服务, type: service, group: 业务层, description: 用户注册、登录、信息管理 } ], edges: [ { source: gateway, target: user-service, label: HTTP } ] }每个字段都有明确用途id节点的唯一标识用于边的 source 和 target 引用。必须是英文、无空格、短横线分隔。label节点显示名称可以是中文。type节点类型决定渲染时的颜色和图标。常见类型有 gateway、service、database、queue、cache、external。group节点所属层级或分组渲染时可以用不同背景色区分。description节点详细描述点击时显示在侧边栏或 tooltip 里。edges里的source和target必须引用已存在的节点 idlabel是连线上的文字说明。注意id字段一定要强制 AI 用英文否则中文 id 在后续程序化处理时容易出编码问题。我踩过这个坑AI 有时候会输出中文 id导致边引用对不上。3.2 用 JSON Schema 做输出校验光定义字段还不够你得让 AI 知道什么算合格。我建议写一份正式的 JSON Schema在 Prompt 里附上同时在代码里也做一次校验。{ $schema: http://json-schema.org/draft-07/schema#, type: object, required: [title, nodes, edges], properties: { title: { type: string }, nodes: { type: array, minItems: 1, items: { type: object, required: [id, label, type], properties: { id: { type: string, pattern: ^[a-z0-9-]$ }, label: { type: string }, type: { enum: [gateway, service, database, queue, cache, external] }, group: { type: string }, description: { type: string } } } }, edges: { type: array, items: { type: object, required: [source, target], properties: { source: { type: string }, target: { type: string }, label: { type: string } } } } } }这份 Schema 的作用有两个一是放在 Prompt 里让 AI 照着填二是代码里用ajv之类的库做校验。校验不通过就触发重试重试时把错误信息也塞回 Prompt让 AI 知道哪里错了。3.3 处理 AI 输出不稳定的几种策略AI 输出 JSON 不稳定是常态我总结了几个应对策略策略一强制 JSON 模式。很多模型 API 支持response_format: { type: json_object }这样的参数开启后模型只会输出合法 JSON不会夹带解释文字。这个一定要开。策略二Few-shot 示例。在 Prompt 里给两到三个完整的输入输出示例让 AI 模仿。示例要覆盖不同复杂度的场景比如一个简单三层架构、一个微服务架构、一个带数据流的架构。策略三分步生成。如果一次性生成完整 JSON 容易出错可以拆成两步先让 AI 列出所有节点确认后再让 AI 补充边关系。这样每步的复杂度降低准确率会提升。策略四后处理修复。代码里写一些容错逻辑比如自动补全缺失的type字段默认设为service自动把中文 id 转成拼音或哈希值自动去重。我实测下来策略一 策略二组合使用成功率能到 90% 以上。剩下的 10% 靠策略四兜底基本不会出现完全不可用的情况。4. 渲染层实现从 JSON 到可交互 HTML4.1 技术选型SVG vs Canvas vs DOM渲染层有三个选择SVG、Canvas、纯 DOM。SVG是最合适的。每个节点就是一个rect或circle每条边就是一个path或line。SVG 元素天然支持事件绑定点击、悬停、拖拽都很容易实现。而且 SVG 是矢量图缩放不失真导出也方便。Canvas性能更好适合节点数量特别多的场景比如上千个节点。但 Canvas 里每个图形不是独立元素事件处理需要自己算坐标交互实现复杂度高很多。对于架构图这种通常几十个节点的场景SVG 完全够用。纯 DOM就是用div加绝对定位来画图。简单场景可以但连线处理很麻烦不推荐。我最终选了 SVG核心原因是交互实现简单。每个节点绑定click事件点击时显示详情面板绑定mouseover事件悬停时高亮相关连线。这些在 SVG 里都是几行代码的事。4.2 自动布局算法分层与力导向的取舍布局是渲染层最核心的部分。JSON 里只有节点和边的关系没有坐标坐标得靠算法算出来。常用的布局算法有两种分层布局和力导向布局。分层布局适合有明确层级关系的架构图比如接入层、业务层、数据层。算法逻辑是根据边的方向做拓扑排序把节点分配到不同层同一层的节点水平排列。这种布局出来的图很规整适合展示系统架构。力导向布局适合展示复杂网络关系节点之间的连线没有明确方向。算法逻辑是模拟物理系统节点之间有斥力边有引力迭代到稳定状态。这种布局出来的图比较自然但不够规整。对于架构图场景我推荐分层布局为主力导向为辅。具体做法是先根据节点的group字段分层同层内用力导向做水平排列避免节点重叠。这样既有层次感又不会太死板。如果你不想自己实现布局算法可以用现成的库比如dagre分层布局或d3-force力导向。dagre特别适合架构图它专门为有向图设计支持节点大小、边标签、层级间距等参数。4.3 交互功能实现拖拽、缩放、点击详情交互功能是“可交互架构图”的核心卖点。我实现了以下几个功能拖拽节点。给每个节点绑定mousedown、mousemove、mouseup事件拖动时更新节点的transform属性。同时要更新与该节点相连的所有边的路径否则连线会断。这个逻辑稍微有点绕但写一次就够了。画布缩放和平移。给 SVG 外层容器绑定wheel事件实现缩放绑定mousedownmousemove实现平移。缩放时要注意以鼠标位置为中心而不是以画布中心否则体验很差。点击节点显示详情。点击节点时右侧滑出一个面板显示节点的label、type、description等信息。如果 JSON 里有更多自定义字段也可以在这里展示。悬停高亮。鼠标悬停在节点上时高亮该节点及其直接相连的边和节点其他元素降低透明度。这个功能对理解复杂架构图特别有帮助。导出为 PNG。用html2canvas或 SVG 转 Canvas 的方案把当前画布导出成图片。这个功能用户很喜欢方便贴到文档或 PPT 里。提示拖拽节点后最好提供一个“重置布局”按钮一键恢复到自动布局的初始状态。用户拖乱了之后不用手动调回来。4.4 样式与主题让架构图看起来专业架构图好不好看配色和字体占一半。我总结了几条经验节点颜色按 type 区分。gateway 用蓝色service 用绿色database 用橙色queue 用紫色cache 用红色external 用灰色。这样一眼就能看出组件的角色。连线用曲线而不是直线。曲线更柔和交叉时也更容易区分。SVG 的path用贝塞尔曲线控制点根据节点位置动态计算。字体用无衬线体。中文用“思源黑体”或“苹方”英文用“Inter”或“Roboto”。字号不要太小节点内文字 14px 起步。加一点阴影和圆角。节点加rx6的圆角和轻微阴影看起来更有质感。背景用浅灰或白色。不要用花哨的背景架构图的核心是信息传达不是艺术创作。5. 完整实操流程从零跑通一句话生成架构图5.1 环境准备与依赖安装这个项目不需要太重的环境我用的技术栈是Node.js 18跑后端服务和构建工具。OpenAI SDK 或兼容接口调用 AI 模型做自然语言解析。任何支持 JSON 模式输出的模型都可以。D3.js 或 Dagre做布局计算。原生 SVG JavaScript做渲染和交互不需要 React 或 Vue保持轻量。初始化项目mkdir arch-diagram-gen cd arch-diagram-gen npm init -y npm install openai dagre d3如果你用其他模型把openai换成对应的 SDK 就行。核心逻辑不变。5.2 Prompt 设计与 AI 调用Prompt 是整个项目的灵魂。我反复调了很多版最终稳定下来的结构是这样的你是一个架构图生成助手。用户会用自然语言描述一个系统架构你需要将其转换为 JSON 格式的架构图数据。 输出必须严格遵循以下 JSON Schema 这里粘贴前面定义的 Schema 要求 1. 节点 id 必须用英文小写字母和短横线不能有中文。 2. 每个节点必须有 id、label、type 三个字段。 3. type 只能是 gateway、service、database、queue、cache、external 之一。 4. 边必须有 source 和 target且必须引用已存在的节点 id。 5. 根据系统描述合理推断层级关系用 group 字段标注。 6. 只输出 JSON不要输出任何解释文字。 示例输入画一个简单的三层 Web 架构包含负载均衡、两台应用服务器和一台数据库。 示例输出 { title: 三层 Web 架构, nodes: [ {id: lb, label: 负载均衡, type: gateway, group: 接入层}, {id: app-1, label: 应用服务器 1, type: service, group: 应用层}, {id: app-2, label: 应用服务器 2, type: service, group: 应用层}, {id: db, label: 数据库, type: database, group: 数据层} ], edges: [ {source: lb, target: app-1}, {source: lb, target: app-2}, {source: app-1, target: db}, {source: app-2, target: db} ] } 现在请处理以下输入 {用户输入}调用代码大概长这样import OpenAI from openai; const client new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); async function generateDiagramJson(userInput) { const response await client.chat.completions.create({ model: gpt-4o, response_format: { type: json_object }, messages: [ { role: system, content: SYSTEM_PROMPT }, { role: user, content: userInput } ], temperature: 0.3 }); return JSON.parse(response.choices[0].message.content); }temperature设低一点0.2 到 0.4 之间保证输出稳定。太高了 AI 会发挥创意加一些你没要求的节点。5.3 布局计算与 SVG 渲染拿到 JSON 之后先用dagre算布局import dagre from dagre; function computeLayout(json) { const g new dagre.graphlib.Graph(); g.setGraph({ rankdir: TB, nodesep: 60, ranksep: 80 }); g.setDefaultEdgeLabel(() ({})); json.nodes.forEach(node { g.setNode(node.id, { width: 160, height: 60 }); }); json.edges.forEach(edge { g.setEdge(edge.source, edge.target); }); dagre.layout(g); return json.nodes.map(node { const pos g.node(node.id); return { ...node, x: pos.x, y: pos.y }; }); }rankdir: TB表示从上到下布局适合分层架构。nodesep和ranksep控制节点间距和层级间距根据节点数量调整。然后生成 SVGfunction renderSvg(nodes, edges) { const svg document.getElementById(canvas); // 绘制边 edges.forEach(edge { const source nodes.find(n n.id edge.source); const target nodes.find(n n.id edge.target); const path document.createElementNS(http://www.w3.org/2000/svg, path); path.setAttribute(d, M${source.x},${source.y} C${source.x},${(source.ytarget.y)/2} ${target.x},${(source.ytarget.y)/2} ${target.x},${target.y}); path.setAttribute(stroke, #999); path.setAttribute(fill, none); svg.appendChild(path); }); // 绘制节点 nodes.forEach(node { const rect document.createElementNS(http://www.w3.org/2000/svg, rect); rect.setAttribute(x, node.x - 80); rect.setAttribute(y, node.y - 30); rect.setAttribute(width, 160); rect.setAttribute(height, 60); rect.setAttribute(rx, 6); rect.setAttribute(fill, getColorByType(node.type)); svg.appendChild(rect); // 文字省略... }); }5.4 交互事件绑定与状态管理交互部分的核心是维护一份“当前状态”包括节点位置、缩放比例、选中节点等。每次交互后更新状态然后重新渲染受影响的元素。const state { nodes: [], edges: [], scale: 1, selectedNode: null }; function onNodeClick(nodeId) { state.selectedNode nodeId; showDetailPanel(nodeId); highlightRelated(nodeId); } function onNodeDrag(nodeId, dx, dy) { const node state.nodes.find(n n.id nodeId); node.x dx; node.y dy; updateNodePosition(nodeId); updateRelatedEdges(nodeId); }状态管理不需要引入 Redux 或 MobX一个普通对象就够了。关键是每次修改状态后只更新受影响的 SVG 元素不要全量重绘否则拖拽会卡。6. 常见问题与排查技巧实录6.1 AI 输出 JSON 解析失败怎么办这是最常见的问题。表现是JSON.parse报错或者解析出来的对象缺字段。排查步骤检查是否开启了 JSON 模式。如果没开AI 可能在 JSON 前后加解释文字比如“好的以下是生成的 JSON”。开启 JSON 模式后这个问题基本消失。检查 Prompt 里是否有明确的 Schema。没有 Schema 约束AI 会自由发挥字段名可能对不上。加一层容错解析。用正则提取第一个{到最后一个}之间的内容再解析。这能处理 AI 偶尔加前后缀的情况。校验失败后自动重试。把校验错误信息拼回 Prompt让 AI 修正。重试次数设 2 到 3 次超过就报错让用户重新描述。提示我习惯在代码里加一个sanitizeJson函数先做基础清洗去 markdown 代码块标记、去首尾空白再解析。这个函数帮我省了很多事。6.2 节点重叠和连线交叉怎么处理节点重叠通常是因为布局参数不合适。dagre的nodesep和ranksep调大一点给节点留足空间。如果节点数量多考虑把画布尺寸调大或者允许用户手动拖拽调整。连线交叉是分层布局的固有问题很难完全避免。几个缓解办法调整节点顺序把关联紧密的节点放在相邻位置。用曲线代替直线交叉处视觉上更容易区分。给连线加不同的颜色或虚线样式区分不同类型的关系。提供“高亮路径”功能鼠标悬停某个节点时只显示与它相关的连线。6.3 复杂架构图渲染性能优化节点超过 100 个时SVG 渲染会开始变慢。优化方向有几个减少 DOM 操作。用DocumentFragment批量插入或者用虚拟 DOM 库。简化图形。节点用简单的矩形不要用复杂的路径或渐变。按需渲染。只渲染视口内的节点视口外的暂时不渲染。这个实现起来复杂但效果显著。考虑 Canvas。如果节点真的很多500换 Canvas 渲染用OffscreenCanvas做离屏渲染。不过说实话架构图通常不会超过 50 个节点。超过这个数量图本身的可读性就很差了应该考虑拆分成多张图。6.4 常见问题速查表问题现象可能原因解决方法JSON 解析失败AI 输出夹带解释文字开启 JSON 模式加 sanitize 函数边引用不存在的节点AI 编造了 id校验边引用自动删除无效边节点 id 是中文Prompt 约束不够强在 Schema 里加 pattern 校验强制英文布局太挤nodesep/ranksep 太小调大间距参数或增大画布拖拽后连线断开没更新边的路径拖拽时同步更新相关边的 d 属性缩放后模糊SVG viewBox 没更新用 transform 缩放不要改 width/height导出 PNG 空白跨域或样式丢失用 SVG 序列化 Canvas 绘制内联样式7. 扩展方向这个项目还能怎么玩跑通基础版本之后我试了几个扩展方向都挺有意思。方向一支持多轮对话修改。用户说“把数据库换成 MongoDB”系统在现有 JSON 基础上修改而不是重新生成。实现方式是维护对话历史每次修改请求带上当前 JSON让 AI 输出 diff 或完整的新 JSON。方向二从代码仓库自动生成架构图。扫描项目目录识别出服务、模块、依赖关系自动生成 JSON。这个对微服务项目特别有用能快速看清服务之间的调用关系。方向三导出为多种格式。除了 PNG还可以导出 SVG、PDF甚至生成 Mermaid 或 PlantUML 代码方便嵌入到文档里。方向四实时协作。多人同时编辑一张架构图用 WebSocket 同步状态。这个复杂度高一些但团队场景下很有价值。方向五接入更多数据源。比如从 Excel 文件读取组织架构数据生成组织架构图从数据库读取表结构生成 ER 图。核心逻辑不变只是输入源从自然语言换成了其他格式。我个人最看好的是方向二和方向五。从真实数据源自动生成架构图比让用户手动描述更可靠也更有实用价值。自然语言输入适合快速原型和头脑风暴但正式文档里的架构图还是应该从代码或配置里自动生成保证和实际系统一致。最后分享一个小技巧如果你想让 AI 生成的架构图更符合团队规范可以在 Prompt 里加一段“团队架构图规范”比如“所有对外服务必须标注为 external 类型”“数据库节点必须放在最底层”。这样生成的图不需要二次调整就能直接用。我试过把团队的命名规范和分层规范写进 Prompt效果立竿见影返工率降了一大半。
返回列表