LLM - 大模型上下文窗口管理的那些事:从长度焦虑到上下文工程
2026/8/4 2:45:35 网站建设 项目流程

文章目录

  • 从「能塞多少」到「该塞什么」
  • 一、上下文窗口到底是什么
    • 1.1 一个直观的定义
    • 1.2 为什么会有这个上限
  • 二、长上下文的幻觉:为什么「读得进」不等于「读得懂」
    • 2.1 「迷失在中间」(Lost in the Middle)
    • 2.2 上下文腐烂(Context Rot)
    • 2.3 注意力预算是有限的
    • 2.4 成本与延迟的现实约束
  • 三、把窗口做大:扩展上下文的底层技术
    • 3.1 位置编码:让模型理解「顺序」
    • 3.2 位置插值:用微调「撑大」已有模型
    • 3.3 高效注意力:击穿 O(n²) 的墙
    • 3.4 KV Cache:推理阶段真正的显存大户
  • 四、上下文管理的四大核心策略
    • 4.1 截断与滑动窗口:最朴素也最危险
    • 4.2 摘要压缩:用信息密度换空间
    • 4.3 检索增强(RAG):把外部知识按需取用
    • 4.4 记忆系统:短期与长期的分层
  • 五、Agent 时代的上下文工程
    • 5.1 从提示词工程到上下文工程
    • 5.2 一次 Agent 调用里,上下文由什么组成
    • 5.3 工具结果是最大的「上下文杀手」
    • 5.4 让 Agent 拥有「外部草稿纸」
    • 5.5 多智能体:用「分而治之」对抗上下文膨胀
    • 5.6 一个贯穿全流程的例子:深度研究 Agent
  • 六、评估与调优:让上下文管理可度量
    • 6.1 关注这几个核心指标
    • 6.2 用「大海捞针」测试评估长上下文能力
    • 6.3 几条经验法则
  • 七、未来的方向
  • 结语

上下文窗口曾经是模型能力的天花板,如今它更像是一门需要精心经营的资源管理艺术。当模型的注意力有了标价,如何在有限的窗口里放对信息、放好信息,成了决定 Agent 成败的关键。

从「能塞多少」到「该塞什么」

两年前,大家谈论上下文窗口时,讨论的焦点几乎只有一个数字:4K、8K、32K、128K、200K、1M……仿佛这个数字越大,模型就越聪明。彼时的技术竞赛,本质上是一场「长度军备竞赛」。

但真正把大模型用进生产环境的工程师很快发现事情没那么简单。窗口开到了 100 万 token,模型却在第 30 万个 token 处「失忆」;上下文塞得越满,回答反而越含糊;一次调用的账单肉眼可见地膨胀,延迟也随之攀升。人们逐渐意识到:上下文窗口的大小决定了能力的上限,而上下文的管理方式决定了能力的下限——而后者才是日常工程中真正稀缺的东西。

于是,一个新的工程学科在 2024 到 2026 年间悄然成型,它有了一个专门的名字:上下文工程(Context Engineering)。它不再关心「模型最多能读多少字」,而是关心「在每一步推理时,究竟该把哪些信息、以什么结构、按什么顺序放进那扇有限的窗口里」。

这篇文章会从底层机制讲起,一路走到 Agent 时代的上下文工程实践。无论你是在做 RAG、搭 Agent,还是单纯想搞清楚为什么你的长文档问答效果时好时坏,都能在这里找到一条清晰的脉络。


一、上下文窗口到底是什么

1.1 一个直观的定义

上下文窗口(Context Window)指的是模型在一次前向推理中,能够「同时看到」的 token 数量上限。它既包括你输入的提示词(Prompt),也包括模型自己生成的内容(Completion),二者共享同一个预算。

这里的基本单位是token,而不是字或词。对中文来说,一个汉字通常占 1 到 2 个 token;对英文来说,一个常见单词大约是 1 个 token,较长或较生僻的词会被切成多个子词(subword)。粗略估算时,可以按「1 个中文汉字 ≈ 1.5 token,1 个英文单词 ≈ 1.3 token」来心算。

