12|进阶②:让 AI 画架构图——Prompt 工程一线实践
处理12|进阶②:让 AI 画架构图——Prompt 工程一线实践这类问题时,先确认目标场景,再按步骤核对配置或玩法细节。
一个反直觉的开场
先说个反直觉的结论:让 LLM 画出一张「能用」的架构图,最不重要的就是模型本身。
我第一次做这功能的时候,思路特别朴素——Draw.io 的文件不就是个 XML 吗?我把一个完整的 .drawio 文件丢给模型当样例,说「照这个格式,把项目架构画出来」。结果你猜怎么着?十次里有六次是废的:要么 id="0" 这个根节点忘了写,要么连线穿过中间的方块、几条线挤在一起糊成一团,要么干脆 XML 都没闭合。
我一度以为是模型不行。直到我换了三家模型,发现失败模式一模一样——这才反应过来:不是模型笨,是我把活儿派错了。模型擅长「这块放哪、连到谁」这种创意决策,但你让它去抠 XML 嵌套层级、平衡画布坐标、给每条线算绕行路径,这是在拿它的短板硬刚。
后面这套从 40% 到 90% 的演化,核心就一句话:把机械的活儿交给代码,把创意的活儿留给模型,然后用 Prompt 把两者之间的接口对齐。下面一步步拆。
1. Draw.io XML:一个 650 行才出一张图的格式
先看清楚对手。Draw.io(现在叫 diagrams.net)底层是 mxGraph XML——一套描述图形节点、连线、布局的标记语言。一个完整文件长这样:
<mxfile><diagramname="架构图"id="diagram-1"><mxGraphModeldx="1422"dy="794"grid="1"page="1"...><root><mxCellid="0"/><!-- 根节点,必须有 --><mxCellid="1"parent="0"/><!-- 默认图层,必须有 --><mxCellid="2"value="前端"style="..."vertex="1"parent="1"><!-- 你的第一个方块 --><mxGeometryx="40"y="40"width="160"height="60"as="geometry"/></mxCell><mxCellid="3"style="..."edge="1"parent="1"source="2"target="4"/><!-- 一条连线 --></root></mxGraphModel></diagram></mxfile>
真正承载「信息」的就是那几个 <mxCell>,外面套了 <mxfile> → <diagram> → <mxGraphModel> → <root> 四层壳,外加两个雷打不动的根节点。一张基础三层架构图,完整 XML 能写到 650 行——其中六成是每次都长一个样的模板。
你让模型从头到尾生成这整坨,等于让它在四层嵌套里不出一个错。这不现实。
2. 「反向剔除」:让代码做 boilerplate,让模型做创意
这是整个设计里我最想安利的一个洞察。
大多数人的 Prompt 思路是正向的:把完整输出格式摆给模型,让它照抄。但面对 Draw.io 这种壳比肉多的格式,这条路是死胡同——你抄得越全,出错点越多。
catbuddy 的做法是反过来——从完整 XML 里,把模型不需要操心的部分全部剔掉。
代码承担包装层。 display_diagram 工具收到模型吐出来的裸 mxCell 片段后,自动包成一个完整文件:
functionensureMxFile(xml: string): string {const trimmed = xml.trim()if (trimmed.startsWith('<mxfile')) return trimmedif (trimmed.startsWith('<mxGraphModel')) {return`<mxfile><diagram name="架构图" id="diagram-1">${trimmed}</diagram></mxfile>`}// 最常见的分支:模型只给了裸 mxCell,代码把四层壳和根节点全补上return`<mxfile><diagram ...><mxGraphModel ...><root>` +`<mxCell id="0"/><mxCell id="1" parent="0"/>${trimmed}</root></mxGraphModel></diagram></mxfile>`}
三个分支覆盖了模型可能的三种输出——有的模型「自作聪明」加了点壳,有的没加。不管哪种,代码都能兜住。
模型只负责裸 <mxCell> 。 Skill 文件里这句话写得很白(关于 Skill 是什么、怎么挂进来,第 05 篇讲过,这里只用):
Generate ONLY bare <mxCell> elements — 别带任何壳标签,系统会自动包成合法的 .drawio。
配三条铁规则:ID 从 "2" 开始递增("0"/"1" 系统占了)、所有 mxCell 是平级兄弟不许嵌套、顶层元素 parent="1"。你压根不用告诉模型什么是 <mxfile>,它只要关心「这块放哪、连到谁」。
这条路为什么走得通?三点:
- 认知负担小:模型只需理解
<mxCell>一种元素,不用扛五层嵌套的模板; - 错误面小:每层壳都是潜在出错点,剔掉四层壳就消灭了四类错误;
- token 省:生成的 XML 缩短约 40%,同样的上下文窗口能画更复杂的图。
一句话:别教模型生成完整 XML,让代码去补它不擅长的机械部分。
3. 四轮迭代:可用率 40% → 90% 是怎么磨出来的
「反向剔除」解决了「壳」的问题,但「肉」本身——布局、连线——还是模型生成。这部分纯靠 Prompt 打磨,我们迭代了四轮。先看全景:
每一档的提升,全都来自从失败案例里提炼具体规则,而不是把「请画得好看一点」这种废话说得更大声。
第 1 版:零约束,可用率 40%
最初的 Prompt 极简——「你是 Draw.io 图表专家,请分析架构生成 XML」,附一个三节点示例。结果约 40% 可用。主要翻车点:忘根节点、容器内子元素 parent 指错、连线漏了 source/target、XML 特殊字符没转义。
第 2 版:加布局约束,可用率 55%
加了空间规则:所有元素 x=0~800, y=0~600、起点 x=40,y=40、相邻间距 150~200px。结果 55%。 布局不再挤成一坨了,但连线还是灾难——多条线重叠、双向连线互相穿过。
第 3 版:连线五规则,突破到 75%
这是我们投入最多的一版。盯着上百个失败案例看,提炼出 5 条连线规则,写进 Skill:
## Edge Routing — 5 Rules1. 平行连线错开:同两节点间的线用 exitY=0.3 / exitY=0.7 岔开。2. 双向连线:A→B 从右出(exitX=1),B→A 从左出(exitX=0)。3. 每条边都显式设 exitX/exitY/entryX/entryY。4. 用 waypoint 绕开障碍:在起点终点之间的图形旁边绕路。5. 连在边的中点,别连角落(不要 entryX=1,entryY=1)。
第 4 条 waypoint 绕行专治「连线穿过中间节点」这个高频毛病——给模型一个带 <Array as="points"> 拐点的示例,它就知道怎么让线绕开挡路的方块。结果跳到 75% ,连线质量已经到了「基本不用手动调」的水平。
第 4 版(当前):完整 Skill + 工具链,90%
当前版把整套规范沉淀成一个可插拔的 Skill 包。关键加了四样:
- 分层颜色表:前端蓝
#dae8fc、后端绿#d5e8d4、数据橙#ffe6cc、基础设施紫、外部红——模型不用「猜」配色,查表就行; - Swimlane 完整示例:游泳道是出错率最高的元素,给整段示例,避免「子元素 parent 指错容器」;
- 三种连线示例:基础连线、容器连线、带 waypoint 绕行,覆盖九成场景;
- 工具链衔接:明确写「下一步 Call
display_diagram({ xml })」,告诉模型生成完该干嘛。
当前约 90%。 剩下 10% 的锅主要是超复杂图(20+ 节点)撞 ID 冲突或超出画布。
这里我想停下来强调一句话,它是这四轮迭代真正的方法论:告诉模型「不要做什么」,往往比「要做什么」更有效。 因为模型的默认行为,常常恰好就是你不想要的那种——它默认会把平行线画重叠、默认会让连线走直线穿过方块。你与其正向描述一个完美结果(它本来就想做但做不到),不如直接堵掉那条默认的歪路。「连在中点,别连角落」「双向线一个从左出一个从右出」——这些都是在做反向剔除:不是加规则,是在删模型的坏习惯。
4. 三个工具的「委托式」分工
Prompt 只是一半,另一半是工具链。catbuddy 给画图配了三个独立的 Agent Tool,分工干净得像流水线(工具怎么注册、怎么被调度,第 04 篇讲过,这里只看它们各自的职责):
display_diagram ——造。 适合中等复杂度(10~15 节点以内)一次成图。它入口处有个有意思的校验,不是用 XML parser,而是直接看开头字符:
functionlooksLikeDrawioXml(xml: string): boolean{consttrimmed = xml.trim()return trimmed.startsWith('<mxfile')|| trimmed.startsWith('<mxGraphModel')|| trimmed.startsWith('<mxCell')}
比 XML 解析宽容,比不校验严格。 万一模型脑子一抽吐出 Mermaid 或 Markdown 表格,直接拒了并给明确指引,而不是把一坨非法 XML 写进文件。
append_diagram ——续。 大项目 30+ 节点的图,模型一次输出会被上下文窗口截断。这个工具允许分片追加,每片都先抽出完整 <mxCell> 再逐条验证:
functionvalidateCells(existingXml: string, cells: string[]): string | null {if (cells.length === 0) return'No complete <mxCell> elements found in xml'const seen = newSet<string>()for (const cell of cells) {const id = cellId(cell)if (!id) return'Every appended mxCell must have an id attribute'if (id === '0' || id === '1') return'Do not append root cells id="0" or id="1"'if (seen.has(id)) return`Duplicate cell id in appended fragment: ${id}`if (hasCell(existingXml, id)) return`Cell already exists in diagram: ${id}`seen.add(id)}returnnull}
ID 缺失、撞根节点、片内重复、跟已有图重复——四种情况各给一句人话错误,模型一看就知道哪错了。
edit_diagram ——改。 图出来后用户常要微调。它做的是基于 ID 的精确增删改,核心是个用 lookahead 断言的正则:
functioncellRegex(cellId: string): RegExp {const id = escapeRegExp(cellId)returnnewRegExp(`<mxCellb(?=[^>]*bid=["']${id}["'])[^>]*(?:/>|>[sS]*?</mxCell>)`,'m',)}
用 lookahead 而非捕获组,保证只命中目标 ID 那个 cell,绝不误伤别人。删除时还会级联清掉引用它的连线,不留悬空的线头。
三个工具各管一摊:display 负责造、append 负责续、edit 负责改。每个工具的 description 里都带完整示例和错误指引,模型自己就能判断什么时候用哪个。
5. get_shape_library:按需加载的「图例手册」
画云架构图(AWS、K8s),你要的不只是方块和连线——你要 EC2 图标、S3 图标、Lambda 图标。Draw.io 内置 1000+ 专业图标,但每个的 style 语法都不一样。
如果把所有图标语法全塞进 System Prompt,不光烧 token,还会在模型根本用不到的时候硬塞给它,干扰判断。
catbuddy 的做法是按需加载——get_shape_library 工具让模型需要时主动查:
const safe = sanitizeName(raw)const filePath = path.join(libDir, `${safe}.md`)const resolved = path.resolve(filePath)// 路径遍历防护:防模型用 ../../ 读到系统文件if (!resolved.startsWith(path.resolve(libDir))) {return'Error: invalid library path.'}const content = fs.readFileSync(filePath, 'utf-8')return content
图标库就是一堆纯 Markdown 文件(aws4.md、kubernetes.md、flowchart.md),每个给出服务的 shape 名和 style 语法,外加一份 100+ 条目的分类清单:Compute(ec2、lambda…)、Storage(s3、efs…)、Database(rds、dynamodb…)、Networking(vpc、api_gateway…)。设计上三个点:
- 文件即图例:不用改代码,加个
.md就支持新图标库; - 路径遍历防护:
sanitizeName+startsWith双保险,堵死../../; - 友好降级:库不存在时返回的是可用库列表,而不是干巴巴一句「not found」。
6. 三层校验:不靠「解析→报错→重生成」循环
你可能以为,画错了就「生成 → 解析 XML → 失败 → 把错误喂回模型 → 重画」对吧?catbuddy 偏偏没有这个循环。取而代之的是三层渐进式校验:
第一层 Prompt 约束是预防性的——在模型动笔前就消掉最常见的歪路:「只生成裸 mxCell」灭包装错误、「ID 从 2 递增」灭 ID 冲突、「转义 <>」灭转义错误。
第二层工具校验是拦截。注意 display_diagram 那句错误消息的措辞——它不只说「格式错误」,而是把模型最容易犯的替代方案点名列出来:
if (!looksLikeDrawioXml(rawXml)) {return'Error: display_diagram.xml must be actual Draw.io XML. ' +'Do not pass Markdown, ASCII diagrams, Mermaid, or plain text. ' +'Generate valid Draw.io XML and retry.'}
这给了模型足够上下文去「理解自己错在哪」,然后在下一次 tool call 里改对。
第三层正则校验是核实。edit_diagram 不信任模型传来的 cell_id 一定存在,每次都做真实性检查——找不到就回「cell 不存在,建议先用 read_file 读 .drawio 看实际有哪些 ID」。
为什么不用「解析→修正」循环?因为把解析器错误喂回模型,效率太低。XML parser 报的是「line 47: unexpected token」,模型很难从这反推出「哦是我第 12 个 mxCell 的 parent 写错了」。三层校验的策略反过来——让每一层的错误消息本身就是一个微型修正 Prompt:发生了什么 + 可能原因 + 下一步建议,全用人话写,前缀统一 Error: 方便模型判断这是错误而非正常结果。
7. 顺手总结:可复用的四条 Prompt 工程原则
这套画图实践里,有四条原则其实跟「画图」无关,换个结构化生成场景照样能用:
| 原则 | 一句话 | 怎么落地 |
|---|---|---|
| 反向剔除 | 系统做 boilerplate,模型做 creative | 生成 JSON 别让它写外层壳、只写 items;模板里 60% 是机械重复的,那部分就不该模型生成 |
| 示例密度 | 一个好示例 > 十条文字规则 | Skill 里近一半篇幅是示例,每种元素都给样例;模型从示例提模式的能力远强于从规则推导 |
| 错误即微 Prompt | 报错是给模型看的,不是给人看的 | 每条错误带「发生什么 + 可能原因 + 下一步」,统一 Error: 前缀 |
| 分层约束 | 别堆一个巨型 System Prompt | Skill(全局知识)+ Tool description(局部)+ Shape Library(领域)+ 错误消息(反馈),每次 tool call 只看相关那部分 |
最后这条尤其想强调:catbuddy 的画图能力是四个信息来源拼起来的,不是一坨 2000 字的 System Prompt。模型决策时综合这四层,但每次只需要看到当下相关的那一层——这比塞一个巨无霸 Prompt 高效得多,也好维护得多。
这篇讲了什么?
- 让 AI 画架构图,靠的是工具链 + Prompt 工程,不是特殊模型能力。 核心哲学是「反向剔除」——代码用
ensureMxFile()补全<mxfile>/<root>/根节点这些机械模板,模型只生成核心的<mxCell>,错误面和 token 都砍掉约 40%。 - Prompt 迭代四轮,可用率 40%→55%→75%→90%。 每次提升都来自从失败案例里提炼具体规则(连线错开
exitY=0.3/0.7、waypoint 绕行),而非笼统喊「画好看点」。方法论是「告诉模型不要做什么」——因为它的默认行为往往就是你不想要的那种。 - 三个工具分工 + 三层校验兜底。
display_diagram(造)/append_diagram(续)/edit_diagram(改)形成完整工具链,get_shape_library按需加载 100+ 云图标语法;校验不走「解析→重生成」循环,而是 Prompt 预防 → 工具拦截 → 正则核实三层,每条错误消息都是一个微型修正 Prompt。
下一篇预告:进阶应用到这儿就收尾了——harness 的核心器官(心脏、手脚、眼睛、记忆、韧性、进阶)我们全拆完了。从第 13 篇起,整个系列转入产品化:怎么把这套 harness 变成桌面、Web、手机都能用的产品。先看最基础的一环——一条消息的旅程:同一条消息,在磁盘里、在网络上、在你屏幕上,其实是三张完全不同的面孔,为什么要这么设计?再聊流式渲染怎么做到丝滑不卡顿。
-
08.24
《龙魂旅人》玉藻前技能说明
-
08.24
《龙魂旅人》绝代妖狐·玉藻前上线时间说明
-
08.24
库洛澄清:刘慈欣未为《鸣潮》撰写大纲,网传合作系虚假信息
-
08.24
《洛克王国世界》PVP上分平衡队搭配推荐
-
08.24
Capcom在Steam平台推出前三款生化危机4:重制版游戏
-
08.24
Weibo Gaming携mindfreak领衔阵容回归
-
-
下载
- |
-
-
下载
- 《行尸走肉第一章》免安装中文汉化硬盘版下载
- 单机|436 MB
- 一款以动作冒险为主题的游戏
-
-
下载
- 《街头霸王X铁拳》免安装中文汉化硬盘版下载
- 单机|111MB
- 一款非常好玩的格斗游戏
-
-
下载
- |
-
-
下载
- 《暗黑破坏神3》免安装繁体中文正式版下载
- 单机|7630 MB
- 一款以角色扮演为主题的游戏
-
-
下载
- 《马克思佩恩3》免安装硬盘版下载
- 单机|27033 MB
- 一款以第三人称射击为主题的游戏