Skip to content

13.5 延迟加载:工具与 Skill 的加载经济学

装 30 个 Skill 几乎不花钱,装 5 个 MCP server 可能吃掉你十分之一的窗口。差别在加载时机。

读完你能做什么:算清每一种扩展机制的常驻成本与触发成本,配置 MCP tool search 的加载策略,并知道什么内容该写成脚本而不是文档。


一、常驻成本 vs 触发成本

任何扩展机制都可以拆成两笔账:

text
常驻成本 = 无论用不用,每次请求都要付的 token
触发成本 = 真正用到时,一次性进入上下文的 token

常驻成本会被每一轮重新计费,触发成本只在触发那一轮新增(之后按缓存读价重复计费)。所以:

常驻成本的权重远高于触发成本。 一个 100 token 的常驻项,在 50 轮会话里的总量是 5,000 token;一个 5,000 token 的触发项,只被完整计费一次。

对照表:

机制常驻成本触发成本备注
Skill(默认)约 100 token/个(name + description)SKILL.md 正文,建议 < 5kdescription 合并 when_to_use 后按 1,536 字符截断
Skill(disable-model-invocation: true0同上只能手动 /name 调用
Skill 的其他 .md 资源0读多少算多少未被读取时完全免费
Skill 的 scripts/0只有输出计入脚本代码从不进上下文
MCP 工具(tool search 开启,默认)工具名 + server instructions单个工具的完整 schemaserver instructions 与工具描述各按 2KB 截断
MCP 工具(alwaysLoad: true全部 schema 常驻0每增一个工具就永久占一份
CLAUDE.md全文常驻官方建议控制在 200 行以内
paths: 的 rules0读到匹配文件时加载压缩后丢失,直到再次匹配
Hook0只有脚本输出计入Hook 是代码,不是上下文
子代理0(父会话侧)只有返回的摘要子代理自己另有一套开销

最后三行是「近乎免费」的三种扩展方式,值得优先使用。


二、Skill:为什么可以放心多装

一个真实的启动构成参考(Claude Code 官方给的代表性数值):

项目token
系统提示词4,200
Auto memory(MEMORY.md680
环境信息280
MCP 工具名(延迟加载)120
Skill 描述(若干个)450
~/.claude/CLAUDE.md320
项目 CLAUDE.md1,800
合计常驻≈ 7,850

Skill 描述只占 450。把 Skill 数量翻到 30 个,常驻成本大约是 3,000 token,占 200k 窗口的 1.5%、占 1M 窗口的 0.3%。

结论:Skill 数量不是问题,Skill 正文的长度和调用频次才是。 一个 8k token 的 Skill 被调用后会常驻整个会话;十个 100 token 的 description 加起来才 1k。

控制 Skill 成本的四个动作

动作省下什么
正文控制在 500 行以内,细节移到独立 .md触发成本从数千降到数百
重要指令写在文件开头压缩后重新附着时只保留前 5,000 token
有副作用的流程加 disable-model-invocation: true常驻成本归零
大量读取型流程加 context: fork触发成本完全不进主会话

反过来:什么该从 CLAUDE.md 搬进 Skill

CLAUDE.md 在会话启动时全文加载,即使你今天做的事和它八竿子打不着。判断标准:

text
这段内容是「每次会话都用得上的事实」吗?
├─ 是 → 留在 CLAUDE.md(目录约定、命令、硬性规范)
└─ 否 → 它是「某类任务的流程」吗?
         ├─ 是 → 做成 Skill(PR 评审流程、数据库迁移步骤、发布清单)
         └─ 否 → 它只在改某类文件时才相关吗?
                  ├─ 是 → 做成带 paths: 的 rule
                  └─ 否 → 大概率可以删

三、MCP:真正的大头

MCP 工具定义是完整的 JSON Schema。一个中等复杂度的工具,schema 常在 300~800 token。一个提供 25 个工具的 server,全量加载就是一两万 token 常驻。

3.1 tool search 是默认开启的

Claude Code 默认延迟加载 MCP 工具:启动时只放工具名和 server instructions,Claude 需要时用搜索工具把具体 schema 拉进来。

ENABLE_TOOL_SEARCH 控制:

取值行为
(未设置)全部延迟。在 Google Cloud Agent Platform、非第一方 ANTHROPIC_BASE_URL、Azure 托管的 Microsoft Foundry 上回退为全量前置加载
true强制全部延迟
auto阈值模式:schema 总量若在窗口的 10% 以内则前置加载,超出部分延迟
auto:N阈值模式,自定义百分比,例如 auto:5
false全部前置加载,不延迟
bash
# 用 5% 阈值:小工具集前置(省一次往返),大工具集延迟
ENABLE_TOOL_SEARCH=auto:5 claude

也可以写进 settings.jsonenv 字段固化。

tool search 需要支持 tool_reference 块的模型:Claude Sonnet 4.5、Haiku 4.5、Opus 4.5 及更新版本。设了 CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS 会让 tool search 失效,且 ENABLE_TOOL_SEARCH 无法覆盖。

3.2 给高频 server 开豁免

每轮都要用的少量工具,让它一直在场更划算 —— 省掉每次搜索的一轮往返:

json
{
  "mcpServers": {
    "core-tools": {
      "type": "http",
      "url": "https://mcp.example.com/mcp",
      "alwaysLoad": true
    }
  }
}

代价有两个,都要提前知道:

  1. 该 server 的全部工具永久常驻,无视 ENABLE_TOOL_SEARCH
  2. alwaysLoad: true阻塞启动直到该 server 连上(上限 5 秒),因为构建第一个提示词时必须有这些工具。

alwaysLoad 需 Claude Code v2.1.121+。MCP server 也可以在单个工具的 _meta 里放 "anthropic/alwaysLoad": true,只豁免那一个工具。

3.3 优先考虑 CLI,而不是 MCP

官方成本文档里的一条建议:ghawsgcloudsentry-cli 这类命令行工具比对应的 MCP server 更省上下文,因为它们不产生任何按工具计的清单开销 —— Claude 直接跑命令就行。

方案常驻成本何时更优
CLI 工具0有成熟 CLI、输出可控、你能接受权限规则的管理成本
MCP server(延迟)工具名清单需要结构化返回、需要认证托管、没有 CLI
MCP server(alwaysLoad全量 schema每轮都要用的少数几个工具

定期用 /mcp 检查有哪些 server 挂着但没在用,用不到就关掉。


四、脚本 > 文档:一条被低估的规则

Skill 里的 scripts/ 目录有一个独特性质:

脚本代码从不进入上下文,只有它的 stdout / stderr 进入。

对比一下同一件事的两种写法:

做法上下文成本可靠性
SKILL.md 里写 80 行「如何校验这份表单」的说明,让 Claude 现场生成代码说明常驻 + 生成的代码进上下文 + 可能出错重试每次结果可能不同
写一个 scripts/validate_form.pySKILL.md 里只写一行「跑这个脚本」一行说明 + 脚本输出(如 Validation passed确定性

确定性操作写成脚本,判断性操作留给模型。 这是渐进式披露的第三层真正的价值所在。

配套的权限技巧:用 ${CLAUDE_SKILL_DIR}allowed-tools 规则精确匹配脚本路径,跑脚本时就不会弹权限确认:

yaml
---
name: render-chart
description: 从 CSV 渲染图表
allowed-tools: Bash(${CLAUDE_SKILL_DIR}/scripts/render.sh *)
---

运行 `${CLAUDE_SKILL_DIR}/scripts/render.sh <csv-file>` 生成图表。

allowed-tools 里的 ${CLAUDE_SKILL_DIR} 替换需 Claude Code v2.1.129+。更早版本会把它当字面量,永远匹配不上。


五、Hook:零上下文成本的预处理层

Hook 完全不进上下文。它的用途是在信息到达 Claude 之前就把它压小

最典型的场景:测试输出。一次 npm test 的完整输出可能是 12,000 token,其中你关心的失败信息只有 300 token。

json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "~/.claude/hooks/filter-test-output.sh" }
        ]
      }
    ]
  }
}
bash
#!/bin/bash
# ~/.claude/hooks/filter-test-output.sh
# 把测试命令改写成只输出失败部分
input=$(cat)
cmd=$(echo "$input" | jq -r '.tool_input.command')

