<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>上下文窗口 on Chico's Tech Blog</title><link>https://realtime-ai.chat/tags/%E4%B8%8A%E4%B8%8B%E6%96%87%E7%AA%97%E5%8F%A3/</link><description>Recent content in 上下文窗口 on Chico's Tech Blog</description><image><title>Chico's Tech Blog</title><url>https://github.com/chicogong.png</url><link>https://github.com/chicogong.png</link></image><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Sat, 02 May 2026 11:00:00 +0800</lastBuildDate><atom:link href="https://realtime-ai.chat/tags/%E4%B8%8A%E4%B8%8B%E6%96%87%E7%AA%97%E5%8F%A3/index.xml" rel="self" type="application/rss+xml"/><item><title>百万级上下文真的能用吗</title><link>https://realtime-ai.chat/posts/long-context-reality/</link><pubDate>Sat, 02 May 2026 11:00:00 +0800</pubDate><guid>https://realtime-ai.chat/posts/long-context-reality/</guid><description>模型标称 1M、2M 上下文,但放得进不等于用得好。聊聊有效上下文、lost in the middle、长上下文下的成本与延迟暴涨,以及怎么实测验证。</description><content:encoded><![CDATA[<p>把一份 80 万字的项目文档整个粘进对话框,模型没报错,也回答了你的问题。你松了口气:看,1M 上下文真香。</p>
<p>但你有没有验证过——它引用的那段需求,是真的从文档第 40 万字的位置取出来的,还是它顺着上下文的语气编了一段听起来很对的话?</p>
<p>这是 2026 年长上下文最尴尬的地方:<strong>&ldquo;放得进&quot;是确定的,&ldquo;用得好&quot;是不确定的,而大多数人只测了前者。</strong> 模型厂商标 1M、2M,你看到的是窗口大小;你真正需要的是这个窗口里有多少 token 是&quot;模型会认真看&quot;的。这两个数字,差得比你想的大。</p>
<h2 id="标称上下文-vs-有效上下文">标称上下文 vs 有效上下文</h2>
<p>先把两个概念分清楚。</p>
<p><strong>标称上下文</strong>(advertised context)是模型 API 允许你塞进去的最大 token 数,超了就报错。<strong>有效上下文</strong>(effective context)是模型在性能开始明显掉档之前,真正能可靠利用的 token 数。</p>
<p>RULER 这个 benchmark 当年就是为了量化这件事造出来的。它的结论很扎心:很多号称 32K+ 的模型,在 32K 长度下能维持及格表现的,只有一半。到了 2026 年,百万级窗口普及之后,这个差距并没有消失——多份独立测试给出的经验值是,<strong>有效上下文通常只有标称值的 60%~70%</strong>,而且性能下滑的方式,简单的 token 计数根本看不出来:漏检的内容、编造的细节、断掉的推理链。</p>
<p>把 2026 年几个主流模型的标称窗口和实测召回放在一起看:</p>
<table>
  <thead>
      <tr>
          <th>模型</th>
          <th>标称窗口</th>
          <th>1M 长度实测召回</th>
          <th>备注</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Claude Opus 4.6</td>
          <td>1M</td>
          <td>~76%</td>
          <td>256K 下约 93%,长度档位领先</td>
      </tr>
      <tr>
          <td>Gemini 3.1 Pro</td>
          <td>1M</td>
          <td>~70%</td>
          <td>次于 Opus</td>
      </tr>
      <tr>
          <td>Gemini 1.5 Pro</td>
          <td>2M</td>
          <td>~55%~65%</td>
          <td>窗口最大,召回反而靠后</td>
      </tr>
      <tr>
          <td>Llama 4 Scout</td>
          <td>10M</td>
          <td>1M 后明显衰减</td>
          <td>标称最大,有效区间远小于标称</td>
      </tr>
  </tbody>
