用静态检查拦住 AI 网页的模板化设计
MIT 开源工具 tastelint 通过确定性规则扫描前端源码,定位渐变、卡片等重复设计模式,并可按评分阻断 CI。基础扫描无需浏览器、网络或模型,另提供可选的模型修改建议与编程助手设计指引。
AI 深度解读
AI 写出的落地页,为什么经常像同一个模板:紫蓝渐变、居中大标题、三张玻璃卡片,再配一个「Get Started」按钮?tastelint 作者想解决的,就是这类代码检查全部通过、页面却缺少产品辨识度的问题。他把常见套路做成静态规则,让它们能被定位、计分,甚至阻止构建。
这个工具值得看的地方,是把模糊的「设计感不对」变成 Agent 可以执行的修改要求。但它能识别套路,不代表它能判断设计好坏。两者的边界,决定了它该怎么接进工作流。
把「太像 AI 生成」拆成可修改的问题
tastelint 是一个 MIT 协议项目,要求 Python 3.10+,扫描器使用标准库实现,仓库带有 37 个离线测试。原文展示的版本是 0.1.0。
它支持 HTML、CSS、SCSS、JSX/TSX、Vue、Svelte、MDX,以及 Tailwind 类名字符串。运行 tastelint scan 时,不启动浏览器,不访问网络,也不调用模型,而是用确定性规则检查源码:相同输入会得到相同结果。
作者共做了 17 条规则,每条带有严重程度和一句修复建议。原文展示的检测对象包括紫蓝渐变、页面整体居中、空泛宣传语、标题里的 emoji、三张同款功能卡片、占位文案、玻璃面板、过大的圆角和阴影。
这套做法有用的地方,是把问题缩小到了具体位置。例如 empty-cta 指向没有明确对象的行动按钮;hype-phrase 指向放到其他产品上也成立的句子。加上 --verbose,报告还会显示修改提示和触发规则的源码行。
Agent 更容易执行「把按钮写成具体动作」,而不是「再做得高级一点」。
作者给出的修改版,把宣传语换成了发货工具的实际任务:
<main className="min-h-screen bg-stone-50 text-stone-900 font-['Fraunces',serif]">
<section className="max-w-3xl px-8 py-24">
<h1 className="text-5xl font-semibold">Label, weigh and book a courier for 40 parcels before lunch</h1>
<p className="mt-6 text-stone-700">Paste your order export. Parcelbay prints the labels, picks the cheapest courier per postcode, and emails buyers their tracking links.</p>
<button className="mt-10 rounded-md bg-emerald-800 px-6 py-3 text-white">Import today's orders</button>
</section>
...
</main>
重点看按钮和说明文字:它们交代了输入订单、打印标签、选择快递、发送追踪链接这些具体动作。代码里的省略号也表明,这只是展示片段,不能直接当作完整组件使用。
分数适合拦截默认套路,不能证明页面优秀
计分从 100 开始:high 每项扣 12 分,medium 扣 5 分,low 扣 2 分;每条规则累计最多扣 12 分,最低为 0。
执行 tastelint scan src --fail-under 90,低于 90 分就失败。因此,只要出现一个 high 问题,分数最多剩下 88,就会触发门禁。这里的上限按规则计算,不能直接拿发现数量乘权重推算总分。
原文中的典型落地页示例得到 24 项发现,其中 3 项 high、20 项 medium、1 项 low,最终为 9/100,退出码是 1。按提示改写后,同页得到 0 项发现、100/100,退出码变为 0。
仓库还提供了五种格式的通用营销页:HTML、CSS、React、Vue、Svelte。运行 tastelint scan examples --quiet,7 个文件共得到 85 项发现,其中 25 项 high、52 项 medium、8 项 low,分数为 0/100。作为对照的小型陶艺工作室页面,执行 tastelint scan examples/quiet-studio --fail-under 90 --quiet 后,2 个文件没有发现问题,得到 100/100。
这些是作者提供的示例结果。它们说明规则能区分作者准备的两组页面,但原文没有展示真实项目上的误报率、用户偏好测试或转化数据。
100 分只能说明没有触发这些规则,不能说明页面已经设计好了。
报告支持 JSON 和 SARIF。运行 tastelint scan src --format sarif -o tastelint.sarif,可以生成供 GitHub code scanning 上传的报告,在 PR 中展示问题位置。需要例外时,可以使用 // tastelint-disable-next-line hype-phrase,或 tastelint-disable-line all。
退出码也区分了失败原因:0 表示通过,1 表示低于门槛,2 表示路径或参数错误,3 表示 review 请求失败,4 表示 review 缺少密钥。接 CI 时,应当区分页面未达标和工具配置出错。
先给 Agent 规则,再让模型补充意见
作者还把设计指南放进 Agent 会读取的位置。执行 tastelint skill install --target claude,会写入 .claude/skills/tastelint/SKILL.md;--target codex 写入 AGENTS.md;--target cursor 写入 .cursor/rules/tastelint.mdc。重复安装 Codex 指引会替换自己的段落,避免不断追加。
指南名为「Plain Surface Charter」,要求明确字体选择、集中使用一种主色、写出只适合当前产品的句子,以及不要把整个页面都压到中轴线上。它还要求 Agent 在宣布完成前执行 tastelint scan <path> --fail-under 90。
扫描之后,才轮到可选的 tastelint review。如果已经是 100 分,它会停止;否则只发送分数、规则 ID 和短代码片段,最多包含 20 项发现,让模型返回注明文件和替换内容的具体修改。
执行 tastelint review examples/sloppy-landing --dry-run,可以检查准备发送的内容,而且不会建立网络连接。原文默认请求地址为 https://atmorouter.dev/v1/chat/completions,模型为 atmo/deepseek-v4.1-flash,密钥通过 ATMOROUTER_API_KEY 提供,再运行 tastelint review src。
这个环节也能改用 OpenAI-compatible 服务,包括本地服务:
tastelint review src --base-url http://127.0.0.1:11434/v1 --model llama3.2 --api-key local
需要留意的是 --base-url 和 --model:它们决定请求交给哪个服务、由哪个模型处理。原文没有提供不同模型的评审效果对照。
作者称默认网关 AtmoRouter 支持 169 个模型、按 token 收费,平均价格比官方低 80.0%。这是原文的商业宣传口径,没有附计价明细或同任务成本测试,不能直接当作团队预算依据。
我的判断
我认可「先把重复问题写成规则,再让 Agent 自查」这个方向。它让反馈更稳定,也能把人工评审从反复指出占位文案、空泛按钮这些事情里解放出来。
但规则把某些视觉选择预设成了风险:紫色、居中布局、三列卡片、玻璃效果都有可能服务于合理设计。它们是否合适,需要结合品牌、内容和页面任务判断。强制统一配色或布局,也可能制造另一套模板。
实现边界更加具体。tastelint 只读源码,不渲染页面,不计算真实布局,也不计算渐变背景上的实际对比度。它判断卡片的依据,是类名叫 card,或者节点同时带有大圆角和阴影、边框或填充。这种启发式检查会漏报,也会误报。
因此,相比人工预览或浏览器中的视觉评审,它更适合作为早期筛查。相比把整页都交给模型点评,它的优势是确定性和可重复执行;至于最终设计是否更好,作者没有给出对照数据,目前仍是经验之谈。
能拿来做什么
- 如果经常让 Agent 生成营销页,可以先用
tastelint scan src检查现有项目,核对哪些规则符合团队设计规范,再决定是否采用 90 分门槛。 - 可以把设计指南安装到实际使用的 Claude Code、Cursor 或 Codex 中,让 Agent 写完页面后自行扫描,优先修正文案、按钮和占位内容。
- 在 PR 流程中接入 SARIF 报告,同时保留页面预览;对合理的设计选择使用逐行豁免,并在评审中解释原因。
- 需要模型给修改方案时,先用
--dry-run查看发送摘要,再选择团队允许使用的服务;不要把静态扫描通过当作视觉验收完成。