目录

本文基于一次为期一周的 Claude Code Skill 优化实践,涉及 prd-analysissystem-designautoforge 三个生产级 Skill,覆盖从 token 级测量到实际代码改动的完整流程。所有数字均来自真实 JSONL session 文件,已做夸张系数修正。

为什么要专门讲 Skill 的成本?

通用 LLM 省钱文章通常讲的是"上下文剪裁、cache 热身、模型降档"。这些对 Skill 也成立,但 Skill 执行环境有几个结构性差异

  1. 长会话 + 深调用栈。Skill 内会派发多个子代理(subagent),每个子代理又可能调起自己的工具循环。一次派发 = 一个独立的对话上下文,子代理之间不共享 prompt cache
  2. 主代理的 context 一旦被撑大就全程 cache_read。Skill 会话常有 15–20 个主代理轮次;任何文件一旦进了主代理 context,就会在每一轮以 cache_read 计费。
  3. 档位由谁决定含糊不清。subagent_type 是内置代理(如 Explore)会强制某个档位,而 general-purpose + 显式 model 才由 Skill 控制。
  4. 输出 token 严重被低估。Sonnet 的 output 价格是 cache_read 的 50×;Opus 是 50×。Skill 作者凭直觉会优化"少读文件",却忽略"少写 prompt"这条更大的杠杆。

以下 6 条是从实测中提炼的、能直接改动 Skill 文件即生效的原则。


第 0 条:算清账是一切优化的前提

在谈任何优化之前,先学会正确计算一次 Skill 会话的成本。

陷阱:按 JSONL 行数累加会双倍计费

Claude Code 把一个 assistant message 的多个 content block 拆成多行写入 JSONL,但每行都带相同的 usage 字段。按行累加 = 每条 message 被计算 2–4 次。

正确做法:按 message.id 去重后再累加 usage

seen = set()
for line in open(jsonl_path):
    obj = json.loads(line)
    if obj.get("type") != "assistant": continue
    mid = obj["message"]["id"]
    if mid in seen: continue
    seen.add(mid)
    # 此时 usage 才是这条 message 的真实值

我在第一次诊断时就踩了这个坑,报出 $57.99,去重后是 $17.12,差 3.4×。

定价直觉(Claude 4 系列,$/1M tokens)

档位inputcache_createcache_readoutput
Opus1518.751.5075
Sonnet33.750.3015
Haiku11.250.105

记住两个比值:

  • output ≈ 50× cache_read(同档位内)
  • Opus ≈ 5× Sonnet ≈ 15× Haiku(output 维度)

陷阱:传输层重试的成本观感偏差

API 超时 → Claude Code 自动重传。重传不计费 output tokens(按 200 OK 结算才计费)。所以"它死循环了 25 分钟"的成本感受是错的——那是时间的浪费,不是的浪费。定位到"哪里烧了钱"时要区分这两种浪费。


第 1 条:任务—档位对齐,别偷懒用默认

错误范式:Skill 里写 Agent({ description: "...", prompt: "..." }),让 modelinherit

后果:父代理是 Opus,子代理就是 Opus。一次派发 9 个 Opus 子代理做"把 Fix: 这行文本替换成那行文本"——单会话轻松 $80+。

任务 → 档位映射表

任务合适档位理由
文件列表、Grep 扫描、关键词匹配Haiku模式匹配是 Haiku 的强项
结构化解析(YAML/Markdown → 数据)、文本分类Haiku 或 Sonnet简单 → Haiku;带语义判断 → Sonnet
确定性文本编辑(按 “Fix: X” 执行替换)SonnetOpus 的推理预算在这里不产生价值
单文件深度评审(发现 AC 漏洞、跨字段一致性)SonnetHaiku 会漏掉硬伤,Opus 是浪费
跨模块架构判断、接口对齐、冲突化解Opus真正需要长链条推理
大文件(>5k tokens)的一次性 parsing+规约SonnetHaiku 会错解结构化 schema

最常见的两个误用

  1. Deterministic edit 走 inherit=Opus:一次 revise 流程 9 个子代理,每个都跑几百行 MultiEdit,成本占全会话 80%。改成 model: "sonnet" 后立刻下降 5×。
  2. Per-file review 走 Explore(内置轻量档位):找出的全是措辞/格式问题,跨字段一致性/AC 完整性被漏掉。导致 --review → --revise 轮数不降,收敛失败。改成 general-purpose + sonnet 后轮次通常 4 → 2。

