RSS

第 8 章 / 共 12 章

把重复劳动固化:命令、技能、子代理、钩子

约 10 分钟 更新于

8.1 四种工具,一个决策

当你第五次敲同样的一段提示时,就该把它固化了。Claude Code 提供了四种固化方式,新手最常见的困惑是”到底该用哪一个”。

用两个维度就能分清:它必须每次都发生吗?它需要常驻上下文吗?

第8章:四种固化方式的选择象限
图 8.1:注意左上角那一格——钩子是唯一确定性执行的。其余三种都依赖模型判断要不要用。凡是”绝不能漏”的动作,只有钩子能保证。
你要的东西用什么触发方式
一段你会反复输入的提示自定义命令 / 技能你输入 /名字
一套只在特定场景用的领域知识或流程技能你调用,或模型判断相关时自动调用
一件消耗大量读取的调查工作子代理你要求,或模型委派
一件必须每次都发生的动作钩子事件触发,不经模型判断

8.2 自定义命令:把常用提示存起来

在项目里建一个文件:

mkdir -p .claude/commands

.claude/commands/review-diff.md

---
description: 审查当前改动,只报正确性问题
argument-hint: [关注点]
allowed-tools: Read, Grep, Bash(git diff *)
---

审查当前的 git diff。$ARGUMENTS

只报告以下两类问题:
1. 正确性缺陷(会导致错误行为的)
2. 与需求不符的地方

不要提风格建议,不要提"可以考虑"的优化。
每条问题必须指出具体文件和行号。

之后在会话里:

/review-diff 重点看并发安全

frontmatter 里几个字段的作用:description 决定它在命令列表里怎么显示;argument-hint 提示参数格式;allowed-tools 限定这条命令能用的工具;model 可以指定用哪个模型跑。正文里 $ARGUMENTS 接收全部参数,也可以用 $1$2 取位置参数。

子目录会形成命名空间,例如 .claude/commands/deploy/staging.md 对应 /deploy:staging

8.3 技能:按需加载的专业知识

技能和自定义命令的形态很像,区别在于加载时机和适用体量。技能放在自己的目录里,可以携带附属文件,并且只在被用到时才进入上下文:

.claude/skills/db-migration/
├── SKILL.md
└── examples/
    └── sample-migration.sql

SKILL.md

---
name: db-migration
description: 编写和审查数据库迁移脚本。当任务涉及 schema 变更、加索引、数据回填时使用。
allowed-tools: Read, Write, Edit, Bash(make migrate*)
---

## 本项目的迁移规范

1. 每个迁移必须成对:`up``down`
2. 加索引一律用 `CREATE INDEX CONCURRENTLY`,不允许锁表
3. 数据回填必须分批,单批不超过 5000 行
4. 迁移文件命名:`YYYYMMDDHHMM_描述.sql`

## 检查清单

- [ ] down 脚本能完整回滚 up
- [ ] 在超过百万行的表上估算过执行时间
- [ ] 不包含 DROP COLUMN(改为两步发布)

description 这一行是最关键的:模型靠它判断当前任务是否需要加载这个技能。写得具体(“当任务涉及 schema 变更、加索引、数据回填时”)比写得笼统(“数据库相关”)触发得准得多。

什么时候用技能而不是 CLAUDE.md? 判据很简单:这些内容是不是每个任务都需要?如果一个月里只有三次任务碰数据库,那它就不该每次会话都占着上下文。

8.4 子代理:给调查工作单独开一个上下文

.claude/agents/codebase-explorer.md

---
name: codebase-explorer
description: 在大型代码库里做只读调查,返回摘要而非文件内容
tools: Read, Grep, Glob
model: haiku
---

你负责在代码库里做定向调查。

规则:
- 只读,不得修改任何文件
- 返回结果必须是摘要:文件路径列表 + 每处的一句话说明
- 绝不要把整段文件内容贴回来
- 如果匹配超过 20 处,先给分类统计,再给最相关的 10 处

三个设计要点:

  • tools 只给只读工具,从机制上保证它不会改东西;
  • model: haiku 让这类机械工作用更便宜的模型;
  • 正文里明确要求”返回摘要而非内容”,这正是子代理的价值所在。

/agents 可以查看和管理当前可用的子代理。

8.5 钩子:唯一确定性的那一档

钩子在生命周期的特定时刻触发,不经过模型判断。典型用途:编辑后自动格式化、提交前强制跑检查、阻止对特定文件的写入。

配置写在 settings 文件里:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write \"$CLAUDE_FILE_PATH\""
          }
        ]
      }
    ]
  }
}

钩子脚本从标准输入拿到一个描述本次事件的 JSON,可以从标准输出返回 JSON 来影响后续行为。退出码有约定:0 表示成功,2 表示拦截这次操作。

一个保护敏感文件的最小例子:

#!/bin/bash
# .claude/hooks/protect.sh
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

case "$FILE" in
  *.env|*/secrets/*|*/.git/*)
    echo "拒绝写入受保护文件:$FILE" >&2
    exit 2
    ;;
esac
exit 0

8.6 一个真实的组合

假设你的团队想做到”每次改动都自动格式化,每次提交前都跑 lint,数据库迁移要按规范写,代码调查不许污染主上下文”:

需求用什么为什么不用别的
改完自动格式化钩子(PostToolUse)必须每次发生,不能靠模型记得
迁移脚本规范技能只在少数任务用到,不该常驻
代码调查子代理需要独立上下文
“审查当前 diff” 这句话自定义命令只是提示复用,不需要更重的机制

8.7 常见坑

8.8 本章练习与检查点

你现在的成果:你的项目现在自带一套工作方式。新同事 clone 下来,Claude Code 就已经知道该怎么在这个仓库里干活了。

广告位 · Multiplex 关联广告