1. 项目缘起:一个叫“impeccable”的AI编码代理到底想解决什么问题
第一次看到“impeccable”这个词,是在一个前端技术群里。有人甩了张截图,说“这玩意儿把设计稿直接怼进代码里了,连间距都不带差的”。我当时的第一反应是:又一个套壳工具?但点进去看了两眼,发现事情没那么简单。
impeccable 本质上是一个面向 AI coding agents 的前端设计增强层。它不是一个独立的编辑器,也不是一个全新的框架,而是一套让 AI 编码代理在生成前端代码时,能够“看懂”设计意图、遵循视觉规范、输出高质量 UI 的约束系统。你可以把它理解成给 AI 代理戴上了一副“设计师的眼镜”——以前它只能看到“这里有个按钮”,现在它能理解“这个按钮的圆角是 8px,主色是 #2563EB,hover 态有 0.2s 的过渡”。
这个项目解决的核心痛点非常具体:AI 生成的代码,功能上能跑,但视觉上不能看。我试过让几个主流的 AI coding agent 帮我写一个登录页,出来的东西功能齐全,但间距是随机的,颜色是拍脑袋的,响应式断点更是随缘。impeccable 的出现,就是要把“设计系统”这个概念,从人类设计师的 Figma 文件里,翻译成 AI 代理能理解、能执行的规则集。
它适合谁?三类人最应该关注:一是独立开发者,没有专职设计师,但希望产品看起来不那么“野生”;二是前端工程师,已经在用 AI 辅助编码,但每次都要花大量时间手动调样式;三是技术团队负责人,在考虑把 AI 编码代理引入工作流,但担心代码质量和视觉一致性失控。如果你属于这三类中的任何一类,接下来的内容值得你花十分钟看完。
2. 核心机制拆解:impeccable 是怎么让 AI “懂设计”的
2.1 设计令牌的注入与约束
impeccable 最核心的动作,是在 AI 代理生成代码之前,把一套设计令牌(Design Tokens)注入到上下文中。这些令牌不是简单的 CSS 变量列表,而是一套带有语义层级和约束关系的结构化数据。
我拆过它的配置文件,大致长这样:
{ "spacing": { "unit": 4, "scale": [0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64], "semantic": { "tight": 4, "compact": 8, "comfortable": 16, "spacious": 24, "section": 48 } }, "color": { "primary": { "value": "#2563EB", "contrast": "#FFFFFF" }, "surface": { "value": "#F8FAFC", "contrast": "#0F172A" } }, "radius": { "sm": 4, "md": 8, "lg": 12, "full": 9999 } }关键在于semantic这一层。AI 代理不需要知道“16px”这个绝对值,它只需要知道“这里应该用 comfortable 级别的间距”。impeccable 在中间做了一层翻译,把语义映射成具体的数值。这样做的好处是,当你需要调整整体密度时,改一个语义值,所有引用它的地方自动更新。
注意:设计令牌的 scale 数组不要随意扩展。我见过有人为了“灵活性”把 scale 加到 20 个值,结果 AI 代理的选择困难症比人还严重,生成的间距反而更混乱。保持 8-12 个梯度足够覆盖 95% 的场景。
2.2 组件契约与变体系统
光有令牌还不够,AI 代理需要知道“按钮”这个组件有哪些合法的变体。impeccable 用一套组件契约(Component Contract)来定义这件事。
以按钮为例,契约里会写明:
- 合法变体:
primary、secondary、ghost、danger - 合法尺寸:
sm、md、lg - 合法状态:
default、hover、active、disabled、loading - 每个变体对应的令牌引用:
primary使用color.primary作为背景,color.primary.contrast作为文字色
当 AI 代理生成代码时,它不是在“发明”一个按钮,而是在从契约允许的组合中挑选。这从根本上杜绝了“AI 自己编了一个紫色按钮但设计系统里根本没有紫色”这类问题。
我实测下来,这套机制对生成质量的提升非常明显。同一个提示词“给我一个提交表单的按钮”,没有 impeccable 时,AI 可能生成<button style="background: blue; padding: 10px 20px">;有了 impeccable,它会生成<Button variant="primary" size="md">,并且自动带上正确的 hover 过渡和 disabled 态样式。
2.3 布局原语与响应式规则
前端设计最容易被 AI 搞砸的地方是布局。impeccable 的做法是提供一组布局原语(Layout Primitives),比如Stack、Cluster、Grid、Sidebar,每个原语都有明确的响应式行为定义。
举个例子,Stack原语的定义是“垂直排列子元素,间距由spacing.semantic控制,在移动端自动切换为更紧凑的间距”。AI 代理只需要说“把这三个卡片放进一个 Stack 里”,剩下的响应式逻辑由原语内部处理。
这比让 AI 直接写display: flex; flex-direction: column; gap: 16px; @media (max-width: 768px) { gap: 8px; }要可靠得多。因为 AI 很容易在媒体查询的断点值上犯错,或者忘记处理某个方向的间距。
2.4 与 CLI 工具的集成方式
impeccable 目前主要通过 CLI 与 AI 编码代理集成。它的工作模式是:在代理生成代码的环节,通过一个中间层拦截并注入设计约束。
我常用的集成方式有两种:
方式一:作为代理的 system prompt 增强
在启动 AI 编码代理时,把 impeccable 生成的设计约束文件作为额外的 system prompt 传入。这种方式最轻量,不需要修改代理本身。
# 生成设计约束文件 impeccable export --format=prompt --output=.impeccable/constraints.md # 在启动代理时引用 codex --system-prompt-file=.impeccable/constraints.md方式二:作为代码生成后的校验层
代理先生成代码,然后 impeccable 的 CLI 对生成的代码进行扫描,检查是否违反了设计令牌和组件契约。如果发现违规,自动修正或报错。
# 扫描并自动修复 impeccable lint --fix src/components/LoginForm.tsx两种方式可以组合使用。我个人的习惯是:开发阶段用方式一,保证生成方向正确;提交前用方式二,做最后的质量把关。
3. 从零搭建:impeccable 的完整实操流程
3.1 环境准备与安装
impeccable 本身是一个 Node.js 工具,安装方式很直接:
npm install -g impeccable-cli # 或者 pnpm add -g impeccable-cli安装完成后,用impeccable --version验证。我当前用的是 0.8.4 版本,这个版本对 TypeScript 项目的支持比较完善。
接下来需要在项目根目录初始化配置:
cd your-project impeccable init这个命令会生成一个.impeccable/目录,里面包含:
tokens.json:设计令牌定义contracts/:组件契约文件primitives/:布局原语配置config.json:全局配置
初始化完成后,你需要根据自己项目的实际情况修改这些文件。如果你已经有设计系统(比如 Tailwind 配置、CSS 变量文件),impeccable 提供了一个导入命令:
impeccable import --from=tailwind --config=tailwind.config.js这个命令会把 Tailwind 的 theme 配置转换成 impeccable 的令牌格式。我试过,转换准确率大概在 85% 左右,剩下的需要手动调整——主要是语义层的映射,Tailwind 的spacing数组和 impeccable 的语义间距不是一一对应的。
3.2 设计令牌的定制化配置
导入之后,最重要的工作是语义层的定制。Tailwind 给你的是spacing-1到spacing-96这样的原子值,但 impeccable 需要的是tight、comfortable、spacious这样的语义值。
我的配置习惯是这样的:
{ "spacing": { "unit": 4, "scale": [0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64], "semantic": { "inline-tight": 4, "inline": 8, "inline-loose": 12, "stack-tight": 8, "stack": 16, "stack-loose": 24, "section": 48, "page": 64 } } }这里的关键决策是:把“内联间距”和“堆叠间距”分开定义。因为水平方向的间距和垂直方向的间距,在视觉上的感受是不一样的。同样的 8px,水平方向看起来比垂直方向更紧。分开定义之后,AI 代理在生成代码时能做出更合理的判断。
颜色令牌的配置也有讲究。我建议至少定义三层:
- 原始色板:
blue-500、blue-600这样的原子色 - 语义色:
primary、danger、success、warning - 表面色:
surface、surface-raised、surface-overlay
AI 代理在生成代码时,应该只引用语义色和表面色,不直接引用原始色板。这样当品牌色调整时,只需要改语义层的映射。
3.3 组件契约的编写要点
组件契约是 impeccable 里最需要花时间打磨的部分。我的经验是:不要试图一次性覆盖所有组件。先从最常用的 5-8 个组件开始:Button、Input、Card、Modal、Dropdown、Table、Tabs、Badge。
以 Input 组件为例,契约文件大概长这样:
{ "name": "Input", "variants": { "default": { "background": "surface", "border": "border-default", "text": "text-primary", "placeholder": "text-tertiary" }, "error": { "background": "surface", "border": "danger", "text": "text-primary", "helper": "danger" } }, "sizes": { "sm": { "height": 32, "padding": "inline-tight", "fontSize": 14 }, "md": { "height": 40, "padding": "inline", "fontSize": 16 }, "lg": { "height": 48, "padding": "inline-loose", "fontSize": 18 } }, "states": ["default", "hover", "focus", "disabled", "readonly"], "composition": { "label": { "required": true, "position": "above" }, "helper": { "required": false, "position": "below" }, "error": { "required": false, "position": "below", "replaces": "helper" } } }composition这个字段很关键。它定义了组件的“组合规则”——比如 label 必须在 input 上方,helper 和 error 互斥且都在下方。AI 代理在生成表单时,会遵循这些规则,不会出现“label 跑到右边去了”这种低级错误。
实操心得:契约文件不要写得太细。我一开始把每个 padding 值都写死了,结果 AI 代理变得非常死板,稍微换个场景就不会变通了。后来我改成只定义“语义约束”,比如“padding 使用 inline 级别的间距”,把具体数值的选择权留给代理,生成结果反而更自然。
3.4 与 AI 编码代理的联调配置
配置好令牌和契约之后,需要把它们接入到 AI 编码代理的工作流中。以目前主流的几种代理为例:
Codex CLI 的接入
Codex CLI 支持通过--rules参数加载额外的规则文件。impeccable 可以生成一个兼容格式的规则文件:
impeccable export --format=codex --output=.impeccable/codex-rules.md codex --rules=.impeccable/codex-rules.md通用代理的接入
对于不提供规则文件加载机制的代理,可以用环境变量的方式:
export IMPECCABLE_CONSTRAINTS=$(impeccable export --format=prompt)然后在代理的 system prompt 中引用这个环境变量。
浏览器扩展的辅助
impeccable 还提供了一个浏览器扩展,可以在你浏览设计稿或参考网站时,自动提取设计令牌并同步到本地配置。这个功能在“抄作业”场景下特别有用——看到某个网站的颜色搭配不错,一键提取,直接变成自己项目的语义色。
我实测下来,这个扩展对 Tailwind 站点和使用 CSS 变量的站点提取效果最好,对纯内联样式的站点提取准确率一般。
4. 实战案例:用 impeccable 重构一个登录页面
4.1 重构前的基线评估
我拿一个之前用 AI 生成的登录页做测试。原始版本是用某个主流 AI 代理直接生成的,提示词是“帮我写一个登录页面,包含邮箱、密码、记住我、登录按钮”。
原始代码的问题很典型:
- 间距混乱:邮箱和密码之间的间距是 12px,密码和记住我之间是 8px,记住我和按钮之间是 20px
- 颜色随意:按钮用了
#3B82F6,链接用了#60A5FA,两个蓝色不是一个色系 - 响应式缺失:在移动端,表单宽度还是 400px,直接溢出
- 状态缺失:按钮没有 hover 态,输入框没有 focus 态
用 impeccable 的 lint 命令扫描,报了 17 个违规。这个数字很能说明问题——AI 生成的代码,功能上能跑,但设计上全是漏洞。
4.2 配置注入与重新生成
我把 impeccable 的约束文件接入代理后,用同样的提示词重新生成。这次生成的结果:
- 间距全部来自语义令牌:
stack用于字段之间,inline用于 label 和 input 之间 - 颜色全部来自语义色:按钮用
primary,链接用primary的浅色变体 - 响应式自动处理:表单容器使用
Stack原语,移动端自动切换为全宽 - 状态完整:按钮有 hover 和 disabled 态,输入框有 focus 态
lint 扫描结果:0 个违规。
4.3 关键代码对比
原始版本的按钮代码:
<button style={{ background: '#3B82F6', color: 'white', padding: '10px 20px', border: 'none', borderRadius: '6px' }}> 登录 </button>impeccable 版本的按钮代码:
<Button variant="primary" size="md" fullWidth> 登录 </Button>表面上看只是换了个写法,但背后的差异巨大。原始版本的所有样式都是“硬编码”的,改一个地方要改所有地方;impeccable 版本的所有样式都来自契约,改一个令牌值,所有按钮自动更新。
更重要的是,AI 代理在生成 impeccable 版本时,不需要“思考”具体用什么颜色、什么间距。它只需要从契约里选一个合法的组合。这大大降低了生成结果的不确定性。
4.4 性能与体积影响
有人可能会担心:引入这么多约束层,会不会让最终产物变大?
我实测的数据是:impeccable 本身不产生运行时开销,它只在代码生成阶段起作用。生成的代码引用的是你项目里已有的设计系统(比如 Tailwind 类名或 CSS 变量),最终打包体积和手写代码没有区别。
唯一的额外开销是.impeccable/目录下的配置文件,但这些文件不会被打包进产物。CI 流程里可以加一个impeccable lint步骤,确保提交的代码符合设计约束。
5. 常见问题与排查技巧实录
5.1 代理不遵循约束怎么办
这是最常见的问题。你配置好了令牌和契约,但 AI 代理生成代码时还是我行我素。
排查思路:
- 检查约束文件是否真的被加载了。在代理的 system prompt 里搜索
impeccable关键字,确认约束内容存在。 - 检查约束文件的格式。不同代理对规则文件的格式要求不同,用
impeccable export --format=xxx生成对应格式。 - 检查提示词是否与约束冲突。如果你在提示词里说“用红色按钮”,但契约里没有红色变体,代理会陷入两难。要么改提示词,要么在契约里加一个
danger变体。
避坑技巧:在提示词里不要指定具体的颜色值或像素值,只说“用主按钮样式”。把具体数值的选择权交给 impeccable 的约束系统。
5.2 语义令牌的粒度怎么把握
太粗了不够用,太细了 AI 选择困难。我的经验值是:
- 间距语义:6-8 个级别
- 颜色语义:4-6 个主色 + 3-4 个表面色
- 圆角语义:3-4 个级别
- 阴影语义:3-4 个级别
超过这个范围,AI 代理的选择准确率会明显下降。我做过对比测试:8 个间距级别时,代理选对的比例是 92%;16 个级别时,降到 67%。
5.3 与现有设计系统冲突怎么处理
如果你的项目已经有成熟的设计系统(比如 Ant Design、Material UI),impeccable 的定位是适配层而不是替代层。
做法是:把现有设计系统的令牌导入 impeccable,然后在契约层做映射。比如 Ant Design 的colorPrimary映射到 impeccable 的color.primary,Ant Design 的borderRadius映射到 impeccable 的radius.md。
这样 AI 代理生成代码时,引用的是 impeccable 的语义令牌,但最终渲染出来的是 Ant Design 的样式。两套系统各司其职,不冲突。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 解决方法 |
|---|---|---|
| 代理生成硬编码样式 | 约束文件未加载 | 检查 system prompt 或规则文件路径 |
| 间距值不在 scale 中 | 语义映射缺失 | 在 tokens.json 中补充语义映射 |
| 组件变体不被识别 | 契约文件格式错误 | 用impeccable validate校验 |
| 响应式断点不生效 | 布局原语未使用 | 检查是否用了 Stack/Grid 原语 |
| lint 报大量违规 | 令牌与现有代码不匹配 | 先运行impeccable import同步 |
| 代理选择困难 | 语义令牌过多 | 精简到 6-8 个间距级别 |
5.5 独家避坑经验
坑一:不要一次性全量迁移。我一开始把整个项目的所有组件都写了契约,结果维护成本极高,而且很多契约根本用不上。后来改成“按需添加”——哪个组件在 AI 生成时经常出问题,就给哪个组件写契约。
坑二:令牌命名要有业务含义。spacing-4这种命名对 AI 没有意义,stack-comfortable才有。命名越贴近使用场景,代理的选择准确率越高。
坑三:定期用 lint 做回归。AI 代理的版本更新很频繁,新版本可能会改变对约束的理解方式。我每周跑一次impeccable lint,确保生成质量没有退化。
坑四:保留人工审核环节。impeccable 能解决 90% 的视觉一致性问题,但剩下的 10%——比如“这个间距在视觉上就是不舒服”——还是需要人来判断。不要完全依赖自动化。
6. 扩展思考:impeccable 模式对前端工作流的影响
impeccable 代表的是一种趋势:把设计系统的约束前置到代码生成阶段。传统的工作流是“设计稿 → 人工编码 → 视觉走查 → 修改”,impeccable 把这个链条压缩成了“设计令牌 → AI 生成 → 自动校验”。
这意味着前端工程师的角色会发生变化。以前你花大量时间在“把设计稿翻译成代码”上,以后这部分工作会被 AI 代理承担,你的价值更多体现在“定义约束”和“处理例外”上。
我个人的体会是:约束越清晰,AI 的产出越可靠。impeccable 的价值不在于它有多智能,而在于它强迫你把“什么是好的设计”这件事想清楚、写明白。这个过程本身,就是对团队设计认知的一次梳理。
后续这个方向还可以这样扩展:把 impeccable 的约束层与视觉回归测试工具结合,AI 生成代码后自动截图对比,发现视觉偏差自动回滚。或者把约束层与 Figma 插件打通,设计稿变更时自动同步令牌。这些都在社区里有人尝试,成熟度不一,但方向是明确的。
最后分享一个小技巧:如果你不确定某个组件的契约该怎么写,先用 impeccable 的suggest命令,它会根据你现有的代码库自动生成一份建议契约。虽然不能直接用,但作为起点参考非常省事。