首页 / 资讯中心 / 文章详情

自然语言秒变架构图:自动化图表生成管线的设计与实践

自然语言秒变架构图:自动化图表生成管线的设计与实践 ★ FEATURED ARTICLE
正好最近有几个读者在问怎么把一段文字描述快速变成一张能看的架构图、流程图或者时序图“diagram-design”这个项目就是我针对这个需求做的一个自动化图表生成工具核心用途是输入自然语言描述输出可直接渲染的图形代码再配合渲染引擎得到成品图。它解决的是手动画图费时、改图更费时的问题适合做方案文档、技术分享、需求梳理的人参考哪怕你完全没有绘图基础也能在几分钟内得到一张结构清楚、配色不算丑的图。1. 整体设计与核心思路1.1 为什么做这样一个生成管线最早我是在整理一份跨系统的调用关系文档。手头有十几个服务、几十个接口用画图软件一个个拖框、拉线花了两个多小时结果产品经理看完提了几个修改意见又要从头调。后来发现其实描述这些关系靠文字就够了缺的只是把文字翻译成图形的那一步。这个项目想做的事情就是把“文字描述”和“图形成品”之间这条翻译链路自动化。整体设计上有几个关键约束第一不能要求使用者懂任何绘图语法第二生成结果必须能二次编辑不能只是一张死图片第三整套流程要能在命令行和脚本环境里跑通方便集成到文档生成流程中。基于这三点我把管线拆成了两段先用语言模型把自然语言整理成结构化的图形描述再用渲染引擎把描述变成图。中间不引入数据库和外部服务保持一个纯本地的轻量工具形态。1.2 技术选型背后的取舍先对比了三个常见的图形描述方案Mermaid、GraphvizDOT语言、PlantUML。Graphviz 擅长自动布局对复杂拓扑图支持好但样式偏老气想调好看工作量不小PlantUML 语法成熟可对中文环境和现代审美支持一般Mermaid 胜在语法直观、渲染效果现代支持流程图、时序图、状态图、甘特图好几种类型而且生态活跃能嵌入很多文档平台。最终我选了Mermaid作为主要的渲染目标。语言模型部分我选了支持函数调用和结构化输出的本地部署模型原因有两个一是避免把项目里的文档内容传到外部服务二是可以做比较细的输出约束。配套的处理框架直接用Python方便做文本处理和后续扩展。渲染引擎方面Mermaid官方提供了命令行工具可以基于Puppeteer做浏览器渲染这条链路在CI环境里很成熟不需要自己造轮子。实际项目里我不建议把整个生成结果直接交给模型自由发挥。正确的做法是让模型只输出“结构化的事实”而不是输出“排版”。排版的事交给渲染引擎事实提取的事交给模型各干各的活。1.3 分层设计的核心思想这个项目内部是严格分层的给后续扩展留了空间输入层接收自然语言描述可能是一段话也可能是一个包含调用关系的接口文档片段语义解析层调用语言模型提取实体、关系、类型输出一个中间数据结构图形映射层把中间数据结构映射成特定图形类型的Mermaid语法渲染层调用Mermaid CLI生成SVG或PNG图片校验层对模型输出做语法检查发现问题自动带着错误信息重试一次这样的分层带来的好处是如果想切换渲染引擎只需要替换图形映射层和渲染层如果想支持更多图表类型只需要扩展语义解析层的类型识别逻辑。后续我把一个新模型接入项目时源码基本没改只调整了提示词模板验证了这个分层的有效性。2. 核心细节与关键实现2.1 语言模型的选择与调参整个项目里语言模型是真正干活的部件选型和调参直接影响生成质量。我自己试过的方案里用7B量级的量化模型跑本地推理速度和效果能达到可用的平衡。不过有几点必须注意第一温度temperature要调低我通常设在0.1到0.2之间。温度过高会出现实体名被随意替换、关系描述发散的问题温度太低又容易死板地照抄原文不会做必要的归纳。经过比较0.2这个值对大多数输入表现最稳。第二上下文长度的设置会影响长文档的解析。如果输入的是一整篇接口文档建议把max tokens设置到模型支持的上限附近防止生成到一半被截断导致最后缺一段关系描述。第三输出格式必须约束成JSON。早期版本我让模型直接输出Mermaid代码结果不同模型产出的风格差异极大有的缩进混乱有的在注释里加多余说明很难统一处理。后来改成两段式第一段输出JSON格式的实体和关系列表第二段由固定的Python模板把JSON渲染成Mermaid代码。这样格式稳定也便于调试。2.2 提示词工程的设计要点提示词是这个项目里花时间最多的地方。我最初写的提示词就是简单一句“把下面的描述转成Mermaid流程图”结果输出里有大量没用的修饰词节点命名也不规范经常出现中文空格、特殊符号导致渲染直接报错。后来我按这样的结构重新设计提示词角色界定明确告诉模型它是个软件架构分析师任务是提取实体和关系不是写文章输出模式要求输出JSON并给出完整的字段说明和示例命名规则要求实体ID只允许字母、数字、下划线展示名称单独放在label字段关系规范化要求关系类型从固定枚举值中选比如“调用”“依赖”“继承”“包含”不能自由发挥异常处理遇到无法归类的实体放进单独的错误处理区域而不是硬塞进结构化结果这套提示词用下来实体提取准确率提升非常明显有个很实际的好处英文ID加中文label的组合方式既保证了Mermaid语法的干净又保留了中文展示效果。2.3 输出解析与语法校验模型输出JSON之后不能直接信必须经过一道严格的校验程序。我在项目里封装了一个解析模块主要做三件事JSON格式校验、必填字段检查、ID合法性检查。处理流程是这样的先用正则提取原始输出里的JSON片段因为模型偶尔会多输出一些解释性文字然后解析JSON失败的话进入重试逻辑接着遍历实体数组检查每个节点是否有合法的ID、标签、类型最后检查关系数组确保起点和终点都能在实体列表中找到对应项否则渲染时会报无定义节点的错。这层校验为什么重要因为Mermaid语法对特殊字符很敏感如果节点文本里有引号、冒号、尖括号轻则显示异常重则整张图渲染失败而且报错信息往往指向不明很难排查。加了前置校验之后大量问题都提前拦截在了解析阶段。3. 实操过程与生成示例3.1 环境搭建与依赖准备项目用Python实现环境准备很直接。核心依赖就两个本地模型推理框架和Mermaid命令行工具。# 创建虚拟环境 python -m venv venv source venv/bin/activate # 安装Python依赖这里以常见的LLM推理库为例 pip install llama-cpp-python pip install pyyaml requests # 安装绘图工具 npm install -g mermaid-js/mermaid-cli # 验证渲染工具是否可用 mmdc --version注意一个容易踩的坑Mermaid CLI需要在Node环境中运行首次执行时还会下载Puppeteer对应的浏览器内核如果网络受限要么手动指定浏览器执行路径要么在配置文件里调整Puppeteer参数。建议先单独执行一次简单的渲染确认环境没问题了再集成到项目里。3.2 生成一张系统架构图来走一遍完整流程。假设我要画一个电商系统的简化架构图输入的文字描述是“用户通过前端页面下单订单服务接收请求调用库存服务锁定库存同时调用支付服务发起支付支付成功后通知物流服务安排发货整个过程中消息队列负责异步通知各服务。”程序拿到这段描述后先调用本地模型提取实体和关系得到JSON结构的中间结果再映射成Mermaid图代码。生成出来的代码大致长这样graph TD A[前端页面] --|下单请求| B[订单服务] B --|锁定库存调用| C[库存服务] B --|支付请求| D[支付服务] D --|支付成功回调| B B --|发货通知| E[物流服务] B -.-|异步事件| F[消息队列] F -.-|事件订阅| C F -.-|事件订阅| D F -.-|事件订阅| E最终渲染出来的SVG图片可以无缝嵌入到文档里如果觉得方向不合适把代码里的TD从上到下改成LR从左到右就可以了不用重新生成。3.3 生成API调用时序图架构图只是第一步实际工作中时序图的需求更频繁特别是描述接口调用流程的时候。这个项目在训练数据里加入了时序图支持只要描述里出现了“先”“然后”“最后”这类顺序词解析模块会自动把它识别成时序图类型。看一个实际的例子描述如下“客户端先发送登录请求网关校验令牌然后调用用户服务获取信息同时缓存服务记录访问日志最后返回登录结果。”识别的结果是标准的时序图sequenceDiagram participant C as 客户端 participant G as 网关 participant U as 用户服务 participant R as 缓存服务 C-G: 发送登录请求 G-G: 校验令牌 G-U: 获取用户信息 G-R: 记录访问日志 G--C: 返回登录结果时序图的要点是参与者participant的识别。有些描述里不会明确出现“用户服务”这类名词而是写“后端校验账号”这时候模型需要结合上下文推断出参与者名称这一步做得好不好很依赖提示词里对参与者识别规则的约束。我在提示词里明确写了参与者必须是系统边界内的独立模块或服务不把数据库、缓存等基础设施单独列成参与者除非描述里明确提到。3.4 快速生成状态图状态图类的需求主要出现在描述业务流程状态流转的时候。比如一个订单的状态流转通常会被描述成“订单创建后进入待支付状态支付完成后变成已支付发货后变成已发货确认收货后变成已完结如果超时未支付则自动取消。”这种描述的特征是有一组明确的状态名以及状态之间的触发条件。解析层识别到“状态”“变成”“流转”等关键词时会自动切换成状态图模式生成的代码是stateDiagram-v2 [*] -- 待支付: 创建订单 待支付 -- 已支付: 支付完成 待支付 -- 已取消: 超时未支付 已支付 -- 已发货: 商家发货 已发货 -- 已完结: 确认收货 已完结 -- [*]状态图生成里最有意思的问题是触发条件的提取。很多时候状态变化的原因分散在描述的不同句子里模型需要把分散的信息聚合到转换边上。这一块的准确率我优化了很久最后是在提示词里加入了一个要求如果描述里某个转换没有明确的触发条件使用“触发”作为默认条件避免生成空边导致视觉上信息缺失。4. 常见问题与排查技巧实录4.1 Mermaid语法错误频发怎么办用得多了你会发现Mermaid语法看着简单实际出错率并不低。最常见的问题是节点文字里带了英文冒号或引号、连字符开头的节点名导致解析歧义、重复定义同一个节点造成覆盖、特殊字符没有转义。这类问题在命令行工具里的报错信息通常不够明确经常只给你一个行列号但真正的原因藏在上下文里。我的排查套路是这样的先让程序把生成的Mermaid代码保存成临时文件然后用mmdc单独渲染这个文件定位是语法错误还是渲染错误如果报错提示含糊就用二分法把图代码大块删除一半再渲染逐步缩小问题范围一旦定位到节点文本优先看有没有特殊字符。我在项目的公共函数里加了一个clean_node_text方法统一处理引号、冒号、换行符的清洗90%的语法问题都能在这一步解决。4.2 大图布局混乱、重叠严重节点数量超过30个以后Mermaid自动布局经常会出现连线交叉严重、节点重叠、整张图比例失衡的问题。这不只是观感问题复杂的连线交叉会严重降低图的可读性架构图变成了“蜘蛛网”完全失去了沟通价值。针对这个问题我总结了几条处理策略。一是对节点做层级归并把相同层级的组件合成子图subgraph这样Mermaid的布局算法能识别出明确的分组边界。二是减少单向连线的数量如果十几条线都指向同一个公共依赖组件考虑在边上做文字标注而不是全部画出来。三是优化图的方向节点以“宽”为主的时候用LR从左到右以“深”为主的时候用TD从上到下这个需要结合图内容预判没有绝对的标准。如果情况允许我对超过40个节点的图会强行拆分一张图只表达一个核心事实不追求大而全。4.3 渲染中文乱码问题Mermaid默认使用的字体在渲染中文时可能会显示成方框或乱码尤其在Linux服务器上常见。我当时第一次在服务器上渲染出一张全是方框的架构图时第一反应是代码问题折腾了很久才发现是字体缺失。解决方案很简单创建一个Mermaid CLI的配置文件指定Puppeteer渲染时使用系统中文字体。配置文件里可以指定字体家族、字号、边框粗细等全局样式。这里有一个扩展经验把样式统一提到配置文件里可以让所有图表保持一致的视觉风格这在输出多张图到同一份文档时尤其重要不会出现一张图加粗、一张图常规的情况。4.4 模型输出不稳定偶尔漏掉关键节点本地模型在短文本上表现很稳定一旦输入的描述条款较多、实体密集时偶尔会漏掉一个关键节点或在关系提取时把方向搞反。这种问题不是语法层面的报错不会出现但生成图的内容是错的而且不细看还不容易发现。我的应对方法是加了一个“关键要素检查提示”。在生成JSON输出前用提示词要求模型从原文里列举出所有名词短语作为候选实体这个步骤相当于多了一个查漏环节。同时在解析后的校验里增加了对关系方向合理性的启发式检查比如“依赖”“继承”这类关系有明确的语义方向如果提取出来的是反向的程序会提示开发者确认。这个功能不完全自动化但能在生成阶段就把明显错误的工作量省掉比出图之后再人工改要快得多。5. 让生成结果真正可用于交付的经验总结这个项目做到后期我觉得最有价值的不是“生成一张图”这件事本身而是把“怎么生成一张能用的图”做成了可以复用的方法论。有几个体会想单独说一说。第一保持生成结果的可编辑性。永远不要直接输出一张PNG了事一定要把中间生成的Mermaid代码一并保存下来。现实中图的调整永远比重新生成更频繁产品经理让你把“订单服务”改成“交易中台”时改一行代码比重画整张图舒服太多了。我现在所有输出都同时生成.mmd源文件和.svg图片文件源文件归档在项目的docs目录下。第二统一模板是保证系列图片一致性的关键。我给这个工具内置了一套可配置的主题参数包括节点颜色、边框宽度、连线颜色、字体大小等。比如你可以把核心服务节点配置成一种颜色基础组件配置成另一种颜色这样输出的图天然带有层次感。Mermaid里可以使用classDef在代码层面定义样式组回顾一下之前那个电商架构图的例子graph TD classDef core fill:#e1f5fe,stroke:#01579b,stroke-width:2px; classDef base fill:#f1f8e9,stroke:#33691e,stroke-width:1px; A[前端页面] --|下单请求| B[订单服务] B --|锁定库存调用| C[库存服务] B --|支付请求| D[支付服务] class A base; class B,C,D core;这个看着不起眼的功能让整套图纸的档次提升非常明显。相同主题的图放在一份文档里一致性带来的专业感比花里胡哨的配色重要得多。第三结合文档流水线才是这个工具发挥最大价值的地方。我后来把它接进了一套文档自动构建流程中写技术方案时只需要在Markdown里嵌入特定标记构建脚本会自动调用生成管线把描述替换成图。这意味着文档维护变成了一件轻松的事需求变了就改描述文字构建时自动出图不再有“图和文档不一致”的问题。虽然这个项目最初的形态只是个命令行小工具但加上这个流水线之后它实质上变成了一个轻量级的文档自动化系统。根据我个人的项目经验这类工具的扩展方向还有不少比如增加输入文档解析功能支持直接读接口定义文档生成服务依赖图或者把生成结果同步导出一份JSON格式的数据结构方便做后续的架构分析。如果你也要做类似的项目我的建议是先把解析层和渲染层的边界划清楚哪怕前期多花一点时间设计数据结构后期扩展时会非常省力。做工具这件事最大敌人其实是“够用就行”的心态一旦中间数据结构设计得足够稳定上游加再多的输入样式都是小工作量的事。
阅读完成 · 觉得有帮助?
咨询建站