技能、子智能体、MCP 还是钩子:该用哪个

10 分钟读完

K14
深入解析 · 工具与能力设计

技能、子智能体、MCP 还是钩子:到底该用哪个?

必须每次都发生的事用钩子;智能体需要触达一个它原本碰不到的系统,用 MCP;它需要知道你们怎么做一件它本来就会做的事,用技能;这项工作会淹没主上下文,用子智能体;只有每一轮都需要的东西才写进指令文件——而插件只是把以上任意几样打包发出去的方式,不是第六个选项。这条规则成立的原因,并不是大多数对比文章给出的那个。上下文成本——现有建议大多以它为轴——正在被工程手段消解;剩下的那根轴是谁来扣扳机。只有钩子的运行不取决于模型的选择;其余一切,无论你措辞多么强硬,都只是请求。

STEP 1

答案,写成一套可以照着走的流程。

按这个顺序提问。顺序是要紧的:第一个回答「是」的问题就决定了结果,而排在前面的问题,选错的代价是丢掉一项保证,而不只是多花几个 token。

# Stop at the first yes.
1. Must it happen every time, whatever the model thinks?
     yes -> HOOK          # lint after edit, block a path
2. Does the agent need a system it cannot reach today?
     yes -> MCP server    # database, tracker, Slack
3. Would the work bury the main thread in output
   you will never read again?
     yes -> SUBAGENT      # wide search, log triage
4. Is it a procedure or reference the agent needs
   only for some tasks?
     yes -> SKILL         # release checklist, API guide
5. Does every turn of every task need it?
     yes -> AGENTS.md / CLAUDE.md / rules
6. Will another repo or team need the same setup?
     yes -> wrap the above in a PLUGIN

同样这六样,并排放在一起。「由谁触发」这一列,正是本文其余部分要讲的。

机制
它添加了什么
由谁触发 · 代价
指令文件
常驻上下文:构建命令、约定、「绝不要做 X」。
始终加载。全文随每一次请求发出,所以它是唯一一处体积仍然是主要代价的地方。
技能
模型照着就能做、但不知道你们版本的流程或参考资料。
模型按描述匹配触发——或由你点名调用。描述每次请求都在;正文只在使用时载入。
MCP 服务器
能力:一条连接、一份凭据、在另一个系统里产生的效果。
模型,一次一个工具调用。在 Claude Code 中,名称在启动时载入,完整 schema 推迟到需要时才载入。
子智能体
第二个上下文窗口,替你做嘈杂的活,再交回一份摘要。
模型或你——取决于所用的框架(见 STEP 4)。它的 token 花在另一个窗口里。
钩子
你的代码,在生命周期事件上运行:工具调用之前、编辑之后、会话结束时。
框架,在事件发生时。除非它返回输出,否则不占上下文。
插件
没有新东西:上面五样的一个打包,一步安装。
由其内容各自的触发方触发——包括钩子,这正是安装插件是一项信任决定的原因。
STEP 2

真正的那根轴,是谁来扣扳机。

大多数对比按内容是什么来分类——知识、能力、自动化。这种分法没错,却几乎没用,因为人们实际犯的错,并不是把知识放错了格子,而是把一项要求放进了一个只能交付建议的地方。

  • 始终。指令文件每一轮都会被读到。它始终在场,但这不等于始终被遵守;模型会把它和窗口里的其他一切放在一起权衡。
  • 模型的判断。技能在模型判定你的任务匹配其描述时载入。MCP 工具在模型决定调用它时运行。在 Claude Code 和 Cursor 中,子智能体在模型判定任务适合它时启动。三种不同的机制,同一个触发器:一次预测。
  • 一个事件。钩子触发,是因为框架走到了 PreToolUse 或 PostToolUse,而不是因为谁选择了它。它是这张清单上唯一一个模型既不能选择、也无法跳过其触发的机制。
  • 一个人。敲下 /deploy、@ 提及一个子智能体、或点名一项技能,都把扳机从模型手里挪到了你手里。Claude Code(disable-model-invocation: true)和 Codex(allow_implicit_invocation: false)都允许你把一项技能设为只能由人调用。

