Skip to content

13.8 诊断与实测方法

不要凭感觉优化上下文。这一篇讲怎么把「感觉变慢了」变成一个可以对比的数字。

读完你能做什么:用 /context/usage、状态栏、token 计数 API 四种方式测出真实消耗,设计一次可复现的 A/B 实验,并识别上下文该清理了的早期信号。


一、四个观测点

工具看什么粒度在哪用
/context当前窗口按类别的占用构成一次快照Claude Code
/usage会话累计 token / 成本、计划用量条、按 skill/subagent/plugin/MCP 的归因会话 / 24 小时 / 7 天Claude Code
状态栏脚本每轮的缓存读写 token逐轮实时Claude Code
count_tokens API发送前预估,可预览 context editing 后的结果请求级API

组织级别再加一个:OpenTelemetry 导出器,按用户和会话上报 token 与成本。


二、/context:先看常驻构成

text
> /context

它会按类别列出当前窗口占用,并给出优化建议,包括加载了哪些 CLAUDE.md 与 auto memory 文件。

一份健康的常驻构成参考(会话刚启动、还没干活时):

类别健康区间超标信号
系统提示词约 4k(不可控)
项目 CLAUDE.md< 2k> 4k 说明该拆 Skill 了
用户 CLAUDE.md< 0.5k
Auto memory< 1k加载上限是前 200 行或 25KB
Skill 描述< 1.5k> 3k 检查是否有 description 写得过长
MCP 工具< 0.5k(延迟加载时)> 5k 说明有 server 在全量前置加载
启动常驻合计< 10k> 15k 需要动手

启动常驻超过 15k,意味着一个 50 轮的会话里,光是「还没开始干活的那部分」就被重复计费了 50 次。


三、/usage:看钱花在哪个自动化上

text
> /usage

会话块显示这样的内容:

text
Total cost:            $0.55
Total duration (API):  6m 20s
Total duration (wall): 6h 33m 10s
Total code changes:    0 lines added, 0 lines removed
Usage by model:
   claude-sonnet-4-6:  1.2k input, 5.3k output, 940.0k cache read, 50.0k cache write ($0.55)

三个读法:

  1. cache read 远大于 input —— 缓存工作正常。上例 940k 读、50k 写,比例健康。
  2. Total duration (wall) 远大于 (API) —— 会话开着但大部分时间在闲置。这类会话的风险是「回来问一句话,付一整天的历史」。
  3. Total cost/clear 后归零(v2.1.211+)。更早的版本会跨 /clear 累加到进程结束。

在 Pro / Max / Team / Enterprise 计划上,/usage 还会:

  • 显示计划用量条(5 小时窗口 + 每周窗口)
  • 把近期用量按 skill、subagent、plugin、单个 MCP server 拆开,各给一个百分比
  • long contextcache misses 单项占 10% 以上时主动标出并给出建议
  • d / w 切换近 24 小时与近 7 天

/usage 里的美元数字是本地按标价估算的,不含促销与合同折扣,也不是账单依据。账单以 Console 的 [Usage] (用量) 页为准。数据只来自本机的会话历史,不包含其他设备与 claude.ai 的用量。


四、状态栏:把缓存命中率挂在眼前

一次性配置,之后每轮都能看见:

bash
#!/bin/bash
# ~/.claude/statusline.sh
input=$(cat)
ctx=$(echo "$input" | jq -r '.current_usage.input_tokens // 0')
r=$(echo "$input" | jq -r '.current_usage.cache_read_input_tokens // 0')
w=$(echo "$input" | jq -r '.current_usage.cache_creation_input_tokens // 0')
total=$(( ctx + r + w ))
printf "ctx %dk | cache r/w %dk/%dk" $((total/1000)) $((r/1000)) $((w/1000))
bash
chmod +x ~/.claude/statusline.sh
json
{
  "statusLine": {
    "type": "command",
    "command": "~/.claude/statusline.sh"
  }
}

诊断规则:

现象结论
w 每轮都在几万量级前缀里有东西在变,对照 13.4 第三节
r 稳定增长,w 只是几 k健康
ctx 涨得比你预期快有工具在返回大量输出,考虑加 Hook 过滤

五、API 侧:发送前就把账算清

count_tokens 端点可以在真正发请求之前给出 token 数,还能预览 context editing 生效后的结果:

python
response = client.beta.messages.count_tokens(
    model="claude-opus-5",
    messages=messages,
    betas=["context-management-2025-06-27"],
    context_management={
        "edits": [
            {
                "type": "clear_tool_uses_20250919",
                "trigger": {"type": "input_tokens", "value": 30000},
                "keep": {"type": "tool_uses", "value": 5},
            }
        ]
    },
)

print("清理前:", response.context_management.original_input_tokens)
print("清理后:", response.input_tokens)
json
{
  "input_tokens": 25000,
  "context_management": { "original_input_tokens": 70000 }
}

用它来调参数,比在生产流量里试便宜得多。

别被顶层 usage 字段骗了

开了服务端压缩之后,顶层的 input_tokens / output_tokens 不含压缩迭代的用量

python
# ❌ 会漏掉压缩那一次采样
cost_in = resp.usage.input_tokens

# ✅ 遍历所有迭代
cost_in = sum(it["input_tokens"] for it in resp.usage.iterations)
cost_out = sum(it["output_tokens"] for it in resp.usage.iterations)

开了 compact-2026-01-12 beta 之后,每个响应都会带 usage.iterations,哪怕这次没触发压缩。

服务端工具会让客户端估算失真

用 web search 这类服务端工具时,cache_read_input_tokens 可能包含工具内部多次调用的累计读取量,远大于你的真实上下文长度。官方举的例子里,SDK 算出 334k,而真实上下文只有 63k —— 足以让客户端压缩提前误触发。这种场景下用 count_tokens 拿准确值。


六、设计一次可复现的对比实验

优化上下文最容易犯的错是「改了一堆,感觉好了」。给一个可以拿数字说话的做法。

实验目标:验证「派 Haiku 子代理做扫描」是否真的比「主会话直接读」便宜。

bash
# 准备:同一个仓库、同一个任务描述、同一个模型
TASK="找出所有实现了缓存失效逻辑的文件,列出路径与行号"

# 组 A:主会话直接做
cd /path/to/repo   # 需替换为你的实际路径
claude -p --max-turns 20 "$TASK" > /tmp/run-a.log 2>&1

# 组 B:强制走子代理
claude -p --max-turns 20 "派一个 scanner 子代理完成:$TASK" > /tmp/run-b.log 2>&1

三条纪律,缺一个结论就不可信:

  1. 每组之间 /clear 或换新进程,避免上一组的缓存影响下一组。
  2. 每组跑 3 次取中位数,模型输出有随机性。
  3. 固定模型与 effort 档,否则你测的是模型差异不是策略差异。

对比指标:

指标从哪读
总输入 token/usage 或 OTel
缓存读 / 写同上
轮数--max-turns 是否被打满
结果正确性人工比对,这一项不能省

最后一项是关键:省了 80% 的 token 但答案错了,不叫优化。


七、上下文该清理了的七个早期信号

在数字之外,模型的行为本身就是仪表盘:

信号含义
开始重复之前已经做过的事历史太长,早期约定被稀释
忘记会话开头定下的硬性规范同上,中段衰减
反复读同一个文件早前那次读取的内容在注意力里权重太低
给出与项目约定冲突的方案CLAUDE.md 的相对权重被历史压过
回答开始变笼统、少引用具体路径精度下降的典型表现
每轮响应明显变慢输入量大,且可能有缓存未命中
自动压缩提示频繁出现早就该主动 /compact/clear

出现前四条中的任意两条,不要试图用更强的措辞去纠正它 —— 那是在往已经拥挤的上下文里再加一条指令。正确动作是把关键约定写进文件,然后 /clear 重开。


八、一份两分钟的自查流程

text
1. /context
   └─ 启动常驻超过 15k? → 精简 CLAUDE.md,检查 MCP 是否全量加载

2. /usage
   └─ cache misses 被标出? → 对照 13.4 找前缀变动源
   └─ 某个 MCP server 占比很高? → 考虑关掉或换 CLI
   └─ wall 时间远大于 API 时间? → 这个会话开太久了

3. 看状态栏
   └─ cache_creation 每轮都很高? → 有东西在动前缀

4. 看行为
   └─ 出现第七节里的信号? → 落盘状态 + /clear

延伸阅读

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