上下文窗口是什么?长上下文有哪些问题?

进阶高频实践约 6 分钟读完

一句话回答

上下文窗口是模型一次调用能处理的 token 总数上限,输入(系统提示、工具定义、检索资料、历史消息)和输出加起来都不能超过它。窗口大不等于能用好:输入越长,成本和首 token 延迟越高,模型也更容易忽略中间部分的信息(lost in the middle)。实践中只放相关的内容,重要信息放在开头或结尾,超长时用检索、摘要或截断。

详细解析

窗口里装了什么

文本
输入:系统提示 + 工具定义 + 检索资料 + 历史消息 + 本轮问题
输出:模型的回答(推理模型还包括思考过程)
要求:输入 + 输出 ≤ 上下文窗口
  • 输入超出窗口时,多数接口直接报错;也有的服务提供自动截掉早期内容的选项
  • 很多模型还单独限制最大输出长度,通常比窗口小得多。输出达到上限会被截断,响应里的结束原因会标明是长度限制
  • 推理模型的思考过程也算输出 token,同样占窗口、同样计费,见 推理模型
  • 多轮对话每轮都要带上历史,越聊越长,需要截断或摘要,见 多轮对话的上下文怎么管理

长上下文的代价

代价 原因
成本 输入 token 每次调用都要付费;多轮对话每轮都重发历史,累计 token 数随轮数近似平方增长
延迟 模型要先处理完全部输入才能输出第一个 token,输入越长首 token 越慢;注意力的计算量随长度平方增长,见 Transformer
显存 KV Cache 随长度线性增长,限制了服务端的并发能力,见 KV Cache
效果 无关内容越多,模型越容易被干扰;中间位置的信息容易被忽略

固定的长前缀(比如系统提示、反复使用的同一份长文档)可以用 Prompt Caching 降低成本和首 token 延迟。

位置效应:lost in the middle

有研究把关键信息放在长上下文的不同位置测试,发现模型的表现呈 U 形:信息在开头或结尾时效果最好,在中间时明显变差。不同模型的程度不同,新模型有所改善,但"标称支持的长度"不等于"在这个长度上都能用好"。常见的"大海捞针"测试只考查能否找到一句话,比真实任务简单;需要综合多处信息的任务,效果随长度下降得更明显。

实践中的做法:

  • 长文档放在前面,指令和问题放在最后,不少厂商的文档也这样建议
  • 给每份资料加标签和编号(如 <doc id="3">),方便模型引用和定位
  • 只放和任务相关的内容:先检索、筛选、重排,再放进上下文
  • 关键约束可以在结尾简短重申一次

长上下文还是 RAG

直接放进长上下文 RAG 检索后再放入
适合 资料在窗口内,需要通读全文(总结、对比、找矛盾) 知识库远超窗口,问题只涉及局部
成本 每次为全文付费 只为检索到的片段付费
延迟 首 token 慢 多一步检索,但输入短
风险 中间信息被忽略,干扰多 检索不到正确的片段就答不出
维护 每次传入最新资料即可 要维护索引和更新

两者可以结合:用 RAG 召回更多、更大的片段,再交给长上下文模型。文档放不进窗口时的处理方法见 文档太长放不进上下文怎么处理,RAG 的链路见 RAG 的完整链路。

代码示例

TypeScript
// 调用前检查预算;countTokens 用对应模型的分词器或厂商的计数接口实现
function checkBudget(messages: Message[], tools: Tool[], limits: { context: number; maxOutput: number }) {
  const input = countTokens(messages) + countTokens(tools)
  // 留一些余量:消息格式、角色标记本身也占 token
  if (input + limits.maxOutput > limits.context * 0.95) {
    throw new Error(`输入约 ${input} token,加上预留的输出会超出上下文窗口`)
  }
}

const res = await llm.chat({ messages, tools, maxTokens: 2000 })
// 结束原因的字段名和取值各家不同,如 finish_reason 为 length、stop_reason 为 max_tokens
if (res.finishReason === 'length') {
  // 输出被截断:提高输出上限、让模型分段输出,或者提示用户内容不完整
}

面试官可能追问

模型标称支持很长的上下文,为什么实际效果不好?

标称长度只说明能放进去,不保证每个位置都用得好。一个原因是模型在超长输入上的训练相对较少,对远距离信息的利用不如近处;另一个原因是内容越多,干扰信息也越多。应该用自己的任务和数据,在不同长度下实测。

为什么资料放得越多,回答反而越差?

相关的片段被大量无关、甚至互相矛盾的内容淹没,模型可能引用错误的片段,或者在几份资料之间摇摆。检索阶段要重视精确度:召回后用重排只保留最相关的几条,见 重排序。

上下文窗口就是模型的"记忆"吗?

不是。窗口只是单次调用时模型能看到的内容,调用结束就没了,模型本身不会记住任何对话。聊天应用里的"记得之前说过的话",是应用每次把历史重新发过去;跨会话的记忆要存在外部,按需检索后再放进窗口,见 Agent 的记忆系统。

Prompt Caching 能解决长上下文的问题吗?

只能解决一部分。它让重复的前缀不必重新计算,降低成本和首 token 延迟;但不改变窗口上限,也不能改善模型对长文本中间部分的利用。

易错点

  • 窗口包括输出:输入把窗口塞满了,模型就没有空间生成回答
  • 多轮对话的成本是每轮输入的累加,不是只算新增的消息
  • "支持 X 长度"不代表在这个长度上的效果和短文本一样好
  • 输出被截断时要检查结束原因,否则会把半截内容当成完整结果

AI 模拟面试官

用自己的话回答,AI 对照参考答案打分、指出遗漏,再追问,最多 3 轮

登录后就可以和 AI 面试官对练,面试记录也会保存下来。登录

这道题你掌握了吗?

选一个最接近的状态,没掌握的题会出现在"我的进度 · 待复习"里。

学习记录暂存在本机浏览器。登录后自动同步到账号,换设备也能看到。