1. 当“无瑕”成为前端交付的硬指标
第一次看到impeccable这个词被用在 AI coding agents 的语境里,我的反应是:终于有人把这件事挑明了。过去大半年,我一直在用各种 AI 编码代理帮我写前端页面,从落地页到后台管理面板都试过。模型生成代码的速度确实快,但交付质量经常让人血压升高——间距忽大忽小、颜色体系东一块西一块、响应式断点形同虚设、组件命名前后不一致。你让它改一个按钮的圆角,它顺手把整个卡片的阴影也改了,还振振有词地说“为了视觉一致性”。
impeccable这个项目切入的正是这个痛点。它不是一个 UI 组件库,也不是又一个设计系统模板,而是一套面向 AI coding agents 的前端设计约束层。核心思路是:在 AI 动手写代码之前,先用一份结构化的PRODUCT.md把设计意图、视觉规范、交互原则固定下来,让 agent 有据可依,而不是每次凭“感觉”发挥。关键词里的npx impeccable install说明它提供了开箱即用的 CLI 安装方式,frontend design和AI coding agents则点明了它的主战场。
这篇文章适合三类人看:一是正在用 AI 代理写前端、但被交付质量折磨的开发者;二是想给团队建立 AI 辅助开发规范的技术负责人;三是对“如何让 AI 产出稳定设计”这件事好奇的独立创作者。我会从它解决的问题本质讲起,拆解PRODUCT.md的设计逻辑,给出完整的安装和配置步骤,再分享我在实际项目中踩过的坑和总结出的调优经验。不管你是刚接触 AI coding agents 的新手,还是已经用了一段时间的老手,应该都能从中拿到可以直接复用的东西。
2. AI 写前端为什么总是“差一口气”
2.1 模型不缺代码能力,缺的是设计上下文
我用 AI 代理生成过上百个前端页面,一个很明显的规律是:给它一个足够具体的 prompt,比如“做一个深色背景、主色 #6366f1、卡片圆角 12px、间距 24px 的三列定价表”,出来的结果通常不会太差。但如果你只说“做一个定价页面”,它就会开始自由发挥,而且每次发挥的方向都不一样。这不是模型能力问题,而是信息缺失问题。
前端开发和后端开发有一个本质区别:后端有明确的正确性标准——接口返回对不对、数据库写入成不成功,跑个测试就知道。前端没有这种硬性标准,一个页面“好不好看”高度依赖上下文。同一个按钮,放在工具类产品里应该是紧凑、低圆角、高信息密度;放在消费类产品里可能是大圆角、大间距、强视觉引导。AI 代理不知道你的产品属于哪一类,它只能根据训练数据里的“平均审美”来猜。
impeccable的做法很直接:把这些上下文从你的脑子里、从设计稿里、从口头约定里抽出来,写成一份 agent 能读懂的PRODUCT.md。这份文件不是给人看的设计规范文档,而是给 AI 代理看的“设计意图说明书”。它要回答的核心问题是:这个产品是什么调性、视觉体系怎么定、组件行为边界在哪里、哪些东西绝对不能动。
2.2 没有约束层时,AI 代理的三种典型翻车
我整理了自己和身边朋友遇到过的翻车案例,大致可以归为三类,每一类都对应着impeccable要解决的一个具体问题。
第一类是视觉漂移。你让 agent 改一个页面的 hero 区域,它改完之后你发现整个页面的字体层级变了。原因是它在生成新代码时“顺手”重新定义了一套 typography scale,和你原有的体系冲突。这种问题在多次迭代后尤其明显,页面会越来越“花”,因为每次修改都引入了一点新的视觉语言。
第二类是组件语义混乱。同一个功能,agent 这次叫PrimaryButton,下次叫MainButton,再下次叫ActionBtn。样式定义也是散的,有的写在 Tailwind class 里,有的抽成了 CSS module,有的直接内联。项目稍微大一点,维护成本就爆炸。
第三类是响应式失忆。agent 在桌面端调得好好的,一到移动端就露馅——要么忘了加断点,要么断点值和你项目里其他地方不一致,要么直接把桌面布局压缩一下交差。它不知道你的项目用的是 640/768/1024/1280 这套断点,还是 600/900/1200 那套。
impeccable的约束层就是针对这三类问题设计的。它通过PRODUCT.md把视觉 token、组件命名规范、响应式策略这些“隐性知识”显性化,让 agent 每次生成代码时都有统一的参照系。
2.3 为什么是 PRODUCT.md 而不是别的格式
你可能会问:为什么不用 JSON 配置、不用 Figma token、不用现成的 design token 标准?我的理解是,PRODUCT.md选择 Markdown 格式是有意为之的。Markdown 对 AI 代理来说是最友好的输入格式之一——它既有结构(标题、列表、表格),又有自然语言描述的空间。纯 JSON 太死板,表达不了“这个按钮在 hover 时应该有轻微的抬升感”这种模糊但重要的意图;纯自然语言又太松散,agent 抓不住重点。
PRODUCT.md的巧妙之处在于它处在两者之间:用结构化的小节划分领域(色彩、排版、间距、组件、动效),每个小节里可以用自然语言补充设计意图。这样 agent 既能快速定位到“色彩规范”这一节,又能理解“主色用于主要行动点,辅助色用于信息提示,两者不要混用”这种带判断的规则。
3. 把设计意图翻译成 agent 能执行的约束
3.1 PRODUCT.md 的骨架应该长什么样
我参考impeccable的设计理念,结合自己项目的实际使用情况,总结出一份比较通用的PRODUCT.md骨架。这份文件不需要写得很长,但每一节都要有明确的约束力。
# Product Design Context ## 1. Product Identity - 产品类型:B2B SaaS 工具 - 目标用户:中小团队的技术负责人 - 视觉调性:专业、克制、高信息密度 - 参考产品:Linear、Vercel Dashboard ## 2. Color System - 主色:#4F46E5(仅用于主要 CTA 和活跃状态) - 中性色阶:gray-50 到 gray-900 - 语义色:success #10B981 / warning #F59E0B / error #EF4444 - 禁止:不要引入色阶之外的新颜色 ## 3. Typography - 字体:Inter(正文)、JetBrains Mono(代码) - 层级:h1 32px/700、h2 24px/600、h3 18px/600、body 14px/400 - 行高:标题 1.3、正文 1.6 ## 4. Spacing & Layout - 基础单位:4px - 常用间距:8 / 12 / 16 / 24 / 32 / 48 - 容器最大宽度:1280px - 断点:640 / 768 / 1024 / 1280 ## 5. Component Rules - 按钮命名:Button / ButtonPrimary / ButtonGhost - 卡片圆角:8px,阴影仅用 shadow-sm - 表单控件高度:36px(紧凑)/ 44px(宽松) ## 6. Motion - 过渡时长:150ms(微交互)/ 250ms(布局变化) - 缓动函数:ease-out - 禁止:不要使用弹跳、旋转等夸张动效这份骨架的关键在于“禁止”和“仅用于”这类词。AI 代理对否定约束的遵守程度,往往比正面描述更高。你告诉它“用主色”,它可能到处用;你告诉它“主色仅用于主要 CTA”,它就会收敛很多。
3.2 色彩和间距:最容易被 agent 忽略的两块
在我实际使用中,色彩和间距是 agent 最容易“自由发挥”的两个领域。原因也很简单:这两个东西在训练数据里变化太多,模型没有强偏好,所以它会根据当前上下文随机选一个“看起来还行”的值。
色彩方面,我的经验是不要只给色值,还要给使用场景。比如不要只写“主色 #4F46E5”,而要写“主色 #4F46E5,用于:主要按钮背景、链接文字、活跃 tab 下划线、focus ring。不用于:大面积背景、正文文字、图标默认色”。这样 agent 在生成代码时,遇到“这里需要一个强调色”的场景,就知道该用主色;遇到“这里需要一个背景色”的场景,就知道不该用主色。
间距方面,最有效的约束是给出一个封闭的间距集合,并明确说“只能从这个集合里选”。我试过只写“使用 4px 基础单位”,结果 agent 生成了 6px、10px、14px 这种“接近但不属于”体系的值。后来我改成“允许的间距值:4/8/12/16/24/32/48/64,禁止使用其他值”,间距一致性立刻好了很多。
3.3 组件命名规范:让 agent 不再自创词汇
组件命名混乱是我最头疼的问题之一。AI 代理每次生成新组件时,都会根据当前语境起一个新名字,导致项目里出现大量功能重复但命名不同的组件。
impeccable的思路是在PRODUCT.md里维护一份“组件词汇表”,明确列出项目里已有的组件名称和用途,并规定新组件必须复用已有命名模式。比如:
| 组件类型 | 命名模式 | 示例 | 用途 |
|---|---|---|---|
| 按钮 | Button + 变体 | ButtonPrimary | 主要行动点 |
| 按钮 | Button + 变体 | ButtonGhost | 次要行动点 |
| 卡片 | Card + 修饰 | CardInteractive | 可点击卡片 |
| 输入 | Input + 类型 | InputText | 文本输入 |
| 反馈 | Toast + 类型 | ToastSuccess | 成功提示 |
有了这张表,agent 在需要新建组件时,会先查表看有没有现成的,没有的话也会按照模式命名,而不是随手造一个FancyBtn出来。
3.4 响应式策略:提前把断点行为说清楚
响应式的问题在于,agent 默认只会做“压缩式适配”——把桌面布局按比例缩小,而不是重新组织信息层级。要让它做出合理的移动端布局,你必须在PRODUCT.md里明确每个关键区域在不同断点下的行为。
我通常会在PRODUCT.md里加一节“Responsive Behavior”,用表格描述主要布局的变化:
| 区域 | Desktop (≥1024) | Tablet (768-1023) | Mobile (<768) |
|---|---|---|---|
| 导航 | 水平展开 | 水平展开 | 折叠为汉堡菜单 |
| 侧边栏 | 固定左侧 240px | 固定左侧 200px | 抽屉式,默认隐藏 |
| 内容区 | 三列网格 | 两列网格 | 单列堆叠 |
| 表格 | 完整列 | 隐藏次要列 | 卡片式展示 |
这张表看起来简单,但它给 agent 的约束力非常强。没有这张表的时候,agent 在移动端经常直接把表格横向滚动交差;有了这张表,它会老老实实把表格转成卡片布局。
4. 从 npx impeccable install 到跑通第一个页面
4.1 安装与环境准备中最容易忽略的细节
npx impeccable install这个命令本身很简单,但在实际跑通之前,有几个环境细节容易被忽略。我第一次装的时候就在这上面浪费了半小时。
首先是 Node 版本。impeccable的 CLI 依赖较新的 Node 运行时特性,我实测下来 Node 18 以上比较稳,Node 16 会在解析某些 ESM 模块时报错。如果你用的是 nvm 或 fnm,先切到 18 或 20 的 LTS 版本再执行安装。
其次是项目根目录的判定。npx impeccable install会在当前工作目录下寻找package.json来确定项目根目录,然后在那里生成配置文件和PRODUCT.md模板。如果你在 monorepo 的子包里执行,它可能会把配置写到子包目录而不是仓库根目录。我的建议是:先在仓库根目录执行一次,确认生成位置符合预期,再决定要不要调整。
第三是包管理器的 lockfile。如果你项目里用的是 pnpm 或 yarn,npx默认走 npm 的解析路径,可能会和现有 lockfile 产生冲突。更稳妥的做法是用对应的包管理器执行,比如pnpm dlx impeccable install或yarn dlx impeccable install。
安装完成后,你会看到项目根目录多出两个东西:一个是PRODUCT.md模板文件,一个是impeccable.config.json(或者类似的配置文件,具体名称以实际版本为准)。PRODUCT.md是给你填设计上下文的,配置文件是给 agent 读取约束用的。
4.2 配置文件里那几个真正影响输出的字段
配置文件里字段不少,但根据我的使用经验,真正影响 agent 输出质量的其实就那么几个。我把它们整理成一张表,方便你优先关注。
| 字段 | 作用 | 我的推荐值 | 不设置的后果 |
|---|---|---|---|
designContext | 指向 PRODUCT.md 的路径 | ./PRODUCT.md | agent 读不到设计约束 |
strictMode | 是否强制遵守约束 | true | agent 会“灵活变通” |
componentRegistry | 组件词汇表路径 | ./PRODUCT.md#components | 组件命名混乱 |
breakpoints | 响应式断点数组 | [640,768,1024,1280] | 断点值不统一 |
forbiddenPatterns | 禁止使用的模式 | ["inline-style","!important"] | 代码质量下降 |
strictMode这个字段值得单独说一句。我一开始觉得开着太死板,agent 稍微想变通一下就被拦住,影响效率。但用了一段时间后发现,关掉strictMode的代价更大——agent 会以“为了更好的视觉效果”为由,不断突破你设定的约束,最后交付的东西和你想要的越来越远。开着strictMode,虽然偶尔需要手动放行一些合理变通,但整体交付质量稳定得多。
forbiddenPatterns也是我强烈建议配置的。我默认会禁止inline-style和!important,因为这两个东西一旦被 agent 用上,后续维护会非常痛苦。内联样式让样式来源分散,!important让优先级体系崩溃。提前禁掉,能省掉大量后期清理工作。
4.3 让 agent 真正读懂约束的验证方法
配置写好了不代表 agent 就会遵守。你需要一个验证方法,确认 agent 确实读懂了PRODUCT.md里的约束。我的做法是设计一个“约束探针”任务,故意在 prompt 里留出模糊空间,看 agent 会不会主动去查约束。
比如我会给 agent 这样一个任务:“在首页加一个促销横幅,引导用户升级到付费版。”这个任务没有指定颜色、间距、组件名称,如果 agent 读懂了约束,它应该会:使用主色作为横幅背景或 CTA 颜色、使用允许的间距值、复用已有的 Button 组件、在移动端做合理的布局调整。
如果 agent 生成的结果里出现了约束之外的颜色、间距或自创组件名,说明它没有真正读取PRODUCT.md。这时候要检查两个地方:一是配置文件里的designContext路径对不对,二是PRODUCT.md的格式是否被正确解析(比如标题层级是否规范、列表是否被正确识别)。
我还会做一个反向测试:在 prompt 里故意要求一个违反约束的东西,比如“把主色用在页面大背景上”,看 agent 会不会拒绝或提醒。如果它照做了,说明strictMode没生效;如果它提醒“主色按规范不用于大面积背景,是否确认”,说明约束层工作正常。
4.4 跑通第一个页面的完整流程
环境准备好之后,跑通第一个页面的流程大致是这样的。我以一个“定价页面”为例,把关键步骤和每步的意图说清楚。
第一步,先手动写一个最简的页面骨架,不要一上来就让 agent 从零生成。我的经验是,给 agent 一个已有的结构,让它在这个结构上填充和调整,比让它凭空造一个页面质量高得多。骨架里包含页面的大致分区(导航、hero、定价卡片区、FAQ、footer),但样式可以先留空或只写最基础的。
第二步,给 agent 一个具体的任务描述,明确引用PRODUCT.md。比如:“参考 PRODUCT.md 中的色彩、间距和组件规范,为定价卡片区生成三个套餐卡片。主推套餐用主色边框强调,其他两个用中性边框。卡片内包含套餐名、价格、功能列表和 CTA 按钮。”
第三步,检查 agent 的输出,重点看四个地方:颜色是否只用了规范内的色值、间距是否落在允许集合里、按钮是否复用了已有组件、移动端断点行为是否符合PRODUCT.md里的描述。这四个地方没问题,基本就说明约束层在正常工作。
第四步,做一次迭代修改,观察 agent 在修改时是否保持约束。比如让它“把主推套餐的价格字号加大一号”,看它会不会顺手改其他东西。如果它只改了价格字号,说明约束层在迭代中也稳定;如果它又引入了新的视觉变化,说明约束还不够强,需要回去补充PRODUCT.md。
5. 实测中那些文档不会告诉你的坑
5.1 约束太松等于没约束,太紧又会让 agent 摆烂
这是我踩过的最大的一个坑。一开始我把PRODUCT.md写得很简略,只列了色值和字体,结果 agent 该漂移还是漂移。后来我走向另一个极端,把每个组件的每个状态都写死,结果 agent 变得非常死板,遇到稍微超出规范的情况就直接说“无法处理”,或者生成一堆冗余代码来“严格遵守”规范。
我最后找到的平衡点是:约束视觉 token 和组件命名,放开布局组合。也就是说,颜色、间距、字体、圆角、阴影这些“原子级”的东西要严格约束;但具体怎么排列、怎么组合,给 agent 一定的自由度。这样既保证了视觉一致性,又不会让 agent 因为过度约束而失去处理复杂布局的能力。
具体操作上,我会在PRODUCT.md里把约束分成两级:一级是“硬约束”(色值、间距集合、组件命名),二级是“软约束”(布局模式、信息层级)。硬约束用明确的“必须/禁止”表述,软约束用“建议/优先”表述。agent 对硬约束的遵守度很高,对软约束则有一定的判断空间。
5.2 agent 在迭代修改时的“顺手牵羊”问题
这个问题我在前面提过,但值得单独展开说,因为它太常见了。你让 agent 改 A,它把 B 也改了,而且改得“有理有据”——“为了保持视觉一致性”“为了优化用户体验”。这种行为的根源是 agent 在生成代码时,会把当前任务和整体设计目标一起考虑,然后做出它认为“更好”的调整。
impeccable的约束层能在一定程度上抑制这种行为,但不能完全消除。我的应对策略是在 prompt 里明确划定修改范围。比如不说“优化一下这个卡片”,而说“只修改这个卡片的 padding,从 16px 改为 24px,其他属性保持不变”。把修改范围收窄到具体属性,agent 顺手牵羊的空间就小很多。
另外,我会在PRODUCT.md里加一条“修改原则”:任何修改只影响被明确指定的属性,不得擅自调整其他属性。这条规则写进去之后,agent 的“顺手”行为明显减少了。
5.3 多轮对话后约束衰减怎么破
多轮对话是另一个约束容易失效的场景。第一轮 agent 还老老实实遵守PRODUCT.md,聊到第五轮、第十轮,它就开始“忘记”约束了。这是因为对话历史变长后,早期的约束信息在上下文中的权重被稀释了。
我的解决办法有两个。一是在每轮 prompt 里都显式引用PRODUCT.md,比如“继续遵守 PRODUCT.md 中的色彩和间距规范,完成以下修改”。虽然啰嗦,但有效。二是定期开新对话,把当前状态和PRODUCT.md一起作为初始上下文重新喂给 agent。我一般每完成一个较大的功能模块就开一次新对话,避免上下文过长导致的约束衰减。
还有一个技巧是在PRODUCT.md里把最关键的约束放在最前面,并且用加粗或引用块突出。agent 对文档开头和突出格式的内容注意力更高,这样即使对话变长,核心约束也不容易被完全遗忘。
5.4 团队协作时 PRODUCT.md 的版本管理
如果你是一个人用,PRODUCT.md随便改改问题不大。但如果是团队协作,PRODUCT.md的版本管理就很重要了。我见过的情况是:A 同学改了PRODUCT.md里的主色,B 同学不知道,继续用旧色值生成代码,结果项目里出现两套色彩体系。
我的建议是把PRODUCT.md当作代码一样管理:纳入 Git 版本控制、修改时走 PR 流程、在 commit message 里说明修改原因。另外,可以在PRODUCT.md顶部加一个“变更记录”小节,记录每次修改的时间、修改人和修改内容。这样团队成员在拉取最新代码时,能快速知道设计约束有没有变化。
还有一点:PRODUCT.md的修改应该和代码修改同步进行。不要先改代码再补文档,也不要先改文档再改代码,两者脱节会导致 agent 读到的约束和实际代码不一致,生成的结果就会混乱。
6. 把 impeccable 用出复利:几个进阶思路
6.1 为不同产品线维护多份 PRODUCT.md
如果你同时维护多个产品,或者一个产品有面向不同用户群的多个界面(比如管理后台和用户前台),可以考虑维护多份PRODUCT.md。impeccable的配置支持指定不同的设计上下文文件,你可以在不同项目或不同目录下使用不同的PRODUCT.md。
我的做法是在 monorepo 里按产品线分目录,每个目录下放一份PRODUCT.md,然后在各自的配置文件里指向对应的文件。这样 agent 在处理管理后台时,会遵守管理后台的设计规范(高信息密度、紧凑间距);处理用户前台时,会遵守用户前台的设计规范(大间距、强视觉引导)。两套规范互不干扰,避免了“一套规范打天下”导致的风格错位。
6.2 把 PRODUCT.md 当作设计决策的活文档
PRODUCT.md不只是给 agent 看的,它其实也是团队设计决策的活文档。我在实际使用中发现,写PRODUCT.md的过程本身就是在梳理设计思路。很多平时口头约定、模糊不清的设计规则,一旦要写成 agent 能执行的约束,就必须想清楚、写明白。
比如“主色用于主要行动点”这句话,写的时候你就会想:什么是主要行动点?一个页面里可以有多个主要行动点吗?如果两个按钮都是主要行动点,主色怎么分配?这些问题想清楚了,不仅 agent 的输出更稳定,团队内部的设计沟通成本也降低了。
我现在的习惯是:每次做设计决策时,先更新PRODUCT.md,再让 agent 按更新后的约束生成代码。这样PRODUCT.md始终反映最新的设计意图,agent 的输出也始终和最新意图对齐。
6.3 结合组件库时的注意事项
如果你的项目已经有一套组件库(比如基于 shadcn/ui、Ant Design 或自研组件库),impeccable的约束层需要和组件库的既有规范对齐。我的经验是,在PRODUCT.md的组件规范一节里,明确写出“优先使用组件库中的 X 组件,不要重新实现”。
比如项目里已经有Button组件,就在PRODUCT.md里写:“所有按钮必须使用@/components/ui/button中的 Button 组件,通过 variant 属性控制样式,禁止新建按钮组件或直接写 button 标签。”这样 agent 在需要按钮时,会去复用已有组件,而不是每次重新造一个。
另外,组件库的 design token 和PRODUCT.md里的 token 要保持一致。如果组件库用的是 CSS 变量,PRODUCT.md里也引用同一套变量名,避免出现“文档里写 #4F46E5,代码里用 var(--primary)”这种不一致。
6.4 观察 agent 输出、反哺约束的循环
impeccable最有价值的用法,是把它当成一个持续优化的循环:agent 输出 → 你检查 → 发现约束漏洞 → 补充PRODUCT.md→ agent 输出质量提升。我用了几个月下来,PRODUCT.md从最初的一页纸变成了现在的四五页,每一条补充都是踩坑换来的。
举几个我后来补充进去的约束例子:“卡片阴影只用 shadow-sm,hover 时用 shadow-md,不要用 shadow-lg”“表格行高固定 48px,不要根据内容自适应”“图标统一使用 lucide-react,尺寸 16px 或 20px,不要混用其他图标库”。这些约束看起来琐碎,但每一条都对应着一次实际的翻车经历。补进去之后,同类问题基本不再出现。
这个循环的关键是:不要指望一次把PRODUCT.md写完美。先写一个基础版本,然后在实际使用中不断补充。每次 agent 输出不符合预期,先问自己“是不是约束没写清楚”,如果是,就补进去。这样用下来,你的PRODUCT.md会越来越贴合项目实际,agent 的输出也会越来越稳。
7. 我对这套约束层的一点个人判断
用impeccable这套思路做前端开发,最大的感受是:AI 代理的上限取决于你给它的上下文质量。模型本身的能力已经足够强了,它缺的不是写代码的能力,而是知道“该写成什么样”的信息。PRODUCT.md本质上就是在补这个信息差。
我现在的项目里,PRODUCT.md已经成了和package.json一样重要的基础文件。新成员加入时,我会让他先读PRODUCT.md了解设计规范;用 agent 生成代码前,我会先确认PRODUCT.md是最新的。这套流程跑下来,前端交付的一致性和效率都有明显提升。
当然,它也不是银弹。PRODUCT.md写得再好,agent 偶尔还是会出幺蛾子,尤其是涉及复杂交互和边界状态的时候。但相比没有约束层时的那种“开盲盒”体验,现在的可控性已经好太多了。如果你也在用 AI 代理写前端,我建议你从今天开始,先写一份最简的PRODUCT.md,哪怕只有色值和间距,也比什么都不给强。用起来之后,你会慢慢体会到约束带来的复利。