主题
第 5 章 · 上下文窗口的真相
上一章,我们见证了一个刚出生的模型如何通过预训练、监督微调和偏好对齐, 从一堆随机数长成了学识渊博、对答如流的智能体。
现在,你打开一个 AI 对话界面,发去了一份几万字的项目文档, 接着又围绕这份文档和它连续讨论了上百个回合。 起初它的回答精准无比,连文档角落里的一个括号备注都能照顾到; 可聊着聊着,诡异的事情发生了—— 它开始失忆了。
它先把你们在第一回合定下的格式约定忘得一干二净, 接着又对文档中间第三章的核心数据视而不见, 甚至开始一本正经地编造一份文档里根本不存在的结论,用一种"虽然我已经不记得前文写了什么,但我先顺着话头圆过去"的语气糊弄你。
你多半会觉得不可思议: "这台机器在预训练时明明吞下了几万亿字的图书馆,连冷门的历史年代都能倒背如流,为什么偏偏记不住我们十分钟前刚聊过的几千字?"
产生这个困惑,是因为人们太容易把模型身上的两种完全不同的记忆混为一谈。
这一章,我们将直面 Transformer 架构最冷酷的物理边界—— 上下文窗口(Context Window)。 理解了它,你就会明白:为什么大语言模型在物理本质上是一个"没有昨天"的生物,以及为什么即便窗口扩到百万 token,它的注意力也绝不平坦。
章节概要
- 两种记忆与"无状态":模型的"学识"焊死在参数权重里,而你们的"对话"全部寄居在临时的上下文窗口中——模型每一次开口,都是在把历史从头重读一遍
- 平方级膨胀的算力黑洞:为什么文本长度每翻一倍,attention 的计算代价会暴涨到原来的四倍
- 隐形的显存杀手:KV 缓存——为了不重复计算旧词而攒下的缓存,如何随着长度膨胀并卡住显卡的脖子
- 应对窗口满载的两种策略:当窗口被塞满时,消息截断与上下文压缩各自丢掉了什么
- U 型注意力曲线:首因效应、近因效应与致命的"中部迷失(Lost in the Middle)"
- "纸面长度"不等于"有效长度":从大海捞针测试到注意力稀释,为什么百万级窗口依然装不下贪婪
一、长期记忆、短期记忆与"无状态"
先把那个最核心的误会解开: 模型"知道地球绕着太阳转",和模型"知道你上一句说了什么",在物理实现上根本不是同一件事。
焊死的参数 vs 临时的黑板
当我们说一个模型懂微积分、懂莎士比亚、懂 Python 语法时,这些知识存在那几百亿、上千亿个参数权重之中。 这些权重是在训练阶段烧掉几千万美元电费、用梯度下降一点一点刻进去的。 一旦训练结束、模型上线服务,这几百亿个数字就彻底冻结、只读不改了。 这相当于模型的长期记忆。
而当你和模型对话时,你发过去的每一个字,都不会修改模型脑子里的哪怕一个参数。 模型用来处理当前这场对话的唯一空间,叫做上下文窗口(Context Window)—— 也就是模型单次调用时,能够同时塞进输入端的那串 token 序列的最大长度。
要直观区分这两者,可以把模型想象成一位站在小黑板前解题的学者:
- 模型的参数权重(长期记忆),是学者苦读二十年装在脑子里的学识与常识——一旦学成出山,脑子里的知识储备就固定了;
- 上下文窗口(短期工作记忆),则是学者面前那块写着当前题目与草稿的小黑板——他这一秒钟能回答出什么,完全取决于此刻黑板上写着哪些内容。
每次你点击发送按钮,前端系统就把当前所有的文字——系统设定、参考资料、之前的对话记录、以及你刚敲下的那句话—— 一并写到这块黑板上,推到学者面前。 学者扫视一眼黑板上的全部文字,顺着最后一行写下本轮的回复。
冷酷的真相:模型是"无状态"的
紧接着,整件事里最反直觉的一幕发生了。
在模型把这句回复递给你的一瞬间, 黑板被擦得干干净净,模型本人的短期记忆也立刻归零。
在计算机科学里,这种特性叫做无状态(Stateless): 模型内部没有任何一个变量,是用来跨回合保存"刚才我和谁聊到了哪儿"的。 它每说完一句话,状态就当场重置一次。
之所以你在聊第二句、第三句时觉得它依然记得第一句说过的话,全靠前端软件在背后替它做拼接。 来看一场只有两轮的简单对话,背后真实发送给模型的数据:
第一轮,你输入「你好,我叫小李」,模型收到的是:
【用户】:你好,我叫小李
模型算完概率,回答:「你好小李!有什么我可以帮你的?」 回答完毕,模型立刻归零,忘了这世上有个叫小李的人。
第二轮,你接着输入一句「我刚刚说我叫什么?」。 如果前端真的只把这八个字发给模型,模型一定会一脸茫然。 为了让对话看起来是连续的,前端软件必须把第一轮你的话、第一轮模型的回答、再加上你第二轮的新问题,悄悄拼接成一份完整的长文档,重新发给模型:
【用户】:你好,我叫小李 【助手】:你好小李!有什么我可以帮你的? 【用户】:我刚刚说我叫什么?
模型从来没有"记住"过你们的对话。 它没有一个持续运转的意识流,它只是在每一轮收到请求时, 以极快的速度把前端重新递过来的整部聊天史从头到尾重读了一遍,然后接着最后一行往下续写。
到了第 50 轮对话,前端就得把前 49 轮的所有来回全部打包重发一次; 到了第 100 轮,就要把前 99 轮全部重发一次。
既然每一轮都要把全部历史重新写回黑板,这块黑板的面积自然就成了最直接的瓶颈。
二、Attention 的算力黑洞:平方级膨胀与 KV 缓存
上下文窗口不能无限扩大,不是工程师抠门,而是被 attention 机制从数学根部锁死了。 这里横亘着两座大山:一座叫计算量的平方爆炸,另一座叫KV 缓存的显存膨胀。
第一座大山:两两配对的平方魔咒
在 attention 机制里,句子里的每一个 token,都要拿自己的 Query 去和前面所有 token 的 Key 做一次比对。 这就好比一个房间里每多走进来一个人,他都必须和房间里已有的所有人逐一握一次手。
我们来算一笔简单的账:
| 窗口内的 token 数量 | 相当于多长的文本 | 两两比对的次数 |
|---|---|---|
| 100 | 一小段话 | 100 × 100 = 1 万次 |
| 1,000 | 一篇千字短文 | 1,000 × 1,000 = 100 万次 |
| 10,000 | 一篇长报道 | 10,000 × 10,000 = 1 亿次 |
| 100,000 | 一本中篇小说 | 100,000 × 100,000 = 100 亿次 |
| 1,000,000 | 一套多卷本丛书 | 1,000,000 × 1,000,000 = 1 万亿次 |
注意看第一列和第三列的增速对比: 文本长度每翻 10 倍,attention 的计算量就要暴涨 100 倍;文本长度每翻 2 倍,计算量就变成原来的 4 倍。
在计算机科学里,这种增长规律叫做平方级复杂度。 当输入只有几百个词时,GPU 眨眼间就能算完; 但当输入拉长到几十万词时,那张两两配对的相关性分数表会膨胀成一个天文数字,再顶级的算力集群也会被拖进泥潭。
第二座大山:隐形的显存吞噬者——KV 缓存
如果说平方级膨胀是算力上的魔咒,那么在实际运行(尤其是自己部署模型或调用长上下文 API)时,还有一个更直接卡脖子的物理瓶颈:显存(VRAM)。
在自回归生成过程中,模型写一句话是一个 token 一个 token 往外蹦的。 写第 1000 个字时,第 1000 个字的 Query 需要去和前面 999 个字的 Key、Value 做比对; 等写到第 1001 个字时,第 1001 个字的 Query 又要去和前面 1000 个字的 Key、Value 做比对。
前面那 999 个字各自的 Key 牌子和 Value 牌子,在写第 1000 个字时明明已经算过一遍了。 因为在从左往右的生成过程中,每个词只往前看已经出现的词,无论后面又新续写了多少个字,前面那 999 个字自己的 Key 和 Value 向量永远不会变。 如果每写一个新字,都要把前面所有旧字的 Key 和 Value 从头重算一遍,生成速度会慢到让人绝望。
工程师的解决办法非常干脆:拿空间换时间。 每个 token 的 Key 和 Value 只要算出来一次,就立刻存进显卡的显存里; 生成下一个词时,直接从显存里读旧词的 Key 和 Value,只算最新这一个词就行。 这块专门存放历史 K、V 向量的显存区域,就叫 KV 缓存(KV Cache)。
它让生成速度飞了起来,但代价是显存占用随上下文长度线性暴涨。
算一笔直观的显存账: 假设你在本地运行一个常见的 70 亿参数(7B)模型,经过 4-bit 量化后,模型全身的参数加起来只占约 4 GB 显存。 但如果你用原版的多头注意力(MHA),把上下文窗口拉满到 32,000 个 token, 光是存这 32,000 个 token 在每一层、每一个头里的 K 和 V 向量,KV 缓存自己就要吃掉大约 16 GB 显存——整整是模型体重的四倍。 很多人明明买了一张能装下模型的显卡,对话一长却直接爆显存崩溃,凶手根本不是模型参数,而是不断膨胀的 KV 缓存。
工程师是怎么给 KV 缓存"瘦身"的?
为了把上下文窗口推向十万乃至百万 token,近几年的大模型架构演进,有一半心血都花在了压缩 KV 缓存上:
- GQA(Grouped-Query Attention,分组查询注意力):原版多头注意力里,如果一排有 32 个头,每个头都要各自存一份 Key 和 Value。GQA 的思路是:让 4 个或 8 个 Query 头共享同一份 Key 和 Value。这样模型的"提问视角"依然有 32 个,但需要缓存的 K、V 直接砍到了原来的四分之一甚至八分之一。Llama 3、Qwen 等主流模型全部采用了这一设计。
- MLA(Multi-head Latent Attention,多头潜在注意力):由 DeepSeek 在 V2/V3 中提出的更激进方案。它不再直接缓存高维的 K 和 V 向量,而是先用矩阵把它们压缩成一个极小维度的"潜在向量(Latent Vector)"存进显存,用的时候再临时解压出来。这一刀直接把 KV 缓存砍掉了 90% 以上,也是如今百万级上下文得以廉价普及的核心功臣之一。
- FlashAttention:由斯坦福大学 Tri Dao 等人提出的硬件级算法优化。它没有改变 attention 的数学结果,而是通过巧妙分块,避免把那张巨大的
注意力分数表真正写进缓慢的高带宽显存(HBM),全程在 GPU 极快的片上缓存(SRAM)里算完即焚,让长文本训练与推理的速度和显存效率双双起飞。
三、当窗口塞满之后:消息截断与上下文压缩
不管是早年 4K、8K 窗口的模型,还是 2026 年拥有 100 万 token 窗口的 DeepSeek-V4、Claude Fable 5、GPT-6 Astra, 只要一场对话一直聊下去,或者你不断往里投喂新资料, 总有一个时刻,历史 token 的总数会撞上窗口的物理上限。
如果直接把超长的数据硬塞给底层 API,服务器会当场报错拒收。 为了让对话不中断,前端软件必须在把数据送进模型之前,先对历史记录做清理。
目前几乎所有系统采用的清理策略,都逃不出以下两把刀:
第一把刀:滑动截断(Truncation)——直接丢弃最早的消息
这是最传统、最简单粗暴的做法。 系统在窗口最上方划出一块永久保护区,用来固定系统提示词(System Prompt); 至于底下的历史聊天记录,则按"先进先出"的队列管理—— 一旦新消息进来导致总 token 数超标,系统就会把最早的那几轮对话从列表里删掉。
这就是长对话失忆的第一重原因: 当你和模型聊到第 100 回合,让它回忆第 5 回合的一个细节时, 不是模型"笨"没记住,而是第 5 回合的那段文字在物理上已经不在发给模型的包裹里了。 模型看了一眼当前窗口,里面根本没有五十回合前那段话,却又因为微调训练出的"有问必答"习惯不敢冷场,于是只能顺着你问题里的暗示当场现编——这就是截断失忆引发幻觉的完整链条。
第二把刀:上下文压缩(Compression / Summarization)——归纳成摘要
直接扔掉旧消息太伤体验,于是现代对话系统与智能体(Agent)框架普遍引入了第二种策略:上下文压缩。
当最早的几十轮对话快要被挤出窗口时,系统不会直接把它们删了, 而是先在后台悄悄调用一次模型,下达一条指令: "请把上面这三十轮对话的核心事实与进展,浓缩成一段 300 字的摘要。"
接着,系统把那占地方的三十轮原始对话撤走, 换上这段浓缩摘要放在保护区下方,然后再接上最近的几轮原话。
这种做法保住了长程叙事的主干骨架——模型依然知道你们讨论过哪几个模块、达成了什么结论。 但代价同样不可避免:高分辨率的细节丢失了。 原本对话里微妙的语气转折、某一句随口提过的关键数字、或者埋在字里行间的伏笔,在被压成摘要的那一刻就被抹平了。
四、注意力的分配不均:U 型曲线与"中部迷失"
看到截断和压缩都会丢细节,很多人会自然地想到: 只要选一个拥有 100 万 token 上下文窗口 的模型,把所有资料和历史一字不删地全塞进去,模型应该就能记住一切了。
很遗憾,这正是长上下文时代最大的幻觉。 装得进窗口,不等于进得了脑子。
2023 年,斯坦福大学的 Nelson F. Liu 等研究者发表了一篇轰动业界的论文,标题直白得像一记耳光: 《Lost in the Middle: How Language Models Use Long Contexts》(中部迷失:语言模型如何使用长上下文)。
他们设计了一个极其干净的受控实验: 给模型输入 20 篇(或更多)从维基百科抽出来的文档片段,总长度几千到几万 token 不等。 在这堆文档里,只有 1 篇包含问题的真实答案,其余全是凑数的干扰项。 实验唯一改变的变量,就是把那篇"藏着正确答案的文档"放在整份输入的不同位置——有时放在第 1 篇(最开头),有时放在第 10 篇(正中间),有时放在第 20 篇(最末尾)。
例如,问题问的是:
who got the first nobel prize in physics (谁获得了第一个诺贝尔物理学奖?)
包含正确答案「威廉·伦琴(Wilhelm Röntgen)」的那段文字,清清楚楚地印在其中一篇文档里。 按理说,Transformer 里任意两个位置的距离都是 1,不管答案放在第几篇,模型都应该一眼看到才对。
然而测出来的准确率曲线,画出了一个深深的 U 字形:
- 当答案文档放在最开头时,模型准确率高达 75%–80% 以上;
- 当答案文档放在最末尾(紧挨着问题)时,准确率同样很高;
- 唯独当答案文档被挪到正中间时,准确率发生了断崖式暴跌——在部分模型上甚至跌到了 40% 以下,比完全不给任何文档、让模型纯靠预训练记忆闭卷盲答的准确率还要低!
也就是说,当关键信息夹在长文本的肚子中间时,模型不仅像得了近视眼一样看不见它,反而还被周围的大片文字晃花了眼。 这一现象由三种底层力量共同造就:
1. 首因效应(Primacy Effect)与"注意力汇聚点"
排在窗口最顶端的内容,天生拥有极高的注意力权重。
表面原因在于,预训练读过的几万亿字人类文章里,开头往往是标题、摘要、总纲,模型习惯了"开头最重要"。 更深层的原因则藏在架构本身: 在现代生成式大模型里,注意力是严格单向(从后往前看)的——排在后面的词只能往左关注排在它前面的词。 这导致了一个极端的不对称:
- 排在倒数第 1 位的词,只有它自己能看到自己;
- 而排在正数第 1 位、第 2 位的词,会被后面成千上万个词在每一层里反复"注视"。
此外,麻省理工学院的 Guangxuan Xiao 等人在 2023 年的研究中还发现了一种被称为注意力汇聚点(Attention Sinks)的现象: 因为 attention 的权重在归一化后加起来必须等于 1,当某个注意力头在当前句子里"没找到什么特别值得关注的对象"时,那笔多余的注意力预算总得找个地方存放。 把多余权重放在全文最开头的头几个 token 上,最不会干扰当前的局部语义。 于是,无论开头写的是什么,窗口最前端的位置都会像磁铁一样吸走大量注意力。
2. 近因效应(Recency Effect)
排在窗口最末端、紧贴着模型即将落笔处的文字,是直接的"当前刺激"。
这一条同样有双重加持: 一方面,自然语言里相邻句子的因果联系永远最紧密,预测下一个词最直接的线索就在前几个词里; 另一方面,主流的位置编码(如 RoPE 旋转位置编码)在数学性质上天然带有随相对距离拉远而温和衰减的倾向——离当前生成位置越近的 token,算出来的相关性越容易拿高分。
3. 中部迷失(Lost in the Middle)
当"最开头"靠着架构特权与首因效应分走了大块蛋糕, "最末尾"又靠着距离优势与近因效应拿走了另一大块蛋糕, 夹在两头之间的那一大片中部区域,就不可避免地沦为了注意力的低谷。
哪怕那段文字完完整整地躺在上下文窗口里,没有被截断、也没有被压缩, 只要它掉进了 U 型曲线的谷底,它在加权混合时分到的权重就会微弱到近乎隐形。
五、"纸面长度"与"有效长度"的鸿沟
理解了平方级计算膨胀、KV 缓存与 U 型曲线,我们还需要戳破长上下文时代的最后一层窗户纸: 厂商宣传页上的"最大上下文长度",和你能真正放心使用的"有效上下文长度",完全是两码事。
从 2024 年到 2026 年,大模型厂商之间展开了一场轰轰烈烈的"窗口军备竞赛": 从 128K 到 200K,再到 100 万 token、200 万 token。 很多模型在发布时都会晒出一张全绿的大海捞针测试(Needle in a Haystack)成绩单—— 往 100 万字的无关杂文里随机塞进一句毫无关联的话(比如"旧金山最好吃的三明治夹着花生酱和酸黄瓜"),然后问模型旧金山最好吃的三明治夹着什么,模型在任何位置都能百分之百找出来。
这给了许多人一种错觉,以为 U 型曲线和中部迷失已经被彻底攻克了。
但随后的多项严格基准测试(例如 NVIDIA 提出的 RULER 等复杂长文本评测)很快泼了一盆冷水: "大海捞针"之所以能全绿,是因为那根"针"和周围的"海"长得太不一样了,纯粹的关键词检索就能把它揪出来。
一旦把考题换成真实世界里的复杂任务—— 比如要求模型在 20 万字的记录里同时找出散落在 5 个不同位置的前后线索,汇总统计,或者根据第 1 万字的一条规则去修正第 15 万字的一处判断—— 绝大多数模型的准确率依然会在 32K 到 64K token 之后出现显著滑坡。
原因很简单:由于注意力机制的总权重恒定为 100%,当窗口里的总词数从 1,000 暴涨到 500,000 时,参与瓜分这 100% 权重的候选者多了整整五百倍。 哪怕模型看清了正确的线索,成千上万个微弱的无关背景词加在一起,也会像往一勺浓汤里兑进十桶白开水一样,把原本清晰的注意力信号冲淡成一碗温吞水。
为什么长窗口会稀释注意力?(Softmax 分母效应)
回到第 3 章里将相关性分数转化为注意力权重的 softmax 公式:
注意看分母
这就是上下文窗口不可违逆的物理定律: 窗口越大,噪音越容易稀释信号;位置越居中,信息越容易石沉大海。 在后面的第二部里,我们将看到人类与 AI 对话的一切编排艺术,几乎都是在与这几条物理定律做正面的周旋。
六、写给下一章
到这里,我们已经把大语言模型这台机器本身看了个通透—— 从七十年的来路,到 Transformer 的引擎内部, 从三阶段的训练管线,到刚刚直面的记忆瓶颈与注意力曲线。
屏幕背后的那个大脑,对你而言已经不再是黑盒了。
但第一部还差最后一块拼图。 前五章里,我们一直在剖析"一台标准的大语言模型"是怎么工作的; 可今天你拉开模型列表,看到的不是一台机器,而是一整个庞大的动物园: GPT、Claude、Gemini、DeepSeek、Llama、Qwen、GLM…… 有的擅长快问快答,有的会在回答前先自己在后台"思考"几分钟;有的严丝合缝却闭源收费,有的开放权重能直接装进你家里的电脑。
它们明明全都脱胎于 2017 年那篇 11 页的论文,为什么会分化成今天这样百花齐放的格局?
下一章,我们把镜头拉远,用一整章走完 Transformer 诞生之后这九年的进化史。 看清一个模型是从哪条技术路线上走来的,你才能真正摸准它的脉搏。
要点回顾
- 无状态的本质:模型本身没有跨回合的动态记忆,它的长期学识焊死在参数权重里,短期交互则全靠前端在每一轮把全部历史记录重新打包放进上下文窗口,"从头重读一遍"。
- 平方级计算魔咒:由于 attention 需要让每个位置与所有前序位置两两比对,文本长度每翻一倍,计算代价暴涨到原来的四倍。
- KV 缓存与显存瓶颈:为了避免在逐词生成时反复重算前文的 Key 和 Value,系统会将它们缓存在显存中;KV 缓存随上下文长度线性膨胀,推动了 GQA、MLA 与 FlashAttention 等关键优化的诞生。
- 截断与压缩:当对话超出窗口上限时,系统要么直接丢弃最早的记录(滑动截断),要么将其浓缩为摘要(上下文压缩),前者丢失整段历史,后者丢失高分辨率细节。
- U 型注意力曲线:模型对窗口最开头(首因效应 + 注意力汇聚点)和最末尾(近因效应)的内容最为敏感,而夹在中间的信息极易遭遇"中部迷失(Lost in the Middle)"。
- 纸面长度 ≠ 有效长度:大海捞针满分只代表单点检索能力;随着上下文拉长,注意力权重的分母稀释效应依然会让多跳推理与复杂指令跟随能力随长度衰减。
参考文献
- Liu, N. F., Lin, K., Hewitt, J., Paranjape, A., Bevilacqua, M., Petroni, F., & Liang, P. (2024). Lost in the Middle: How Language Models Use Long Contexts. Transactions of the Association for Computational Linguistics, 12, 157–173. arXiv:2307.03172.
- Dao, T., Fu, D. Y., Ermon, S., Rudra, A., & Ré, C. (2022). FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness. In Advances in Neural Information Processing Systems (NeurIPS 2022), pp. 16344–16359. arXiv:2205.14135.
- Ainslie, J., Lee-Thorp, J., de Jong, M., Zemlyanskiy, Y., Lebrón, F., & Sanghai, S. (2023). GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints. In Proceedings of EMNLP 2023, pp. 4895–4901. arXiv:2305.13245.
- Xiao, G., Tian, Y., Chen, B., Han, S., & Lewis, M. (2024). Efficient Streaming Language Models with Attention Sinks. In ICLR 2024. arXiv:2309.17453.
- Hsieh, C.-P., Sun, S., Kriman, S., Acharya, S., Rekesh, D., Jia, F., Zhang, Y., & Ginsburg, B. (2024). RULER: What's the Real Context Size of Your Long-Context Language Models? In COLM 2024. arXiv:2404.06654.