内容来源:Nick Liu - Software Engineer。https://nick-liu.com/posts/claude-code-skills-hooks-subagents/
原题:Hooks are guarantees, skills are knowledge, subagents are other people.
原发布时间:2026-07-15
Claude Code 提供四个放置自定义配置的位置。选错层级,就要在可靠性或 Token 上付出代价。这是作者根据自身配置编写的决策指南,连踩过的坑也一并保留。


我的 Claude Code 配置现在包含两个 Hooks、十项 Skills 和三个自定义子智能体,其中大多数最初都被放在了错误层级。模型十次有九次能够遵循的指令,曾经一直写在提示词里,直到我接受一个事实:十次有一次失败,就意味着我每天都会输一次。反复粘贴到对话中的工作流变成了 Skill;原本不断消耗最昂贵模型配额的大批量工作,则变成了一支使用廉价模型的智能体队伍。功能相同,失败模式却完全不同。
驯服 Claude Code 会话 · 第 6 部分,共 6 部分: 1 · 2 · 3 · 4 · 5 · 6
使用 Claude Code 2.1.x · macOS 测试
四个层级
Claude Code 有四个扩展点,分别回答四个不同问题:
| 层级 | 它回答的问题 | 由谁决定运行 |
|---|---|---|
CLAUDE.md |
模型在这里应该始终知道什么? | 没有人;它始终位于上下文中 |
| Hook | 什么事情必须每次发生,绝无例外? | 由 Shell 脚本确定性决定 |
| Skill | 在这里,我们应该如何做 X? | 模型在判断 X 与当前任务相关时决定 |
| 子智能体 | 什么工作值得拥有独立上下文、工具或模型? | 模型主动委派,或由你提出要求 |
最重要的是最后一列。我每次把配置放错位置,归根结底都是因为忽略了究竟由谁决定它是否运行。
Hooks:当一件事必须每次发生
Hook 是在某个事件发生时触发的脚本,例如会话开始、工具调用前或工具调用后。整条路径中没有模型判断,因此它是唯一能够做出承诺的层级。
我的测试用例是会话命名。本系列第 2 部分介绍了 Hook 本身;简单来说,我希望每个会话都以项目和分支命名,以便日后找到。把它当成习惯时,这项做法逐渐松懈;作为提示词指令,它大多数时候有效,而“大多数时候有效”恰恰会制造一堆没有名称的会话。改成 SessionStart Hook 后,每次会话都会运行,包括通过 SSH 启动的会话,以及那些根本不向我显示提示词的工具启动的会话。
由此得出的规则是:如果漏执行一次就会令你不满,就不能依赖模型判断。 命名、通知、权限关卡、提交前 Lint,全都属于 Hook 的领地。
Skills:当它是知识,而非保证
Skill 是一个指令文件夹;当任务看起来相关时,模型会加载它。如果一套操作知识常驻上下文会让 CLAUDE.md 膨胀,Skill 就是正确归宿。
我的配置中最清晰的例子就是这个博客。写作约定、Shortcode 规则、来源政策、发布前检查清单,加起来大约 200 行。它们只在撰写文章时有用,而写文章只占我全部会话的一小部分。改成 Skill 后,只有真正需要的那一刻才产生成本。我的 Dotfiles 仓库也经历了同样的迁移:其中的 CLAUDE.md 只保留一份简短的硬性规则清单,再指向六项 Skills 来提供详细工作流。
陷阱也可能出现在相反方向。Skill 是否触发,取决于模型对相关性的判断,因此 Skill 只是包装良好的建议。每当我发现自己在 Skill 描述中写下 MUST,这通常就是一项信号:它其实应该放进 Hook。
子智能体:当工作需要独立上下文或更便宜的模型
子智能体在单独的上下文窗口中运行,拥有自己的工具权限,以及——这也是改变我用量账单的关键——自己的模型。
我的主循环使用能力最强的模型,但配额总是迅速耗尽,因为这个模型同时还在执行 Grep 搜索、批量编辑和检查清单验证。于是,我在 ~/.claude/agents/ 中定义了三个智能体:一个使用中档模型的批量工作者、一个同样使用中档模型的侦察员,以及一个使用最便宜模型的验证员。根据 Anthropic 的模型定价,最便宜层级的每 Token 成本只有顶级模型的十分之一。本文本身的八项发布前检查就由廉价验证员完成;该检查生成的会话记录也从未进入主上下文窗口,这就是第二项不那么显眼的收益。
配置过程中遇到一个坑:智能体定义会在会话开始时注册。我创建文件后,尝试在同一会话中向它们分派工作,结果收到“agent type not found”。重启后,它们就出现了。
用流程图做决定
flowchart TD
Q1{"是否必须<br/>每次都发生?"} -- 是 --> H["Hook<br/>(确定性脚本)"]
Q1 -- 否 --> Q2{"是否在<br/>每次会话中都需要?"}
Q2 -- 是 --> C["CLAUDE.md<br/>(始终位于上下文中)"]
Q2 -- 否 --> Q3{"是否属于模型应该<br/>按需加载的知识?"}
Q3 -- 是 --> S["Skill<br/>(相关时加载)"]
Q3 -- 否 --> Q4{"是否需要隔离上下文、<br/>不同工具或更便宜的模型?"}
Q4 -- 是 --> A["子智能体<br/>(独立上下文与模型)"]
Q4 -- 否 --> P["直接放进提示词"]
下面是我自己配置中的实际示例,每个分支各举一个:claude-name-session 必须每次运行,所以它是 Hook。“使用 yadm,不要用 Git”适用于每个 Dotfiles 会话,因此写在该仓库的 CLAUDE.md 中(参阅记忆文档)。博客约定按需加载,所以是 Skill。检查清单验证需要便宜模型和用完即弃的上下文,因此交给子智能体。而一次性的“重命名这个变量”,上面任何机制都不需要。
经验总结
• 选择层级时应该问“由谁决定它运行”,而不是“配置放在哪里”。Hooks 通过代码作出决定,Skills 和委派通过模型判断作出决定,CLAUDE.md 则根本不需要决定。
• 如果漏执行一次就会让你烦恼,它就是 Hook。在 Skill 描述中写 MUST,说明你可能选错了层级。
• Skills 适合携带成本高、加载成本低的知识。如果它适用于每次会话,就升级到 CLAUDE.md;如果只适用于一类任务,就继续保留为 Skill。
• 子智能体既是上下文功能,也是定价功能。把机械工作路由给便宜模型,把昂贵上下文留给需要判断的任务。
• 智能体定义在会话开始时加载。创建、重启,然后再分派。
参考资料
• Claude Code Hooks 文档
• Claude Code Skills 文档
• Claude Code 子智能体文档
• Claude Code 记忆(CLAUDE.md)文档
• Anthropic 模型定价
• 文中引用的配置:claude-name-session(SessionStart Hook),以及 Dotfiles 中的 Skills 布局;~/.claude/agents/ 智能体队伍已在这台机器上创建并测试