生命不息,折腾不止。Agent 贵不贵,不看你用哪个模型,看你把什么活儿塞进了主对话。

第六课我们把门禁和仪表盘装上了,账单能看见了。看见之后更心疼:让它查一个线上 500,它 grep 一遍日志,三万行进主对话;之后每一轮,这三万行都跟着重新算一遍钱。

问题不在模型贵,在于我把「查」和「想」混在了一个上下文里。这一课讲两件事:**subagent(子代理)**负责干脏活,Skill负责把流程固化。合起来就是一句话——让主对话只做决策,让打杂的活去别的地方算。

一、先算清这笔账:贵的不是模型,是上下文

大模型的计费是按 token 算的,而对话是复利:你这轮塞进去的东西,后面每一轮都要重新读一遍。

举个例子,我上周真实的场景:

  1. 主对话起步大概 8k token(系统提示 + CLAUDE.md + 前几轮);
  2. 我让它「查一下 nginx 为什么 502」,它 grep -rn "502" /var/log/nginx/,一下搜出三万多 token 的日志,全进了主对话;
  3. 接着分析、改配置、重启、再看日志……又跑了 20 轮。

那三万行,在剩下 20 轮里被反复读了 20 次。钱不是花在「看日志」上,是花在「每次都重看同一批日志」上。

官方的说法很直白:subagent 在自己的上下文窗口里干活,详细的输出留在它那边,只有相关的摘要回到主对话。这就是省钱的机制,也是它唯一真正值钱的地方。

但先说反面:subagent 不是免费的。社区里有人实测,同时开两个 subagent,token 消耗最高能到普通对话的 5.9 倍——因为他把本该自己干的活也派出去了,而且并行并没有让速度变快。还有人把 subagent 全钉在便宜模型上,账单降了三成多,但复杂任务的返工率上去了。

所以派活之前先过三道筛子:

情况 怎么办
输出会灌满屏幕(日志、搜索结果、测试输出、网页正文) 派给 subagent
结论一句话能说完,过程不需要我看 派给 subagent
需要来回改、需要我盯着一步步看 自己干

记住一句话:只派「输出很长、结论很短」的活。 反过来,你派出去的是一个需要来回讨论的任务,那是在花两份钱。

二、subagent 就是一个 Markdown 文件

不用装插件、不用写代码。在目录里放一个 .md,frontmatter 写清楚它是谁、什么时候上场、能碰什么工具,正文写它的系统提示词。

两个位置,两种作用域:

1
2
mkdir -p ~/.claude/agents      # 个人级:所有项目都能用
mkdir -p .claude/agents # 项目级:只在这个仓库生效,建议进 git

同名冲突时的优先级,从高到低:托管设置(企业) → --agents 命令行参数 → 项目 .claude/agents/ → 用户 ~/.claude/agents/ → 插件自带的 agents。

最小的一个能跑的 subagent 长这样:

1
2
3
4
5
6
7
8
---
name: log-hunter
description: 排查服务和容器日志里的报错。当用户提到线上报错、500、异常堆栈、日志里查不到原因时使用。
tools: Read, Grep, Glob, Bash
model: haiku
maxTurns: 25
---
你是一名线上故障排查员。你的任务是从日志里找出真正的根因,而不是复述报错。

常用字段就这几个,别贪多:

字段 作用 我的建议
name 唯一标识,小写加连字符 起个见名知意的名字
description Claude 靠它决定什么时候派活 写「什么时候用我」,不是「我会什么」
tools 工具白名单,不写就继承全部 能只给读工具就别给 Bash
disallowedTools 明确禁掉的工具 比 tools 更省事,禁 Write 就行
model haiku / sonnet / opus / inherit 打杂的一律 haiku
maxTurns 最多跑几轮就停 必填,防它自己绕圈
skills 启动时预加载哪些 skill 只在真需要时用,会占它的上下文
isolation 填 worktree 就在临时 git worktree 里跑 要并行改代码时才用

⚠️ 一个很坑的细节:Claude Code 只在会话启动时扫描 agents 目录。你在一个已经跑着的会话里新建了 .claude/agents/,它是不认的,得退出重开。我第一次折腾时对着「找不到这个 agent」愣了半天。