举个例子,一个宣称支持 128K 上下文的模型,理论上能一次性处理约 8~9 万个汉字,相当于一本中篇小说。但请记住这个额度是输入和输出共用的——如果你把 12 万 token 全塞进了输入,那留给模型回答的空间就只剩下几千 token 了。

1.2 为什么会有这个上限

上下文窗口不是产品经理拍脑袋定的数字,它背后是实打实的计算与显存约束。理解这一点,才能理解后续所有优化技术的动机。

主流大模型基于 Transformer 架构,其核心是自注意力机制(Self-Attention)。在计算注意力时,序列中的每一个 token 都要和其他所有 token 计算相关性。这意味着当序列长度为 n 时,注意力矩阵的规模是 n × n,计算复杂度和显存占用都是O(n²)

这个平方关系是残酷的:序列长度翻一倍,注意力的计算量和中间显存就要翻四倍。从 8K 扩到 128K 是 16 倍的长度增长,朴素实现下对应的却是约 256 倍的注意力开销。这就是为什么长上下文一度是一件极其昂贵的事。

除了注意力矩阵本身,还有一个常被忽视但在推理阶段更关键的显存消费者——KV Cache,我们会在第三节专门展开。


二、长上下文的幻觉:为什么「读得进」不等于「读得懂」

在深入技术之前,必须先破除一个普遍的误解:把窗口开大、把资料全塞进去,模型就能用好这些信息。大量实验和线上事故都证明,事实恰恰相反。

2.1 「迷失在中间」(Lost in the Middle)

这是长上下文领域最著名的现象之一。研究者把关键信息放在一段长上下文的不同位置进行测试,结果发现了一条清晰的 U 形曲线:模型对开头结尾的信息记得最牢,而放在中间的关键信息,检索准确率会显著下降。

这意味着,如果你把一份 50 页的合同一股脑丢给模型,然后问一个答案恰好埋在第 25 页的问题,模型很可能视而不见——哪怕这段文字确确实实在它的上下文窗口里。

实践启示很直接:把最重要的指令和信息放在上下文的开头或结尾,不要指望模型在漫长的中段保持同等的注意力。

2.2 上下文腐烂(Context Rot)

随着上下文变长,模型整体性能会缓慢而持续地退化,这种现象被称为「上下文腐烂」。它不是在某个长度突然崩溃,而是像温水煮青蛙一样逐步劣化:

  • 前面提到的约束条件被逐渐「遗忘」;
  • 早期的错误结论被当作事实继续沿用,形成累积误差;
  • 对新指令的响应变得迟钝,倾向于重复之前的模式。

上下文腐烂对 Agent 尤其致命。一个需要几十步才能完成的任务,Agent 会不断把工具调用的结果、中间思考、报错信息追加进上下文,窗口迅速被「历史包袱」填满,真正重要的当前目标反而被稀释。

2.3 注意力预算是有限的

可以用一个更本质的视角来理解上述现象:模型的注意力是一种守恒的、有限的资源。上下文里的 token 越多,分摊到每个 token 上的「注意力份额」就越薄。塞进十条无关信息,不仅浪费了预算,还会主动干扰模型对那一条关键信息的聚焦。

所以,上下文管理的第一性原理其实非常反直觉:多,往往是有害的。好的上下文不是尽可能全,而是尽可能准——用最少的 token 承载解决当前问题所必需的、且仅必需的信息。

2.4 成本与延迟的现实约束

最后是最朴素的两条:钱和时间。

  • 成本:绝大多数模型 API 按 token 计费,输入 token 通常也要收费。把 100K token 反复喂给模型做多轮对话,账单会以惊人的速度累积。
  • 延迟:输入越长,首个 token 的返回时间(TTFT)越久,因为模型需要先「读完」整段输入(即 prefill 阶段)。对交互式应用来说,这直接伤害体验。

这两条现实约束,是「能塞满也不该塞满」的最后一块拼图。


三、把窗口做大:扩展上下文的底层技术

理解了长上下文的代价,我们再来看工程界是如何在「物理上」把窗口做大、做便宜的。这一节偏底层,但它是理解现代长上下文模型为何能存在的钥匙。

3.1 位置编码:让模型理解「顺序」

