长上下文时代的迷思:"能装下"不等于"能用好"
随着 Claude 3.5 Sonnet、Gemini 1.5 Pro 等模型支持数十万甚至上百万 Token 上下文,许多开发者误以为可以把整个项目的所有文件一次性全部扔给模型。
实测表明,随着上下文急剧膨胀:
- 注意力钝化(Attention Degradation):模型在超长文本中间段落的信息召回准确率显著下降(Lost in the Middle 现象);
- 推理延迟与费用飙升:首字延迟(TTFT)与单次调用成本呈线性甚至超线性上升。
还有第三个常被忽略的代价:噪声会污染推理方向。当上下文里同时存在三份风格互斥的实现时,模型不一定会选择正确的那一份,它更可能把它们平均化,产出一个谁都不像的折中结果。上下文工程因此不是"省钱的技巧",而是"决定输出质量的架构决策"。
动态分层上下文拓扑设计
一个成熟的智能体系统应当将上下文划分为四个层级:
- L0 核心指令层(不可裁剪):System Prompt、角色边界、格式规范;
- L1 动态工作记忆层(高优先级):当前任务目标、最近 3 轮工具调用输入输出;
- L2 摘要记忆层(滑动折叠):历史轮次的定期压缩摘要;
- L3 外部持久索引层(按需检索):知识库与代码库,仅在执行特定检索工具时召回 Top-K 片段。
四层的关键不在"分层"本身,而在裁剪顺序与恢复成本:
- L0 一旦被动过,模型的行为契约就变了,因此它必须短、稳定、可版本化。把业务规则塞进对话历史而不是 L0,是最常见的失控来源。
- L1 是唯一的"真相来源",应当只保留未被后续动作覆盖的调用结果。一次被后续编辑覆盖的文件读取、一次失败的命令输出,都可以立刻降级。
- L2 的摘要必须由确定性结构兜底:任务目标、已确认事实、待办、决策记录四个字段固定存在,避免自由摘要丢掉关键约束。
- L3 的内容永远不进上下文常驻区,只以"检索结果 + 出处"的形式临时出现,用完即弃。
把预算写成可执行的分配表
以下比例是工程经验值,不是规范,需要按任务类型校准。它的价值在于让"该裁谁"变成一次查表,而不是临场判断:
| 单轮预算 | L0 指令 | L1 工作记忆 | L2 摘要 | L3 检索 | 留给输出 |
|---|---|---|---|---|---|
| 8K 级 | 10% | 45% | 15% | 20% | 10% |
| 32K 级 | 5% | 35% | 20% | 30% | 10% |
| 128K 级 | 3% | 25% | 22% | 40% | 10% |
两条实践约束比比例本身更重要:
- 给检索结果设硬上限,而不是"有多少塞多少"。Top-K 的 K 与单条片段长度都要设限,否则一次宽泛查询就能吃掉整轮预算。
- 输出预留要按最坏情况留。上下文快满时模型并不会自动缩短回答,被截断的输出往往比缺一段背景更难处理。
四类常见反模式
- 全量重放:每次调用都从第一轮开始重放完整历史。轮次越多,成本越高且注意力越散——应当由 L2 摘要承接早期轮次。
- 工具返回值原样入栈:一个列表类工具返回 200 条记录,实际只需要 3 条。工具侧就应当支持字段筛选与分页,而不是让上下文层去背锅。
- 摘要链无限套娃:对摘要再摘要、反复压缩,几轮之后具体数字、文件名、接口签名会先蒸发。压缩必须保留标识符级别的事实。
- 把错误当上下文留着:失败的工具输出继续占据 L1,会持续暗示模型沿用过时的世界状态。
怎么确认裁剪真的有效
不要凭感觉判断"够不够聪明",用三个可观测口径:
- 召回自检:在长上下文里埋一条只有中段才有的约束,检查回答是否遵守。这是 Lost in the Middle 的直接量化手段。
- 单位成本产出:统计"每千 Token 输入换来的有效动作数",而不是只看总 Token。裁剪后如果有效动作数下降,说明裁错了内容。
- 决策可追溯:把每轮实际注入的上下文分区记录到链路里,出问题时能回答"这个错误结论是被哪一段上下文带偏的"。
小结
上下文工程的核心命题是:在有限的注意力窗口里,维持一份尽可能小但始终为真的世界状态。分层给出的是结构,预算表给出的是纪律,而真正的收益来自一个习惯——每写入一段内容进上下文之前,先问它属于哪一层、什么时候可以被丢弃。