第 2 条:subagent_type 决定控制权归属

Claude Code 的子代理有两类:

  • 内置专用代理Explorecode-reviewerPlan 等)——档位由 harness 决定,Skill 无法覆盖。
  • general-purpose——档位由 Skill 的 model 参数决定。

写 Skill 时的默认应该是:general-purpose + 显式 model。只有当任务真的匹配某个内置代理的定位(例如纯代码库探索走 Explore),才用内置代理。

反例

review-mode.md 一开始派 Explore 做 per-file PRD review,看起来便宜,实际用的是 Haiku 档位,召回能力不足。改成 general-purpose + sonnet 后单轮成本上升 3×,但总轮次减半,整体成本持平或下降,而且用户能明显感觉"review 报告开始切中要害"。

版本锁死反模式

不要在 Skill 里写 model: claude-sonnet-4-6。模型在旋转(4.5 → 4.6 → 4.7),版本号锁死后 Skill 会腐烂。永远用档位别名:opus / sonnet / haiku。Skill 作者表达的是"我要什么档位",不是"我要哪个具体模型"。


第 3 条:cache_read × 轮次是最容易被忽略的隐性成本

主代理一旦 Read 了一个文件,它就会出现在后续每一轮的 prompt cache 里,每轮按 cache_read 计费。这个成本不会出现在任何单次 Read 工具的统计里——它是后续所有轮次"白嫖 cache"的尾巴。

量化一个典型例子

REVIEW 报告 1000 行 ≈ 30k tokens,主代理是 Opus,revise 过程 20 轮:

  • 主代理成本30,000 × 20 × $1.50/M = $0.90
  • 8 个 Fix 子代理(Sonnet)各跑 4 轮30,000 × 4 × 8 × $0.30/M = $0.29
  • 合计:$1.19 —— 全部来自"读了一次但 cache 里放了很久"

模式:把大文件 read 委派给子代理,主代理只拿 manifest

主代理不读大文件,改为派一个 Sonnet 子代理:

  • 输入:大文件的路径
  • 输出:2–4k tokens 的结构化摘要(YAML/JSON manifest)

主代理 context 只多了一份 2–4k 的 manifest,同样 20 轮:

  • 主代理成本3,000 × 20 × $1.50/M = $0.09(↓ 90%)
  • 子代理的 30k × 1 次 read 只在它自己的短会话里,成本微小
  • 如果 Fix 子代理在下游仍需要原始文本,让它们自己去读,因为它们的上下文在完成后就扔了,不会持续放大。

何时该用这个 pattern:任何 > 5k tokens 的中间产物(review 报告、long-form 调研结果、架构文档),主代理只需要"索引"层信息,不需要 body。


第 4 条:Template A vs Template B —— 输出 token 是最贵的

子代理派发有两种常见风格:

  • Template A(引用式):prompt 说 “去读 {path},按里面的 Fix: 指令执行”。
  • Template B(内联式):prompt 直接写 “把第 34 行 X 替换为 Y,第 56 行 …"。

两者的成本差异发生在主代理

  • Template B:主代理需要在 prompt 里写出全部 edits。如果一个 cluster 有 2k tokens 的 edits,主代理 output = 2k tokens。Opus output 成本 2k × $75/M = $0.15 —— 仅这一次派发一个子代理
  • Template A:主代理的 prompt 只写 ≈ 200 tokens(路径 + 目标文件列表)。子代理去读文件自己抽,主代理 output ≈ $0.015。差 10×。

何时该用哪个

场景推荐理由
源文件(findings / edits)已经持久化A子代理直接读,省掉主代理的 output token
源是会话内动态生成的(interactive mode)B没有持久化文件可引用,只能内联
每个 cluster 的 edits 很少(<500 tokens)Boutput 成本微小,A 的文件读取反而是开销

一句话总结:Skill 作者的第一反应通常是"少让子代理读文件”(Template B 思维),但实际主代理的 output 才是更贵的那一侧


第 5 条:Cluster 大小 和 MultiEdit —— 两个工程细节,一个大规律