Transformer 的注意力机制本身是「无序」的——它并不天然知道哪个词在前、哪个词在后,顺序信息完全依赖**位置编码(Positional Encoding)**注入。而位置编码的设计,直接决定了模型能否泛化到训练时未见过的长度。

早期的绝对位置编码(如原始 Transformer 的正弦编码、可学习位置向量)有一个硬伤:训练时见过的最大位置是多少,推理时就基本只能用到多少,外推能力很差。

RoPE(旋转位置编码)的出现改变了格局。它不再给每个位置分配一个固定向量,而是通过「旋转」的方式把位置信息编码进 Query 和 Key 的向量角度中,天然地表达了 token 之间的相对距离。RoPE 具备一定的外推潜力,且几乎成了当下开源大模型的标配。

ALiBi则走了另一条路:它直接在注意力分数上,按 Query 和 Key 的距离施加一个线性的惩罚偏置,距离越远惩罚越大。这种方式简单、外推性好,无需显式的位置向量。

3.2 位置插值:用微调「撑大」已有模型

真正让「128K、200K 上下文」变得普及的,是一系列基于 RoPE 的位置插值(Position Interpolation)技术。核心思路是:与其从头训练一个长上下文模型(成本极高),不如把一个已经训好的短上下文模型的位置编码「压缩/拉伸」到更长的范围,再用少量长文本微调。

  • 线性位置插值(PI):把超出范围的位置索引等比例缩放回训练范围内,简单有效,但对高频信息有损。
  • NTK-aware 插值:借鉴神经正切核的思想,对不同频率的维度采用不同的缩放策略,高频少缩、低频多缩,缓解了 PI 的信息损失。
  • YaRN:进一步精细化了频率分段策略,用极少的微调步数就能把上下文窗口扩展数倍,是目前长上下文微调的常用方案之一。

这些方法的意义在于:扩展上下文不再需要天价的从头预训练,中小团队也能在开源模型上「撑大」窗口。

3.3 高效注意力:击穿 O(n²) 的墙

FlashAttention是工程上里程碑式的工作。它没有改变注意力的数学定义,而是重写了计算方式:通过分块(tiling)计算并巧妙利用 GPU 的高速缓存(SRAM),避免了把巨大的 n × n 注意力矩阵完整写入显存。结果是显存占用从 O(n²) 降到接近 O(n),速度还更快。FlashAttention 及其后续版本,是今天所有长上下文推理的底层基石。

此外还有一类稀疏注意力(Sparse Attention)线性注意力(Linear Attention)的思路:前者让每个 token 只关注一个子集(如滑动窗口 + 少量全局 token,代表如 Longformer、BigBird),后者用核函数近似把复杂度降到 O(n)。它们在超长序列上有优势,但通常要在效果上做一定妥协。

3.4 KV Cache:推理阶段真正的显存大户

这是理解推理成本绕不开的一环。在自回归生成时,模型每生成一个新 token,都需要用到前面所有 token 的 Key 和 Value 向量。为了避免重复计算,这些 K、V 会被缓存下来,这就是KV Cache

KV Cache 的大小与「序列长度 × 层数 × 注意力头数 × 头维度」成正比,随上下文线性增长。在长上下文场景下,KV Cache 往往比模型权重本身还要吃显存。围绕它诞生了一系列关键优化:

  • MQA(多查询注意力)/ GQA(分组查询注意力):让多个注意力头共享同一组 K、V,大幅压缩 KV Cache 体积。GQA 在效果和压缩比之间取得了很好的平衡,已被广泛采用。
  • PagedAttention(vLLM 的核心):借鉴操作系统虚拟内存分页的思想,把 KV Cache 切成小块非连续存储,几乎消除了显存碎片,让吞吐量成倍提升。
  • KV Cache 量化与驱逐:把缓存量化到低比特,或按重要性淘汰旧的、不重要的缓存条目,进一步节省空间。

对上层应用开发者来说,不必手写这些优化,但理解它们能帮你想明白一件事:为什么复用同一段前缀(prefix)的请求会更快更便宜——因为它们的 KV Cache 可以被缓存复用,这正是「prompt 缓存」类功能的原理。


四、上下文管理的四大核心策略