三、实战:写一个「日志排查员」

名字就叫 log-hunter,目标是只带回结论,不带回日志。关键是提示词里把输出格式钉死,不然它还是会贴一屏日志回来。

存成 ~/.claude/agents/log-hunter.md:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
---
name: log-hunter
description: 排查服务和容器日志里的报错。当用户提到线上报错、500、502、异常堆栈、日志排查时使用。
tools: Read, Grep, Glob, Bash
model: haiku
maxTurns: 25
---

你是线上故障排查员,只负责定位根因,不改任何文件。

工作方式:
1. 先用 Grep/Glob 缩小范围,再 Read 具体片段,不要整文件读。
2. Bash 只允许用只读命令(tail / grep / journalctl / docker logs / ss / df)。
3. 找到根因就停,不要继续漫游。

输出格式(严格遵守,总长度不超过 300 字):
- 现象:一句话
- 根因:一句话
- 证据:文件:行号 或 日志原文片段(最多 3 行)
- 建议:最多 2 条,写具体命令或配置项

禁止事项:
- 禁止贴超过 3 行的日志原文
- 禁止推测未在日志中出现的原因,不确定就写「证据不足」
- 禁止执行任何写操作

注意 tools 里我故意没给 Write 和 Edit。这不是靠提示词求它别改,是权限层面它根本没有这个手——第六课讲的「确定性控制」,在这里同样适用。

用起来就是一句话:

1
用 log-hunter 查一下 nginx 里 502 的原因,重点看 upstream 超时

Claude 会派活过去,界面上出现一行 log-hunter(排查 nginx 502),跑完只带回来那四行结论。

效果对比(同一个故障):

以前 现在
主对话新增 约 30k token(原始日志) 约 300 token(结论)
后续 20 轮每轮多读 30k × 20 0.3k × 20
用的模型 Opus 档跑全程 苦力给 Haiku,主对话只做决策

这就是「压成本」的真相:不是换个便宜模型硬扛,是把活分给对的人。

四、Skill:把「每次都贴一遍的流程」存成文件

subagent 解决「谁去干」,Skill 解决「怎么干」。判断标准很简单:同一段话你已经贴第三遍了,就该做成 Skill。

为什么不做进 CLAUDE.md?因为 CLAUDE.md 是每一轮全量加载的,你往里堆一千行部署流程,等于每轮对话都在为它付钱。而 Skill 平时只暴露一行 description(几十个 token),真正被调起来的时候,才把正文加载进上下文——官方叫这个「渐进式披露」。

拿我自己发博客的流程举例,建目录、写 SKILL.md:

1
mkdir -p ~/.claude/skills/deploy-blog
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
---
name: deploy-blog
description: 把 Hexo 文章发布到 Cloudflare Pages:上传、生成封面、构建、wrangler 部署、三项验收。当用户要求发布或部署博客文章时使用。
disable-model-invocation: true
allowed-tools: Bash Read
context: fork
---

## 当前改动

!`git status --short`

## 发布步骤

1. 确认 source/_posts/ 下新文章的 front-matter 没有 cover 字段
2. 运行 node scripts/gen-covers.js,检查输出里出现「已为「标题」生成封面」
3. 运行 npx hexo generate
4. source /root/.cf-env && wrangler pages deploy public --project-name kqnb-blog --commit-dirty=true
5. 验收三项:文章页 HTTP 200、封面 content-type 是 image/svg+xml、页面 title 包含文章标题

## 纪律

任何一步失败就停下,把真实报错原样贴出来。禁止假装成功,禁止编造验收结果。

这里几个字段值得单独说:

  • !`git status --short` :动态上下文注入。Claude 看到这段文字之前,Claude Code 会先把命令跑掉,把输出内联进来。所以它拿到的是「当前真实工作区」,不是它猜的。
  • disable-model-invocation: true:只允许我手动打 /deploy-blog 触发。发布这种事,不能让它自己想起来就干。
  • allowed-tools: Bash Read:调用这一轮里,这两个工具不用逐次确认;等你发下一条消息,授权就自动失效。不是永久放权。
  • context: fork:让这个 skill 在自己的子代理上下文里跑,主对话只收结果。构建日志几百行,不会污染主对话。

