为什么 Context 是新王 (Why Context is the New King)
第 1 章:为什么 Context 是新王
2024 年 3 月,一家位于硅谷的 AI 法律科技初创公司经历了一次刻骨铭心的生产事故。他们的合同审查 SaaS 产品在上线后的第九周,突然出现了一个诡异的 bug:系统在多轮对话中,会在第 7 轮左右开始"忘记"用户最初提供的合同条款。当一位企业客户正在审阅一份价值 120 万美元的 SaaS 服务协议时,系统在第 8 轮回复中突然建议了一个与初始条款完全矛盾的修改方案——而客户已经基于这个错误建议在合同上做了电子签名。等工程团队定位到问题根源时,损失已经发生:客户发起合同撤销,企业法务团队集体投诉,最终这家初创公司不仅失去了这个客户,还在随后的两个月里流失了 17% 的付费订阅用户。
事后复盘报告只有三行字:"Context window 超限,未实现有效的滚动压缩与会话状态外置。"
这不是孤例。2024 年 11 月,GitHub Copilot Workspace 的一次版本升级中,由于 context retrieval 模块的 embedding 模型被替换但未重新索引整个向量库,导致一个企业级代码库中超过 30% 的语义检索结果出现 top-1 命中错误;Replit 的 AI Agent 在某个深夜的一次长任务执行中,因为忘记清理累积的工具调用历史,让 context 从 4000 tokens 一路膨胀到 87,000 tokens,单次推理成本飙升至原价的 22 倍。这些事故指向同一个根源:我们低估了 context 的工程复杂度。
当我们把所有的研究热情都投入到模型架构的改进(更大的 MoE、更长的有效注意力、更巧妙的 RLHF)时,生产环境里的 LLM 应用正在被另一个更朴素、更直接、更可控的因素所支配——喂给模型的 context。这就是为什么 "context engineering" 在 2024 年下半年开始取代 "prompt engineering",成为 LLM 应用开发的核心学科。本章的目的,就是要把这件事讲透。
---
从 prompt 到 context:LLM 交互范式的演进
要理解 context engineering 为何崛起,先要回到 prompt engineering 的黄金时代。
Prompt Engineering 的辉煌与局限
2022 年末到 2023 年中,是 prompt engineering 的高光时刻。OpenAI 发布的 GPT-3.5 和 GPT-4 让人们意识到:只要写出足够精妙的 prompt,就能从模型里"挖"出惊人的能力。于是整个行业涌现出一批"prompt 大师",他们撰写诸如 "Let's think step by step"、"You are an expert..."、"Take a deep breath" 之类的魔法咒语,在 Twitter、Reddit 和各种博客上广泛流传。prompt engineering 一度被视为一种"零代码"的 AI 开发范式,门槛低、上手快、效果立竿见影。
但很快,prompt engineering 的局限性开始暴露。
第一个局限是表达能力的天花板。 Prompt 本质上是"自然语言作为配置语言"的尝试,它试图用一段连续的散文式描述,去控制一个高度复杂的生成过程。这就像试图用一份产品需求文档(PRD)去直接控制 CPU 的寄存器——粒度根本不对。Prompt 无法表达的条件分支、循环逻辑、外部状态管理、检索增强、多智能体协同等概念,都被强行塞进一段平铺直叙的文字里,结果是 prompt 越长,越难调试,越难复用。
第二个局限是缺乏工程化基础。 Prompt 没有版本控制的标准(虽然有一些工具尝试用 Git-like 的方式管理 prompt,但缺乏统一的语义),没有类型系统(你无法声明"这个 prompt 必须包含一个用户 ID 字段"),没有测试框架(如何对一个概率性输出做单元测试?),没有监控指标(一个 prompt 在生产环境跑了三个月,token 成本涨了 5 倍,你可能完全不知道)。换句话说,prompt 是"手工业",而我们要做的是"工业"。
第三个局限是 prompt 与 context 的混淆。 在很多工程师的认知里,"prompt" 就是"发送给模型的全部内容"。但当我们把目光转向生产环境,会发现发送给模型的"prompt"实际上是一个由多种异构信息动态组装而成的复杂结构——包括系统提示、用户输入、对话历史、检索召回的文档、工具调用的结果、结构化的业务数据、少样本示例、缓存的中间结果等等。这个结构根本不是一段"prompt",它是一个context。
Context Engineering 的诞生
2023 年下半年,随着 LangChain、LlamaIndex 等框架的兴起,以及 RAG(Retrieval-Augmented Generation)成为主流架构,行业开始意识到:真正决定 LLM 应用表现的,不是那个写在最前面的"系统提示",而是整个 context 的设计与组装质量。
Context engineering 的核心转向可以用一句话概括:
> 从"如何写一句好 prompt"转向"如何为模型构建一个高质量的信息环境"。
这个转向有几个关键标志:
第一,context 是多模态、多源、动态的。 它不再是静态文本,而是由检索系统(向量数据库 + BM25)、工具调用(API responses)、记忆系统(short-term + long-term)、业务系统(CRM 拉取的用户数据)、监控系统(实时反馈)等多种数据流实时组装而成。
第二,context 是分层的、有优先级的。 系统提示、当前任务、近期历史、远期历史、外部知识、工具结果——它们在 context window 中的位置(prefix、middle、suffix)和权重,对模型的影响截然不同。这就是为什么会有 "lost in the middle" 这种现象的发现。
第三,context 是一类需要被工程化管理的资源。 它有成本(每个 token 都要钱)、有容量(context window 是有限的)、有时延(拼装 context 需要时间)、有衰减(信息过载导致注意力稀释)、有副作用(错误信息会污染整个生成过程)。我们必须像对待数据库查询、内存分配、网络请求一样,对 context 进行严格的工程管理。
第四,context 是可观测的。 一个成熟的 context engineering 体系必须能回答这些问题:当前 context 用了多少 token?哪些部分是高频出现的?哪些部分是冗余的?检索召回的文档里有几个真正相关?工具调用历史里有没有死循环?这些可观测性要求,是 prompt engineering 时代完全不需要考虑的。
```mermaid flowchart LR A[Prompt Engineering] --> B[Context Engineering] A1[静态文本] --> B1[动态组装] A2[单次交互] --> B2[多轮/多源] A3[无观测] --> B3[全链路可观测] A4[手工艺] --> B4[工程化系统] A --> B A1 --> B1 A2 --> B2 A3 --> B3 A4 --> B4 ```
图 1-1:从 Prompt Engineering 到 Context Engineering 的范式演进
---
Context Window 瓶颈:当信息超过模型"视野"时会发生什么
要理解 context engineering 的重要性,必须先理解一个基本物理约束:context window 是有限的。
Context Window 的物理现实
Context window,也称为"上下文窗口",是指 LLM 在单次推理中可以"看到"的最大 token 数。这个数字在过去三年里发生了翻天覆地的变化:
- GPT-3 (2020): 2,048 tokens
- GPT-3.5 (2022): 4,096 tokens
- GPT-4 (2023): 8,192 / 32,768 tokens
- Claude 2 (2023): 100,000 tokens
- GPT-4 Turbo (2024): 128,000 tokens
- Claude 3 (2024): 200,000 tokens
- Gemini 1.5 Pro (2024): 1,000,000 tokens (实验版) / 2,000,000 tokens
- 各类开源模型:4K ~ 128K 不等
表面上看,context window 一直在指数级增长。但实际上,context window 并不等于有效 context。这里有几个关键的概念区别。
有效 Context vs. 声明 Context
Liu 等人在 2023 年发表的那篇著名论文 "Lost in the Middle" 给整个行业泼了一盆冷水。研究者发现:当 context window 中的信息量增加时,模型对中间位置信息的检索准确率会显著下降。 换句话说,模型对"开头"和"结尾"的信息记忆最好,对"中间"的信息最容易遗忘。
这个发现的核心启示是:声明 128K context 不等于模型真的能用好 128K context。 实际的"有效 context"可能只有声明值的 50% 到 70%,而且高度依赖信息的位置和结构。
为什么会这样?这要从 Transformer 注意力机制的本质说起。标准的 self-attention 是 $O(n^2)$ 的计算复杂度(其中 $n$ 是序列长度),但更深层的问题是信息路由的稀释。当 context 中有 100 段相关文档时,每段文档能从最终生成中"抢到"的注意力权重是有限的。如果某段关键信息被埋在 50 段无关信息中间,它被注意到的概率就会大幅下降。
Context Window 满了,然后呢?
很多工程师在初次接触 LLM 应用开发时,都会遇到一个看似简单的问题:"context window 满了怎么办?" 然后他们会自然地想到几个方案:
方案 1:截断(Truncation) 最简单粗暴——丢掉最早的信息或最晚的信息。但代价显而易见:你可能恰好丢掉了最关键的系统提示,或者刚刚发生的对话转折。
方案 2:滑动窗口(Sliding Window) 保留最近 N 轮对话,更早的丢掉。问题在于,"最近"未必是"最相关"。用户可能在第 2 轮抛出一个关键约束条件,到了第 20 轮讨论具体实现时,那个约束已经被滑动窗口丢掉了。
方案 3:摘要压缩(Summarization) 用 LLM 把早期对话压缩成摘要,再插入 context。这种方法看起来优雅,但有两个隐患:摘要本身会丢失细节(而 LLM 推理常常依赖于微妙细节),而且摘要过程本身消耗 token 和时间。如果摘要失败,整个后续推理都会偏离轨道。
方案 4:检索增强(Retrieval-Augmented Generation, RAG) 把历史信息、外部知识都存入向量数据库,需要时按相关性检索回来。这是最"工业"的方案,但带来了新的工程复杂度:embedding 模型的选择、chunking 策略、检索排序、重排模型、上下文窗口的二次组装。
方案 5:分层 context(Hierarchical Context) 根据信息的重要性和时效性,把 context 分成多个层级:核心层(system prompt + 当前任务)、工作层(近期对话 + 工具结果)、知识层(RAG 召回)、档案层(长期记忆 + 历史摘要)。每层有不同的保留策略和组装规则。
这五个方案不是互斥的,在真实的工业级系统里,它们往往被组合使用。但无论哪种方案,背后的核心问题都是同一个:context 是一种稀缺资源,必须被精打细算地管理。
一个反直觉的发现:Context 越多,效果越差
Stanford 和 Samaya AI 在 2024 年联合发布的一项研究(NeurIPS 2024 best paper)进一步证实了一个反直觉的现象:context 越多,效果不一定越好;在某些情况下,context 越多,效果反而越差。
研究者将这种现象命名为 "context contamination"(context 污染)。它的具体表现是:当 context 中包含与当前任务相关但不直接有用的信息时(比如讨论话题 A 时,context 里塞进了大量关于话题 B 的资料),模型会"分心",生成质量显著下降。这就好比让一个外科医生在手术过程中,旁边不断有人念一些无关紧要的健康科普——医生的注意力会被分散,手术质量必然下降。
更糟糕的是,context 污染的影响是非线性的。少量无关信息可能影响不大,但当无关信息积累到一定阈值时,模型表现会出现"悬崖式"下跌。这种现象给 context engineering 提出了一个艰难的要求:不仅要把对的信息放进去,还要把错的信息挡在外面。
---
Context 作为 AI 的操作系统:一种新的系统思维
如果你理解了上面所有的痛点,那么一个自然的类比就会出现:context 就是 LLM 应用的操作系统。
为什么说 Context 是 OS
让我们先回顾一下传统操作系统(OS)的核心职责:
1. 资源管理:CPU 时间片、内存分配、磁盘 I/O、网络带宽——这些都是稀缺资源,OS 必须高效、公平地分配它们。 2. 进程隔离:每个进程有自己的地址空间,互不干扰。 3. 抽象层:OS 向上层应用提供统一的 API(文件系统、进程、线程、网络 socket),隐藏底层硬件细节。 4. 调度决策:OS 决定哪个进程先运行、哪个内存页被换出、哪个网络包优先传输。 5. 安全边界:OS 防止一个进程越权访问另一个进程的内存或硬件资源。
现在,让我们把 LLM 应用看作运行在 context 这个"操作系统"之上的"应用程序":
| 操作系统职责 | Context Engineering 对应 | |---|---| | 资源管理 | Token 预算分配、context window 利用 | | 进程隔离 | 不同任务/用户/会话的 context 隔离 | | 抽象层 | 统一的 context 组装 API,屏蔽不同模型的 prompt 格式差异 | | 调度决策 | 信息优先级排序、检索召回排序、压缩策略选择 | | 安全边界 | Prompt injection 防御、PII 过滤、敏感信息脱敏 |
这个类比之所以深刻,是因为它揭示了一个事实:LLM 应用的"程序"实际上是两部分的组合——一部分是写在 Python/JavaScript 里的传统代码,另一部分是精心组装好的 context。 前者负责确定性逻辑,后者负责"引导"非确定性的 LLM 生成。两者共同构成了完整的应用行为。
Context OS 的架构层次
借鉴传统 OS 的分层架构,我们可以把 context OS 也分成几个层次:
```mermaid flowchart TD A[应用层 Application] --> B[Context API 层] B --> C[Context 组装引擎] C --> D[数据源适配层] D --> E[向量数据库] D --> F[关系数据库] D --> G[外部 API] D --> H[缓存层] D --> I[长期记忆] C --> J[LLM 推理] ```
图 1-2:Context OS 的分层架构
最底层是数据源适配层。它负责对接各种异构数据源——向量数据库(用于语义检索)、关系数据库(用于结构化业务数据)、外部 API(用于实时信息获取)、缓存层(用于热点 context 复用)、长期记忆存储(用于跨会话的用户偏好)。这一层的关键挑战是数据格式统一:向量是 float32 数组,关系数据库是行和列,API 返回的是 JSON,缓存里是序列化对象——这些必须在进入上层之前被归一化成统一的"信息单元"。
中间层是 Context 组装引擎。这是 context OS 的"内核"。它接收应用层的请求("为当前对话生成一个 context"),根据预定义的策略,决定:
- 系统提示的内容和长度
- 哪些历史对话需要保留,哪些需要摘要或丢弃
- 触发哪些检索查询,召回多少文档
- 是否需要调用外部工具,工具结果如何回填
- 各种信息块按照什么顺序拼接
- 总 token 数控制在什么预算内
这一层的输出是一个完整的、组装好的 context,准备送给 LLM 推理。
上层是 Context API 层。它向上层应用提供简洁、统一的编程接口。比如:
```python context = ctx.build( task="customer_support", user_id="u_12345", session_id="s_abcde", max_tokens=8000, priority="balanced" ) response = llm.generate(context) ```
应用开发者不需要关心 context 是怎么组装出来的,他们只需要声明意图。
最上层是应用层。各种 LLM 应用——聊天机器人、AI Agent、RAG 系统、代码助手、数据分析工具——都运行在这个 context OS 之上。
"Context Kernel" 的核心调度算法
借鉴 OS 内核的进程调度算法(FCFS、SJF、Round-Robin、MLFQ),我们也可以设计 context kernel 的调度策略。一个朴素的实现是:
```python class ContextKernel: def schedule(self, info_blocks, token_budget):
info_blocks: 候选信息块列表
每个块有 priority、recency、relevance、cost 属性
scored_blocks = []