Why Context Engineering Matters / 为什么我们需要「上下文工程」
如果有一家法律 SaaS 公司,把它的 RAG 召回数量从 5 个 chunk 提到 20 个 chunk,把上下文窗口从 8k 换成 128k,然后信心满满地上线——结果准确率从 78% 掉到 61%,延迟变成 3 倍,成本变成 4 倍——你会觉得这是段子吗?
这不是段子。这是 LexFlow(化名)在 2024 年初真实踩过的坑。这个故事稍后展开,但它代表了一类在 LLM 应用圈里反复发生的错误:把"更大的上下文窗口"等同于"更好的效果"。
而这,正是本书想讲清楚的核心命题:context(上下文)不是一个可以无限堆砌的 buffer,而是一个有预算、有成本、有上限的稀缺资源。理解这一点,是从「会用 LLM」走向「能工程化 LLM」的分水岭。
过去两年,prompt engineering(提示工程)几乎成了 LLM 应用的代名词。各种 CoT、Few-shot、Role Prompt 的技巧被总结成了无数博客和课程。但到了 2024–2025 年,越来越多人发现:当你需要让模型回答一个超出它训练知识范围的问题时——比如查询公司内部文档、最新财报、专业领域的细节——光改 prompt 是不够的。你需要把正确的信息塞进模型的输入里。
但"塞进去"这件事,比想象中难得多。
Prompt Engineering 的天花板:从指令技巧到系统级资源分配
要讲清楚 context engineering 的来龙去脉,得先回顾一下 prompt engineering 是怎么走到天花板的。
2020 年 GPT-3 发布的时候,Brown 等人在论文里展示了几个惊人的现象:通过简单的自然语言指令和 few-shot example,一个 175B 参数的语言模型可以完成翻译、问答、算术等任务,根本不需要 fine-tune。这就是 prompt engineering 的起点。它的核心假设是:模型在预训练阶段已经学会了大量知识,我们只需要"问对问题"就能把它引出来。
随后几年,prompt engineering 的工具箱迅速扩充:
- **Instruction tuning 时代(2022)**:FLAN-T5、InstructGPT 让模型学会了"听指令",prompt 从「字符串拼接」变成了「结构化指令」。
- **Chain-of-Thought(2022,Wei et al.)**:让模型「think step by step」,在数学和推理任务上有显著提升。
- **ReAct、ToT、Self-Consistency(2023)**:把 prompting 从单次生成拓展到多步推理树、自我一致性投票。
- **Program-aided LM、Function Calling(2023+)**:让模型可以调用外部工具,prompt 里开始混进代码、JSON schema、API 描述。
这些技巧的共同点是:它们都在优化"如何提问",而不是"给什么信息"。一个 CoT prompt 不会让模型知道你们公司上个季度财报的具体数字;一个 ReAct loop 也不会让模型回忆起三个小时前对话里提到的客户名字。如果模型在"知识缺口"上,再精妙的提问技巧也只是在缺口旁边跳舞。
这就是 prompt engineering 的天花板:当 gap 在知识/信息层面时,prompt 技巧无法跨越它。
但这并不是说 prompt engineering 没用了。它依然重要,只是它的作用域变了——从「主要杠杆」降级为「系统中的一个组件」。现代 LLM 应用里,prompt 的角色更像是「指令格式 + 输出规范」,而真正决定效果的是:模型的 context window 里到底装着什么。
把视角往上抬一层,我们就会发现:要让 LLM 应用在生产环境跑得稳,单靠 prompt 是不够的。我们需要把 context 当作一个系统级资源来管理——这就要引入 context engineering。
而 context engineering 要解决的第一件事,是回答一个看似简单实则致命的问题:喂给模型的 context,凭什么是有效的?
Context Rot:上下文超过窗口一定比例后,模型召回率为何断崖式下降
2023 年,Liu 等人在论文 *Lost in the Middle* 里做了一个非常扎实的实验:他们让 17 个不同的 LLM 在一段长 context 中寻找关键信息,并对比关键信息在 context 不同位置(开头、中间、结尾)时的召回准确率。
结果令人不安:性能曲线不是平的,而是U 形的。
- 关键信息放在 context 的开头或结尾:召回率很高,往往超过 80%。
- 关键信息放在 context 的中间位置:召回率显著下降,某些模型和任务组合下甚至跌 10–30 个百分点。
换句话说:模型并不是把 context 里的所有 token 当成"同等重要"来处理,而是有强烈的位置偏好。更糟的是,中间位置是最容易被忽略的「无人区」。
2024 年,Chroma 团队在他们公开的 *Context Rot* 研究里把这个现象推得更远。他们发现:
1. 空 context 不一定是最坏的。塞一些完全不相关的内容进去,效果反而更糟。50% 的噪声比例可以让某些任务的准确率下降 25%。 2. Context 越长,有效利用率越低。即使是「全相关」的 100k token context,关键信息被回忆起的概率也低于 32k 版本。 3. 位置偏好的程度跟模型规模、训练方法有关,但没有任何一个主流模型能完全免疫。
为什么会这样?几个机制叠加在一起:
- **Attention dilution(注意力稀释)**:标准的 self-attention 是 O(n²) 的全局 softmax,n 越大,每个 token 分到的注意力权重就越被稀释。塞 100 个无关 token 进去,就等于让真正重要的 token 在 softmax 分母里被「淹没」一点。
- **Attention sinks(注意力池)**:Xiao 等人在 2023 年的论文里发现,Transformer 的早期几层和首几个 token 会吸收不成比例的注意力。这意味着 context 开头天然有「主场优势」,其他位置都需要竞争。
- **Recency bias**:训练数据里 recent token 通常更重要,模型学到了一种「靠后偏好」。结合上面两条,我们看到一个清晰的 U 形或 L 形曲线。
工程上,这意味着:窗口大小 ≠ 有效 context 大小。一个号称 128k 的窗口,在实践中可能只有 30k–50k 的"有效区间"——而且这个区间还分「开头 8k」「结尾 8k」两个高优先级位置,以及「中间 30k–110k」的低优先级地带。
LexFlow 的案例把这个现象演绎得淋漓尽致——我们马上展开。但先把这个直觉记住:context 是有「腐烂率」的,灌得越多,每单位 context 的价值越低。
Attention is All You Need, but Context is All You Pay For
如果你以为 context rot 只是一个「效果问题」,那你就低估了一半的故事。另一半是算力与显存账单。
Transformer 的 attention 机制有一个著名的性质:self-attention 的计算量随序列长度 $n$ 平方增长。具体来说,对一个长度为 $n$ 的 context,attention 的 FLOPs 大致是:
$$F_{\text{attn}} \approx 2 \cdot n^2 \cdot d$$
其中 $d$ 是 hidden dimension。从 8k 到 128k,$n$ 涨了 16 倍,attention 的 FLOPs 涨了 256 倍。这意味着:单纯换窗口大小,prefill 阶段的计算成本是 256 倍的差距(实际有 flash attention、sparse attention 等优化,但量级仍然显著)。
更麻烦的是 KV cache。生成式推理时,每个新生成的 token 都要和历史所有 token 做 attention。为了避免每次重新计算,我们把每个 token 的 K 和 V 向量缓存起来。这个 cache 的内存占用是:
$$M_{\text{KV}} = 2 \cdot n \cdot L \cdot H \cdot d_h \cdot \text{bytes}_{\text{elem}}$$
其中 $L$ 是层数,$H$ 是 KV head 数,$d_h$ 是 head dim。以一个典型 7B 模型(32 层、32 KV head、head_dim=128、fp16)为例,每个 token 的 KV cache 约 0.5MB。听起来不多?
- 32k context:16GB KV cache per request
- 128k context:64GB KV cache per request
- 200k context:100GB KV cache per request
如果你的服务要支撑 100 并发长 context 请求,光 KV cache 就需要数 TB 的显存。这是为什么 Anthropic、Google、OpenAI 都把长上下文定价调高的根本原因——不是"趁火打劫",而是物理成本在那。
经济账也清晰:
- GPT-4 Turbo 输入定价:$10 / 1M tokens
- Claude 3.5 Sonnet:$3 / 1M tokens
- 一个 128k 请求,光输入就 $1.3
- 如果 90% 是噪声,你就是用 10 倍的钱办 10% 的事
把账算清楚后,context engineering 的本质就呼之欲出了:context 是一份预算,预算有三个维度——token 成本、注意力质量、显存/算力开销。任何只优化一个维度而忽略其他两个的方案,都会撞墙。
真实案例:LexFlow 的 128k 灾难
让我们把 LexFlow 的故事完整拆开,看看「prompt 升级失败」是怎么发生的,以及怎么救回来。
背景。LexFlow 是一家做合同审查 SaaS 的公司,客户上传 50–500 页的合同 PDF,系统抽取关键条款(违约金、责任限制、终止条件等)并给出风险摘要。在 2023 年,他们的系统是这样的:
- 模型:GPT-4(8k context)
- RAG 管线:合同按段落 chunk,平均每 chunk 800 tokens;用 OpenAI `text-embedding-3-small` 做向量检索,召回 top-5
- Prompt 结构:system prompt + top-5 chunks + 用户指令
- 关键指标:法律 benchmark 上 78% 召回率,单次成本 $0.02,平均延迟 3.7s
"升级"决策。2024 年初,GPT-4 Turbo(128k context)发布。LexFlow 的工程团队做了一个看似合理的判断:
> "既然窗口变大了,我们就能塞更多 chunk 进去了。以前只能给模型 5 个,现在可以给 20 个,准确率应该会更高吧?"
于是他们改了 RAG 召回的 top-k,从 5 改到 20。Prompt 模板没动,re-ranker 也没加("等 v2 再优化")。
结果。上线两周后:
- 准确率:**78% → 61%**(掉了 17 个百分点)
- 平均延迟:**3.7s → 12.4s**(3.3 倍)
- 单次成本:**$0.02 → $0.08**(4 倍)
- 客户投诉:「AI 以前能抓到的违约条款现在抓不到了」「总结里出现了合同里没有的内容」
诊断。事后分析,LexFlow 团队发现了几个叠加的失败原因:
1. 召回质量没跟上窗口扩张。他们的 retriever 召回的 20 个 chunk 里,recall@20 ≈ 0.7。也就是说 20 个里面有 14 个算"可能相关",但只有 8 个真的包含了关键条款。其他 6 个是"语义相近但法律含义不同"的伪相关,12 个完全不相关。 2. Position 灾难。把 20 个 chunk 按 retriever 分数排序后,关键条款被夹在第 4 到第 12 位之间,正好掉进「Lost in the Middle」的最危险地带。 3. Attention 被稀释。128k context 里只有约 6k 是真正有用的(8 个关键 chunk × 800 tokens),剩下 122k 都在跟它抢注意力。 4. KV cache 压力。prefill 从 8k 涨到 128k,延迟的 3.3 倍增长里有相当一部分来自 KV cache 加载和 attention 计算。
修复。LexFlow 花了 6 周回到正轨。关键改动:
- **加 re-ranker**:用 Cohere `rerank-3` 对召回的 20 个 chunk 做二次排序,按法律条款类型加权。
- **降 top-k**:从 20 降到 8,但 8 个全部经过 re-ranker 确认高相关。
- **换窗口**:从 GPT-4 Turbo 128k 切到 Claude 3.5 Sonnet 200k,但**实际只用 32k 上下文**(8 个相关 chunk + system prompt + 用户问题 + 输出预留)。多出来的窗口不是用来塞东西,而是用来「不焦虑」。
- **Pre-LLM relevance gate**:加了一个轻量分类模型(基于 embedding 相似度)作为「context 守门员」,把低于阈值的 chunk 在进 LLM 之前过滤掉。
- **Position 策略**:把最高置信度的关键条款放在 context 开头(system prompt 后、用户指令前),次要的放结尾作为支持证据。
修复后指标:
- 准确率:**84%**(高于原始 78%)
- 延迟:**4.1s**(基本回到原状)
- 成本:**$0.025**(略高于原始,但远低于 128k 灾难)
LexFlow 的故事不是孤例。2024 年公开复盘过的几个 RAG 升级翻车案,包括 Notion AI 的早期问题、某医疗 SaaS 的诊断辅助、某代码助手的 long context 「升级」——根因几乎都是同一组:context engineering 缺位,prompt engineering 升级变成了 context 通货膨胀。
Context Engineering 的五大支柱
从 LexFlow 的修复方案里,我们其实已经能看到 context engineering 的工作面。但要把这些工作面系统化,我们需要一套框架。我把 context engineering 的核心能力归为五大支柱,它们不是替代关系,而是互补工具集,共同受一个统一的预算心智模型支配。
```mermaid flowchart TD PE[Prompt Engineering] -->|ceiling| CE[Context Engineering] CE --> P1[Long Context] CE --> P2[Compression] CE --> P3[Summarization] CE --> P4[Retrieval Memory] CE --> P5[State Management] P1 --> Budget[Context Budget] P2 --> Budget P3 --> Budget P4 --> Budget P5 --> Budget Budget --> Goal[Effective LLM Apps] ```
这张图想说的是:prompt engineering 是一个工具,不是答案;context engineering 是一个系统,由五个相互协作的支柱支撑;这些支柱共同受一个「context budget」的统一约束。下面分别展开。
支柱一:Long Context Management(长上下文管理)。这是最直觉的支柱——「让窗口装得下」。但它远不只是「选个大窗口模型」那么简单。它包含:
- 评估模型的真实有效窗口:RULER、LongBench、Needle-in-a-Haystack 等 benchmark 告诉我们,宣称窗口和有效窗口可能差 2–4 倍。
- Position 策略:把关键信息放在模型 attention 偏好的位置(开头、结尾、避免堆中间)。
- 结构化 context:分隔符、章节标签、显式编号,让模型更容易「定位」而不是「线性扫描」。
- Long context 的适用场景判断:什么时候真的需要塞 50k+ token?通常是「跨章节推理」「完整文档问答」「代码库级理解」这类需要**全局视野**的任务。对于大多数 QA、摘要、抽取任务,短而精的 context 反而更好。
支柱二:Compression(压缩)。当我们判断 context 里有冗余,就该压缩。压缩分两个层次:
- **Token-level 压缩**:用 LLMLingua 这类模型,把自然语言 prompt 压缩到 20–50% 长度而保留关键语义。代价是可能损失一些微妙语气,但换来大幅 cost 下降。
- **Semantic-level 压缩**:去掉冗余句子、合并相似段落、把长列表转成结构化摘要。这通常是「写一个小模型或规则」就能做的事。
压缩的决策点:进 LLM 之前做一次,把过大的 context 先压一压再喂。
支柱三:Summarization(摘要)。摘要和压缩的区别在于:压缩是「无损降维」,摘要允许「有损降维」。它解决的是对话式应用里 context 无限增长的问题——多轮 agent、客服助手、长时间 coding session,几小时后 context 就破 100k 了。
策略:
- **Rolling summary**:每隔 N 轮,把历史对话压缩成一个滚动摘要,下一轮从摘要 + 最近 N 轮拼接。
- **Hierarchical summary**:多层摘要,meta-summary 概括最近 1k 轮的概要,细节摘要保留最近 100 轮。
- **Extractive + Abstractive 混合**:先用 extractive