5个Claude Hook模式:强制规则,杜绝Agent遗忘

这是今早我 ~/.claude/hooks/ 目录下的文件数:

$ ls ~/.claude/hooks/ | wc -l
160
$ ls ~/.claude/rules/ | wc -l
95
$ ls ~/.claude/skills/ | wc -l
75

大多数人只配一两个 Hook,然后抱怨 Claude “忘记”规则。
Claude 并不会忘记——它一开始就没有被强制遵守规则。
写在 Markdown 文件里的规则只是“建议”,而写在 Hook 里的规则是一堵“墙”。

下面是我日常开发中真正有效的 5 个 Hook 模式,每个解决一类用提示工程无法搞定的问题。


1. 成本闸门 Hook(PreToolUse 监听 Bash)

2026 年 6 月 15 日起,Anthropic 把 Agent SDK 的计费从订阅中分拆出来。无头模式 claude -p 调用会被记到 SDK 账单,而不是 Pro 订阅。如果你的 Agent 不小心在定时任务里触发 claude -p,你的账单会在一夜之间变形。

解法:在 PreToolUse 阶段拦截 Bash 操作,阻止这种模式。

#!/usr/bin/env bash
# @event: PreToolUse
# @matcher: Bash
PAYLOAD=$(cat)
CMD=$(echo "$PAYLOAD" | jq -r '.tool_input.command // ""')

if echo "$CMD" | grep -qE '\bclaude[[:space:]]+(-p|--print)'; then
  echo "BLOCKED. claude -p 的计费在 2026-06-15 之后已不属于订阅范围。" >&2
  echo "请改用交互式的 Agent 子任务。" >&2
  exit 2
fi

这个 Hook 在过去两周内触发了三次,每次都是我自己差点提交一个带 claude -p 的脚本。


2. 破折号驱逐 Hook(PreToolUse 监听 Write)

AI 生成的文本里到处都是长破折号(em dash)。编辑们一眼就能看出机器味。一个在 Write 操作时拦截长破折号的 Hook 能让输出听起来更有人味。

#!/usr/bin/env bash
# @event: PreToolUse
# @matcher: Write|Edit
PAYLOAD=$(cat)
CONTENT=$(echo "$PAYLOAD" | jq -r '.tool_input.content // .tool_input.new_string // ""')

if echo "$CONTENT" | grep -qF $'\xe2\x80\x94'; then
  echo "BLOCKED. 检测到长破折号。请改用句号或逗号。" >&2
  exit 2
fi

Agent 永远没法带着错误的标点投稿,因为写操作失败后它会原地重写。


3. 密钥泄露预先拦截 Hook(PreToolUse 监听 Bash)

Pre-push Hook 只能在推送时抓到密钥,但那时密钥已经进入了本地提交历史。在 PreToolUse 监听 Bash,当 git add 打算加入 .env* 或其他类似密钥形状的文件时,提前两步阻止泄漏。

#!/usr/bin/env bash
# @event: PreToolUse
# @matcher: Bash
PAYLOAD=$(cat)
CMD=$(echo "$PAYLOAD" | jq -r '.tool_input.command // ""')

SECRET_PATTERNS='\.env($|\.|/)|credentials\.json|\.pem$|\.key$|service-account.*\.json'

if echo "$CMD" | grep -qE "git[[:space:]]+add.*($SECRET_PATTERNS)"; then
  echo "BLOCKED. 拒绝将密钥文件加入 Git。" >&2
  echo "请先将文件加入 .gitignore,或者使用 --explicitly-allow-secret 参数。" >&2
  exit 2
fi

我的两个仓库根目录都有 .env.local,但它们永远进不了 Git,因为这个 Hook 在 add 落地之前就开火了。


4. 会话启动全景面板(SessionStart 事件)

SessionStart Hook 可以读取状态文件,告诉你两次会话之间发生了什么变化。我的面板会展示待办文档、agent-os 仓库的未提交改动、夜间安全公告,以及过去 24 小时内崩溃超过三次的 Hook。

#!/usr/bin/env bash
# @event: SessionStart

PENDING_DOCS=$(ls ~/.claude/cache/docs-pending/ 2>/dev/null | wc -l)
SECURITY_FLAGS=$(ls ~/.claude/cache/security/ 2>/dev/null | wc -l)
DRIFT=$(cd ~/repositories/agent-os && git status --porcelain 2>/dev/null | wc -l)

[ "$PENDING_DOCS" -gt 0 ] && echo "[DOCS-PENDING] 有 $PENDING_DOCS 个待办文档"
[ "$SECURITY_FLAGS" -gt 0 ] && echo "[SECURITY] 有 $SECURITY_FLAGS 条安全公告"
[ "$DRIFT" -gt 0 ] && echo "[REPO-DRIFT] agent-os 有 $DRIFT 个未提交文件"

exit 0

信息被动送达,不需要你记住去检查。每次会话打开,世界状态已经自动加载到 Agent 的视野里。


5. 复杂度上限守卫 Hook(PreToolUse 监听 Write)

这一条只对不断演进的环境有效。我给每类资产设了硬上限(95 条规则、160 个 Hook、75 个技能)。当尝试写入一条新规则导致上限被突破时,Hook 拦截写入,强制你先删除或合并一条既有规则。

#!/usr/bin/env bash
# @event: PreToolUse
# @matcher: Write
PAYLOAD=$(cat)
PATH_OUT=$(echo "$PAYLOAD" | jq -r '.tool_input.file_path // ""')

case "$PATH_OUT" in
  */.claude/rules/*.md)
    COUNT=$(ls ~/.claude/rules/*.md 2>/dev/null | wc -l)
    CAP=95
    if [ "$COUNT" -ge "$CAP" ] && [ ! -f "$PATH_OUT" ]; then
      echo "BLOCKED. 规则已达上限 $COUNT/$CAP。" >&2
      echo "请先合并或删除一条既有规则,再添加新规则。" >&2
      exit 2
    fi
    ;;
esac
exit 0

没有这个 Hook,我的规则集一个月内就会膨胀到 200 条,而且每条都没有分量。有了它,每条新规则都必须通过替换更弱的内容才能占位。


Hook 在实践中带来什么改变

一旦你拥有这种规模的 Hook,三件事就不再做了:

  • 你不再写那些 Agent 会默默忽略的规则。
  • 你不再在推送时才发现错误——那时修复成本最高。
  • 你不再跟 Claude 说“记住要做 X”,因为 X 已经在工具边界被强制执行了,不管模型是否记得。

这种思维转变,和当年 DevOps 从“记得正确部署”到“流水线不允许你错误部署”的转变如出一辙。Hook 就是 Claude Code 的流水线。

如果你有一个离不开的 Hook,欢迎在评论区分享。我特别好奇那些我还没碰过的部分——比如 PreCompact 和通知类 Hook。只有更多人分享那些真正触发过的模式,这个模式库才会越来越大。

类似文章