给 agent 的 diff 设预算:把验收变成自动闸门
能写代码只是第一步,能产出可以合入的代码才是另一回事。现在一轮会话产生几百上千行改动很常见,OpenAI 官方也说自家 PR 量自 Q4 以来每周翻倍不止,代码一多,评审就变成瓶颈。[3] 问题的关键不在 agent 写得慢,而在人审不过来:改动一旦超过某个规模,任何人的注意力都不足以把它看全。
所以先别急着让 agent 写得更多,先给它的 diff 设一个预算。
把”完成”定义成可检查的清单#
agent 说”做完了”往往只是它自己觉得做完了。真正算数的完成,是能逐条拿出证据的状态。OpenAI 给 Codex 用户的核心建议正是:别停在”让它改”,要让它补测试、跑相关检查、确认结果、并在你接受之前先自己审查一遍。[4] 而”好”的标准可以写在提示词里,也可以写进 AGENTS.md,让每个新会话自动继承。[4]
我建议把验收收敛成一张三行的卡片,直接放进仓库说明文件:
- 校验:pnpm lint && pnpm test && pnpm typecheck 全绿
- 规模:机械改动不超过 800 行,复杂逻辑不超过 500 行;超了就拆成
可评审的阶段,先落最小的一块
- 禁改:对外接口、事件名、CLI 参数、配置项,改前先列清单给人确认
三行不是随手写的。第一行是行为底线,第二行直接抄自 OpenAI 自己仓库的规矩,第三行来自它对破坏性改动的定义。[2]
变更预算为什么是 800 行#
规则不是越多越好。OpenAI 维护 Codex 仓库时把”变更规模”单独列成一节:除非是机械改动,一次改动总行数不超过 800 行,复杂逻辑改动要压在 500 行以内;超出的部分先拆成可评审的阶段,找到最小且完整的一块先落地。[2] 这是他们的血泪经验,不是拍脑袋的数字:人类评审者能真正消化的大小有上限,超过之后错误率会快速上升。
对 vibe coding 来说这条规则更值钱。单个 agent 会话里发生的改动往往被当成一件事,但”一件事”和”一次合入”是两码事。让 agent 自己把大改动切成小步骤提交,比人拿到 2000 行的巨型 diff 后再要求返工便宜得多。
验收闸门要机械执行,别指望自觉#
规则写进 AGENTS.md 只是第一步,它还停留在”建议”层面。真正让规则生效的,是在 agent 声称完成的那一刻强制执行。Claude Code 的 Stop 事件专门干这个:主 agent 每次回复结束时触发,钩子可以阻止它停止,附上原因让它继续干活。[1] 官方文档里有个现成的套路——挂一个 Stop 钩子跑测试,测试不过就 exit 2 把 stderr 喂回给模型。[1] 下面这段是加了”变更预算”检查的版本:
# .claude/hooks/gate-stop.sh
INPUT=$(cat)
ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active // false')
if [ "$ACTIVE" = "true" ]; then
exit 0 # 已经在补救循环里,放行,避免死循环
fi
if ! git diff --quiet --exit-code; then
ADDED=$(git diff --numstat | awk '{a+=$1} END {print a+0}')
if [ "$ADDED" -gt 800 ]; then
echo "改动 $ADDED 行,超过 800 行预算。先拆小再收尾。" >&2
exit 2
fi
fi
if ! npm test >/tmp/gate-test.log 2>&1; then
echo "测试未通过:$(tail -5 /tmp/gate-test.log)" >&2
exit 2
fi
exit 0
{
"hooks": {
"Stop": [
{
"hooks": [
{ "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/gate-stop.sh" }
]
}
]
}
}
脚本里那个 stop_hook_active 不是装饰。Claude Code 在钩子连续阻止 8 次后会强制结束回合,官方明确要求先检查这个字段,避免卡在一个永远无法解决的条件上;补救循环里的第二次 Stop 直接放行,人还保有最终判断权。[1] 测试和行数检查也可以分挂成两个钩子,或把 lint 这类慢检查放进 async: true 的后台任务,让 agent 先继续写,结果回来再反馈。[1]
子代理和云上评审,同一套心智#
自己搭的规则能管住主会话,未必管得住子代理。好消息是 Claude Code 里 settings 文件的钩子同样在子代理内运行,子代理每次工具调用都会触发同一套钩子,输入里还带着 agent_id 和 agent_type,审计日志能分清是谁的动作。[1] 上一篇文章里让写代码的子代理各自落在独立 worktree,现在每个 worktree 里提交前都会先过这道闸,等于是把验收下沉到了每个并行 worker。
Codex 侧没有等价的 Stop 钩子,但提供 codex review:对未提交改动、基线分支 diff、单个 commit 或自定义评审指令做非交互审查。[5] 这条命令正好补上”合入前最后一道检查”的位置,可以手动跑,也可以写进 AGENTS.md 让 agent 收尾前自查。GitHub 上的 Codex Code Review 还支持从 AGENTS.md 读取自定义规则来约束审查,但官方说得很清楚:规则驱动的评审只是增量审查者,测试、分支保护和人工批准才是硬性执行。[3]
快速上手#
今天挑一个最容易失控的仓库做四件事:
# 1. 建脚本和配置(上面的代码,存成两个文件)
mkdir -p .claude/hooks && chmod +x .claude/hooks/gate-stop.sh
# 2. 先手动喂一次 stdin,验证脚本行为
echo '{"stop_hook_active": false}' | .claude/hooks/gate-stop.sh; echo "exit=$?"
# 3. 把三行验收卡片追加进 AGENTS.md / CLAUDE.md
printf '\n- 校验:pnpm lint && pnpm test 全绿\n- 规模:新增超 800 行先拆小\n' >> AGENTS.md
# 4. 新开会话,确认 /hooks 里能看到 gate-stop,再让它改一个小功能
头两次触发会有点吵,这是正常的:agent 会把超预算的改动自己拆掉,或停下来问你”是否接受拆成两次提交”。等这套流程救过你一次,再往闸门里加规则。记住评审规则的写法:先写有后果、不显然的约束,说清”违规是什么、安全路径是什么”,机械检查交给 CI,AGENTS.md 只留那些评审者会反复追问的问题。[3] 给 diff 设预算不是限制产出,是把产出压回人能负责的范围——agent 写多快由模型决定,合入多快应该由你来定。
Sources#
[1] https://docs.anthropic.com/en/docs/claude-code/hooks — Hooks reference — Claude Code Docs [2] https://raw.githubusercontent.com/openai/codex/main/AGENTS.md — openai/codex 官方仓库 AGENTS.md [3] https://developers.openai.com/blog/custom-code-review-rules-for-codex — Custom Code Review rules for Codex(OpenAI 官方博客) [4] https://learn.chatgpt.com/guides/best-practices — Best practices — Codex(ChatGPT Learn 官方指南) [5] https://learn.chatgpt.com/docs/developer-commands — Developer commands — Codex CLI 参考