上下文工程导论:从 Prompt 到 Context 的范式跃迁
第 1 章 上下文工程导论:从 Prompt 到 Context 的范式跃迁
2024 年下半年,一个看似细微的术语漂移正在 AI 工程社区中悄然发生:越来越多原本自称"Prompt Engineer"(提示词工程师)的从业者,把招聘网站的职位头衔悄然改写为"Context Engineer"(上下文工程师)。这一字之差并非营销话术,它折射出一个已经无法被忽视的工程现实——当 GPT-4o 把上下文窗口稳定在 128K tokens、Claude 3.5 Sonnet 推到 200K、Gemini 1.5 Pro 跨越 1M 甚至 2M tokens 的门槛时,单条 Prompt 的边界早已溢出 LLM API 的输入框,向整个应用栈蔓延。开发者真正管理的,不再是一段精心设计的字符串,而是一条贯穿检索(Retrieval)、记忆(Memory)、工具调用(Tool Use)与历史会话(Conversation History)的多向数据流。本章的目的,就是把这条数据流从工程直觉中抽出,给出它的形式化定义、它的产业演化史,以及它作为新一代 LLM 系统设计中心的七项设计原则。
1.1 Prompt Engineering 的天花板:从离散指令到连续上下文的演进
要理解"上下文工程"为何会取代"提示词工程",必须先承认后者曾经取得的巨大成功。2020 年 GPT-3 发布前后,研究者发现了一个违反直觉的现象:把少量监督样本直接拼接到输入文本之前,模型在未见过的任务上准确率会显著上升。这便是 Brown 等人在 *Language Models are Few-Shot Learners*(NeurIPS 2020)中命名的 In-Context Learning(ICL,上下文学习)。Wei 等人随后在 *Chain-of-Thought Prompting Elicits Reasoning in Large Language Models*(NeurIPS 2022)展示了"让我们一步步思考"(Let's think step by step)这样一句自然语言指令,便能让 PaLM 540B 在 GSM8K 数学题上的准确率从 18% 跃升到 57%。这些发现共同塑造了一种工程美学:通过精雕细琢的 Prompt 模板,加上 Few-shot 示例与推理链指令,几乎可以让任何基础模型"通灵"。在 2022 至 2023 年间,Prompt Engineering 几乎成为通往 LLM 应用的全部技术栈。
但这种工程美学有其难以突破的天花板。它本质上把 Prompt 视为一段离散的、一次性的、人工撰写的字符串。我们可以用三个具体场景说明这个模型的局限:
场景一:实时数据检索。 在客户支持场景中,用户问"我的订单 #12345 状态如何?"。单纯优化 Prompt 写法——无论加多少"请基于事实回答"的指令——都无法让模型知道订单的真实状态,因为这些数据根本不存在于 Prompt 中。RAG(Retrieval-Augmented Generation,检索增强生成)的引入,本质上是承认 Prompt 不再是一个封闭字符串,而是一段从外部语料库中按需拼装而来的连续上下文。
场景二:多轮状态维护。 当用户进行第七轮对话时,模型对前六轮内容的记忆完全依赖输入框中累积的对话历史。这段历史动辄数千 tokens,会干扰当前轮次的注意力分配,甚至导致"上下文污染"——前序对话的错误假设被模型当成既定事实复述。仅仅调优当前轮的 Prompt 措辞对此毫无帮助。
场景三:工具调用的返回结果。 当 LLM 调用一个返回 50KB JSON 的数据库工具时,返回值必然进入上下文窗口。如何决定哪些字段保留、哪些丢弃、是否需要二次摘要?这已经不再是"Prompt 写法"问题,而是上下文管理问题。
这三个场景共同指向一个结论:随着 LLM 应用从"对话玩具"演化为"生产系统",瓶颈从"如何写好一段 Prompt"转移到"如何构造和管理 LLM 看到的整段上下文"。术语的更替,本质上是工程重心的位移。
形式化地理解这种位移:Prompt 是 Prompt 工程的一阶对象(first-class object),即它占据工程视野的中心;而在上下文工程中,Prompt 降格为上下文对象的一个子集——一个高级的、可被替换的拼装单元。我们可以如下建立映射关系:
$$ P \subseteq C, \quad C = P \cup R \cup M \cup T \cup H $$
其中 $P$ 是 Prompt 模板(包含 system prompt、user query、few-shot exemplars),$R$ 是检索内容(retrieved chunks),$M$ 是工作记忆(working memory),$T$ 是工具输出(tool outputs),$H$ 是历史会话(history)。当 $|C| \leq N_{\max}$(上下文窗口上限)时,$C$ 就是 LLM 实际看到的全部输入。Prompt 工程师关注的是 $|P|$ 的措辞;上下文工程师关注的是 $|C|$ 的整体结构与预算分配。
更进一步,ICL 的原始论文其实已经隐含着这一洞见。Brown 等人发现,ICL 的效果与示例的"分布"有关,而与单个示例的"措辞"关系不大。这意味着模型实际上从整个上下文分布中归纳任务结构,而非解析某一条指令。这一观察,在五年后才被工程界真正吸收。
1.2 上下文工程的定义:预算、状态、窗口三位一体的抽象模型
要给"上下文工程"一个严格定义,必须先定义它所操作的三个核心抽象:上下文窗口(Context Window)、上下文状态(Context State)、上下文预算(Context Budget)。这三者构成了上下文工程的最小完备抽象集——窗口定义了上限,状态定义了内容,预算定义了权衡。
1.2.1 上下文窗口:硬上限与软衰减
上下文窗口(Context Window)是模型在单次前向传播中能够处理的最大 token 序列长度,记作 $N_{\max}$。它的物理意义由两个因素决定:(1)位置编码(Positional Encoding),例如 Rotary Position Embedding(RoPE,Su et al., 2021)或 ALiBi(Press et al., 2022)允许的相对距离范围;(2)注意力机制的二次复杂度约束——这也是 FlashAttention、Sliding Window Attention、LongRoPE 等优化技术的存在理由。
然而,$N_{\max}$ 是一个硬上限(hard limit),并非软衰减(soft decay)。Liu 等人在 *Lost in the Middle: How Language Models Use Long Contexts*(TACL 2024)通过大量实验证明:当输入长度逼近但未达到 $N_{\max}$ 时,模型对位于"中间地带"(约 30%–70% 位置)的信息检索准确率显著下降,呈 U 形曲线。这意味着工程上的"有效窗口"通常小于名义窗口:
$$ N_{\text{effective}} = \alpha \cdot N_{\max}, \quad \alpha \in [0.6, 0.95] $$
其中 $\alpha$ 取决于模型架构、训练数据分布、推理时的注意力 sink 等因素。这一发现直接催生了后续我们将反复使用的设计原则——可检索性——即不能假设信息"放进去就能被找到"。
```python
上下文窗口的形式化数据结构示例
from dataclasses import dataclass, field from typing import List, Optional import time
@dataclass class ContextWindow: """Context Window:一次 LLM 调用所能携带的全部 token 序列。""" system_prompt: str = "" # 系统指令,预留 ~ 200 tokens user_query: str = "" # 当前用户输入 retrieved_chunks: List[str] = field(default_factory=list) tool_outputs: List[dict] = field(default_factory=list) history: List[dict] = field(default_factory=list) few_shot: List[dict] = field(default_factory=list)
N_max: int = 200_000 # 上下文窗口硬上限(以 Claude 3.5 Sonnet 为例)
def total_tokens(self) -> int: return self._count_tokens() # 调用 tokenizer 估值
def utilization(self) -> float: return self.total_tokens() / self.N_max ```
1.2.2 上下文状态:时序演化的张量
上下文状态(Context State)刻画的是:在第 $t$ 轮对话(或第 $t$ 个任务步骤)时,进入 LLM 的全部信息的逻辑总和。它不是一个静态字符串,而是一个随时间和外部事件演化的对象:
$$ S_{t+1} = \Phi(S_t, u_{t+1}, \tau_{t+1}, \epsilon_{t+1}) $$
其中 $S_t$ 是第 $t$ 轮的状态,$u_{t+1}$ 是新增的用户输入,$\tau_{t+1}$ 是可能的工具调用返回,$\epsilon_{t+1}$ 是检索系统拉取的新内容,$\Phi$ 是上下文组装函数(context assembly function)。这个函数 $\Phi$ 是上下文工程的核心:它决定哪些历史保留、哪些被压缩、哪些被丢弃。
把 $S_t$ 视为一个"高维张量"在工程上有益:它的轴包括 token 维度、时间维度、来源维度、生命周期维度,每个轴都可以独立切分与查询。类比操作系统中的进程控制块(Process Control Block):进程状态包括寄存器、内存、文件句柄、信号掩码;上下文状态则包括系统提示、检索槽、工具槽、历史摘要、用户输入。这种类比并非文字游戏,它直接启发了后续章节中将讨论的上下文调度器(Context Scheduler)设计。
1.2.3 上下文预算:稀缺资源的分配律
上下文预算(Context Budget)是三者中最具经济学色彩的抽象。它认识到:上下文窗口是一种稀缺资源,每次调用都有代价(在 Anthropic Claude 3.5 Sonnet 中,200K 窗口的输入费用约为 8K 窗口的 3 倍——尽管单价比短上下文便宜,但 billable tokens 总量激增)。与此同时,模型对不同来源的信息利用率不同:
$$ B(t) = B_{\text{total}}(t) - \sum_{i} c_i \cdot \ell_i(t) $$
其中 $B(t)$ 是第 $t$ 轮的剩余预算,$c_i$ 是第 $i$ 类信息(系统提示、检索块、工具输出、历史)的单位 token 成本系数,$\ell_i(t)$ 是该类信息在第 $t$ 轮的占用长度。上下文预算工程(context budgeting)就是选择 $c_i$ 的策略——例如,对历史轮次设置高压缩率,对当前检索结果设置低保真。
更进一步,预算还需要考虑延迟成本和质量成本。长上下文不仅 token 费用更高,TTFT(Time To First Token)也线性甚至二次增长(取决于 attention 实现)。同时,过多冗余信息会触发"上下文中毒"(context poisoning,模型被错误信息误导)和"上下文分心"(context distraction,模型被无关信息带偏)等问题(参考 Anthropic 工程博客 *Effective Context Engineering for AI Agents*, 2024)。这些都是预算工程必须外化的代价函数。
三位一体的关系可以用如下方程组总结:
$$ \begin{cases} S_t \subseteq \mathbb{T}^{N_{\max}} & \text{(状态必须被窗口所容纳)} \\ |B(t)| \leq B_{\text{total}} & \text{(预算守恒)} \\ \forall i: \ell_i(t) \cdot c_i \leq B_i^{\text{cap}} & \text{(按类设上限)} \end{cases} $$
任何上下文工程系统,最终都在求解这三个约束下的最优 $S_t$ 与 $\ell_i(t)$ 分配。
1.3 上下文窗口的产业演化:从 4K 到 2M 的窗口战争
上下文窗口的增长是 LLM 产业近三年最具戏剧性的军备竞赛之一。理解这条曲线,既能帮助我们校准对模型能力的期望,也能让我们看清"长上下文"背后被掩盖的工程代价。
1.3.1 时间线:从 2K 到 10M
下表提炼了主流模型上下文窗口的增长路径(数据截至 2025 年初):
| 年份 | 模型 | 上下文窗口 | 关键技术 | |---|---|---|---| | 2020 | GPT-3 | 2,048 tokens | 绝对位置编码,长度外推困难 | | 2022 | GPT-3.5 / ChatGPT | 4,096 tokens | 同上 | | 2023.03 | GPT-4 | 8K / 32K | ALiBi,长上下文注意力 | | 2023.07 | Claude 2 | 100,000 tokens | 自研 RoPE-X | | 2023.11 | GPT-4 Turbo | 128,000 tokens | RoPE 扩展 | | 2024.02 | Claude 3 Sonnet/Opus | 200,000 tokens | 长文微调,cursor cache | | 2024.02 | Gemini 1.5 Pro | 1,000,000 tokens | MoE + 稀疏注意力 | | 2024.05 | Gemini 1.5 Pro (实验) | 2,000,000 tokens | 进一步稀疏化 | | 2024.07 | Magic AI | 100,000,000 tokens | 长上下文研究预览 | | 2024.10 | Claude 3.5 Sonnet | 200,000 tokens | 优化中段注意力 |
从 2K 到 2M,三个数量级的跨越耗时不到四年——这是 LLM 历史上最快的参数级演化之一。但数字飙升的同时,工程现实远比"装得多就好"复杂。
1.3.2 使能技术:稀疏注意力与位置编码的协同演化
窗口扩张的背后,是一系列底层技术的协同演化:
1. 位置编码的长度外推(Length Extrapolation)。原始 Transformer 使用绝对位置编码,训练时见过的最大长度即推理时的上限。ALiBi 通过添加线性偏置到注意力分数上,允许训练在 4K、推理外推到更长的序列。RoPE(Rotary Position Embedding)则将位置信息编码为复数域上的旋转,使其对长度变化具备更强的不变性。Llama 3 的技术报告(Meta, 2024)显示,在 RoPE 基础上进一步训练可以稳定扩展到 128K。
2. 稀疏注意力与滑窗机制。Longformer(Beltagy et al., 2020)提出"局部滑窗 + 全局 token"组合,将复杂度从 $O(n^2)$ 降到 $O(n)$。Mistral 7B 与 Gemini 1.5 都采用了类似思路:大多数 token 看到的是局部窗口(约 4K–8K),只有少数"锚点 token"获得全局视图。Gemini 1.5 之所以能号称 1M 窗口,本质上正是这套稀疏模式 + MoE(Mixture of Experts)架构的组合。
3. FlashAttention 与显存优化。FlashAttention(Dao et al., 2022;FlashAttention-2, 2023;FlashAttention-3, 2024)通过 IO 感知的 kernel 融合,把 100K 上下文的注意力计算从不可行变为可行。它没有增加可处理的窗口,但让开发者愿意在生产中使用长上下文——因为延迟与显存成本可接受。
4. KV Cache 的工程优化。PagedAttention(vLLM, Kwon et al., 2023)借鉴操作系统虚拟内存的思想,把 KV Cache 分页管理,使得 200K 上下文的并发推理成为可能。Anthropic 在 Claude 3.5 Sonnet 中引入的 "cursor cache"(提示词缓存),则是把 KV Cache 引入 API 层——用户显式标记可复用的前缀,API 端把这些前缀的 KV 缓存下来,命中时输入价格降低约 90%、延迟降低约 85%。
1.3.3 幻象与现实:当长上下文不再等于"长记忆"
一个普遍的误解是:128K 窗口意味着"模型能记住 128K 的内容"。事实远比这复杂。Anthropic 与 Google 在 2024 年都公布了"针在 haystack"的检索准确率(NIAH, Needle In A Haystack)——在不同上下文长度下的随机位置插入"针"(一段关键事实),考察模型能否准确回忆。结果显示:
- **GPT-4 与 Claude 3 系列在 200K 内的 NIAH 准确率普遍 > 95%**(前提是"针"在头尾)。
- **中段(30%–70%)的准确率会显著下降**,尤其在 100K 以上区间。
- **Gemini 1.5 Pro 在 1M 下 NIAH 仍可保持高准确率**,但研究者提示这一定程度上归功于其在训练阶段见过超长文档,以及稀疏注意力机制对中段并无特殊惩罚。
换言之,长上下文 ≠ 强检索能力。一个更精确的描述是:长上下文提