</table>
<p>注意 Gemini 1.5 Pro 这一行:它标 2M,是表里窗口最大的,但 1M 长度下的召回反而排在后面。<strong>窗口大小和有效质量,不是同一个排行榜。</strong> 标称 10M 的 Llama 4 Scout 也一样,过了 1M 之后衰减得很明显,适合做的是&quot;检索式&quot;任务,不是&quot;全局理解&quot;任务。</p>
<p>所以下次看到发布会上&quot;业界最长 2M 上下文&quot;的字样,你心里应该自动换算:能放 2M,能用好的可能就 1.2M 上下。剩下那 80 万 token,是放进去给你心理安慰的。</p>
<h2 id="lost-in-the-middle模型其实在跳读">Lost in the middle:模型其实在&quot;跳读&rdquo;</h2>
<p>为什么有效上下文会缩水?最经典的一个原因叫 lost in the middle。</p>
<p>2023 年那篇同名论文做了个很干净的实验:把同一条关键信息(needle)放在长文档的不同位置,看模型能不能答对。结果画出来是一条 <strong>U 形曲线</strong>——信息放在开头或结尾,模型答得很好;放在中间,准确率断崖式下跌。</p>
<p>说人话就是:模型读长文档的方式,和一个赶时间的人翻书很像——认真看了前言和结论,中间几百页基本是扫过去的。</p>
<p>这背后是注意力的问题。有研究把它归因为&quot;注意力稀释&rdquo;:context 越长,softmax 要把有限的注意力权重摊到越多的 token 上,每个 token 分到的&quot;关注&quot;就越薄。再叠加位置编码带来的偏置,中间段就成了被冷落的区域。有些极端的测量甚至说,某些前沿模型的有效注意力区间,比标称窗口短了高达 99%。</p>
<p>要补充一句公平话:<strong>这事在 2026 年比 2023 年好了不少。</strong> 像 Gemini 2.5 Flash 这种,做简单的事实型问答(needle-in-a-haystack)时,不管 needle 放在哪个位置,基本都能答对——简单检索上的 lost in the middle 已经被很大程度上修掉了。</p>
<p>但别高兴太早。needle 测试本身有个大问题:它太简单了。最近的研究(《Lost in the Haystack》)发现,<strong>needle 越小、越像&quot;一行字&quot;,越好找;一旦你要找的&quot;目标内容&quot;本身是一大段、需要跨段落拼起来的,定位难度立刻上去。</strong> 真实业务里,你要模型做的从来不是&quot;找一句话&quot;,而是&quot;把分散在第 3、19、56 章的三处约束综合起来判断&quot;。这种多跳、聚合类的任务,才是中间段塌方的重灾区。needle 测试绿灯,不代表你的任务绿灯。</p>
<h2 id="成本和延迟长上下文是按复利收费的">成本和延迟:长上下文是按复利收费的</h2>
<p>就算质量没问题,还有一笔账要算:钱和时间。</p>
<p>标准 self-attention 是 <strong>O(n²)</strong> 的。这个 n 是 token 数,平方意味着——prompt 翻一倍,attention 的计算量翻四倍。把 1M token 喂进一个标准 Transformer,光 attention 就是 1M × 1M 量级的矩阵运算,接近一万亿次操作。这笔账不是线性涨的,是带复利的。</p>
<p>最直接的体感是 <strong>TTFT(首 token 延迟)</strong>。模型要先把你的整个 prompt &ldquo;读&quot;一遍(prefill 阶段),才能吐第一个字。prefill 的耗时随上下文长度<strong>加速增长</strong>:从 4K 涨到 32K 你可能没什么感觉,从 32K 涨到 128K 就开始难受,而按观测到的幂律曲线(指数约 1.24)外推到 1M,<strong>第一个字出来可能要等 60~90 秒</strong>。</p>
<pre class="mermaid">flowchart LR
  A[4K prompt<br/>TTFT ~1s] --> B[32K prompt<br/>TTFT 数秒]
  B --> C[128K prompt<br/>TTFT 十几秒]
  C --> D[1M prompt<br/>TTFT 60-90s]
  style C fill:#fde7c2,stroke:#e8b23c
  style D fill:#f5b7b1,stroke:#c0392b
