上下文管理——AI编码的核心能力

你有没有想过,为什么Claude Code能在长达几十轮对话后还记得你最初说的需求?而普通AI聊着聊着就"失忆"了?
秘密在于上下文管理——这不是简单的"能记多少字",而是像专业收纳师整理衣柜一样,在有限空间里最大化有用信息的密度。今天咱们聊聊Claude Code如何把上下文管理变成一门核心能力。
为什么上下文管理是核心能力
想象你的书桌有200厘米宽,看起来够用了吧?但你随手一放就是:咖啡杯、笔记本、参考资料、草稿纸、零食包装…很快就没地方干活了。
AI的上下文窗口(200K token)就像这张书桌。系统提示词占15-20K,每次工具调用返回5-50K,读几个文件、搜几段代码,半壁江山就没了。更糟的是,过期的工具结果、冗余的文件内容、已解决的讨论堆在"桌面"上,AI的注意力被稀释,回答质量直线下降。
上下文管理的核心命题:在有限空间里,最大化信息密度,同时确保关键内容不丢失。
Claude Code用五条原则解决了这个问题。
原则一:为一切设定预算
你有没有做过家庭预算?房租不能超过收入30%,餐饮控制在20%,娱乐最多10%…如果某一项超支,其他就得削减。
Claude Code的上下文管理也是严格的"预算制":
预算体系一览
| 内容来源 | 预算限制 | 工程理由 |
|---|---|---|
| 单个工具结果 | 50K字符 | 防止单个超大结果占满上下文 |
| 单消息中所有工具结果 | 200K字符 | 防止10个并行工具各50K导致500K洪泛 |
| 技能列表描述 | 上下文窗口的1% | 1000个技能也不能喧宾夺主 |
| 压缩后恢复文件 | 最多5个文件,单文件5K token,总计50K | 恢复太多等于没压缩 |
| 压缩后恢复技能 | 单技能5K token,总计25K token | 保持技能系统可用但不臃肿 |
这些数字不是拍脑袋定的。比如技能列表的1%预算:随着用户安装越来越多技能,如果不加限制,技能描述可能占满整个上下文。Claude Code的解决方案是三级截断级联——先截断描述到250字符,再截断低优先级技能,最后只保留内置技能的名称。哪怕你装1000个技能,技能列表永远不会超过上下文窗口1%的空间。
双重防护的智慧
注意50K和200K这两个数字的设计:
// 单个工具结果不能超过50K
export const DEFAULT_MAX_RESULT_SIZE_CHARS = 50_000
// 但单消息中所有工具结果总和不能超过200K
export const MAX_TOOL_RESULTS_PER_MESSAGE_CHARS = 200_000
这就像:每个抽屉最多放50件东西(合理),但如果你同时打开10个抽屉,总数不能超过200件(防止"合法但危险"的组合)。
反模式:无界内容注入
把工具结果、文件内容不加限制地塞进上下文,就像把超市购物车里的所有东西全堆到桌上——看起来很丰富,实则没地方干活了。
原则二:保留重要内容
搬家时你会怎么做?把所有东西打包,到新地方再全部拆开?那太蠢了。聪明的做法是:大部分装箱,但把最常用的东西(洗漱用品、换洗衣服)放在随身背包里,到新家第一天就能用。
Claude Code的压缩-恢复机制就是这个思路。
压缩会丢失细节
自动压缩(Auto Compact)把整段对话摘要成几句话,释放了上下文空间。但压缩有个副作用:具体的代码内容、文件路径、精确行号都丢失了。如果压缩后模型完全忘记之前读过什么文件,它就得重新读取——浪费工具调用和等待时间。
选择性恢复策略
// 压缩后恢复最近5个文件
export const POST_COMPACT_MAX_FILES_TO_RESTORE = 5
恢复策略的精妙之处:
- 压缩前快照:用
cacheToObject()记录当前状态 - 执行压缩:对话变成摘要
- 选择性恢复:
- 最近5个文件(不是全部)
- 单文件最多5K token(不是完整内容)
- 总量不超过50K token
- 已发送的技能不重注入(省4K token)
这就像搬家时的"随身背包"——只带最急用的,而不是把整个家背着走。
关键洞察:恢复太多等于没压缩,恢复太少等于压缩过度。5个文件、5K token、50K总量,是经过工程验证的"甜点"。
反模式:全量压缩或全量保留
要么什么都不恢复(模型被迫从零开始),要么试图保留一切(压缩效果为零)。就像搬家时要么全装箱(第一天没法生活),要么全不装(搬家车装不下)。
原则三:告知而非隐藏
你有没有遇到过这种事:别人给你一份"精简版"资料,但没告诉你删掉了哪些。你基于这个版本做决策,结果关键信息早被删了,你浑然不知。
AI也会遇到同样的问题。如果上下文被截断或压缩了,但模型不知道,它会基于不完整信息做错误决策,甚至"编造"它记不清的内容。
透明截断的四种实现
1. 工具结果截断通知
当工具结果超过50K时,Claude Code不会默默截断。而是:
- 完整结果写入磁盘
- 模型收到预览消息,包含"这是截断的,完整版在xxx路径"
- 模型知道:(1) 当前不是全部,(2) 如何获取全部
2. 缓存微压缩通知
当旧工具结果被清理时,notifyCacheDeletion()明确告知模型"某些旧内容已被删除"。防止模型引用已不存在的内容。
3. 文件读取分页
FileReadTool默认读2000行,但明确告知模型这一点。如果需要后面内容,可以指定offset——模型知道"默认只看到前2000行"。
4. 压缩摘要中的显式声明
压缩提示词要求摘要包含"进行到哪一步了"和"还需要做什么"——压缩后的模型清楚自己处于任务的哪个阶段。
这就像整理书桌时,在收纳箱上贴标签:“这里面是参考资料,需要时可以取出”——而不是直接扔到地下室,假装它不存在。
反模式:静默截断
在模型不知情的情况下截断工具结果或删除上下文。模型可能引用已不存在的内容,做出错误决策。就像你问朋友"那份报告呢",朋友说"我扔了",但你不知道,还在问"报告第三页的数据"——朋友一脸懵。
原则四:熔断失控循环
你有没有见过自动售货机吞了钱不出货,然后你疯狂按退款键,但它就是没反应?如果这是一个AI Agent,它可能会无限重试"退款操作",每次失败都再试一次,直到天荒地老。
在AI系统中,无限重试尤其危险——每次重试都要花钱(API调用),而且失败原因往往是系统性的(比如上下文大到无法压缩),重试不会改变结果。
Claude Code的熔断器设计
// 连续3次压缩失败后停止尝试
// BQ数据:1,279个会话出现过50+次连续失败,最高达3,272次
// 浪费约250K次API调用/天
const MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES = 3
这个数字3来自真实数据:BigQuery显示有1279个会话出现过50次以上连续压缩失败,最高达到3272次。每天浪费约25万次API调用。熔断器把无限循环变成"3次失败后停止"。
熔断器家族
| 子系统 | 熔断条件 | 熔断行为 |
|---|---|---|
| 自动压缩 | 连续3次失败 | 停止压缩直到会话结束 |
| YOLO分类器 | 连续3次/总计20次拒绝 | 回退到用户手动确认 |
| max_output_tokens恢复 | 最多3次重试 | 停止重试,接受截断输出 |
| Prompt-too-long处理 | 丢弃最旧轮次仍超限 | 丢弃20%,不无限丢弃 |
每个熔断器遵循相同模式:设定合理重试上限,超过后降级到安全但功能受限的状态,而非崩溃或无限循环。
反模式:无限重试
“压缩失败?再试。又失败?换参数再试。“在AI Agent中,这像自动售货机吞钱后疯狂按退款键——机器坏了,按再多次也没用,还浪费你的时间。
原则五:保守估算
你有没有做过预算时发现:计划花1000块,实际花了1200?AI的token估算也一样——低估导致溢出,高估只是略微浪费空间。
Claude Code在每个场景都选择保守估算:
估算策略对照
| 内容类型 | 估算策略 | 保守程度 | 原因 |
|---|---|---|---|
| 普通文本 | 4字节/token | 中等 | 英文实际约3.5-4.5 |
| JSON内容 | 2字节/token | 高度保守 | 结构字符token化效率低 |
| 图片/文档 | 固定2000 token | 高度保守 | 元数据不可用时用固定值 |
| 缓存token | 从API usage获取 | 精确 | 只有API返回的计数是权威的 |
JSON按2字节/token估算特别有意思。JSON的结构字符({}、[]、""、:、,)token化效率远低于自然语言——100字节的JSON可能消耗40-50个token,而100字节英文只需25-30个。如果用4字节/token的通用估算,JSON密集的工具结果会被严重低估,可能导致上下文溢出。
保守的收益大于成本
高估token消耗的最坏结果:提前触发压缩,用户多等几秒。
低估token消耗的最坏结果:prompt_too_long错误,API调用失败,需要紧急丢弃上下文,可能丢失关键信息。
这就像买保险——宁可多交点保费,也不要出事了发现保额不够。
反模式:精确计数的幻觉
试图在客户端精确计算token数量。只有API服务端的tokenizer才能给出精确值——客户端的任何计数都是估算。既然是估算,就应该向安全方向偏移,而不是追求虚假的"精确”。
五条原则的关系
为一切设定预算
↓
保留重要内容 ←─┐
↓ │
告知而非隐藏 │
↓ │
熔断失控循环 ──┤
↓ │
保守估算 ──────┘
预算是基础:定义每个内容来源的token上限。
保留与告知是执行:决定压缩后恢复什么,并确保模型知道什么被截断了。
熔断与估算是保障:防止自动化流程超出预算,确保预算不被低估绕过。
这对构建AI Agent的启示
模式1:分层Token预算
- 解决的问题:多个内容来源竞争有限的上下文空间
- 核心做法:单项限制(50K)→ 聚合限制(200K/消息)→ 全局限制(上下文窗口-输出预留-缓冲)
- 代码模板:为每个来源设定独立预算+总量预算,截断级联处理超额
模式2:压缩-恢复循环
- 解决的问题:压缩丢失关键上下文
- 核心做法:压缩前快照 → 压缩 → 选择性恢复最近/最重要的内容
- 前置条件:能追踪哪些内容是"最近使用的”
模式3:熔断器
- 解决的问题:自动化流程在异常条件下无限循环
- 核心做法:连续N次失败后停止,降级到安全状态
- 前置条件:定义"失败"的判定标准和降级后的行为
实战:审计你的Agent
1. 测量上下文消耗
在真实场景中测量每个内容来源占用多少token,找出最大消耗者。是工具结果?是文件读取?还是技能列表?
2. 为工具结果设定大小限制
确保文件读取、数据库查询、API调用的结果有字符/行数上限。不要等到prompt_too_long才想起来限制。
3. 实现压缩后恢复
如果你的Agent使用上下文压缩,设计恢复策略——让压缩后的模型不需要从零开始。最近用过的文件?最近编辑的位置?关键配置信息?
4. 截断时告知模型
告诉模型"这是截断的,完整版在哪里"——比静默截断后模型自己发现信息缺失好得多。
5. 添加熔断器
对任何可能循环执行的自动化流程设定重试上限。宁可降级也不要无限循环。
总结
上下文管理的五条原则:
| 原则 | 核心做法 | 反模式 |
|---|---|---|
| 为一切设定预算 | 每个来源都有token上限,多级防护 | 无界内容注入 |
| 保留重要内容 | 压缩后选择性恢复关键信息 | 全量压缩或全量保留 |
| 告知而非隐藏 | 截断/压缩时明确告知模型 | 静默截断 |
| 熔断失控循环 | 连续N次失败后停止重试 | 无限重试 |
| 保守估算 | 宁可高估token消耗也不要低估 | 追求虚假精确 |
这就像专业收纳师的工作:
- 预算:每件物品都有指定的收纳位置,不能乱放
- 保留:换季衣服装箱,但常用物品放在手边抽屉
- 告知:收纳箱贴标签,知道什么在哪里
- 熔断:收纳不了时停止购买,而不是堆到天花板
- 保守估算:买收纳盒时宁可大一点,不要买小了装不下
理解上下文管理,你就能:
- 在有限上下文窗口内最大化信息密度
- 避免"聊着聊着就失忆"的尴尬
- 在自己的AI Agent中实现专业级的上下文管理
下篇咱们聊聊生产级AI编码模式——从Claude Code学到的工程实践。