Anthropic 自己的功能指南把后果说得毫不含糊:写在 CLAUDE.md 或技能里的「绝不要编辑 .env」这类指令,「是请求,不是保证」,而一个拦下这次编辑的 PreToolUse 钩子才是强制执行。凡是带合规、安全或数据丢失色彩的事,这句话就是全部的决定。如果得知模型在五十次运行里漏掉过一次会让你不高兴,那么决定它是否运行的,就不能是模型。

反过来同样要紧。钩子不会推理。「每次编辑后运行格式化工具」是钩子;「把 linter 抱怨的问题都修好」是技能,因为决定怎么修是判断。常见的好配置是两者兼用:一个 PostToolUse 钩子运行 linter 并把输出回传,一项技能说明拿这些输出该怎么办。

STEP 3

上下文成本曾经决定这件事,如今大体上不再是了。

那条耳熟能详的建议——「别把流程包成 MCP 服务器,每个工具定义都会待在每一次请求里」——在写下的时候是对的。此后,各家框架已经把这笔成本从几乎所有地方挪走了,只剩下始终加载的那一档。

  • MCP 的 schema 被推迟载入。Claude Code 的文档现在写明工具搜索默认开启:会话开始时只载入工具名称和服务器说明,完整的 JSON schema 留在窗口之外,直到 Claude 需要某个具体工具。一台闲置的服务器几乎不花钱。
  • 技能正文被推迟载入。Claude Code 和 Codex 都先载入技能的名称和描述,只在它被选中时才载入正文;Cursor 则把它的技能描述为「在相关时」载入——这就是智能体技能里讲的渐进式披露。
  • 子智能体把 token 花在别处。它们总量上更贵——Codex 的文档直说子智能体工作流比可比的单智能体运行消耗更多 token——但这笔花销落在一个用完即弃的窗口里,这正是要点所在。
  • 钩子不花任何上下文,除非它返回输出。一个把 400 行 lint 报告打进对话的钩子,已经把自己变成了一份昂贵的指令文件。
  • 指令文件是例外。它的全文在会话开始时载入,并留在每一次请求里。Anthropic 的建议是把 CLAUDE.md 控制在 200 行以内,把参考资料移进技能——这是唯一一档行数仍是主要代价的地方。

留在每一次请求里的,是一串描述——技能描述、子智能体描述、工具名称。这把失败模式从成本转到了检索:描述含糊,模型就会挑错技能或一个都不挑,这正是工具文档与可发现性所处理的问题。这也给出了规模上的提示:子智能体描述合计超过 15,000 个 token 时,Claude Code 会在启动时发出警告。

STEP 4

同一个名字,在不同工具里不代表同一个触发器。

既然要紧的是触发器,就要逐个框架去核对。名称已经趋同,行为并没有。

  • 子智能体。Claude Code 在任务匹配某个子智能体的 description 时会自行委派,Cursor 也说它的智能体基于同样的依据「主动委派」。Codex 的本地版本则只在收到直接请求、或项目文件或技能中有指令要求时才会派生智能体。把同一份 code-reviewer 定义在它们之间搬来搬去,它就从模型会主动伸手去用的东西,变成了没人吩咐就没人调用的东西。
  • 钩子是确定的,但不是完整的。它们每次都会在自己的事件上触发,但只针对经过钩子的那些路径。Codex 写明,网页搜索这类托管工具不走本地钩子路径,并告诉你要「把工具钩子当作一道有用的护栏,而不是完整的强制边界」。Cursor 通用的 preToolUse 在 schema 里接受 ask,却并不执行它。钩子保证自己会运行,不保证自己能看到一切。
  • 继承随子智能体一起来。在 Codex 中,子智能体继承父级的沙箱与审批设置,自定义智能体文件无法替换它们;在 Cursor 中,它们继承全部工具,包括 MCP。隔离的是上下文,不是权限——这道缝隙在子智能体究竟继承了什么里被拆开来讲。
  • 指令文件大体趋同。Codex 和 Cursor 读 AGENTS.md;Claude Code 读 CLAUDE.md。用哪个文件,是本页上最无关紧要的选择,正如AGENTS.md vs CLAUDE.md vs Cursor 规则 vs 技能所论证的。