</pre><p>对任何交互式产品,这都是致命的。我之前写语音 Agent 的延迟预算时讲过,用户回应超过 800ms 就觉得别扭——你拿一个 prefill 要等一分钟的长上下文方案去做对话,等于直接出局。长上下文是离线批处理的工具,不是实时对话的工具。</p>
<p>钱也一样。虽然到 2026 年部分厂商(比如 Anthropic 的 Opus 4.6 / Sonnet 4.6)取消了长上下文的 2 倍溢价,1M 窗口按标准价 GA,但<strong>每次调用你都为整个 prompt 的所有 token 付费</strong>。一个 80 万 token 的上下文,你每问一句,这 80 万 token 就重新计一次费。一天问一百次,就是八千万 input token。多轮对话场景下,这个数字会失控。</p>
<p>更隐蔽的是 GPU 内存。多百万 token 的输入,KV cache 能吃掉 80%~90% 的显存。这意味着同样的硬件,长上下文请求能并发的数量大幅下降,单位成本进一步被推高——这部分不一定体现在 API 标价里,但会体现在你自建推理时的账单上。</p>
<h2 id="什么任务真的需要超长上下文">什么任务真的需要超长上下文</h2>
<p>讲了这么多问题,不是说长上下文没用。是说它<strong>被滥用了</strong>。</p>
<p>很多人把长上下文当成 RAG 的&quot;平替&rdquo;——&ldquo;既然能塞进去,我干嘛还搭检索系统&rdquo;。这个判断在大多数场景是错的。把无关内容大量塞进 context,不只是浪费钱,还会主动加重注意力稀释,把真正相关的那几千 token 淹掉。<strong>给模型的信息越精准,它表现越好;喂得越多越杂,反而越差。</strong></p>
<p>那什么任务是长上下文真正不可替代的?我的判断是,符合下面特征的:</p>
<ul>
<li><strong>需要全局视野,且无法预先切片检索。</strong> 比如让模型审一份 60 万字的合同,找出所有相互矛盾的条款。你没法提前知道哪两条会冲突,RAG 切片检索这时候帮不上忙——它本质上是个 N×N 的全局比对。</li>
<li><strong>跨度大、上下文强耦合的代码理解。</strong> 让模型理解一个几十万行的代码库的某个改动会牵连到哪里。调用关系是网状的,切片会切断它。</li>
<li><strong>长链多跳推理。</strong> 一份侦探小说式的材料,线索分散在几十处,需要全部在场才能推出结论。检索 top-k 很容易漏掉那个&quot;看起来不相关但其实是关键&quot;的片段。</li>
<li><strong>多模态长素材。</strong> 一段两小时的视频、一本带大量图表的书,本身就是一个不可切分的整体。</li>
</ul>
<p>发现规律没有?这些都是&quot;<strong>信息之间有强关联、无法干净切分</strong>&ldquo;的任务。反过来,如果你的任务是&quot;从一堆文档里找出和问题相关的那部分再回答&rdquo;——那是检索任务,老老实实用 RAG,又快又便宜又准。长上下文和 RAG 不是谁取代谁,是分工:<strong>能切就检索,不能切才全塞。</strong></p>
<h2 id="怎么实测一个模型的长上下文质量">怎么实测一个模型的长上下文质量</h2>
<p>最后给点能落地的。别信发布会,自己测。一个最小可行的验证流程:</p>
<pre class="mermaid">flowchart TD
  A[用你自己的真实长文档] --> B[在不同深度埋入可验证事实]
  B --> C[深度: 10% / 30% / 50% / 70% / 90%]
  C --> D[长度档: 32K / 128K / 256K / 1M]
  D --> E[每格跑多次取准确率]
  E --> F[画热力图: 深度 x 长度]
  F --> G{中间段是否塌方?}
  G -->|是| H[砍到有效长度内使用]
  G -->|否| I[再上多跳/聚合任务复测]
</pre><p>几个关键点:</p>
<p><strong>第一,用你自己的文档,别用公开 benchmark 的语料。</strong> 主流模型很可能在训练时见过 RULER、LongBench 这些数据,刷分会虚高。拿你业务里真实的合同、文档、代码去埋点,测出来的才作数。</p>
<p><strong>第二,测两类任务,别只测 needle。</strong> needle 检索(找一句话)和多跳聚合(综合好几处信息推结论)要分开测。前者现在大多数模型都能过,后者才是真正区分模型的地方。只测 needle 你会得到一个过于乐观的结论。</p>
<p><strong>第三,把&quot;深度 × 长度&quot;做成热力图。</strong> 横轴是上下文总长度,纵轴是关键信息埋入的相对位置(10%、30%、50%、70%、90%)。每个格子跑十几次取准确率。这张图一画出来,模型在哪个长度档开始掉、中间段塌不塌,一目了然。你的&quot;有效上下文&quot;就是这张图上还保持绿色的那个区间。</p>
<p><strong>第四,把延迟和成本一起记进表。</strong> 不只记准不准,把每个长度档的 TTFT 和单次调用费用也记下来。很多时候你会发现,128K 档的质量和 1M 档差不了几个百分点,但延迟和成本差了一个数量级——那 1M 就没有用的必要。</p>
<p>我的总结很简单:<strong>百万级上下文是个真实的能力,但它的有效区间,要靠你自己量出来,而不是读厂商的标称值。</strong> 把整本书塞进去之前,先问自己两件事——这个任务真的不能切片吗?我验证过模型在这个长度下中间段不塌方吗?这两个问题答不上来,那个 1M 窗口,大概率只是个让你安心的数字。</p>
<hr>
<p>参考资料:</p>
<ul>
<li><a href="https://arxiv.org/pdf/2404.06654">RULER: What&rsquo;s the Real Context Size of Your Long-Context Language Models?</a></li>
<li><a href="https://direct.mit.edu/tacl/article/doi/10.1162/tacl_a_00638/119630/Lost-in-the-Middle-How-Language-Models-Use-Long">Lost in the Middle: How Language Models Use Long Contexts</a></li>
<li><a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC12478432/">Lost in the Haystack: Smaller Needles are More Difficult for LLMs to Find</a></li>
<li><a href="https://tokenmix.ai/blog/1m-token-context-reality-check-2026">1M Token Context Reality Check 2026: Gemini vs Claude Latency</a></li>
<li><a href="https://www.digitalocean.com/community/tutorials/long-context-inference-production-cost">Long-Context Inference at Scale: The Hidden Infrastructure Cost</a></li>
<li><a href="https://dasroot.net/posts/2026/05/prefill-bottleneck-token-generation-latency-prompt-processing/">The Prefill Bottleneck Problem</a>
</content>
</invoke></li>
</ul>
]]></content:encoded></item></channel></rss>