AI 画的架构图总跟真实系统对不上:文本生成图的排查与修法
数据截至 2026-07,各产品的额度与报错口径以官方最新说明为准。
多数人把这件事归错了因:图对不上系统,怪的是模型不会画图。真正的断点通常在上游——你喂进去的是你记忆里的系统,不是仓库里的系统。 模型把一段有偏差的描述翻译成一张排版工整的图,偏差不会消失,只会被工整的线条盖住,然后你在评审会上把它当依据用。
换成文本语法生成图(Mermaid、PlantUML、Graphviz、D2 这类),真正的收益不是”画得快”。拖框工具画一张新图未必比写二十行文本慢。收益在改:图变成了纯文本,能进版本库,能被 diff,能在 pull request 里逐行看改了哪条边,能被脚本校验。代价也在这儿——它从此带上代码的全部毛病:过期、冲突、口径不一致。
这篇只管结构类图:架构图、时序图、流程图、状态机。站内另外两篇管别的环节——文档那篇讲文字型交付物怎么组织和更新(见 AI 写文档),设计稿转代码那篇讲从视觉稿到前端实现(见 设计稿转代码)。本篇不碰美化、不碰白板协作。
一、先判断你要哪一类图,错配是最贵的返工
返工里有相当一部分不是画错了,是画的品种不对。你说”给我画个架构图”,模型给了一张方框加箭头的部署拓扑,你想要的其实是一次请求怎么在三个服务之间流转。这两张图的信息维度完全不同,改多少遍都不会变成对方。
在开口之前,把四个问题的答案先定下来:
- 看图的人是谁。给新同事看的入职图,粒度到”服务”就够;给做容量评估的人看,必须带上实例数与队列。
- 图上一个方框代表什么。进程?代码模块?物理机?团队?一张图里混用两种粒度,是所有”看着别扭又说不出哪儿不对”的根源。
- 箭头代表什么。同步调用、异步消息、数据流向、依赖关系,这四种语义在视觉上长得一样。要么分图,要么用不同线型并在图例里写死。
- 这张图要活多久。一次性沟通的草图,画完即弃;要长期维护的,得有明确的更新触发条件。
这四条必须你来定,模型定不了——它没有你的受众,也不知道你们团队把”服务”这个词用成了什么意思。定完再让它生成,第一版的可用率会有明显差别。
二、现象与成因的判别表
图生成出来之后先别改,先归因。同一个”图不对”,成因不同,处置动作也完全不同。
| 现象 | 大概率成因 | 怎么验证 | 处置动作 |
|---|---|---|---|
| 图里有仓库中不存在的服务或模块 | 模型在补全常见架构范式,而不是读你的代码 | 拿图上每个节点名回仓库搜;搜不到、且不属于你自己起的抽象名,就是编的 | 改成先给它目录树和入口文件清单,再让它生成 |
| 节点名对,但调用方向反了 | 描述里只说了”A 和 B 有关系”,没说谁发起 | 在代码里找调用点,看是谁 import 谁、谁在发请求 | 描述改成主谓宾句式:A 通过 HTTP 调用 B 的某接口 |
| 图渲染失败或半张图空白 | 语法错误,多为节点 ID 含空格、中文括号、保留字 | 把图源单独丢进渲染器跑一遍,看报错行号 | 节点 ID 全用英文短标识,显示文字放在方括号里 |
| 图能渲染但线条互相穿插、读不了 | 节点太多,或布局方向和信息流向不一致 | 数一下节点数,超过十几个基本必乱 | 拆子图,或换布局方向(自上而下改成自左向右) |
| 改一行文字,整张图布局全变 | 布局是算法算出来的:标签文字长度变了,节点尺寸就变,整体排布跟着重算 | 对图源做 diff,确认这次只改了标签文字;再把标签改回原长度重渲一次,布局若复原即可确认 | 接受布局会漂,别把布局当稳定资产;想少抖就固定节点声明顺序、控制标签长度 |
| 图和代码都对,但和上周的图不一样 | 同一系统被不同人用不同粒度描述过 | 比对两版图的节点粒度定义 | 把粒度约定写进仓库的说明文件,作为生成前提 |
| 中文标签渲染成方块或问号 | 渲染环节缺字体,不是图源的问题 | 换成纯英文标签重渲一次,正常则确认是字体 | 给渲染环境装中文字体,或改用带字体的渲染方式 |
最后一行容易被误判成图源的问题,实际上它发生在渲染环节,改图源改多少遍都没用。
三、AI 能接哪三步,哪两件必须你自己定
把这个环节拆开看,分工其实很清楚。
机器能干得比你好的三步。
第一步是语法翻译。你用大白话描述”用户请求先过网关,网关校验完转给订单服务,订单服务同步查库存、异步发消息给通知服务”,让它输出对应语法的图源。这一步机器几乎不会错,因为它是纯粹的形式转换,没有事实成分。
第二步是格式互转。同一份结构,从流程图改成时序图,从一种图语言换成另一种,从横排改成竖排。手工重画要十分钟,转换是几秒钟的事。这是文本图相对拖框最实在的优势——拖框工具里换个方向就是重画。
第三步是批量整理。十几个节点的命名风格不统一、有的带前缀有的不带、图例缺失,让它统一一遍比手工快。
必须你定的两件事。
一是边界。哪些东西上图、哪些不上,这是判断题不是检索题。第三方依赖画不画?降级链路画不画?内部的历史遗留旁路画不画?我的做法是给一张图设一个”读者能带走的三句话”,凡是不服务于这三句话的节点全部砍掉。模型不会砍,你不让它砍它就一直加。
二是事实。图上的每条边都是一句可以被证伪的断言:A 调用了 B。这句话真假只能从代码、配置、部署清单里查。让模型凭印象补出来的边,看起来最合理的那几条往往最危险,因为它们符合通用范式,所以没人会去质疑。
验收动作:图生成完,拿出所有节点名做一次存在性核对,再抽查三到五条关键边回代码里找调用点。这两步花不了十分钟,但它是这张图从”示意”变成”依据”的分界线。
# 粗提图源里的节点标识(以 Mermaid 为例),逐个回仓库数一下有多少文件提到它
grep -oE '^[[:space:]]*[A-Za-z][A-Za-z0-9_]*' docs/arch.mmd \
| sed 's/^[[:space:]]*//' | sort -u > /tmp/nodes.txt
while read -r n; do
printf '%s: %s\n' "$n" "$(git grep -l -- "$n" | wc -l)"
done < /tmp/nodes.txt
这个正则只做粗提取,会把 graph、subgraph 这类图语言关键字一起捞出来,看结果时自己跳过即可;节点标识写在行首之外的位置也会漏掉,所以它是筛查工具,不是判定工具。文件数为 0 的节点,要么是你自己起的抽象名(那就写进图例说明),要么就是模型编的。想更严一点,把节点标识和显示文字分开核:标识回代码里搜,显示文字回文档和部署清单里搜,两边都落空的才判定为编造。
四、把图钉进仓库:渲染、diff 与协作层的坑
图源进版本库之后,才开始遇到真正跟工程有关的问题。
渲染在哪一层做,决定了后面有多累。 嵌在 Markdown 里靠平台渲染最省事,但各平台支持的语法子集不同,换平台可能就不显示;本地生成图片提交进仓库显示稳定,但每次改图都产生二进制 diff;构建时渲染最干净,代价是要维护渲染环境。
我倾向第三种,前提是把渲染命令固化下来,别让每个人手上的版本不一样:
# Graphviz
dot -Tsvg docs/arch.dot -o build/arch.svg
# PlantUML(需要 Java 运行环境)
plantuml -tsvg docs/arch.puml
# Mermaid 命令行
mmdc -i docs/arch.mmd -o build/arch.svg
二进制图片进仓库会持续报复你。 PNG 的 diff 是不可读的,两个人同时改图必然冲突,而且冲突后没法手工合并,只能一方重画。这是把图源文本化的核心动因——文本冲突至少能看懂冲突标记两侧各自改了什么,人工合并是可行的。如果你已经遇到了图源的合并冲突,处理思路和代码冲突一致,见 git 冲突让 AI 解。
别指望模型自己去读大仓库。 让它”看看这个项目的架构画张图”,它读到的往往只是你打开的那几个文件加上目录名,剩下的靠推。大仓库的上下文获取本身就是个独立问题,见 大仓库上下文不足。稳妥的做法是你先把范围收窄到一个模块,给它明确的文件清单,一次只画一层。
关于工具选择:部分海外在线图表服务与托管型 AI 绘图产品,对中国大陆的可用性、账号注册与支付方式存在限制,具体以各家官方说明为准;本文只讨论走官方渠道能正常使用的用法,不涉及绕开区域限制的任何做法。落到工程上,长期维护的图建议用本地开源渲染器:图源、渲染器版本、字体都在你自己手里,不会因为某个在线服务改了策略或停服,就让整套文档一夜之间打不开。
五、什么情况下别再折腾
这一节比前面几节都重要。文本生成图有明确的能力边界,撞上了就该换路,硬磨只会耗掉一下午。
止损点一:同一张图来回改超过三轮还不对,停。 三轮之后还在错,说明问题不在图源,在你的描述本身有歧义,或者你自己对这块系统的理解就是模糊的。这时候正确动作是回去读代码,而不是继续换措辞。改措辞是在赌,读代码是在确认。
止损点二:布局怎么调都读不了,停止调布局,改拆图。 自动布局引擎的能力有上限,节点密到一定程度,任何参数都救不回来。拆成”总览图 + 若干局部图”,总览图只留主干,局部图各自展开一块。这是结构问题,不是渲染问题。
止损点三:这张图要的是设计感而不是准确性,换工具。 对外宣传材料、需要精确控制位置和留白的示意图,文本语法是错的选择——布局由算法决定,你控制不了。这类图用图形编辑器手工画反而快。
止损点四:图的生命周期不到一周,别进仓库。 没有维护意愿的图留在仓库里,半年后就是误导后人的错误资料。
回滚点:如果你把一张长期维护的图从图片改成了文本源,切换后一个月内出现两次以上”渲染环境出问题导致文档打不开”,回滚成图片是合理决策。文本化的收益是可维护性,如果稳定性的代价超过了它,这笔账不划算。
六、避坑清单
用中文或含空格的字符串当节点标识。 为什么会踩:写着顺手,模型也乐意配合。踩了会怎样:多数图语言的标识符不接受空格和部分标点,渲染直接失败,报错信息还常常指向下一行,让你查错地方。怎么避:标识符一律英文小写加下划线,显示文字放进方括号或引号里,两者分开。
让模型一次画完整个系统。 为什么会踩:省事,而且它真的会给你一张看起来很完整的图。踩了会怎样:节点数一多,编造的比例就上升,而且你没法逐条核实。怎么避:一次画一层,每层节点控制在十个以内,画完核实再画下一层。
图和代码分家。 为什么会踩:图放在文档系统里,代码在仓库里,各改各的。踩了会怎样:代码改了没人想起改图,三个月后图变成考古材料。怎么避:图源和代码放同一个仓库、同一个 pull request 里改,把”改了服务间调用关系必须同步改图”写进评审清单。
把生成的图直接当评审依据。 为什么会踩:图排版工整,看起来就很权威。踩了会怎样:一条编造的依赖边,可能让整场架构评审的结论偏掉。怎么避:图上标注生成日期和核对状态,未经核对的图明确标”草稿,未核对”。
图例缺失。 为什么会踩:画的人心里清楚实线虚线各是什么意思。踩了会怎样:换个人看,同一张图能读出三种意思。怎么避:只要图里出现两种以上线型或颜色,就必须有图例,这一条没有例外。
指望模型输出的图源格式每次一致。 为什么会踩:第一次给的格式很规整,就默认它每次都这样。踩了会怎样:脚本化处理时格式一变就断。怎么避:加一层格式校验,或者干脆先渲染再判断成功与否;结构化输出不稳定本身是个通用问题,见 结构化输出不稳。
提交前自检
这张图提交之前,过一遍下面六条:
- 图上每个节点名,我能在仓库里指出它对应哪个目录或哪个服务;
- 关键路径上的每条边,我能说出它对应哪一次调用或哪条消息;
- 一个方框代表什么,全图口径一致,没有混用粒度;
- 出现两种以上线型或颜色的,图例齐全;
- 图源和相关代码在同一个提交里,渲染能在干净环境跑通;
- 图上有生成日期,未核对的部分明确标注了状态。
六条全过,这张图才配当依据用。过不了的,标成草稿也行,别让它悄悄流进决策链路。