Agent 写的代码能跑。但"能跑"不等于"好"。两者之间的差距是代码异味积累的地方——可以并行的串行 await、硬编码常量、不断膨胀的函数签名、过早的抽象。人类审查者能发现这些。Agent 不会,除非环境告诉它去看。
本指南介绍三种缩小这一差距的机制。
#问题:上下文优先级
当 agent 在执行"添加 sitemap 生成"时,它的注意力集中在让 sitemap 正常工作上。它不会同时思考新代码是否引入了硬编码字符串、刚扩展的函数调用是否变得容易冲突、或者构建文件是否接近复杂度阈值。
这不是能力限制。Agent 被要求时可以分析代码质量。问题是默认工作流中没有任何东西要求它这样做。
#机制 1:变更后 Skills
Skill 是 agent 在完成任务后运行的结构化检查清单。它将 agent 的视角从"实现功能"切换到"审视我刚写的东西"。
一个面向构建层的 review skill 通常会检查:
- 可以用
Promise.all的独立await调用 - 出现多次的字符串字面量
- 参数越来越长的 render 或模板调用
- 重复的过滤/映射逻辑
- 接近项目自定义尺寸或复杂度阈值的构建文件
Skill 不修复任何东西。它呈现发现,使重构决策变得显式。
本项目的例子:
skills/build-code-review/SKILL.md针对build/build.mjs把这一模式固化成带具体阈值(如约 300 行)的 skill。其他项目应该依据自己的构建层选取合适的阈值。
#何时使用
在修改特定领域的文件后(构建系统、API 层、测试基础设施)。Skill 应该是领域特定的,而不是通用的——"审查所有东西"的 skill 太宽泛,没有实用价值。
#如何触发
当前:把该 skill 和它对应的触发文件列在项目的 agent 指令文件中(例如 AGENTS.md、CLAUDE.md、.cursorrules)。Agent 读取这些指令后在相关文件变更时运行 skill。
未来:hooks 在相关文件变更后自动调用 skill。
#机制 2:Hooks
Hooks 是无需人类提示即可运行的自动化触发器。它们是最强的机制,因为不依赖 agent 记住要做某事。
#常见的 hook 时点
大多数 agent runtime 会暴露几类 hook 时点:
- 在编辑类工具调用之后(如写入或修改文件)
- 在 git commit 之后
- 在触及敏感操作之前
一个作用于特定目录(比如构建层)的 post-edit hook,可以在该目录下的任意文件发生变化时自动运行对应的 review skill。
本项目的例子: 一个作用于
build/**的 post-edit hook 会触发 build code review skill。Agent Skills 兼容的 runtime(例如 Claude Code)提供PostToolUse和PostCommit等 hook 点;其他 agent 的命名可能不同,但模式一致。
#局限性
- Hooks 为每个匹配操作增加延迟
- 过于宽泛的 hooks 产生噪音并拖慢工作流
- Hook 配置需要显式权限(agent 不能自行修改自己的 hooks)
#建议
从 skills(手动触发)开始。只有在 skill 经过多个会话验证其价值后,才提升为 hooks。这避免了过早自动化的陷阱。
#机制 3:Lint 规则
Lint 规则是最可靠的机制,因为它们在构建时运行、是确定性的、不依赖 agent 行为。
与 agent 编写的代码相关的规则:
max-params(ESLint):标记超过 N 个参数的函数。强制使用对象解构,减少合并冲突并提高可读性。no-await-in-loop(ESLint):捕获循环内可以并行化的串行 await。- 项目特定的自定义规则(如"模板文件中不允许硬编码 URL")。
#与冲突预防的关联
许多提高代码质量的 lint 规则同时也减少了合并冲突频率。这不是巧合——使代码难以审查的模式同样使代码难以合并。
#三种机制的分层
三种机制形成递进关系:
- Lint 规则 在构建时捕获确定性的、可模式匹配的问题
- Skills 捕获需要上下文的判断性问题(这个复杂度合理吗?这是正确的抽象吗?)
- Hooks 将已证明价值的 skills 自动化
不要从 hooks 开始。从 skill 开始,验证它能捕获真实问题,然后再考虑自动化。
#Agent 仍然做不到的事
即使有了这三种机制,agent 仍然难以:
- 识别重构在当前规模下是否有可衡量的影响
- 判断一个模式是"正在向问题演进"还是"已经是问题"
- 知道何时记录信号留待以后,何时立即行动
这些需要人类判断。目标不是消除人类审查,而是通过更早捕获机械性问题来减少到达人类审查的问题数量。