Cluster sizing:3 文件是甜点

Fix 子代理每个对话有 N 轮,每轮都要 cache_read 当前所有打开文件的内容。

  • 3-file cluster,每文件 5k,共 15k context
  • 40 次 edit → 40 轮(每 Edit 一轮)
  • cache_read 总量 15k × 40 = 600k

5-file cluster?25k × 40 = 1000k大 66%

所以 Skill 应该更多更小的 cluster(并行派发),而不是少而大的 cluster

MultiEdit vs 连续 Edit

同一文件有多个 edit 时:

  • 连续 Edit:每个 Edit 是一轮。第 N 个 Edit 会 cache_read 前 N-1 轮的所有对话状态。成本 O(N²)。
  • MultiEdit:一轮搞定。成本 O(N)。

Skill prompt 必须强制子代理在 >1 edit 时用 MultiEdit。不是"建议",是"forbidden 连续 Edit"。


第 6 条:收敛速率是成本信号

这一条最容易被忽视。

如果你观察到每轮 review 都报出问题、revise 修完后下一轮又报新问题、但总问题数不下降——这不是在正常迭代,而是在档位错配。症状:

  • Haiku 档位的 review 子代理找出的是浅层(措辞、格式)问题
  • revise 修完后文本变化微小,Haiku 下次从另一个浅层角度又找到一批
  • 深层硬伤(AC 不完整、跨字段矛盾、数据模型错位)一直留着
  • 用户感觉"好像在收敛",但永远到不了零问题

判断方法:连续 3 轮 --review 的 Critical findings 种类是否在减少?如果换了维度而数量类似,就是假收敛。

修复方法:不是加更多轮次,是升档一次到位。把 per-file review 从 Haiku 升到 Sonnet,单轮成本上升 3–5×,但总轮次常常 4 → 2,净成本下降且硬伤能真正被发现。


落地 Checklist(10 条)

在给一个 Skill 做成本 review 时,按顺序问自己:

  1. 所有 Agent() 调用是否显式写了 model?(不是 inherit)
  2. model 是档位别名(sonnet)还是具体版本号(claude-sonnet-4-6)?必须是别名。
  3. 是否有 subagent_type: "Explore" 用在需要语义判断的任务上?改 general-purpose + sonnet
  4. 主代理是否读了 > 5k tokens 的中间产物?能否委派给子代理 + 只消费 manifest?
  5. 子代理派发 prompt 是引用式(A)还是内联式(B)?出现可持久化的源文件时应该是 A。
  6. Fix / edit 子代理的 cluster size 有没有上限?推荐 ≤3 文件。
  7. Skill 是否强制 MultiEdit(而不是连续 Edit)?写进"forbidden"而非"suggested"。
  8. 有没有"验证读"(edit 之后再 Read 一次)的多余动作?禁掉。
  9. 有没有 Grep/Glob 重复发现(已在 prompt 里给过路径,子代理又自己搜了一遍)?禁掉。
  10. 有没有"review 不收敛"的历史症状?如果有,九成是档位问题。

量化收益

一次 /prd-analysis --revise 会话,按上述原则改造前后:

改造前改造后
子代理档位9 × Opus (inherit)9 × Sonnet (显式)
Cluster size无限制≤3 文件
Edit 方式混用 Edit / MultiEdit强制 MultiEdit
REVIEW 文件读取主代理读Clustering 子代理读
Template混用 A/B有持久化时强制 A
总成本~$80~$15

5× 降本,收敛速度持平或更好。唯一的代价是 Skill 文件里多了 ~150 行关于"谁在什么时候用什么模型"的规则——但这些规则本来就应该存在,只是之前隐式依赖 harness 默认。


结语

Skill 的成本优化和传统 LLM 应用的成本优化,不是同一个问题。Skill 的会话更长、调用栈更深、模型选择的影响被轮次放大。最贵的两样东西——output token主代理的持续 cache_read——恰好是直觉最不容易对准的两样。

Skill 作者应该把 “task → tier → subagent_type → template” 这条链条当作和"业务逻辑“同等重要的设计决策。写一行 Agent({ prompt: "..." }) 默认省略一切的代价,可能就是下一个账单里那个 $94。