主题
03.1 上下文窗口的物理学
上下文窗口不是"你能贴多少字",是一块被多方瓜分的稀缺内存。
读完你能做什么:知道每一个 token 花在哪了,并能在会话变慢变笨之前提前干预。
一、Token 的基本换算
| 内容 | 约等于 |
|---|---|
| 1 个 token | 约 0.75 个英文单词 |
| 1 个汉字 | 约 1-2 个 token(中文的 token 效率低于英文) |
| 1 行普通代码 | 约 10-20 token |
| 1 页 A4 纯文字 | 约 500-800 token |
| 1 个中等大小的源文件(300 行) | 约 3000-6000 token |
| 1 份 50 页 PDF | 约 25,000-40,000 token |
一条实用心算:中文文本的 token 数大约是字数的 1-1.5 倍;英文大约是单词数的 1.3 倍。
二、谁在占用你的上下文
这是最多人低估的部分。在一个 Claude Code 会话开始时,还没输入任何东西,上下文里已经有了:
text
┌───────────────────────────────────────────┐
│ 系统提示词(工具定义、行为指令、安全规则) │ ← 数千 token
├───────────────────────────────────────────┤
│ MCP 工具定义(每个工具的 schema) │ ← 每个工具几百 token
├───────────────────────────────────────────┤
│ CLAUDE.md(用户级 + 项目级 + 本地) │ ← 你写多少算多少
├───────────────────────────────────────────┤
│ .claude/rules/ 中的无条件规则 │ ← 同上
├───────────────────────────────────────────┤
│ Auto Memory 的 MEMORY.md 索引 │ ← 前 200 行 / 25KB
├───────────────────────────────────────────┤
│ Skill 的 name + description(每个技能) │ ← 每个几十 token
├───────────────────────────────────────────┤
│ 环境信息(工作目录、git 状态、平台) │ ← 少量
├───────────────────────────────────────────┤
│ │
│ ↓ 剩下的才是你的 ↓ │
│ │
│ 对话历史 + 读取的文件 + 工具返回结果 │
│ │
└───────────────────────────────────────────┘2.1 查看真实占用
text
> /context这条命令会显示:
- 每一部分实际占用了多少
- 哪些 Memory files 真的被加载了
- 剩余可用空间
这是诊断"为什么会话变慢/变笨"的第一个动作。
2.2 查看 Skill 的 token 成本
text
> /skills在列表中按 t 可以按 token 数排序,立刻看到哪个技能最占地方。
三、五大隐形消耗源
消耗源 1:MCP 工具定义
症状:装了 8 个 MCP server,每个暴露 20 个工具 = 160 个工具定义常驻上下文。
成本:一个工具定义(名字 + 描述 + 参数 schema)通常 100-500 token。160 个工具可能吃掉 3-8 万 token。
对策:
- 启用 tool search(工具延迟加载),见 07.5
- 按项目隔离 MCP 配置(
--scope project) - 临时禁用不需要的 server
bash
# 只加载指定的 MCP 配置,忽略其他所有
claude --strict-mcp-config --mcp-config ./project-mcp.json消耗源 2:臃肿的 CLAUDE.md
症状:CLAUDE.md 写了 800 行,涵盖了整个项目的架构说明。
成本:每次会话开头就吃掉几千 token。而且文件越长,单条指令的遵守率越低。
对策:
- 官方建议单个
CLAUDE.md控制在 200 行以内 - 把"只在特定目录生效"的内容移到
.claude/rules/并加paths前置元数据 - 把"多步流程"移到 Skill(按需加载,不用时零成本)
text
> /doctor在 v2.1.206+ 中,/doctor 会主动为已提交的 CLAUDE.md 提出精简建议 —— 它会砍掉 Claude 能从代码库自己推导的内容(目录结构、依赖列表、架构概览),保留那些它推导不出来的(坑、决策理由、与工具默认行为不同的约定)。
消耗源 3:大文件的全量读取
症状:让它「看一下这个文件」,那个文件有 5000 行。
对策:明确指定范围。
text
❌ 看一下 src/services/order.ts
✅ 读 src/services/order.ts 的第 200-350 行,那里是对账逻辑
✅ 先用 grep 找到 reconcile 相关的函数定义,再只读那几个函数消耗源 4:工具返回的大量输出
症状:跑一次测试,输出 3000 行日志,全部进上下文。
对策:
bash
# ❌ 全量输出
npm test
# ✅ 只要失败部分
npm test 2>&1 | grep -A 5 -E "(FAIL|Error|✕)" | head -50
# ✅ 或者先看统计,再按需深入
npm test 2>&1 | tail -20在提示词里明确要求:
text
运行测试时,用管道过滤只保留失败信息,不要把完整输出读进来。消耗源 5:对话历史的复利效应
症状:第 40 轮时明显变慢。
机制:每一轮推理,前面所有轮次的内容都要重新参与计算。第 40 轮的成本 ≈ 前 39 轮内容总和 + 本轮。
对策:见 03.4 压缩 Compact 与会话续航。
四、上下文压力的三个阶段
| 占用率 | 表现 | 该做什么 |
|---|---|---|
| < 50% | 一切正常 | 继续 |
| 50-75% | 开始出现指令遗忘、格式漂移 | 重申关键约束;准备 /compact |
| > 75% | 明显变笨、变慢;早期约束基本失效 | 立刻 /compact 或 /clear |
| 接近上限 | 自动触发压缩,可能丢失关键信息 | 已经晚了 |
关键:不要等到系统自动压缩。自动压缩发生时你无法控制保留什么,手动 /compact 时你可以指示保留重点。
五、一个反直觉的事实
上下文里的内容不是越多越好,即使它们全都是相关的。
原因有三:
- 注意力被摊薄 —— 100 条相关信息里,每一条获得的注意力权重都低于 10 条相关信息中的每一条。
- 矛盾信息互相干扰 —— 同一个函数的三个版本同时在上下文里,模型不知道该以哪个为准。
- 成本线性增长,收益边际递减 —— 第 50 个文件带来的信息增量,远小于第 5 个。
实践原则:给它刚好够用的上下文,不是所有可能相关的上下文。
text
❌ 我把整个 src 目录都读进来了,你分析一下架构问题
✅ 我先给你 3 个核心文件(入口、路由、核心 service)。
看完后,告诉我你还需要读哪些文件才能做出判断。第二种写法让模型自己驱动信息收集,比你一次性倒进去效果好得多,成本也低得多。
六、上下文预算表(实用工具)
在开始一个大任务前,先做预算:
| 项目 | 预估 token | 备注 |
|---|---|---|
| 系统 + 工具定义 | 待测 | /context 查看 |
| CLAUDE.md + rules | 待测 | 同上 |
| 需要读的代码文件 | 文件行数 × 15 | |
| 参考文档 | 页数 × 700 | |
| 预计对话轮数 × 每轮平均 | 轮数 × 1500 | 含你的输入和它的输出 |
| 合计 | 应 < 窗口的 70% |
如果超预算,优先砍:
- 参考文档 → 改成按需检索(RAG 或 grep)
- 代码文件 → 改成先索引后精读
- 对话轮数 → 拆成多个会话
七、快速诊断脚本
放在项目里,随时检查你的配置有多重:
bash
#!/usr/bin/env bash
# .claude/scripts/context-audit.sh
# 粗略估算常驻上下文的字符数(token 约为字符数的 1/3 到 1/1)
echo "=== CLAUDE.md 系列 ==="
for f in ./CLAUDE.md ./.claude/CLAUDE.md ./CLAUDE.local.md ~/.claude/CLAUDE.md; do
[ -f "$f" ] && printf "%-40s %6s 行 %8s 字符\n" "$f" "$(wc -l < "$f")" "$(wc -c < "$f")"
done
echo
echo "=== .claude/rules/ ==="
if [ -d ./.claude/rules ]; then
find ./.claude/rules -name '*.md' -exec sh -c \
'printf "%-40s %6s 行\n" "$1" "$(wc -l < "$1")"' _ {} \;
fi
echo
echo "=== Skills ==="
find ./.claude/skills ~/.claude/skills -name 'SKILL.md' 2>/dev/null -exec sh -c \
'printf "%-40s %6s 行\n" "$1" "$(wc -l < "$1")"' _ {} \;
echo
echo "=== MCP 配置 ==="
[ -f ./.mcp.json ] && echo "项目级 MCP server 数量: $(grep -c '"command"\|"url"' ./.mcp.json)"
echo
echo "提示:运行 /context 查看真实占用"bash
chmod +x .claude/scripts/context-audit.sh
./.claude/scripts/context-audit.sh