几个容易踩的点:

  1. .claude/commands/deploy.md 和 .claude/skills/deploy/SKILL.md 是等价的东西,都产生 /deploy。自定义命令已经合并进 Skill 了,老文件继续能用,但新东西建议用 Skill——它支持带辅助文件的目录。
  2. 正文要短。Skill 一旦加载,内容会在后续轮次里一直占着上下文,每一行都是重复开销。写「做什么」,别写「为什么」。
  3. 长资料放旁边。同目录下建 reference.md、scripts/,正文里写一句「需要时读 reference.md」。这样一千行的参考资料平时不花钱。
  4. description 决定它会不会被自动调用。description 加 when_to_use 加起来超过 1536 个字符会被截断,所以关键的触发场景写最前面。

五、三个开关,按收益从高到低

开关一:模型降档。 打杂的活挂 Haiku 档,主力模型只做决策和汇总。同代 Haiku 的输入单价通常只有 Opus 的五分之一左右(具体数字以官方定价为准,别信我这张嘴)。两种写法:frontmatter 里写 model: haiku,或者用环境变量 CLAUDE_CODE_SUBAGENT_MODEL 全局接管所有 subagent。

开关二:上下文隔离。 subagent 天生隔离;Skill 加 context: fork;纯查代码用内置的 Explore——它启动时连 CLAUDE.md 和 git 状态快照都不加载,就是为了快和便宜。反过来,如果你要它遵守项目里的规矩,就别用 Explore,用自己定义的 agent(或用 omitClaudeMd: true 精确控制加载哪些)。

开关三:减少往返。 这一个最土也最有效。一次把要求写全:输入是什么、输出格式、边界在哪、怎么算验收通过。你跟它来回猜三轮,等于三倍输入加累积上下文——比一开始多写两百字贵得多。

顺手列几个我踩过的反模式:

  • 给每个小任务都开 subagent,结果每次派活本身就要花钱;
  • 让 subagent 改代码,却不给它验收标准,它改完回来你重跑一遍,等于干了两遍;
  • 把 subagent 的中间过程打印回主对话,「省上下文」白省了;
  • 一口气并行开五个——并行省时间不省钱,改同一个仓库还会打架。

六、和前面几课串起来

这套东西不是孤立的:

  • 第六课的 Hooks 可以挂在 subagent 身上。agent 的 frontmatter 里就能写 hooks,给「手上权力更大」的 subagent 单独上一道门禁,比给它写十句「小心点」靠谱得多。
  • subagent 的 model 是成本的主旋钮。我自己的做法是走中转站(ai.aklibk.com),一个 key 同时挂着主力模型和便宜模型:主对话用强的,subagent 挂便宜的,切换只改一个字段,代码一行不动。这比「全站降级到便宜模型」聪明得多——难的地方照样想得清楚,打杂的地方省得干净。
  • 项目级的 .claude/agents/ 和 .claude/skills/ 一定要进 git。团队里每个人的 Agent 长得一样、干活方式一样,新人 clone 下来就自带一支队伍,不用口头传帮带。

七、验收清单

改完别急着说成功,对着这张表过一遍:

  • 主对话里 90% 的内容是「决策和结论」,不再是原始日志和搜索结果
  • 每个 subagent 的 tools 都是白名单,能不给 Bash 就不给
  • 每个 subagent 都写了 maxTurns
  • 每个 description 都说清了「什么时候用我」,而不是「我很厉害」
  • 所有「我每次都贴一遍」的流程,都变成了 Skill
  • 拿同一个任务跑两遍(改造前 / 改造后),对比主对话的 token 增量——别凭感觉,账要看数

到这里,一个 Agent 已经从「一个人硬扛」变成了「带了一支小队」。但下一课有个新麻烦:这支小队要是同时改同一个仓库,会互相踩脚。我一个人折腾那会儿就被 git 冲突教做过人。

生命不息,折腾不止。下一篇是 Claude Code 第八课:让三个 subagent 并行改同一个仓库还不打架——用 isolation: worktree 给每个 Agent 发一份独立的代码副本,改完再合并;顺带讲讲子代理之间怎么互相传话,把「一个人干三天」拆成「三个人干一天」。