在 Vercel,我们使用编码智能体来设计和构建页面,这些页面必须在外观和感觉上都是 Vercel 的样子。字体排印、色彩和构图,都需要承载我们对自己发布的页面所倾注的同一种判断力。
我们最近介绍过 product-design,这是一项技能,教智能体在我们的代码库中工作时如何按我们的方式做设计。这项技能存放在每个仓库中,紧挨着它所约束的代码,说明智能体如何找到并理解我们的设计系统,以及所构建内容对应的产品指南。
当智能体在我们的代码库中工作时,这套做法效果很好,因为技能所需的一切都近在手边。但报告、提案,以及那些仍要看起来像 Vercel、却是在无法读取这些文件的工具中制作的一次性页面,又该怎么办?我们的答案是 design.md,一个任何智能体都能加载的公开文件。
我们是如何构建 design.md 的
product-design 之所以好用,是因为设计系统和产品指南就放在仓库里,智能体可以直接读取。我们需要一种方式,让该环境之外的智能体和工具也能获得同样的知识,使最终产出的页面依然像是我们自己设计的。这带来两个要求:
- 一个公开的 URL,任何人无论在什么环境中运行智能体,都可以指向它。
- 涵盖
product-design最初之所以有用的全部内容的指导,从品牌、布局和文案,到设计系统、响应式和信息架构。
我们最先尝试的朴素做法,是直接把 product-design 移植成一份公开提示词,把技能的参考文件合并成一个任何智能体都能通过 URL 读取的文件。我们发现的问题是:提示词对我们视觉语言的描述本身没有问题,但每个模型对这些描述的理解都不一样,同样的指导会生成截然不同的页面。
部分原因在于设计语言本身是主观的。像“保持布局整洁”这样的说法几乎可以指任何东西。什么是“整洁”?此外,更大的问题是提示词丢掉的其他一切。在我们的代码库里,智能体读取 product-design 时,周围有真实的组件,以及它所描述事物的已上线示例。而公开提示词里没有这些,每个模型只能凭文字重建我们的风格。
所以我们要做的,是把那个环境所提供的东西提炼进一个文件里,而判断我们是否在接近目标的唯一办法,就是看产出的页面。我们把移植版搁置一旁,从头开始编写新文件,这一次每次改动都用一套可重复的评测提示词来测试。
我们写了七条,取自真实用例,并配有模拟输入:
- 用量与性能报告
- 续约提案
- 基准测试报告
- 交互式规划页面
- 自建与采购决策简报
- 安全治理简报
- 演示文稿
提示词保持不变而文件在变,因此输出中的任何差异都可以追溯到指导内容。
第一次对比
这些评测让我们既能衡量文件实际起到的作用,也能衡量不同智能体对它的解读。第一次测试,我们想知道 design.md 究竟能否改变模型的产出。我们在同一环境中用同一个模型把续约提案评测运行了两次,一次不加载 design.md,一次加载,两次运行的提示词、数据和视口完全一致。
提示词、数据、模型和视口都相同。每个版本只生成一次,不重新抽取。
没有 design.md 时,模型生成了一个泛泛的 SaaS 仪表盘。加载之后,页面以续约建议本身开篇,把商务依据汇集到一个网格中,把同类对象的数值放在同一刻度上,使其真正可比,并且保留了辅助细节,又不让它们与摘要争夺注意力。由此我们得出结论:该文件改变的不仅是页面的样式,就像我们最初发现的那样,还有页面的结构和层次,这给了我们足够的信号,让我们继续以这种方式逐条构建指导。
让系统运转的三个部分
在测试和重建 design.md 的过程中,其范围演变成了一个三部分的系统,整个机制由此运转:
- design.md 提供指导,向智能体展示如何界定读者要完成的任务、组织证据并选择构图。
- 一份公开的样式表,定义了有边界、有文档的类名和 token 词汇表。
- 一个评测闭环,把反复出现的人工反馈转化为更好的指导和确定性检查。
每一层各自覆盖打造高质量、符合品牌调性的 Vercel 页面的一部分工作。编码在 design.md 中的判断力,为智能体提供如下方面的指导:
- 让页面既能供高管快速浏览,又能供详细审阅。
- 撰写文案时给出具体的论断和诚实的说明。
- 统筹层次、字体排印和色彩,使证据与文字相互支撑。
- 如何以 Vercel 的名义发布,细致到我们的文字标志和三角形标志的资源使用规则。
design.md 还为我们绝不想看到的、反复出现的生成式设计套路命名,通过给这些套路起名字,智能体识别并避开它们的可靠性要高得多。


