架构图这件事,画过的人都懂那种痛。需求评审前夜,对着白板拍照的草图,硬生生要在绘图工具里一个框一个框地拖,一条线一条线地连,改一版布局全乱,再改一版配色又崩。我做过统计,一张中等复杂度的系统架构图,从梳理关系到最终导出,纯手工操作平均要花掉两到三个小时,而这其中真正用于"思考架构"的时间可能不到三分之一,剩下的全耗在拖拽和对齐上。这两年大模型能力上来了,我一直在琢磨能不能让 AI 直接把这件事干掉——我把想法落地成了一套可复现的方案:用 Codex 这类代码生成模型,配合 Draw.io 的 MCP 接口,让 AI 读懂需求描述后自动产出可编辑的架构图文件。这套东西解决的核心问题就一个:把"画图"这个体力活从架构设计流程里剥离出去,人只管描述和审阅。适合谁看?如果你是会写点代码的架构师、技术负责人,或者单纯被画图折磨过的开发者,这篇内容应该能帮你省下大量时间。下面我把自己踩过的路、调过的参数、翻过的车,原原本本拆开讲。
1. 方案整体设计与技术选型拆解
1.1 为什么是"代码模型 + 绘图工具接口"这个组合
先把这套方案的骨架说清楚。核心链路是:自然语言描述 → Codex 生成结构化图形描述 → 通过 MCP 协议调用 Draw.io 能力 → 输出可编辑的图表文件。这里有两个关键决策点,我逐个解释为什么这么选。
第一个决策是用代码生成模型而不是纯对话模型。架构图本质上是一种结构化数据,Draw.io 的底层文件格式是 XML(具体是 mxGraphModel 那套结构),节点、边、坐标、样式全是标签和属性。让一个擅长写代码的模型去生成 XML,比让一个只会聊天的模型去"描述一张图"靠谱得多。Codex 系列模型对标记语言的结构感很强,生成嵌套标签时不容易漏闭合,这是实测下来的明显优势。我试过用普通对话模型直接输出 XML,闭合标签错漏率大概在三成以上,而代码模型能压到个位数百分比。
第二个决策是走 MCP 而不是直接拼 XML 字符串。MCP 是 Model Context Protocol 的缩写,你可以把它理解成"AI 和外部工具之间的标准插座"。为什么不直接让模型吐一段 XML 存成文件?因为直接拼字符串有几个硬伤:一是模型对坐标计算不敏感,容易把框叠在一起;二是没法做增量修改,改一个节点要重新生成整份文件;三是缺少校验环节,生成的 XML 语法错了要到打开时才发现。走 MCP 的话,绘图工具侧可以提供"创建节点""连接两个节点""自动布局"这类原子能力,模型只需要决定"画什么、连什么",具体的坐标和样式交给工具侧处理,职责分离,稳定性高一个量级。
提示:MCP 的本质是工具调用的标准化协议,不同绘图工具的实现细节不同,但"模型决策 + 工具执行"这个分工模式是通用的,理解了这一点,换任何绘图后端都能套用。
1.2 整体架构分层与数据流向
我把整套方案分成四层,从下往上说。最底层是绘图引擎,负责实际的图形渲染和文件序列化,Draw.io 的桌面版和网页版都内置了这套引擎,我们通过它的文件格式和接口来对接。往上一层是 MCP 服务层,这一层是我自己写的适配器,把绘图引擎的能力包装成 MCP 工具,比如create_node、create_edge、auto_layout、export_file这几个核心方法。再往上是模型编排层,负责把用户的自然语言需求拆解成一系列工具调用序列,Codex 在这里扮演"总指挥"的角色。最顶层是交互层,可以是一个命令行脚本,也可以是一个简单的 Web 界面,用户在这里输入需求、查看结果。
数据流向是这样的:用户在交互层输入"画一个包含网关、三个微服务、一个数据库的架构图",编排层把这句话连同工具清单一起发给模型,模型返回一串工具调用指令,MCP 服务层逐条执行,绘图引擎实时构建图形对象,最后导出成.drawio文件。整个过程里,模型不碰坐标,只碰语义;引擎不碰语义,只碰几何。这个边界划清楚,后面所有的调试都会轻松很多。
1.3 选型对比:几种常见路线的取舍
在定方案之前我横向试了几条路,列个表对比一下,方便你判断自己该走哪条。
| 方案路线 | 实现方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 纯提示词生成 XML | 模型直接输出 mxGraphModel | 零依赖,最快上手 | 坐标易重叠,语法错误率高,难增量修改 | 一次性草图、简单图 |
| 模型 + 绘图 DSL | 模型生成简化的图形描述语言,再转 XML | 结构清晰,易校验 | 需要自己设计 DSL 和转换器 | 中等复杂度、需批量生成 |
| 模型 + MCP 工具调用 | 模型决策,工具执行几何操作 | 稳定性高,支持增量修改 | 需要写 MCP 适配层,前期投入大 | 复杂架构图、需反复迭代 |
| 模型 + 图像生成 | 直接出位图 | 视觉效果好 | 不可编辑,文字易错乱 | 演示配图,非工程用途 |
我最终选 MCP 路线,核心原因是架构图是要改的。评审会上领导一句"这个服务拆成两个",纯 XML 方案就得推倒重来,而 MCP 方案只需要追加两条工具调用。前期多花两天写适配层,后面每次修改省半小时,这笔账怎么算都划算。如果你只是偶尔画一张图,纯提示词方案也够用,别过度设计。
2. 核心细节解析与实操要点
2.1 Draw.io 文件格式的关键结构
要吃透这套方案,得先搞明白 Draw.io 的文件长什么样。它的核心是mxGraphModel这个根节点,下面挂root,root里固定有两个基础单元格:id="0"的根单元格和id="1"的默认父级。所有你画的图形,都是mxCell节点,带vertex="1"的是图形(矩形、圆形这些),带edge="1"的是连线。每个mxCell必须有唯一的id,parent指向父容器,图形还要有mxGeometry子节点描述位置和尺寸。
这里有个新手特别容易踩的坑:连线的 source 和 target 必须指向已存在的 vertex id。模型生成时如果 id 对不上,打开文件就是一堆断线或者干脆报错。我的做法是在 MCP 服务层维护一个 id 注册表,每次创建节点时分配一个语义化 id(比如svc_order、db_main),创建连线时校验两端 id 是否存在,不存在就拒绝执行并返回错误给模型,让它重新决策。这个校验环节把断线率从两成压到了几乎为零。
另一个细节是样式属性。Draw.io 的样式是一串分号分隔的键值对,比如rounded=1;fillColor=#dae8fc;strokeColor=#6c8ebf;。手写这串东西很反人类,但模型生成起来反而顺手,因为它就是文本。我在适配层预置了几套样式模板(服务节点、数据库节点、外部系统节点),模型只需要指定"用数据库样式",适配层负责展开成完整样式串。这样既保证了视觉一致性,又降低了模型的输出负担。
2.2 MCP 工具接口的设计原则
写 MCP 适配层的时候,接口设计直接决定了模型好不好用。我总结了三条原则,都是踩坑换来的。
第一条:工具粒度要"语义级"而非"像素级"。什么叫语义级?create_node(type, label, group)是语义级,set_x(id, 100)是像素级。前者模型能理解,后者模型会疯掉——它根本不知道 100 该给谁。我一开始设计了细粒度的坐标设置接口,结果模型要么乱给坐标,要么反复调用把图搞乱。改成语义级之后,模型只需要说"在网关组里加一个订单服务",位置由布局算法决定,输出质量立刻稳定。
第二条:每个工具都要有明确的失败反馈。MCP 工具执行失败时,返回的错误信息要足够具体,模型才能自我纠正。比如连线时 target 不存在,不要只返回"失败",要返回"target id 'svc_xxx' 不存在,当前可用 id 列表:[...]"。模型拿到这个信息,下一轮就能改对。我实测过,带详细错误反馈的版本,任务一次成功率比只返回布尔值的版本高出四成。
第三条:提供批量操作接口。模型一次决策往往涉及多个节点,如果只能一个一个建,调用轮次会爆炸。我加了create_nodes_batch(nodes)和connect_batch(edges)两个批量接口,一次调用建十个节点,轮次从十几次降到两三次,响应速度和稳定性都上来了。
2.3 提示词工程:怎么让模型"看懂"架构需求
模型再强,提示词写不好也白搭。我的系统提示词里固定包含四块内容:角色设定(你是一个架构图生成助手)、工具清单(每个工具的用途和参数说明)、输出规范(先规划再执行,每次调用前说明意图)、约束条件(节点数量上限、必须包含图例、连线不能交叉等)。
其中"先规划再执行"这条特别关键。我要求模型在第一次工具调用前,先用一段文字列出它打算画哪些节点、怎么分组、连哪些线。这段规划文字不消耗工具调用,但能显著提升后续执行的连贯性。实测下来,有规划步骤的版本,生成的图结构合理度明显更高,因为模型在"想"的时候把关系理顺了,执行时就不容易漏。
还有一个技巧是给模型喂示例。我在提示词里放了一个完整的"输入需求 → 工具调用序列"的示例,模型会模仿这个模式。示例不用长,一个包含五六个节点的小架构就够,但效果立竿见影。这其实就是少样本学习的老套路,在结构化输出任务上依然好用。
注意:提示词里的工具说明要和实际接口严格一致,参数名、类型、必填项一个都不能错。我吃过这个亏,提示词里写的是
label,接口实际要的是name,模型一直传错参数,排查了半天才发现是文档和实现对不上。
3. 实操过程与核心环节实现
3.1 环境搭建与依赖准备
动手之前先把环境理清楚。我用的技术栈是 Python 做 MCP 服务层,因为生态成熟、调试方便。核心依赖就三个:mcp官方 SDK 负责协议通信,lxml负责 XML 的解析和生成,requests负责和模型 API 通信。装依赖这一步没什么坑,pip install mcp lxml requests一把梭就行。
目录结构我建议这样组织:mcp_server/放适配层代码,prompts/放提示词模板,output/放生成的图表文件,examples/放测试用例。这个结构看着简单,但后面调试时能帮你快速定位问题——出图不对先看 prompts,文件损坏先看 mcp_server 的序列化逻辑,需求理解偏差先看 examples 里的对照。
配置模型 API 的时候,把密钥放环境变量里,别硬编码进代码。我见过有人把密钥提交到代码仓库,虽然及时删了,但历史记录里还留着,这种低级错误不值得犯。用os.environ.get("MODEL_API_KEY")读取,本地开发用.env文件配合python-dotenv加载,干净利落。
3.2 MCP 服务层的核心代码实现
服务层是整个方案的心脏,我挑最关键的三个函数讲。第一个是节点创建函数,核心逻辑是分配 id、构建 mxCell 结构、注册到 id 表。代码大概长这样:
def create_node(node_type, label, group=None): node_id = generate_semantic_id(node_type, label) style = STYLE_TEMPLATES.get(node_type, STYLE_TEMPLATES["default"]) cell = { "id": node_id, "value": label, "style": style, "vertex": "1", "parent": group or "1" } id_registry[node_id] = cell return {"status": "ok", "id": node_id}这段代码里generate_semantic_id是个小设计,它把类型和标签拼成 id,比如svc_order、db_user,好处是调试时一眼能看出这个 id 对应什么,比cell_001这种强太多。STYLE_TEMPLATES就是前面说的样式模板字典,预置了七八种常用样式。
第二个是连线函数,重点是校验:
def create_edge(source_id, target_id, label=""): if source_id not in id_registry: return {"status": "error", "message": f"source {source_id} 不存在,可用: {list(id_registry.keys())}"} if target_id not in id_registry: return {"status": "error", "message": f"target {target_id} 不存在,可用: {list(id_registry.keys())}"} edge_id = f"edge_{source_id}_{target_id}" # 构建 edge 结构并注册 ...错误信息里带上可用 id 列表,这个细节前面提过,是提升模型自我纠正能力的关键。
第三个是导出函数,把 id 注册表里的所有 cell 组装成完整的 mxGraphModel,用 lxml 序列化成 XML,再按 Draw.io 的压缩格式(可选,不压缩也能打开)写入.drawio文件。这里要注意 XML 声明和命名空间,漏了的话文件打不开。我一开始就漏了xmlns属性,生成的图在 Draw.io 里一片空白,排查了半小时才定位到。
3.3 一次完整的生成流程实录
拿一个真实需求走一遍。需求是:"画一个电商下单链路,包含 API 网关、订单服务、库存服务、支付服务、订单数据库、库存数据库,网关调用三个服务,订单服务写订单库,库存服务写库存库,支付服务调用外部支付渠道。"
第一步,编排层把需求连同工具清单发给模型。模型返回规划文字:"我将创建 7 个节点:网关、订单服务、库存服务、支付服务、订单库、库存库、外部支付渠道;创建 6 条连线……"这段规划我一般会打印出来人工扫一眼,结构不对就中断重来,比等图画完再改省事。
第二步,模型发起批量创建调用,create_nodes_batch一次传入 7 个节点定义,适配层逐个创建并返回 id 列表。模型拿到 id 后,发起批量连线调用,6 条边一次建完。这里有个细节:模型需要把"网关调用三个服务"翻译成三条具体的边,它得知道三个服务的 id,所以批量创建接口的返回值必须包含 id 映射,模型才能正确引用。
第三步,调用auto_layout做自动布局。我用的是分层布局算法,按节点类型分层:网关在最上层,服务在中间层,数据库在最下层,外部系统放侧边。布局算法会计算每个节点的坐标,避免重叠。这一步是纯几何计算,和模型无关,稳定性很高。
第四步,导出文件,用 Draw.io 打开检查。第一次跑的时候我发现支付服务和外部渠道的连线穿过了库存服务,视觉上很乱。解决办法是在布局算法里给外部系统单独分配一个列,并且连线走正交路由(edgeStyle=orthogonalEdgeStyle),拐直角弯而不是斜穿。改完之后图面清爽多了。
整个流程从输入需求到出图,实测耗时约 40 秒,其中模型推理占大头,工具执行不到 5 秒。对比手工两三个小时,效率提升是数量级的。当然,生成的图还需要人工微调,比如调整某个框的大小、改个颜色,但这些都是分钟级的操作,大头已经省掉了。
3.4 参数调优:布局与样式的关键配置
布局这块有几个参数值得单独说。层间距(layer spacing)我设的是 80 像素,太小了连线挤在一起,太大了图显得松散,80 是试出来的甜点值。节点间距(node spacing)设 40 像素,同层节点之间留这个空隙,视觉上不拥挤也不空旷。连线路由统一用正交样式,斜线在架构图里几乎总是显得乱。
样式方面,我定了套配色规范:服务节点用浅蓝填充配深蓝边框,数据库用浅黄配深黄,外部系统用浅灰配深灰,网关用浅绿。这套配色不是随便选的,浅色填充保证文字可读,深色边框区分类型,整体饱和度低,打印出来也不刺眼。模型只需要指定节点类型,配色由模板决定,这样生成的图风格统一,不会出现一张图里七八种颜色的灾难。
提示:布局参数没有绝对最优值,和图的规模有关。节点少于 10 个时间距可以小一点,超过 20 个就要适当放大,否则连线会密集交叉。建议把间距做成可配置参数,按节点数量动态调整。
4. 常见问题与排查技巧实录
4.1 生成结果不符合预期的排查路径
出问题别慌,按固定路径排查效率最高。我总结了一个排查顺序:先看规划文字,再看工具调用日志,最后看生成的文件。规划文字错了,说明需求理解有偏差,改提示词;规划对但调用日志里有失败,说明接口或参数有问题,查适配层;规划和调用都对但文件打开不对,说明序列化有 bug,查导出逻辑。
举个真实例子。有次生成的组织架构图,所有节点都堆在左上角重叠在一起。看规划文字没问题,看调用日志发现auto_layout返回成功,但文件里坐标全是 0。定位到布局函数里有个 bug:计算完坐标后忘了写回 cell 的 geometry。这种问题看日志看不出来,必须打开文件对比。所以我的建议是,调试阶段每次生成后都用脚本解析一下输出文件,打印每个节点的坐标,异常一眼就能发现。
4.2 模型调用失败与重试策略
模型 API 调用失败是常态,网络抖动、限流、超时都会遇到。我的策略是指数退避重试,第一次失败等 1 秒,第二次等 2 秒,第三次等 4 秒,最多重试三次。超过三次就中断并报错,不要无限重试,否则可能触发更严格的限流。
还有一种失败是"模型返回了无法解析的内容"。比如它本该返回工具调用,却返回了一段解释文字。这种情况我的处理是把错误信息连同原始返回一起塞回给模型,让它重新决策,通常第二次就能纠正。如果连续两次都这样,说明提示词可能有问题,得人工介入看看。
4.3 常见问题速查表
把高频问题整理成表,方便对照排查:
| 现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 文件打不开或空白 | XML 缺命名空间或根节点 | 用文本编辑器看文件头 | 补全xmlns和mxGraphModel根节点 |
| 节点全部重叠 | 布局未执行或坐标未写回 | 解析文件打印坐标 | 检查布局函数是否写回 geometry |
| 连线断裂 | source/target id 不存在 | 查 id 注册表 | 加强连线前校验,返回可用 id |
| 文字显示为方块 | 字体未指定或编码问题 | 检查 style 里的 fontFamily | 指定通用字体如 Helvetica |
| 生成速度慢 | 工具调用轮次过多 | 看调用日志统计轮次 | 改用批量接口,减少轮次 |
| 图形风格不统一 | 模型自创样式 | 检查 style 属性 | 强制使用样式模板,禁止模型自定义 |
4.4 几个血泪教训
最后分享几个我踩过的坑,都是文档里不会写的。第一个坑是 id 冲突。早期我用自增数字做 id,结果模型在增量修改时自己编了个 id,和已有的撞了,导致节点被覆盖。后来改成语义化 id 加随机后缀,冲突概率降到可忽略。第二个坑是中文标签的转义。XML 里某些字符要转义,模型生成时偶尔会漏,导致文件损坏。解决办法是在序列化前统一做一次转义处理,别指望模型每次都记得。
第三个坑是过度依赖自动布局。自动布局对规整的层次结构效果好,但对网状结构(比如微服务之间互相调用)就力不从心,容易画出蜘蛛网。我的经验是,网状结构超过一定密度时,手动指定几个关键节点的位置,剩下的交给算法,效果比全自动好。这个"一定密度"大概是平均每个节点超过三条连线的时候。
第四个坑是忽略了图例。架构图没有图例,读者看不懂颜色和形状的含义。我在提示词里强制要求生成图例节点,虽然模型偶尔会忘,但加上这条约束后,出图的专业度明显提升。图例本身也是几个节点加文字,用同一套工具就能画,成本很低。
这套方案我用了大半年,从最初的纯提示词方案一路迭代到现在的 MCP 版本,中间推翻重来了两次。现在它已经成了我日常工作的固定环节,需求评审前十分钟输入描述,图就出来了,剩下的时间全用来打磨架构本身。如果你也在被画图困扰,建议从最简单的纯提示词方案起步,跑通了再往 MCP 迁移,别一上来就搞全套,容易在适配层里迷失。真正值钱的不是工具本身,而是你对"哪些该交给 AI、哪些必须人来把关"这个边界的判断,这个判断只能靠一次次实操喂出来。