技术上把窗口做大之后,回到应用层,我们手里的核心命题始终是:在有限(且有成本)的窗口里,如何动态地维护一份高质量的上下文。下面是四类经典且互补的策略。

4.1 截断与滑动窗口:最朴素也最危险

最简单的做法是当对话或历史超长时,直接砍掉最早的部分,只保留最近的 N 个 token,像一个滑动的窗口。

它的优点是实现零成本、延迟可控。缺点也很明显:被砍掉的可能恰好是关键信息——比如用户在对话最开始交代的核心需求。因此,纯粹的截断很少单独使用,通常会配合「锚定关键信息」的技巧:把 System Prompt、用户的核心目标等固定在窗口顶部永不删除,只对中间的对话历史做滑动。

4.2 摘要压缩:用信息密度换空间

更聪明的做法是压缩(Compaction):当历史变长时,用模型自己把早期的对话或工具结果总结成一段更短的摘要,用摘要替换原文。

这在长对话和长任务 Agent 中极为常见。典型实现是「滚动摘要」:维护一段持续更新的摘要,每当新的对话积累到一定量,就把它和旧摘要一起压缩成新的摘要。

压缩的艺术在于保留什么。一个好的压缩策略会刻意保留:未完成的任务目标、已确认的关键事实与约束、重要的中间结论、以及待办事项;而丢弃寒暄、冗余的思考过程、已经失效的临时信息。

下面是一个滚动摘要的简化伪代码,用来演示这个思路:

MAX_HISTORY_TOKENS=4000SUMMARY_TRIGGER=6000defmanage_history(history,running_summary,llm,count_tokens):total=count_tokens(history)iftotal<SUMMARY_TRIGGER:returnhistory,running_summary# 保留最近的对话,压缩较早的部分recent,older=split_by_tokens(history,keep_tokens=MAX_HISTORY_TOKENS)summary_prompt=f"""这是此前的对话摘要:{running_summary}以下是新增的对话,请更新摘要。务必保留: - 用户尚未完成的目标 - 已确认的关键事实与约束 - 重要的中间结论 舍弃寒暄与冗余推理。 新增对话:{format(older)}"""running_summary=llm.complete(summary_prompt)returnrecent,running_summary

这段逻辑的关键,不在代码本身,而在那段压缩提示词——它定义了「什么信息值得被记住」,这才是压缩策略真正的护城河。

4.3 检索增强(RAG):把外部知识按需取用

面对海量、远超任何窗口的知识库(企业文档、代码库、历史工单),把全部内容塞进上下文既不现实也不明智。检索增强生成(RAG)的思路是:把知识存在外部(通常是向量数据库),在每次回答前,只检索出与当前问题最相关的几个片段,注入上下文。

RAG 的本质,就是一种「按需加载」的上下文管理:把上下文窗口当作 CPU 的高速缓存,把向量库当作大容量硬盘,用检索这个动作在两者间搬运数据。它的效果高度依赖三件事:

  • 切分(Chunking):文档如何切块,块太大浪费预算、太小丢失语境;
  • 检索质量:向量召回、关键词召回、二者混合,以及重排序(Rerank);
  • 注入方式:检索到的片段如何组织、如何标注来源、放在上下文的什么位置。

结合第二节的「迷失在中间」,一个实用技巧是:把 Rerank 后最相关的片段放在离问题最近的位置(通常是末尾),而不是随意堆放。

4.4 记忆系统:短期与长期的分层

更完整的方案是构建一套记忆系统(Memory),把人类记忆的分层思想借鉴过来:

  • 短期记忆 / 工作记忆:当前会话的上下文窗口本身,容量小、访问快、随会话结束而清空。
  • 长期记忆:持久化存储,可以是向量库(语义记忆)、结构化数据库(事实记忆),甚至是一份不断更新的用户画像文档。跨会话存在,用时检索。

一个成熟的 Agent 通常会在这两层之间主动搬运信息:从当前对话中提炼出值得长期记住的偏好或事实,写入长期记忆;在新会话开始时,把相关的长期记忆检索回来放进上下文。这套机制,是让 Agent 从「一次性工具」进化为「越用越懂你的助手」的关键。