STEP 5

五种看似合理、实则放错了位置的做法。

  • 把必须守住的规则写进指令文件。AGENTS.md 里的「绝不要推送到 main」一直管用,直到某次长会话里它不管用了。为了模型留着这句话,同时加一个拒绝该命令的 PreToolUse 钩子。
  • 把流程包成 MCP 服务器。如果服务器的工具只是返回一段指令,你造出来的就是一项多了一跳网络、一个要保持运行的进程和一道第三方信任边界的技能。MCP 的价值在于另一端确实有东西要去触达——智能体技能里的那条规则依然成立,只是如今的理由不再是 token。
  • 为一个人设开子智能体。一个读三个文件就作答的「资深评审」子智能体,换来的是一个你并不需要的额外上下文窗口,和一份你无法核查的摘要。人设是技能;子智能体是给那种阅读量会把你淹没的情形用的,正如子智能体所论证的。
  • 有副作用的技能仍允许模型调用。一个模型能靠匹配描述触发的 /deploy 技能,就是一次模型可以自己决定执行的部署。把它设为只能由人调用。
  • 把插件当技能来装。插件可以携带钩子,而钩子会以你的权限、在模型从未察觉的事件上运行你的命令。Claude Code 会合并来自所有来源的钩子,而不是让某一个覆盖另一个,所以插件的钩子会和你自己的一起触发。安装插件就是安装自动化;生命周期钩子与框架配置讲了为什么这条路径的变更管控最少,Agent Plugins 发布时没有信任模型则讲了这份共享格式漏掉了什么。
STEP 6

真实的活儿通常要两种机制,按触发器分工。

有用的做法,是把一项工作中与某个机制的触发方式相符的那部分交给它,而不是把整项工作硬塞进一个机制里。

  • Lint 与格式化。钩子:每次编辑后运行格式化工具和 linter,只回传失败项。技能:你们团队如何解决那些反复出现的问题。
  • 与数据库对话。MCP:连接本身,配只读凭据。技能:schema、命名上的怪癖,以及人人都会写错的那三条查询。如果写操作必须被拒绝而不只是被劝阻,就在这个 MCP 工具上加一个钩子。
  • 代码评审。技能:检查清单,点名调用。子智能体:在大型 diff 上运行它,让主线程拿到的是结论,而不是 40 个文件的阅读量。
  • 发布。流程用只能由人调用的技能;绝不能跳过的步骤用钩子,比如在本次会话中测试尚未通过时拒绝打 tag。
  • 第二个仓库。把这一整套打包成插件——并像审查一次 CI 变更那样审查它的钩子,因为它们就是 CI 变更。

这周就盘点一下手上的东西:把 AGENTS.md 或 CLAUDE.md 里每一句以「绝不」或「始终」开头的话列出来,逐条问一次:漏掉一次会不会造成伤害。会的,变成钩子;大段参考资料变成技能;需要连上活系统的,变成 MCP 服务器;总是把上下文塞满的任务,交给子智能体。然后在你实际使用的框架里逐个核对触发方式,因为同一份文件,在一个工具里会被模型调用,在另一个工具里却纹丝不动。按「必须由谁决定它发生——模型、事件,还是你」来选,正确的格子自然就出来了。

相关阅读:协议本身见什么是 MCP,把服务器一侧做好见MCP 工具设计,书面规则为何始终只是建议见指令层级,始终加载那一档背后的算术见上下文预算。