窗口越做越大,从 4K 到 128K 再到百万级,但工程结论反直觉:窗口大了,更要省着用。 这篇文章讲为什么,以及怎么花这笔预算。
三个物理事实
注意力不均匀。 lost in the middle 现象在长上下文里普遍存在:开头与结尾的内容被"看见"的程度高于中段。塞满 200K 的仓库代码再问一个中段函数的问题,命中率不如精准检索 20 个片段。
成本随长度线性甚至更差。 输入 token 计费之外,超长上下文的预填充(prefill)延迟明显增长,缓存命中才是长会话经济的救命稻草——前缀稳定(system + 工具定义 + 早期对话)才能吃上缓存,前缀一变,全量重算。
位置本身就是信号。 越晚进入上下文的信息,对即将生成的 token 影响越强。重要的约束放后面重申一次,不是啰嗦,是利用位置权重。
预算分配:一个起点模板
假设总预算 100 份,我会这样起步(按任务调):
| 区块 | 预算 | 说明 |
|---|---|---|
| System + 角色约束 | 5% | 稳定不变,吃缓存 |
| 工具定义(名+schema+说明) | 10% | 工具描述质量直接决定调用准确率 |
| 检索材料(RAG/文档) | 30% | 有引用价值才进来 |
| 对话/任务历史 | 35% | 滚动窗口,旧轮次摘要化 |
| 当前任务与输入 | 15% | 永远完整保留 |
| 余量 | 5% | 给输出留呼吸空间 |
原则:每样东西进入上下文都要付"租金",付不起租金的挤出去。
什么时候不用长上下文
百万窗口在手,三种场景我依然选 RAG/摘要:
- 重复访问同一批材料:每次全量塞入是按次付费;检索只取相关片段,缓存友好
- 材料会动态变化:全量上下文 = 每次变更全量失效;检索可以增量更新索引
- 需要精确引用定位:RAG 的片段天然带出处,全量上下文里模型引用页码的可靠性并不高
反过来,一次性深读几个文件再重构的场景,长上下文直接塞比 RAG 打散更合适——按访问模式选工具,不按窗口大小炫技。
实操检查清单
- system 与工具定义是否稳定(缓存命中)
- 是否存在"每次请求都重塞一遍的大块静态材料"——该进检索或缓存
- 对话历史有没有滚动摘要机制
- 关键约束是否在输入末尾重申
- 有没有观测:每个区块实际占多少 token(不测不知道,测了吓一跳)
上下文工程的第一性原理一句话:上下文是稀缺资源,工程就是预算管理。 下一篇讲预算里最贵的那块——记忆——怎么压缩。