五、Agent 时代的上下文工程

如果说前面四种策略是「术」,那么这一节要谈的是「道」——为什么在 Agent 时代,上下文管理会从一个可选的优化项,升级为决定成败的核心工程学科。

5.1 从提示词工程到上下文工程

2023 年的主题词是「提示词工程(Prompt Engineering)」,关注的是如何雕琢一句静态的指令。而到了 Agent 时代,主题词变成了「上下文工程(Context Engineering)」,关注的是如何在一个多步骤、动态演进的任务中,持续地为模型的每一次调用构建最优的上下文。

二者的差异是根本性的:提示词工程面对的是一句话,上下文工程面对的是一个随时间不断变化、可能膨胀到失控的信息流。

可以给上下文工程下一个务实的定义:在每一步推理时,用尽可能少的 token,为模型提供完成当前这一步所必需的全部信息——不多,不少。

5.2 一次 Agent 调用里,上下文由什么组成

拆解一个典型 Agent 单步调用的上下文,通常包含这几个部分:

  1. System Prompt:角色、能力边界、行为准则。相对静态,应放在最前面并保持稳定(有利于 prompt 缓存)。
  2. 工具定义(Tools):可用工具的名称、描述、参数 schema。工具越多,这部分越占空间。
  3. 对话/任务历史:用户指令、模型的历次思考与行动、工具返回结果。这是增长最快、最需要管理的部分。
  4. 检索到的知识:RAG 或记忆系统按需注入的外部信息。
  5. 当前状态/草稿区:任务的待办清单、已完成步骤、中间产物。

上下文工程,本质上就是对这五个部分做动态的编排、压缩与取舍。

5.3 工具结果是最大的「上下文杀手」

Agent 场景里,最容易撑爆上下文的往往不是对话,而是工具返回的原始结果。一次数据库查询可能返回上千行,一次网页抓取可能带回几万字的 HTML,一次文件读取可能是整个代码文件。

把这些原始结果不加处理地追加进上下文,是新手最常犯的错误。成熟的做法有几种:

  • 结果精炼:工具返回后,先用规则或小模型提取出真正需要的字段,只把精炼结果放进上下文。比如数据库查询只保留关键几列的前若干行加一个总数。
  • 引用而非内联(Reference over Inline):把大块结果存到外部(文件系统、状态对象),上下文里只放一个引用/句柄和简短摘要,需要时再按需读取。这就是所谓的上下文卸载(Context Offloading)
  • 及时清理:一旦某个工具结果被消费完、不再需要,就从上下文中移除它,而不是让它一直占着预算。

5.4 让 Agent 拥有「外部草稿纸」

一个非常有效的模式是给 Agent 配一块持久化的外部工作区——最常见的形式就是文件系统或一份结构化的状态对象。

Agent 可以把长期计划、待办清单、阶段性成果写到「草稿纸」上,而不是全部囤在上下文里。需要时再读回相关部分。这样做的好处是双重的:既释放了宝贵的上下文预算,又让任务状态得以在上下文被压缩甚至重置后依然存续。很多优秀的编码类 Agent,都会主动维护一份todo清单文件,本质上就是把「工作记忆」外置到了一块不受窗口限制的空间。

5.5 多智能体:用「分而治之」对抗上下文膨胀

当单个 Agent 的任务复杂到上下文注定会爆炸时,一个有效的架构是多智能体(Multi-Agent)+ 上下文隔离

思路是:由一个主 Agent(Orchestrator)负责拆解任务,把每个子任务派发给独立的子 Agent。每个子 Agent 拥有自己干净、独立的上下文窗口,只专注于自己那一小块工作,完成后只把精炼后的结论返回给主 Agent。

这样,海量的中间探索过程被隔离在各个子 Agent 的上下文里「阅后即焚」,主 Agent 的上下文始终保持清爽,只汇集各路的最终成果。深度研究类、复杂编码类的 Agent 系统,普遍采用这种分而治之的结构来突破单窗口的容量极限。

5.6 一个贯穿全流程的例子:深度研究 Agent

