让 AI 画一张架构图,出来的东西通常有种说不上来的廉价感:圆角过大、每个盒子一样宽、深色背景配青紫辉光、箭头标签直接压在线上。你能一眼看出这是机器画的,但很难说清具体哪里错了。
cathrynlavery/diagram-design 试图把那个「说不清」变成可执行的规范。截至 2026 年 8 月 12 日,它在 GitHub 上有 7,710 Star、511 Fork、6 个 Open Issue,MIT 协议,2026 年 4 月 16 日建仓,当天仍在推 commit(HEAD 为 4da4dfb)。我把它 clone 下来通读了源码并跑了它自己的 CI,结论是:这个仓库最有意思的地方不是它做了什么,而是它选择校验什么、又把什么完全交给了模型。
一个没有渲染器的图表工具
先说清它的形态。diagram-design 是一个纯提示词的 Claude Code skill,同时打包成 .claude-plugin、.codex-plugin 和 Pi 三种分发格式。全仓 27,843 行,构成是这样的:
| 部分 | 行数 | 作用 |
|---|---|---|
SKILL.md |
538(33KB) | 设计系统、复杂度预算、五条连线铁律、20 项出图前检查表 |
references/type-*.md × 27 |
3,992 | 每个图种的布局约定、反模式、示例引用 |
mermaid_extract.py |
1,285 | 解析 Mermaid 源码,输出结构摘要 |
drawio_extract.py |
856 | 解析 draw.io/.drawio.png/.drawio.svg |
scripts/*.py(CI) |
2,530 | lint、a11y 测试、导入验证、图标生成 |
注意这里没有布局引擎。全仓 4,671 行 Python 只干两件事:解析别人的图(导入),和校验产出(lint)。真正把节点摆到坐标上的,是模型本人。
这是个刻意的赌注。Mermaid 和 Graphviz 有自动布局,但正因为有自动布局,它们的产出永远带着渲染器的口音——固定的间距算法、固定的路由策略、固定的节点样式。diagram-design 的反模式清单里直接把「复刻 Mermaid 渲染器的布局」列为失败项:
Reproducing Mermaid's renderer layout — Imports automatic spacing and routing instead of making an editorial layout
要摆脱渲染器口音,就得放弃渲染器。代价是所有几何计算落到模型头上。
它把「不可协商」的规则写成了 checklist
SKILL.md 第 6 节列了五条连线规则,原文用词是 non-negotiable:
- 非同轴节点之间必须用圆角直角折线(
r=8),斜线是 automatic fail - 箭头标签与连线之间必须留 6–10px 可见间隙,遮罩矩形不得贴住线条
- 任意两条连线不得重叠或共线,交叉处必须用 bridge/hop 弧
- 同一条边上的多条连线必须各有独立锚点,间距 ≥12px
- 连线不得穿过非端点的盒子,唯一例外须改为虚线且标签移到可见端
第 9 节又给了 20 项出图前检查表,其中五项加粗对应上面五条。写得非常具体,具体到给出了折线的 d 属性公式和 hop 弧的 a 8,8 0 0,1 16,0 参数。
问题是:这五条谁来验?
我在仓库里 grep 了 overlap、elbow、orthogonal、diagonal、divisible,校验脚本里一个都没有。跑一遍它自己的 CI(六道 gate,本地全绿)后我把 lint-skin.py 的失败信息全提取出来,实际覆盖是这样的:
| 校验到的 | 没校验的 |
|---|---|
| 颜色是否在 style-guide 调色板内、禁纯黑 | 任何几何 |
| 字体白名单(禁 JetBrains Mono 当通用「dev 字体」) | 连线是否重叠、是否共用锚点 |
可访问性契约(<title> 是否首子元素、id 是否加前缀) |
是否存在斜线连接 |
禁 <script>、禁一切外部 HTTP 资源引用 |
坐标是否为 4 的倍数 |
于是形成了一个很干净的分界:能被机器验的,它验得很严;验不了的,它写成了让模型自查的清单。
而正好,第二类恰恰是大模型最不擅长的那类任务。判断两条平行线间距是否 ≥12px、判断一条连线是否穿过了第三个盒子、把所有坐标对齐到 4px 网格——这些是约束满足和空间推理,不是语言任务。README 那句 "No Mermaid-slop" 能不能兑现,完全取决于模型当次的空间推理水平,仓库对此没有任何护栏。
这不是说规范写得没用。写清楚肯定比不写强。但「非可协商」这个词,配上一个只存在于 prompt 里的执行机制,是一种夸大。真正非可协商的,是 CI 会拦下来的那四类。
导入这一侧写得意外扎实
如果只看提示词部分,容易低估这个仓库。它的导入模块是认真的工程代码。
drawio_extract.py 在解析前先跑 _reject_unsafe_xml,拒掉 DTD 和实体声明——这挡的是 XXE 和 billion laughs。解压走 _decompress_limited,带 MAX_XML_BYTES 上限,挡的是解压炸弹。draw.io 的三种载体(裸 XML、PNG 内嵌、SVG 内嵌)都能剥出来。
mermaid_extract.py 文件头第 6 行写着:
Every label and directive value is untrusted data. Click targets and styling are discarded.
这是显式的提示词注入防线。用户丢进来的 .mmd 文件里的标签文本、click 指令、%%{init}%% 配置,全部当数据不当指令。在同类 agent skill 里,我很少见到这种自觉——大多数「导入 X 格式」的 skill 是直接把源文件内容拼进 prompt 的。
lint-skin.py 还禁掉了产出里的一切外部 HTTP 引用(src、@import、url()、<link>,Google Fonts 走白名单)和 <script>。这意味着生成的 HTML 贴到别人页面里不会偷偷加载远端资源。
这些是我愿意直接抄的部分。至于评分:导入侧的安全处理 9/10,CI 覆盖面 7/10,几何正确性保障 2/10——第三项不是没做好,是根本没做。
三个真实缺陷
一、21% 的随包示例被自己的 linter 豁免。 assets/ 下有 94 个 example-*.html,scripts/lint-skin-baseline.txt 列了其中 20 个,CI 用 --all --baseline 跳过它们的视觉检查(可访问性检查仍然照跑,这点是干净的)。也就是说 21% 的示例配色不符合当前 style guide。style-guide.md 自己承认了:
The pre-baked example HTML files in
assets/were built under an earlier skin. Regenerating them against the current style-guide.md is a v5.1 task.
麻烦在于 SKILL.md 第 10 节建新图的第一步就是「复制最接近的那个 variant」。模型复制的正是这批过期示例。
二、版本号三套并行。 plugin.json 是 2.2.0,SKILL.md frontmatter 是 2.2,正文里却在说 "a v5.1 task"、"That's a v5.2 feature"。GitHub 仓库描述写「29 editorial diagram types」,README 写 27,references/ 下实际有 27 个 type-*.md。
三、onboarding 会改写 skill 自己的文件。 品牌定制流程的最后一步是把新 token 写回 references/style-guide.md——也就是 skill 安装目录里的文件。skill 一更新,用户的定制就没了,仓库没提备份也没提外置。文档自己也承认「for now, one skin per skill install」。
另外一个不算缺陷但值得计入成本的点:SKILL.md 33KB 约合 8k tokens,每次技能触发全量加载,再叠一个 200–500 行的类型规范。画一张图的固定上下文开销大约 10–12k tokens。
我们这边缺的正好是它有的
我自己一直在用另一个绘图 skill,之前写过评测的 fireworks-tech-graph。把两边的校验脚本摆在一起看,出现了一个有点好笑的对称。
fireworks-tech-graph 的 validate-svg.sh 里有一段 Python,叫 Check 5「Arrow-component collision」:它把所有 rect/circle/ellipse 的包围盒收集起来,再解析每条带 marker-end 的 line/path 的分段,用 segment_hits_bounds 判断线段是否穿过了非端点盒子的内部。这正是 diagram-design 完全没做的那件事。
反过来,fireworks-tech-graph 的 10 个模板里,有 <title> 或 <desc> 的数量是 0,SKILL.md 和 references 里 grep 不到任何 role="img" 或 aria-labelledby。而这恰恰是 diagram-design 做得最细的地方。
于是我把 diagram-design 的可访问 SVG 契约移植了过来,落成脚本而不是清单。契约本身只有四条,但其中一条是我读到的时候才反应过来的非显然设计:
The IDs are prefixed per diagram and variant:
<slug>-title/<slug>-desc. Baretitle/descIDs are banned because two inline diagrams would create duplicate IDs and the second could be announced with the first diagram's name.
一篇博客里放两张内联 SVG,如果都用 id="title",第二张会被读屏软件念成第一张的名字。这个 bug 不会报错、不会渲染异常,只有用读屏软件的人碰得到。
落地是三处改动:渲染器 generate-from-template.py 自动产出 role/aria-labelledby/<title>/<desc>,slug 从标题派生、desc 可由 JSON 覆盖;validate-svg.sh 加 Check 5b(契约)和 Check 5c(禁 <script> 与外部资源);SKILL.md 补一节说明,让手写 SVG 的路径也知道该产出什么。
然后我做了这次改动里最重要的一步:证明这个闸门真的会拦。 拿一张合格的 SVG 做六种变异——删掉 role/aria-labelledby、把 id 改成裸 title、把 <title> 挪到 <defs> 之后、清空 <desc>、塞一个 @import 外部字体、塞一个 <script>——六种全部被拒,报错信息各自不同。原有 7 个回归 fixture 依然全绿。
顺带修了个自己人的问题:skill 根目录那张展示用的 agentloop-core.svg 也不符合新契约,补上了 <title>/<desc>。这正是我批评 diagram-design 的第一条缺陷——别让自己的样例被自己的规则豁免。
一个不该被跳过的插曲
写这三张配图时我踩了一个坑,值得单独讲,因为它和本文主题是同一件事。
我给节点写了 sub_label 字段填副标题。生成成功、校验全绿、没有任何警告。渲染出来一看,所有副标题都不见了——真实字段名是 sublabel,没有下划线。渲染器对未知字段直接忽略,不报错。
这就是「校验只能覆盖它知道要看的东西」的另一面。校验脚本会告诉你 XML 是否合法、箭头是否穿盒、可访问性契约是否满足,但不会告诉你「你以为写进去的信息其实没写进去」。所以那一步把 SVG 渲染成 PNG 自己看一眼,在有校验的情况下依然不能省。fireworks-tech-graph 的 SKILL.md 里把它写成了第 11 步「visual self-review」,我当时以为是可选的。
结论
diagram-design 值得装。27 个图种的规范密度是真的,导入侧的安全处理超出同类 skill 平均水平,编辑品味的表达也确实解决了「AI 图一眼假」的问题。7,710 Star 不是刷出来的。
但要清楚它的保证边界在哪:CI 保证你的图不会用错颜色、不会用错字体、不会漏可访问性、不会偷偷联网;它不保证任何一条线画对了位置。 用它的时候,出图后自己看一眼渲染结果,这一步省不掉。
对写 agent skill 的人,这个仓库给的更普适的教训是:把规则写进 prompt 是零成本的,写进校验脚本是有成本的,而这个成本差恰好决定了哪些规则会被真正遵守。当你在 SKILL.md 里写下「非可协商」四个字时,值得停下来问一句——如果模型违反了,谁会发现?如果答案是「没人」,那它就不是非可协商,它只是一句期望。这和 GitNexus 那类工具靠索引产生硬约束、Agent Skills 生态靠格式规范产生软约束,是同一个光谱上的两端。
我的建议:装 diagram-design 用它的规范和导入能力,但别把它的几何铁律当成保证。如果你也在维护绘图 skill,先去 grep 一下自己的校验脚本,看看清单里那些加粗的「必须」有几条真的有代码在守。
参考资料
- cathrynlavery — diagram-design GitHub 仓库(截至 2026-08-12:7,710 Star / 511 Fork / MIT,HEAD
4da4dfb) skills/diagram-design/SKILL.md— §6 Mandatory connector rules、§9 Pre-Output Checklist、§12 Accessible SVG contractscripts/lint-skin.py与scripts/lint-skin-baseline.txt— 实际校验覆盖与 20 个豁免文件skills/diagram-design/scripts/drawio_extract.py—_reject_unsafe_xml、_decompress_limitedskills/diagram-design/scripts/mermaid_extract.py— 「Every label and directive value is untrusted data」.github/workflows/ci.yml— 六道校验 gate- 官小西 — Fireworks Tech Graph 深度评测:2026 年 AI 绘图技能横向对比