← 返回 AI Agent 课程
【中文精读】Lost in the Middle: How Language Models Use Long Contexts
导读:为什么读这篇。这是「上下文位置偏差」(position bias)/「context rot」现象最重要的一篇实证论文,也是上下文工程(context engineering)领域「长上下文 ≠ 有效上下文」这条核心信条的学术源头。在它发表之前(2023 年中),业界的默认心智模型是:上下文窗口是一块均匀可用的内存,只要东西塞得进去,模型就能用。这篇论文用两个可控实验干净利落地证伪了这个假设——模型对相关信息出现在上下文中的位置极其敏感,信息放在开头或结尾时表现最好,埋在中间时性能大幅塌陷,最差时甚至不如完全不给上下文。今天你在 agent 开发中习以为常的做法——重要指令放 system prompt 开头、最新状态放对话末尾、RAG 检索结果要 rerank、context 要压缩而不是无脑堆长——追溯上去,实证依据基本都锚在这篇论文。读它的意义不在结论本身(结论早已成为常识),而在于搞清楚这个结论是如何被严格测出来的:控制变量的实验设计(只动答案位置,其他全部不变)是评估任何长上下文系统时都该复用的方法论。
一、论文概览与核心论点
论文标题直译是「迷失在中间:语言模型如何使用长上下文」。作者提出的问题很朴素:下游任务(检索增强问答、多文档摘要等)都要求模型在长输入里定位并使用相关信息,那么模型真的能均匀地利用整个上下文窗口吗?
回答方式是两个可以精确控制「相关信息位置」和「上下文长度」这两个变量的任务:多文档问答(multi-document QA)和合成键值检索(synthetic key-value retrieval)。核心发现可以概括为三句话:
- 模型性能随相关信息位置呈显著的 U 型曲线(U-shaped performance curve):信息在上下文最开头(对应认知心理学中的首因效应,primacy bias)或最末尾(近因效应,recency bias)时性能最高,在中间时显著退化。
- 退化幅度大到反直觉:GPT-3.5-Turbo 在 20 文档设置下,答案文档在中间位置时的准确率(53.8%)低于它完全不看任何文档的闭卷(closed-book)基线(56.1%)——给了 20 篇文档、其中确实有一篇含答案,还不如什么都不给。
- 扩展上下文窗口的模型并不能更好地使用上下文:GPT-3.5-Turbo 与其 16K 版、Claude-1.3 与其 100K 版,在同一份能装进两者窗口的输入上,性能曲线几乎重合。窗口更大只意味着「装得下」,不意味着「用得好」。
引言最后一段的立场值得转述:作者认为,声称一个模型能鲁棒地使用长上下文,不能只看它能接受多长的输入,而必须证明其性能对相关信息位置的变化不敏感——这实际上为后来所有长上下文评测(各种 needle-in-a-haystack 变体)定下了协议。
二、任务与实验设置(能复述的程度)
2.1 多文档问答(multi-document QA)
这是论文的主实验,构造方式如下:
- 数据来源:NaturalQuestions-Open(真实的 Google 搜索查询 + 人工标注的 Wikipedia 答案)。作者选取长答案标注为「段落」类型的 2655 个查询;文档是切成至多 100 token 的 Wikipedia 段落块。
- 输入构成:每个问题给 k 篇文档,其中恰好 1 篇是含标注答案的 Wikipedia 段落(答案文档),另外 k−1 篇是干扰文档(distractor)——用在 MS-MARCO 上微调过的 Contriever 检索器取「与查询高度相关但不含任何标注答案」的段落,按相关度递减排列。也就是说干扰项是「难负例」,不是随机噪声(附录里另做了随机干扰文档的消融,趋势一致)。
- 两个自变量:① 位置——通过重排文档,把答案文档放到不同的位置(如 20 文档设置下测位置 0、4、9、14、19);② 长度——通过增减干扰文档数量,设 k = 10 / 20 / 30 三档。
- 两个对照基线:closed-book(不给任何文档,纯靠参数记忆答题)和 oracle(只给那一篇答案文档)。GPT-3.5-Turbo 的 closed-book 为 56.1%,oracle 为 88.3%——这两个数把「上下文帮了多少忙」的量程标定出来了。
- 指标:准确率,判定标准是任一标注答案字符串是否出现在模型输出中;贪心解码。
2.2 合成键值检索(synthetic key-value retrieval)
为排除「自然语言语义」这个混淆因素,作者又设计了一个纯符号任务:输入是一个序列化成字符串的 JSON 对象,含 k 个键值对,键和值都是随机生成的 128 位 UUID;要求模型返回指定键对应的值。1 个目标键值对 + k−1 个干扰键值对,同样通过移动目标键位置、增减干扰对数量来控制两个变量。设 k = 75 / 140 / 300 三档,每档 500 个样本。这个任务没有任何语言理解成分,纯粹测「从上下文中按位置捞信息」的能力——它也是后来「大海捞针(needle-in-a-haystack)」类评测的直接前身。
2.3 评估的模型
- 开源:MPT-30B-Instruct(8192 token 窗口,ALiBi 位置编码)、LongChat-13B-16K(LLaMA-13B 经旋转位置编码压缩 + 16K 序列微调扩展而来)。
- 闭源(均为 0613 版本):GPT-3.5-Turbo(4K)及其 16K 版、Claude-1.3(8K)及其 100K 版;GPT-4-8K 只在 500 个样本的子集上跑了 20 文档设置(作者注明完整评估要花超过 6000 美元)。
- 分析用:Flan-T5-XXL、Flan-UL2(编码器-解码器架构)、MPT-30B 基座(对比指令微调影响)、Llama-2 7B/13B/70B 全家(对比规模与 SFT/RLHF 的影响)。
三、主要结果:U 型曲线有多深
论文最著名的那张图(图 1 / 图 5)无法搬运,一句话转述:横轴是答案文档在上下文中的位置,纵轴是准确率,每个模型的曲线都呈两头高、中间凹的 U 型,且上下文越长(文档越多)凹得越深;GPT-3.5-Turbo 的 20 文档曲线在中间位置跌破了图中标出的 closed-book 水平虚线。
关键数字(答案在开头 / 中间最低点 / 结尾):
| 设置 | 模型 | 开头 | 中间最低 | 结尾 | 最大落差 |
| 10 文档 | GPT-3.5-Turbo | 76.8% | 61.2% | 62.4% | 15.6 pt |
| LongChat-13B-16K | 72.1% | 58.9% | 58.5% | 13.6 pt |
| Claude-1.3 | 62.9% | 58.3% | 59.7% | 4.6 pt |
| 20 文档 | GPT-3.5-Turbo | 75.8% | 53.8% | 63.2% | 22.0 pt |
| LongChat-13B-16K | 68.6% | 52.5% | 55.0% | 16.1 pt |
| 30 文档 | GPT-3.5-Turbo-16K | 73.4% | 50.5% | 63.7% | 22.9 pt |
| LongChat-13B-16K | 66.9% | 51.3% | 55.1% | 15.6 pt |
解读几个要点:
- 落差超过 20 个百分点:同样的问题、同样的 20 篇文档,只是把答案那篇从第 1 位挪到第 10 位,GPT-3.5-Turbo 准确率从 75.8% 掉到 53.8%。论文原话是性能可以「下降超过 20%」。
- 低于闭卷基线:20 文档中间位置 53.8% < closed-book 56.1%(低 2.3 pt);30 文档下 16K 版最低 50.5%,低 5.6 pt。含答案的上下文摆错了位置,产生的是负价值——干扰文档的干扰超过了答案文档的贡献。
- 首因 > 近因:注意曲线两端并不对称,开头(76.8%)明显高于结尾(62.4%)。这与很多人「模型偏爱最近 token」的直觉相反——至少在这种一次性长输入的问答格式里,开头位置是最强的。
- 扩展上下文版本没有优势:GPT-3.5-Turbo 与 16K 版在 10 文档下的曲线几乎完全重合(76.8/61.2/62.4 vs 76.9/61.0/62.5);Claude-1.3 与 100K 版的 closed-book(48.3% vs 48.2%)和 oracle(76.1% vs 76.4%)也几乎相同。只要输入装得进两个版本的窗口,「大窗口版」没有任何额外的上下文利用能力。
- Claude-1.3 是个有趣的例外:它整体绝对分数不算最高,但曲线最平(落差仅 4~5 pt),在这套评测里是位置鲁棒性最好的模型。GPT-4(子集评测)绝对性能最高,但同样存在 U 型。
键值检索任务上模式类似但分化更大:Claude-1.3 及 100K 版接近满分;GPT-3.5 系列和 MPT-30B-Instruct 在目标键位于中间时明显掉分(GPT-3.5-Turbo-16K 在 300 对设置下最差 45.6%);LongChat-13B-16K 出现了一个滑稽的失败模式——目标在开头时它倾向于写一段检索代码而不是直接输出值。这说明位置偏差不依赖自然语言语义,是更底层的行为。
四、分析:为什么会「迷失在中间」(第 4 节)
论文第 4 节做了三组归因实验,这是全文方法论上最扎实的部分。
4.1 架构:解码器 vs 编码器-解码器
纯解码器(decoder-only)模型是因果注意力,每个 token 只能看到它之前的内容,查询(query)出现在末尾时,前面的文档在被编码时「不知道自己将要被问什么」。编码器-解码器(encoder-decoder)模型的编码器是双向注意力,理论上能相对查询双向地加工每篇文档。实验证实:Flan-UL2 在其 2048 token 训练长度之内时对位置变化相当鲁棒(最好与最差仅差 1.9 个百分点);但一旦输入超出训练长度、需要外推,U 型曲线照样出现。Flan-T5-XXL(训练长度 512)同样如此。结论:双向编码有帮助,但只在训练见过的长度范围内有效——位置偏差部分来自架构,部分来自「训练分布里没见过这么长」。
4.2 查询感知上下文化(query-aware contextualization)
既然因果注意力的问题是「文档编码时看不到查询」,一个廉价补救是把查询在数据前后各放一份。结果两极分化:在键值检索任务上这一招近乎药到病除——GPT-3.5-Turbo-16K 在 300 对设置下从最差 45.6% 直接拉满到 100%;但在多文档 QA 上几乎无效,只在答案位于最开头时略有提升,其他位置反而略降。作者的启示是:纯粹的「按键索引」式检索可以靠提示工程修复,但真实语言任务里的中间塌陷是更深层的问题,不是把 query 复读一遍能解决的。
4.3 指令微调是不是元凶
一个自然的怀疑:指令微调数据通常把任务说明放在开头,是不是这教会了模型偏爱开头?对比 MPT-30B 基座与 MPT-30B-Instruct:两者都有 U 型,基座反而更严重(最好最差差约 10%,指令微调后缩到约 4%)。所以指令微调不是原因,反而是缓解剂。Llama-2 全家的补充实验给出了更细的图景:7B 只表现出近因偏置(recency bias),没有 U 型;13B 和 70B 才出现 U 型(首因效应随规模涌现);13B 基座落差约 20 pt,chat 版缩到约 10 pt;70B 有无 RLHF 趋势基本一致。作者据此推测,更早期文献没观测到首因偏置,是因为当时研究的模型太小(<1B)。这条「U 型随规模涌现」的发现经常被引用,值得记住。
五、更多上下文总是更好吗?开放域 QA 案例(第 5 节)
最后一节把结论落到实际的检索增强(retrieval-augmented)系统:Contriever 检索 + 模型阅读的标准 open-domain QA 管线。检索器的召回率(recall)随取回文档数增加持续上升,但阅读器(reader)的准确率远在召回率饱和之前就先饱和了:文档数从 20 加到 50,GPT-3.5-Turbo 只提升约 1.5%,Claude-1.3 约 1%,代价却是显著更长的输入、更高的延迟和费用。图表结论一句话转述:recall 曲线还在爬坡,reader 准确率曲线已经躺平。这直接推出两条工程建议:要么用重排序(reranking)把最相关的文档推到上下文开头,要么干脆截断检索列表、少给文档——「把召回率再堆高 10 个点」对端到端效果几乎没有意义,因为瓶颈在模型用不好长上下文,不在检索器找不到。
六、相关工作与结论、局限
相关工作部分把发现接到了两条脉络上:一是当时爆发的长上下文架构研究(作者的立场是「窗口长度」与「使用能力」必须分开评估);二是认知心理学里经典的序列位置效应(serial-position effect)——人类自由回忆列表时同样是首尾记得牢、中间遗忘多。LLM 的 U 型曲线与之惊人地同构,尽管成因机制未必相同。
结论重申:模型在长输入中定位与使用信息的能力远未达到「窗口内均匀可用」,新的长上下文模型应当以「位置鲁棒性」为评测标准。局限方面值得自己心里有数的几点:① 评测的都是 2023 年中的模型,后来的模型(GPT-4-Turbo、Claude 3 之后)在 needle 类测试上的表面分数已大幅改善,但「中间信息利用弱于两端」的现象在更难的多跳/聚合任务上仍反复被后续工作复现——这也是「context rot」一词延续至今的原因;② 任务都是「单点定位」型(一篇答案文档 / 一个目标键),没有覆盖需要综合全上下文的任务;③ 判定答案靠子串匹配,偏宽松;④ 归因分析给出了相关因素(架构、训练长度、规模、指令微调)但没有给出机制层面的最终解释(后续工作指向注意力汇聚 attention sink、旋转位置编码的长程衰减等,超出本文范围)。
对开发者的启示
- 位置即资源:把最关键的信息放在上下文的开头或结尾,永远不要埋在中间。system prompt 的核心指令放最前,当前任务/最新状态放最后;长文档问答时把问题在材料前后各放一份(query-aware contextualization)在纯检索型任务上尤其有效。
- RAG 必须 rerank,且宁少勿多。检索器给的「按相关度排序」要真正体现在拼接顺序上(最相关的放两端);盲目增加取回文档数在 reader 侧收益趋零、成本线性涨,甚至因干扰增多而变负——先测 reader 饱和点再定 k。
- 不要把厂商标称的窗口长度当有效长度。「装得下」和「用得好」是两回事,同一模型的扩展窗口版并不更会用上下文。为你自己的任务做位置扫描评测(固定内容、只动关键信息位置),用「最好-最差落差」衡量可用性,而不是只看 needle 测试的满分截图。
- 长上下文塞满 ≠ 免费的记忆,压缩与外置优于堆长。agent 的历史轨迹、工具输出无脑追加,会把早先的关键决策推进「中间坟场」;定期摘要压缩、把长期状态写入文件/数据库按需取回,比依赖模型自己从 10 万 token 中间捞信息可靠得多。
- 警惕「负价值上下文」。本文最惊悚的数字是「有答案的 20 篇文档 < 闭卷」:不相关内容不是中性填充物,是主动的干扰源。往上下文里加任何东西之前,问一句——它值回它挤占的位置吗?