if [[ "$cmd" =~ ^(npm test|pytest|go test) ]]; then
  filtered_cmd="$cmd 2>&1 | grep -A 5 -E '(FAIL|ERROR|error:)' | head -100"
  echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"allow\",\"updatedInput\":{\"command\":\"$filtered_cmd\"}}}"
else
  echo "{}"
fi

验证它生效了:

bash
claude --debug
# 触发一次 npm test,调试日志里应出现:modified tool input keys: [command]

同类场景:日志筛选、大 JSON 的字段裁剪、把二进制/图片输出替换成描述。每一个都是把上万 token 压到几百的量级。


六、一张加载策略决策表

内容性质推荐载体常驻成本
每次会话都要用的硬事实(构建命令、目录约定)CLAUDE.md全文
某类任务的流程(发布、评审、迁移)Skill~100 token
只在改某类文件时相关的规范paths: 的 rule0
有副作用、要你控制时机的流程Skill + disable-model-invocation0
大量读取 / 探索类工作Skill + context: fork,或子代理0
确定性的检查、转换、校验scripts/ 下的脚本0
工具输出的裁剪与过滤Hook0
每轮都要用的少数几个外部工具MCP + alwaysLoad全量 schema
偶尔要用的大量外部工具MCP + tool search(默认)工具名
有成熟 CLI 的外部服务直接用 CLI0

/context 核对现状,用这张表决定该往哪一行迁移。


延伸阅读

基于 VitePress 构建 · 内容采用原作者授权