把上面的策略串起来,看一个「深度研究 Agent」如何管理上下文:

  1. 接到问题:主 Agent 把研究问题拆成若干子课题,写入一份计划文件(外部草稿纸)。
  2. 并行探索:为每个子课题派发子 Agent。子 Agent 各自搜索、抓取、阅读,原始网页内容全部留在自己的独立上下文里,绝不回传主 Agent。
  3. 精炼回传:每个子 Agent 只把「这个子课题的核心发现 + 关键来源链接」这几百字的摘要交还主 Agent。
  4. 滚动压缩:随着子课题增多,主 Agent 定期把已完成课题的发现压缩进一份滚动摘要。
  5. 按需回溯:撰写最终报告时,若需要某个细节,再通过引用句柄去读取当初存下的原文,而非一直把它放在上下文里。

整个过程中,任何一个窗口都没有被无意义地塞满,而一个远超单窗口容量的研究任务却被顺利完成。这就是上下文工程的价值所在。


六、评估与调优:让上下文管理可度量

上下文管理不能凭感觉,需要建立可观测、可评估的闭环。

6.1 关注这几个核心指标

  • Token 用量分布:统计每次调用中,System Prompt、工具定义、历史、检索内容各占多少。你往往会惊讶地发现,某个部分悄悄吃掉了大半预算。
  • 有效信息占比:在注入的上下文中,真正被模型用到的比例有多高。过低说明检索或压缩不精准。
  • 任务成功率 vs. 上下文长度:画出这条曲线,你能直观看到自己的系统在多长的上下文下开始「腐烂」。
  • 成本与延迟:每次任务的平均 token 成本和 P95 延迟,是所有优化最终要对齐的现实指标。

6.2 用「大海捞针」测试评估长上下文能力

评估一个模型(或你的 RAG 系统)在长上下文下的检索能力,业界常用「大海捞针(Needle in a Haystack)」测试:在一段很长的无关文本中,藏入一句特定信息(针),然后在不同上下文长度、不同插入位置下提问,观察模型能否准确找到。这套方法能有效暴露前面提到的「迷失在中间」问题,帮你判断到底该在多长的窗口内工作。

6.3 几条经验法则

  • 先精简,再扩窗:在把窗口开大之前,先问自己现有的上下文里有多少是冗余的。多数时候,问题出在信息质量而非窗口大小。
  • 锚定关键信息:核心指令放开头,最相关的检索内容放结尾,别把要紧的东西埋在中间。
  • 让历史可压缩、让结果可引用:从架构上就为压缩和卸载留好接口,而不是等窗口爆了再打补丁。
  • 为缓存而设计:把静态内容(System Prompt、工具定义)放在前缀且保持稳定,最大化利用 prompt 缓存来省钱省时。

七、未来的方向

上下文管理仍在快速演进,几个值得关注的趋势:

  • 更长且更「实」的窗口:百万级 token 窗口正在普及,但真正的挑战从「能读多长」转向「在多长的窗口内还能保持稳定的推理质量」。评测正从长度比拼转向有效性比拼。
  • 架构层面的突破:状态空间模型(如 Mamba 一类)、混合注意力架构,试图在根本上摆脱 O(n²) 的桎梏,让超长序列处理变得廉价。
  • 原生记忆能力:把记忆的存取从「外挂工程」逐渐内化为模型或框架的原生能力,让 Agent 天然具备跨会话的持久记忆。
  • 自适应上下文管理:让 Agent 学会自己判断何时该压缩、何时该检索、何时该卸载,把上下文工程本身也变成一种可学习、可自动化的能力。

结语

回到开头那个转变:从「能塞多少」到「该塞什么」。上下文窗口的容量固然重要,但它终究只是一个舞台的大小;真正决定演出效果的,是导演如何在这个舞台上安排每一个角色的出场与退场。

对今天的开发者而言,与其焦虑地追逐更大的窗口数字,不如把功夫下在上下文的经营上:让每一个进入窗口的 token 都物有所值,让每一次调用都只带上此刻真正需要的东西。当你开始像管理一份稀缺预算那样管理上下文,你会发现,很多曾经归咎于「模型不够聪明」的问题,其实只是上下文没有放对而已。

这,就是上下文窗口管理这件事的全部要义。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询