什么是上下文窗口?
一份 token 预算,同时装下 prompt、对话历史、检索到的文档和正在生成的回答。所有东西都在抢同一块空间。
先动手,再深入阅读
为答案留出空间 · 5 分钟上下文窗口(context window)是模型一次能考虑的最大 token 数量。「200K 上下文窗口」意味着 200,000 个 token。
容易被忽略的是,这一份预算要同时装下所有东西:
- system prompt
- 到目前为止的完整对话历史
- 你粘贴进来的任何文档或代码
- 当前的问题
- 正在生成的回答
最后一项最容易让人栽跟头。输出和输入共用同一份预算。在一个 200,000 token 的模型上,用输入占掉 199,000 个 token,就只剩大约一千个 token 留给回答。
为什么会有上限
有两个原因,而且彼此独立。
注意力的开销随长度的平方增长。每个 token 都要和其他每个 token 做比较,所以上下文长度翻倍,注意力的计算量就变成四倍。
而 KV cache 显存随长度线性增长,还要乘以每一层、每一个头。对同时服务大量用户的服务商来说,cache 占用的显存通常才是硬约束。
这个上限是资源决定的,不是随意划出来的产品档位。
超出上限会怎样
不同系统的处理方式不一样,搞清楚自己面对的是哪一种很重要。
直接报错。 裸 API 通常直接返回错误。干脆利落,你马上就知道。
截断。 最旧的内容被丢掉。这在聊天框架里很常见,而且危险,因为它是静默发生的——你精心写好的 system prompt 可能悄悄从最前面掉出去,而对话看上去一切正常。
滑动窗口。 只持续保留最近 n 个 token。对话可以无限进行下去,但模型是真的忘掉了更早的轮次。
摘要压缩。 更早的历史被压缩成一段摘要并取而代之。保住大意,丢掉细节,还可能悄悄扔掉那个真正关键的细节。
「它怎么忘了我之前说的话」通常就是这个原因:不是模型有缺陷,而是某种上下文管理策略在悄悄运行,没告诉你。
标称长度不等于可用长度
一个能接受 200,000 个 token 的模型,未必能好好利用中间那部分。
有两个独立的原因。有些架构用的是近似注意力,它是真的不会看遍所有位置。即便用的是精确注意力,模型也必须学会利用远处的位置——极远距离上的位置模式在训练数据里本就少见。容量是有的,练出来的本事未必有。
可观察到的结果是一个被反复记录的现象:长上下文开头和结尾的信息,比埋在中间的信息更容易被可靠利用。
实用的应对方式就是把重要的东西放在两端。如果某份文档至关重要,别把它埋在 80,000 个 token 里的第 40,000 位。
管理这份预算
检索,而不是硬塞。 挑对 2,000 个 token,通常胜过发 100,000 个然后碰运气。既更便宜又更准确——这就是检索增强生成(RAG)存在的理由。
主动给对话历史设上限。 自己决定保留什么,而不是让框架悄悄截断。保留 system prompt 加上最近几轮,往往比一直全留着直到崩掉更好。
把稳定的内容放在最前面。 不只是出于注意力的考虑——固定的前缀还能启用前缀缓存,降低成本和延迟。
盯住平方级增长。 每一轮都要重发整份记录,所以 n 轮对话的 token 总量按 n² 增长。长对话变贵的速度比感觉上要快。
给输出留出余量。 如果你需要一个 2,000 token 的回答,就得为它留好预算。
发送前先数一遍
每家服务商都会提供 tokenizer,而粗略的英文估算规则只能算个数量级——遇到代码、非英文文本,以及任何格式不常规的内容,它都会误导你。
但凡撞上上限这件事有影响,就用真正的 tokenizer 去数,别靠估。
要点回顾
- 一份预算要装下 system prompt、历史、文档、问题,还有生成的回答。
- 上限之所以存在,是因为注意力开销是平方级的,而 KV cache 显存是线性但很大的。
- 超限后的行为各不相同:直接报错、静默截断、滑动窗口、摘要压缩——「它忘了」的抱怨大多来自静默截断。
- 标称长度不保证中间部分被用好;把重要内容放在两端。
- 检索胜过硬塞;对话成本随长度的平方增长。
下一篇: AI 模型为什么会产生幻觉?