design.md 的节选。明确命名反复出现的生成式设计套路,有助于智能体识别并避开它们。
我们创建样式表,是因为智能体总是自己发明字体排印、间距和布局,所以我们把这些决策完全从模型手中拿走。样式表把设计系统的基本构件,如页头、表格、统计条和图表样式,封装成任何页面都能通过公开 URL 使用的 CSS。随后,design.md 记录样式表所提供的类名和 token,让智能体在 HTML 中直接使用这些名称来构建页面,而不是重新发明。
这样做还有一个好处:智能体实际上从不读取样式表本身。样式表在页面于浏览器中渲染时才加载,因此代码不会进入模型的上下文,从而为设计指导腾出更多空间。
最后,评测闭环是让另外两部分发挥作用的关键。确定性检查用来帮助发现机械性的错误,例如表格没有遵守可用宽度;而无法自动化的主观部分,如层次、构图,以及页面是否真正为读者提供了他们所需,则由人来判断。
指导内容如何进入这个文件
design.md 中的每一行指导,都是通过评测闭环赢得其位置的。我们从固定场景生成页面,审阅返回的结果,把我们认可的修正写入文件,并重新运行这些场景,看每次改动是否站得住脚,因为对一个产物有益的改动可能悄悄损害另一个。除此之外,没有任何内容能进入文件。
场景与轮次
七条提示词各自成为一个场景,即提示词与其模拟输入和渲染设置一起被冻结。例如,续约提案总是使用同样的虚构客户数据和同样的视口设置运行,使 design.md 成为各次运行之间唯一变化的因素。一轮是指针对当前版本的文件,从每个场景生成一个全新的页面。完整的一轮覆盖全部七个场景,并同时在 Claude Opus 4.8 和搭载 GPT-5.5 的 Codex 上运行。
如果我们想研究某个具体问题,例如只影响表格的规则改动,就可以只重跑受影响的场景或单个模型,让迭代循环保持紧凑。
一次生成全部七个页面也便于并排对比,而引人注目的是,design.md 并没有把每个页面都推向同一个模板。交互式规划页面把控件放在最醒目的位置,因为人们打开规划页面是为了修改数字并查看结果。续约提案则以建议及其背后的商务对比开篇,因为它的读者要决定是否续约。每个页面都采用相同的 Vercel 字体排印、色彩和间距,但每一个都围绕读者想要完成的事情来组织结构。
不同场景的输出共享同一种视觉语言,却采用不同的页面结构。
审阅每一次运行
为了审阅每一轮产出的页面,我们构建了一个本地应用,用来显示整页渲染结果并进行盲测 A/B 对比。这个应用最终成了我们的评测框架,负责运行每个场景并存储结果。每次存储的运行都保存了提示词、输入、模型配置、所用的 design.md 版本、截图,以及审阅者留下的任何反馈。审阅者把每一条修正都记录在产生它的那次具体运行上。
我们的本地审阅框架,显示整页渲染结果以及每次运行的盲测 A/B 对比。
把修正转化为规则和检查
评审人员记录的每一条修正,都会落在能够稳定执行它的最窄位置上。判断层面的改动以文字形式写进 design.md,可复用的机制写进样式表,凡是能机械检查的,就变成代码中的确定性检查。harness 本身的问题留在 harness 里;而当某一个模型出现其他模型没有的失误时,我们先不把它写进规则,直到它重复出现。
以早期的一份续约提案为例。它的商务条款表被压缩到与正文相同的宽度,尽管页面完全有空间让这张表宽出一倍。
design.md 纳入反馈前后的对比
评审时,我们指出证据类表格应当使用其可用的全部宽度。但回头翻看以往的输出,我们发现到处都有同样的问题。因此这条修正最终落在了两个地方:
design.md中的一条规则,说明期望的行为。- 代码中的一项确定性检查,下次同样的布局问题出现时就能抓出来。
这些落地之后,后续的续约提案提示词生成的页面,表格都能正确占满宽度。为了验证这类改动,我们在编码之后重新跑了受影响的场景。在里程碑节点上,我们做得更进一步:进行盲测 A/B 轮次,把更新后的 design.md 与文件的早期版本对比,以决定每项改动是保留、修订还是撤销。
衡量它是否有效
构建这个文件前后跑了 200 多次,包括完整轮次、针对性检查、试运行,以及所有走不通的死胡同。除了人工评审,每一轮还有一个模型评审写出点评,每一轮的反馈都用于改进下一次运行。
为简洁起见只显示每第 3 次生成,每一轮都得到了用于改进下一轮的反馈
经过所有这些运行之后,我们想知道,我们编码进去的修正是否真的防住了它们所针对的问题。于是我们选了三个桌面端场景,对每个场景都让搭载 GPT-5.5 的 Codex 生成两次页面,一次加载 design.md,一次不加载。每次生成我们都只保留第一次尝试,不重抽。然后我们对全部六个页面运行确定性检查,统计每组中已知问题(比如表格没有占满可用宽度)出现的次数。加载 design.md 生成的页面有 39 处这类问题,没有加载的有 91 处,在这次测试中相当于减少了 57%。
这些数字有两点需要说明。检查只能抓出我们已经见过并记录下来的问题,所以这项测试并不能说明一个页面整体设计得好不好。六个页面的样本量也太小,无法对质量或可靠性下结论,而且不论有没有这个文件,每一个页面都至少还有一处严重到足以阻止上线的问题。但这项测试确实能说明一点:一旦我们把某个问题命名并编码,它往往就不再出现。
design.md 如何保持更新
评测循环让这个文件得以上线,但让它保持更新的是真实使用。在我们的 Slack 里,这种使用通过 @design-agent 实现,它是基于 eve 构建的智能体,我们用它做各种事情,从设计点评、文案备选、图标推荐,到根据粘贴的数据生成报告站点。你不用设置提示词,也不用去找源文件,只要在讨论串里提到这个智能体即可。对于网站类请求,它会加载当前的 design.md,依据已发布的样式表构建页面,并把整页截图和部署 URL 发回讨论串。与我们固定的场景不同,每个这样的讨论串都记录了一次真实的请求、一份真实的输出,以及随后的任何反馈或引导,让我们看到这些指导在实际环境中表现如何。
每周,我们会把所有反馈汇集到一处,包括 Slack 讨论串,以及来自 GitHub 评审和 Figma 的评论。自动化会把反复出现的评论归类,每一条重复的抱怨都会变成一项修改提案。然后由人逐条审阅提案,检查系统是否已经处理了该问题,并决定被采纳的修复应该落在哪里,是 @design-agent、product-design 技能、design.md、样式表,还是一项确定性检查。如果有人开始要求我们从未测试过的某类页面,这个请求就会成为一个新的评测场景。
为了判断这一切是否奏效,我们会统计每类抱怨在相似工作中随时间出现的频率。一旦我们编码了某个修复,这个计数就应该开始下降。如果没有下降,说明修复有问题:规则可能不清晰,可能在需要时没有被加载,样式表可能缺少能表达它的基础元件,也可能它需要的是确定性检查而不是文字描述。
自己动手搭建
你可以自己搭建同样的循环,从一个反复出现的产物和一次手动对比开始。
1. 选一个反复出现的产物
选用最近一项有真实读者和真实输入的任务,比如提案、性能报告、基准测试或微型站点。避免“让它符合品牌调性”这样宽泛的目标。在生成任何东西之前,先写下一份简短的评分标准。好的标准会检查:提供的事实是否保留下来、读者的决策是否清晰,以及你一直在手动做的那条修正是否真的解决了。
2. 先保存基线
在没有任何新设计上下文的情况下生成一次页面,并保存提示词、输入、配置和截图。即使第一次输出看起来很粗糙也要保留,除非是 harness 本身出了故障。没有“之前”,就无法判断新的上下文有没有帮助。
3. 从你最近的十条修正入手
收集你在设计评审、拉取请求或 Slack 中一直在给出的反馈,把每条修正改写成可观察的形式。也就是写成 Let evidence tables use the full available width 而不是 Make the table feel less cramped,因为两者之中只有一个可以被检查。
把这些决策放进一个文件,分为范围、读者与任务、可观察的决策、可用的基础元件几个部分。这个文件就是你的第一版 design.md。
4. 约束可重复的机制
如果你的输出总是自己发明字体排印、间距或布局,就发布一份样式表,并写明智能体可以使用的确切类名和 token。判断留在文字里,可重复的机制则推进 CSS 或确定性检查。
5. 运行一次配对对比
用相同的输入、模型和视口再生成一次页面,但这次加载你的文件。把它与基线打乱顺序,在不知道哪个是哪个的情况下,依据你的评分标准给两者打分。
起步时你并不需要运行器或模型评审,单次试验就能暴露那些巨大而明显的问题。若要衡量可靠性,请运行多次独立的首次尝试试验(Anthropic 的智能体评测指南),并报告结果成立的频率。
6. 把修正编码进去
结合你不得不发送的任何后续提示词来审阅输出,然后自问:
- 用户是否不得不重复说明或手动引导?
- 是不是缺少某条规则,或规则不够清晰?
- 样式表能否表达这条修正?
- 这个问题是否足够机械,可以用代码检查?
- 这条修正能否推广到这份输出之外?
更新指导,而不是手工调整生成的页面。你下一次对比会告诉你,首次尝试是否真的改善了。
等手动循环开始见效之后,再添加工具:
- 纳入指导应当适用和不应适用的场景。
- 编辑时保留一小部分隐藏的留出集。
- 记录模型和指导的版本。
- 把机械检查自动化。
- 使用多位盲评人员。
无论自动化做到什么程度,最终改动都应由人审阅。
然后让这个循环持续运转。按固定节奏收集反馈,并观察在你修改指导之后,每类抱怨是否真的变少。如果人们在生产环境中仍在纠正同样的错误,那么通过评测就没那么重要了。
如果你想看可运行的示例,我们的都是公开的。我们每天把 design.md 加载到 v0、Codex 和 Claude 等工具中,生成有 Vercel 风格的产物,而 eve design agent template 能帮你搭出一个和我们所用的类似的 Slack 设计智能体。
