我明明写了 Rules,为什么 Claude Code 还是会犯错?
在前几篇文章里,我一直在整理自己使用 Claude Code 的经验。
从最开始的 Prompt,到后来的 CLAUDE.md,再到 Rules,我的目标其实一直很简单:让 Claude Code 在项目里表现得更稳定。
一开始我以为,只要把需求讲清楚,Claude Code 就能少犯很多错。后来发现确实如此。任务目标、项目背景、限制范围、验收标准写得越清楚,它越不容易乱跑。
但在真实项目里,这还不够。
因为很多规则不是某一次任务才需要,而是整个项目长期都需要。
比如:
- 不要改敏感配置
- 不要顺手重构无关代码
- 修复前先分析原因
- 修改后必须验证
- 命令连续失败后不要一直重试
- 不确定时先问,不要自己猜
这些内容每次都手动重复,非常低效。
于是我开始写 Rules。
1. Rules 能解决什么,不能解决什么
Rules 的效果是明显的。
它能把一些稳定要求沉淀下来,让 Claude Code 每次进入项目时都能看到。以前 Claude Code 的行为有点随机,同样一个任务,今天可能非常谨慎,明天可能非常激进。
写了 Rules 之后,它做事更像同一个人了。
但 Rules 不是万能的。
我后来发现,Claude Code 还是会出现一些问题:
- 交付时说“已经完成”,但没有提供验证证据
- 命令失败后继续尝试类似命令
- 工具返回异常结果后没有停下来分析
- 上下文变长后忘记某些关键限制
- 对敏感文件和危险操作不够谨慎
最开始我以为,是 Rules 写得不够细。
于是我继续补规则。
不能动哪些文件,什么操作必须确认,失败几次要停下来,交付前必须跑测试,哪些情况不能直接说完成。
但规则越写越多,我越发现一个问题:
Rules 解决的是“模型倾向”,不是“系统强制”。
你在 Rules 里写“不要动 .env”,Claude Code 会更谨慎,但这不代表它永远不会碰。
你在 Rules 里写“交付前必须验证”,Claude Code 会更倾向于验证,但这不代表它每一次都会真的完成验证。
你在 Rules 里写“失败三次要停下来分析”,它可能会遵守,但也可能在复杂任务里滑过去。
所以我后来对 Rules 的定位变了。
Rules 适合定义工作习惯。
但不适合单独承担安全边界和强制检查。
这时候就需要 Hook。
2. Hook 的核心价值:不靠模型自觉
Hook 和 Rules 最大的区别在于:
Rules 是写给模型看的。
Hook 是写给系统跑的。
Rules 告诉 Claude Code:“你应该怎么做。”
Hook 则是在 Claude Code 生命周期的某个节点自动触发:“到这里了,就执行这个动作。”
这点非常关键。
因为模型是会忘的,也会误判,还会在上下文很长时忽略一部分规则。但 Hook 不是靠模型想起来才执行,而是由事件触发。
也就是说,Hook 更适合处理那些不能靠自觉完成的事情。
比如:
- 工具执行前拦截危险命令
- 修改文件后自动格式化
- 工具失败后注入提醒
- 任务结束后检查是否有验证证据
- 会话开始时恢复上下文
- 上下文压缩后重新注入关键规则
这些动作如果全部写进 Rules,也不是不行,但稳定性会差一些。
因为 Rules 的前提是:模型得记得,并且愿意执行。
Hook 的前提是:事件发生,自动触发。
3. Hook 可以按生命周期理解
Hook 的事件名称看起来比较多,但不用一开始就全部背下来。
我后来更喜欢按 Claude Code 的任务生命周期理解 Hook。
大致可以分成四类。
3.1 开始前:SessionStart / UserPromptSubmit
这一类 Hook 发生在任务真正执行之前。
SessionStart 可以在会话开始或恢复时触发。
UserPromptSubmit 可以在用户提交提示词之后、Claude 正式处理之前触发。
适合做什么?
适合做初始化和输入检查。
比如:
- 加载项目上下文
- 注入关键规则
- 检查当前工作目录
- 提醒当前项目限制
- 判断用户需求是否涉及高风险操作
- 在执行前要求补充必要信息
这类 Hook 解决的是“任务开始前先摆正方向”。
很多时候 Claude Code 跑偏,不是因为它不会写代码,而是它一开始就没有站在正确上下文里。
如果某些上下文每次都需要提醒,那就可以考虑放到开始前的 Hook 里。
3.2 执行前:PreToolUse
PreToolUse 是我认为最重要的一类 Hook。
它会在工具执行前触发。
比如 Claude Code 准备执行 Bash 命令,准备编辑文件,准备读取某些内容,这时候 PreToolUse 可以先检查这个操作是否安全。
适合做什么?
适合做安全拦截和权限判断。
比如:
- 禁止读取或修改 .env
- 禁止触碰密钥文件
- 禁止修改生产配置
- 禁止执行 rm -rf
- 禁止 force push
- 禁止 reset hard
- 对高风险操作要求用户确认
这些场景不应该只写在 Rules 里。
因为一旦出错,代价太高。
安全边界应该尽量放在工具执行前,而不是事后发现。
换句话说:
危险操作,能在 PreToolUse 拦,就不要只靠 Rules 提醒。
3.3 执行后:PostToolUse
PostToolUse 会在工具执行成功后触发。
这一类 Hook 适合做重复动作和结果处理。
比如:
- 代码修改后自动格式化
- 命令执行后记录日志
- 工具返回空结果时提醒
- 工具返回异常时注入警告
- 编辑文件后触发检查
- 统计工具调用次数
这类动作有一个共同点:它们不需要模型创造性思考。
格式化就是格式化。
记录就是记录。
失败检测就是失败检测。
越是这种固定、重复、机械的动作,越适合 Hook。
因为它不应该依赖模型“记得”。
3.4 结束后:Stop / StopFailure / SessionEnd
这一类 Hook 发生在 Claude Code 准备结束响应或会话时。
我自己最早真正用起来的,就是 Stop Hook。
原因也很简单:Claude Code 太喜欢说“已经完成”了。
但“已经完成”到底是什么意思?
是代码改了?
是测试跑了?
是构建通过了?
是问题复现过了?
还是只是它觉得自己应该完成了?
这就是 Stop Hook 的价值。
它可以在 Claude Code 结束任务前,要求它做最后检查。
比如:
- 是否有验证证据
- 是否说明改了哪些文件
- 是否跑了必要命令
- 是否有失败项
- 是否有未验证内容
- 是否需要提醒用户下一步手动确认
这类 Hook 不会让 Claude Code 变成完美工程师,但能减少“看起来完成了,其实没完成”的情况。
4. 哪些地方应该用 Hook,甚至必须用 Hook
我的判断标准很简单。
如果只是希望 Claude Code 更稳定,可以先写 Rules。
如果不能接受 Claude Code 忘记,就应该考虑 Hook。
具体来说,我觉得这几类场景非常适合用 Hook。
4.1 安全边界
安全边界优先级最高。
比如:
- 敏感文件
- 密钥
- 生产配置
- 数据库结构
- 删除操作
- Git 高风险命令
- 外部网络请求
这些操作一旦出错,不是“下次注意”就能解决的。
所以它们不适合只放在 Rules 里。
正确思路应该是:
Rules 里写原则。
Hook 里做拦截。
比如 Rules 可以写“不要修改敏感配置”,但 PreToolUse 里应该真正判断:当前工具调用是否试图访问敏感文件。如果是,就拦住。
4.2 交付验证
交付验证也非常适合 Hook。
Claude Code 最常见的问题之一,就是把“代码改完了”当成“任务完成了”。
但在真实开发里,完成至少应该包含验证。
比如:
- build 是否通过
- test 是否通过
- lint 是否通过
- 类型检查是否通过
- Bug 是否复现并确认修复
- 修改范围是否符合预期
这些检查不应该只靠 Claude Code 最后自觉汇报。
Stop Hook 可以作为最后一道验收门。
它不一定替你完成所有测试,但至少可以要求 Claude Code 明确说明:验证了什么,没验证什么,结果是什么。
4.3 重复动作
重复动作也很适合 Hook。
比如:
- 自动格式化
- 自动记录日志
- 自动统计失败次数
- 自动通知
- 自动恢复上下文
- 自动注入规则
这些动作本身不复杂,但很容易被忘。
而且越是高频操作,忘一次的成本就越高。
如果一个动作每次都要做,那就不要每次都提醒模型做。
把它变成 Hook。
4.4 模型容易自信但不可靠的地方
还有一类问题比较隐蔽。
不是 Claude Code 不会做,而是它有时候会错得很自然。
比如:
- 命令失败了,但它继续尝试类似方案
- 工具返回空结果,它当成正常结果继续分析
- 测试没跑完,它误以为成功
- 修改了多个文件,但没有检查影响范围
- 上下文压缩后,忘了前面的关键限制
这类问题如果靠人工盯,会很累。
Hook 可以在这些节点加入提醒或拦截。
它不能保证所有问题都消失,但可以让错误不那么容易一路滑到底。
5. 我建议先从 3 个 Hook 开始
如果你还没用过 Hook,不建议一开始就写很多。
可以先从 3 个最实用的开始。
第一个:Stop Hook
解决问题:
Claude Code 说完成了,但你不知道它到底有没有验证。
适合动作:
- 要求输出验证证据
- 要求列出修改文件
- 要求说明执行过的命令
- 要求说明未验证内容
- 要求标记风险点
这是最适合新手开始的 Hook。
因为它直接解决“已完成不可信”的问题。
第二个:PreToolUse Hook
解决问题:
Claude Code 可能执行高风险操作。
适合动作:
- 拦截敏感文件
- 拦截危险命令
- 拦截强制推送
- 拦截删除操作
- 对高风险操作要求确认
这是安全边界的第一道门。
如果你的项目里有 .env、密钥、生产配置,建议尽早加。
第三个:PostToolUse Hook
解决问题:
Claude Code 执行完工具后,可能忘记处理后续动作。
适合动作:
- 自动格式化
- 自动记录工具结果
- 检测失败输出
- 检测空结果
- 统计工具调用次数
- 触发后续检查
这个 Hook 适合把重复动作自动化。
6. 结论:Rules 是提醒,Hook 是刹车
我现在不会说 Rules 不重要。
恰恰相反,Rules 很重要。
它能让 Claude Code 形成稳定的工作习惯,让它知道项目里应该怎么做、不应该怎么做。
但 Rules 不能替代 Hook。
因为规则是提醒,Hook 是系统动作。
当一件事只是“希望它做得更好”,Rules 足够。
当一件事是“不能接受它忘记”,就应该考虑 Hook。
尤其是安全边界、交付验证、重复动作和高代价错误。
这些地方不能只靠模型自觉。
Claude Code 很强,但它不是永远可靠的执行者。真正稳定的用法,不是期待它每次都完美发挥,而是把关键节点变成流程,把高风险动作放进拦截,把必须验证的事情变成系统检查。
我以前总想让 Claude Code 更听话。
后来我发现,更重要的是让它更可控。
如果你已经开始写 Rules,下一步可以试试 Hook。
先别追求复杂。
先问自己一个问题:
我的项目里,哪件事最不能靠 Claude Code 自觉?
然后,把它做成 Hook。
更多推荐


所有评论(0)