AI 生成代码有个问题:它偶尔会忘记你的规范。尤其是对话轮数多了之后,之前 steering 里的约束可能被"稀释"。
我写了一个 preToolUse Hook,每次 AI 要写文件时自动检查。相当于给 AI 装了个"写之前先 lint"的闸门。从此 AI 产出的代码合规率从 85% 到接近 100%。
问题场景
arduino
第 1 轮对话:AI 老老实实用 dayjs
第 5 轮对话:AI 突然用了 moment(上下文稀释)
第 8 轮对话:AI 用了 class 组件(忘了你说只用函数组件)
你发现的时候:
├── 要么在 CR 时才看到 → 已经提了 MR
├── 要么在构建时 lint 报错 → 要来回改
└── 总之:发现越晚,修复成本越高
解决方案:preToolUse Hook
json
{
"name": "Code Standards Check",
"version": "1.0.0",
"description": "AI 写文件前自动检查是否符合编码规范",
"when": {
"type": "preToolUse",
"toolTypes": ["write"]
},
"then": {
"type": "askAgent",
"prompt": "在写入文件前,请检查以下编码规范:\n1. 是否引入了 moment?应使用 dayjs\n2. 是否使用了 class 组件?应使用函数组件\n3. 是否使用了 var?应使用 const/let\n4. 日期格式化是否用了 dayjs 而非 new Date\n5. 样式文件是否使用 .m.less 后缀(CSS Modules)\n6. import 路径是否使用了 @ 或 @@ 别名\n\n如果有违规,请修正后再写入。如果全部合规,继续执行写入。"
}
}
工作原理
markdown
AI 准备写文件
↓
preToolUse Hook 触发
↓
AI 自我审查即将写入的内容
↓
┌─────────┐ ┌────────────┐
│ 发现违规 │ │ 全部合规 │
└────┬────┘ └─────┬──────┘
↓ ↓
修正代码 继续写入
↓
再次写入
实际拦截案例
案例 1:moment 引入被拦截
AI 在第 6 轮对话中准备写入:
typescript
import moment from 'moment'
const formatDate = (date: string) => moment(date).format('YYYY-MM-DD')
Hook 触发后 AI 自动修正为:
typescript
import dayjs from 'dayjs'
const formatDate = (date: string) => dayjs(date).format('YYYY-MM-DD')
案例 2:样式文件后缀
AI 准备创建 styles.less,Hook 提醒后改为 index.m.less。
案例 3:路径别名
typescript
// 准备写入
import { Button } from '../../../components/Button'
// Hook 修正后
import { Button } from '@@/Button'
为什么不只靠 Steering
erlang
Steering(软约束):
├── 写在配置文件里
├── AI "应该"遵守
├── 上下文长了可能被忽略
├── 效果:~85% 合规率
└── 类比:贴在工位上的规范海报
Hook(硬约束):
├── 每次写文件都触发
├── AI "必须"通过检查
├── 不受上下文长度影响
├── 效果:~99% 合规率
└── 类比:门禁刷卡,不符合进不去
两者结合效果最好:Steering 让 AI 一开始就按规范写,Hook 在最后关口兜底。
进阶:按文件类型差异化检查
不同文件的规范不同,可以在 prompt 里加判断逻辑:
json
{
"then": {
"type": "askAgent",
"prompt": "检查即将写入的文件:\n\n如果是 .tsx/.ts 文件:\n- 检查 import 规范(别名、dayjs、函数组件)\n- 检查导出方式(优先 named export)\n\n如果是 .m.less 文件:\n- 检查是否使用了全局选择器(应该都是局部类名)\n- 检查是否有 !important(尽量避免)\n\n如果是 services/ 下的文件:\n- 检查请求函数是否有返回类型\n- 检查是否使用了统一的 request 实例"
}
}
性能影响
arduino
每次写文件额外耗时:约 1-2s(AI 自检)
每天写文件次数:约 30-50 次
每天总额外耗时:约 1-2min
vs 不用 Hook 的代价:
├── CR 发现规范问题 → 来回改:15min/次
├── lint 报错 → 回去修:5min/次
├── 按每天 3 次规范问题计算:45min/天
ROI:每天省 40+ 分钟,值。
配置建议
javascript
建议放进 Hook 的规则:
├── 高频违规的(moment、var、class 组件)
├── 机械性的(路径别名、文件后缀)
└── 难以事后修复的(全局样式污染)
不建议放的:
├── 主观性强的("这个变量名不够语义化")
├── 需要全局上下文的("这个组件应该提取出去")
└── 频繁变化的规则(每周都在调整的)
💬 你们有没有遇到 AI 生成代码"偶尔出格"的问题?用什么方法来兜底?
🔗 完整 Skills 源码已开源 :github.com/sleepyccat/...,欢迎 Star ⭐ 和 PR。