第一章 失忆的先知——我们为什么会需要上下文工程
第一章 失忆的先知——我们为什么会需要上下文工程
第 47 轮:一次生产事故的复盘
凌晨 1:17,我的手机屏幕被一条 SLO 告警点亮。
那是周五的深夜,团队是客服系统的方向。告警来源是会话平均轮次——指标曲线笔直地向上跳,从常态的 12 轮窜到了 47 轮。在我们的设计里,机器人应当在 8 轮之内完成"理解需求 → 收集信息 → 给出方案 → 转人工"的标准流程。47 轮意味着几乎所有用户都被迫重复自己说过的话。
凌晨 2:44,舆情系统抓出几条微博。当值的不是客服主管,是 CEO 转发了其中一条:"它是不是把我忘了?"
复盘报告后来确认了事故的路径:一位用户在第 1 轮提出"套餐资费变更 + 上月话费争议 + 国际漫游退订"三项复合诉求。机器人在第 1 到第 30 轮之间,通过引导式问询顺利收集到了几乎所有必要的资料,并在第 28 轮时已经准备好了一份临时方案。然后——从第 30 轮到第 35 轮——用户提出了两项补充请求,机器人愉快地接了过去。接着,从第 36 轮到第 46 轮,机器人出现了一种"温和的遗忘":它依然能即时回答当前轮的话,但对早先出现过的两个关键事实——"套餐资费变更"和"国际漫游退订"——的提及频率明显下降。
第 47 轮,用户说:"麻烦帮我转人工。"
机器人生成了一段"转人工备注":
> 用户咨询套餐问题,请人工跟进。
九个字。而人类坐席收到这九个字时,看见的是一份近乎空白的工单——用户和机器人花 46 轮辛辛苦苦搭建起来的全部上下文,被浓缩成了一句模糊的"套餐问题"。人工客服不得不从头开始提问,会话又被延长到了 12 轮。最终用户在第 59 轮给出了那句被 CEO 转发的评价:"它是不是把我忘了?"
是的。它把您忘了——但不是技术意义上的"忘记"。完整的对话日志还在我们这边,1.8 万字符,全须全尾。它把您忘了,是工程意义上的遗忘:当一段对话的信息密度高到压垮注意力的预算时,模型会本能地把权重让给"最近的 token",而把更早期的关键事实挤出它真正在乎的位置。
这不是某一个团队的失误。这是大语言模型在长程任务中一种结构性的脆弱:当上下文窗口被撑满,被遗忘的永远不是离你最远的那些话,而是被中间无数其他 token 慢慢稀释掉的关键信息。
这正是"上下文工程"这门新学科诞生的全部理由。
从 RNN 到 Transformer:上下文窗口的诞生
接下来我想说清楚"上下文窗口"这个工程概念是怎么来的。
在 RNN 与 LSTM 主导的时代,"上下文"是被压缩成一份固定长度 hidden state 的。LSTM 的突破在于门控结构让信息可以"较长时间地"被保留,但理论上无论你堆叠多少层,模型在处理第 N 个 token 的时候,它能看见的"过去"仍然只是那 512 维或 1024 维的向量。那份向量承载了整段历史的全部信息。
这种设计有两个工程后果:第一,它很省存储——不需要为每一轮对话都备一份独立的笔记;第二,但很难精确回溯——你想让模型记得"用户在第 7 轮提到的那个订单号",模型不得不在那 1024 维的向量里"反推"出那个细节。这是一种模糊的、长程的"印象",而不是一种精确的"地址"。
Transformer 的关键反转,是一次工程哲学的转变:它不在"压缩历史"上花力气,而是让每一个新进入的 token 直接和历史中的每一个 token 做一次点积注意力。这是一份全连接的记忆网络——理论上,从第 1 个 token 到第 N−1 个 token,每个位置都对当前 token 的生成"投票",权重由学习得到。但这种设计立刻撞上了工程瓶颈:自注意力的计算复杂度是 $O(N^2)$,N 是序列长度。当 N 从 1k 涨到 8k 再到 32k、128k 时,计算成本和显存占用会以二次方速度上升。
这是一个硬约束,不是一个能轻易绕过去的算法选择。
因此"上下文窗口"——context window——这个概念被工程化成了一个明确的数据结构:一段固定长度(比如 8k、32k、200k)的、可被一次性送进模型做完整注意力的 token 序列。它既是模型的"视野",也是用户必须精打细算的"预算"。
我用一种非常粗略的框图来描述这种关系:
> 上下文窗口 = 大模型的随机存取存储器(RAM)。 > Prompt = 写入这片内存的程序指令。 > 对话历史 = 在这片内存里不断被覆盖、被重排、被淘汰的数据页。
这三者共同决定了一个系统在每一轮的"工作记忆"。它不是模型"自己"的记忆——它是一种被人类精心安排、被工程手段不断维护的外部档案。理解这一点,是踏入上下文工程的第一步。
重新定义"上下文":它不是输入,而是一种动态契约
理解了上下文窗口的工程本质,下一步要打破的,是一种很常见的误解——以为"上下文"就是"我刚才发给模型的那段话"。
在真实的生产系统里,"上下文"至少是三层东西叠在一起:
第一层是指令层(Instruction)。 这是系统的"宪法"——系统提示词、角色定义、行为约束、输出格式。它解释"你是谁,你要怎么回答"。一旦被设下,就在整段对话中几乎保持不变。
第二层是知识层(Knowledge)。 这部分既包括模型权重里已经被蒸馏下来的世界知识,也包括通过检索增强(RAG)临时从外部文档库拉进来的"现役事实"。知识层的体量是最不稳定的——一次检索可能只调出 200 字,也可能调出 8 000 字,取决于"当前任务需要什么"和"召回算法给什么"。
第三层是状态层(State)。 这是最容易被低估的一层——当前会话走到第几轮、用户的核心诉求是什么、哪些事实已经被锁定、哪些承诺已经被给出、当前还有哪些未结清的问题。当对话从第 1 轮走到第 47 轮,第三层的密度是单调增长的,但模型看见第三层的机会,却在前两层面前不断萎缩。
这三层不是静态叠加的,它们会动态竞争 token 预算。指令层通常有 1k–4k 的固化占用,知识层随检索触发可以在 2k–20k 之间大幅波动,状态层——也就是对话历史——往往是那个最先被压缩、被摘要、被选择性遗忘的对象。
这就是为什么"上下文工程"不是"提示词工程"的同义词。
提示词工程关心的是第一层;上下文工程关心的是这三层如何在整个会话生命周期里彼此让位、彼此补位、共同维持一种"模型始终在对的时刻看见对的信息"的契约。一旦理解了这种"动态契约"的本质,许多产品的失败就都可以被重新解读。在这之后的章节里我们会反复回到一个核心命题:一个系统在第 N 轮的体验,不取决于它在第 N 轮"有多聪明",而取决于它在第 N 轮"想起了哪些该想起的、忘记了哪些该忘记的"。
五大支柱:上下文工程的全景图
如果上下文工程是这样一门"在动态预算下维持正确信息流"的手艺,那么这门手艺至少有五根支柱。
```mermaid flowchart TD Goal[上下文质量 在对的时刻看到对的信息] P1[长上下文 扩展模型的视野边界] P2[压缩 在限定窗口内保留更多信号] P3[摘要 将历史熔炼为可复用的精华] P4[检索记忆 跨会话与跨文档的外部知识] P5[状态管理 在多轮交互中维护身份与意图] P1 --> Goal P2 --> Goal P3 --> Goal P4 --> Goal P5 --> Goal P1 -.-> P5 P2 -.-> P3 P4 -.-> P3 ```
> 读法提示: 先看中心目标,再看五根支柱如何各自发力,最后看虚线——它们彼此之间并非独立,而是相互牵制、相互补位。
第一根支柱:长上下文(Long Context)。 它解决的是"模型的视野有多远"的问题——把上下文窗口从 4k 推进到 32k、128k、1M。这听起来像是一种"全赢了"的工程胜利,但实际上是一种"代价重排"。窗口越长,模型能在一次前向里看见的信息越多;与此同时,预填充(prefill)阶段的时间显著延长,KV 缓存占用的显存显著膨胀,系统的吞吐量显著下降。长上下文不是免费午餐,它是一个把成本结构从"单次生成"转移到"上下文准备"的工程选择。
第二根支柱:压缩(Compression)。 它解决的是与长上下文相反方向的问题——当窗口不可延长时,怎么让更多信息"装进"同一段预算里。压缩有两类方法学:无损式的方法把自然语言改写为模型更容易压缩的等价形式(比如把表格转成代码块),带损式的方法则使用专门训练的小模型把一段长文档压成几百个"软 token"。两种压缩都改变同一件事:在限定的 token 预算里,模型的"信噪比"被显著抬高。
第三根支柱:摘要(Summarization)。 摘要和压缩容易被混为一谈,但它们的边界是清楚的:压缩关心"形式",摘要关心"内容"。当你用摘要把对话历史压成 1 000 字时,你关心的是"关键决策还在不在、核心承诺还在不在、用户的核心诉求还在不在"——这是一种语义上的提炼,而不是形式上的压缩。摘要失败时最典型的现象是"细节保留,意图丢失"——仍能复述用户说过什么,却忘了用户到底要干什么。 这正是上一节提到的客服机器人在第 47 轮做的事。
第四根支柱:检索记忆(Retrieval Memory)。 它解决的是窗口无法容纳的"长尾信息"。它把会话之外的事实库——历史工单、知识库文档、第三方系统数据——通过向量化召回拉回到窗口之内。它是"上下文的外延"——模型本身不"记住"那些事,但每次需要时都能精确调出。检索不是简单的相似度匹配,它需要的是任务驱动的选择:检索的边界由"当前对话真正需要哪些事实"决定,而不是由"哪段文字的向量最接近查询"决定。
第五根支柱:状态管理(State Management)。 它是五根支柱里最不性感、却最工程性的一根。它关心的是:当对话从第 1 轮推进到第 N 轮,谁负责记录"我们现在走到哪儿了、谁负责干啥、有什么不能忘、有什么可以扔"。状态可以是结构化的(一份机器可读的工单草稿),也可以是半结构化的(一份由对话自动维护的"对话纪要")。它的工程产物是一份"会话影子文档",由模型和系统共同维护,每一轮结束后都要更新一次。
把这五根支柱画在同一张图上,我们看到的是它们全部指向同一个中心目标——"上下文质量"——但没有任何一根可以独立完成这个目标。它们之间的虚线,是这本书后续章节里会反复回到的工程权衡。读者在这一章需要建立的,不是对每一根支柱的"详细技术"理解,而是对它们的相对位置和彼此牵制关系的"工程直觉"。
一份五步诊断:上下文失效的五种典型模式
为了让这一章不只是"画了一张地图",我留一份简短但可操作的诊断清单。下一次当你遇到用户报告"机器人又忘了",可以顺着这个清单逐项排查:
1. 指令漂移(Instruction Drift)。 系统提示词在某次迭代后悄悄变长,挤占了对话历史的预算。 2. 摘要盲区(Summary Blindness)。 自动摘要保留了字面细节,但漏掉了用户的隐含意图。 3. 检索过载(Retrieval Overflow)。 检索召回的文档挤满了窗口,把对话历史推到了"lost-in-the-middle"的高危区。 4. 状态失联(State Desync)。 状态层(用户档案、未结工单)与会话历史不一致,模型在两个版本之间被迫"二选一"。 5. 上下文抖动(Context Jitter)。 每轮都重新生成或检索上下文,导致同一事实出现多种版本,模型反复"自我矛盾"。
这份清单不是穷举,但若你的系统出现"机器人不听话"的现象,从这五种模式入手通常可以快速锁定根源。
易逝的黄金:成本、延迟、质量的三角约束
理解上下文工程,最不能跳过的是那道三角约束。任何一次"把更多 token 塞进上下文"的决定,几乎都同时触碰三个角。
先看成本。 一次主流大模型调用的成本主要来自两个部分:输入 token 和输出 token 的单价,以及计算这些 token 注意力所需的算力。从 2023 到 2025,输入 token 的单价已经下降了一个数量级,但模型对长上下文的"渴望"却反向增长。文献与运维经验中常见的对比是:把上下文从 4k 扩到 128k,单次会话的端到端成本上升通常不是 32 倍——而是 50 倍到 200 倍,原因是注意力与 KV 缓存的二次方增长。
再看延迟。 延迟是一个对用户体验影响极直接、却容易被低估的维度。当一段对话的上下文长度突破某个临界点(不同模型在不同实现下阈值不同),用户开始感受到"机器人说话之前变慢了"。一种常见的产品体验是"机器人越说越慢、最后用户主动请求转人工"——这往往不是模型变笨了,而是 prefill 阶段开始压垮推理节点。一个粗略的工程经验:当上下文突破 32k 时,prefill 时间往往会成为端到端延迟的主导项。
最后看质量。 质量是三角约束里最微妙的一角。一段常见的直觉是"上下文越多,模型越聪明"——但大量的实证研究与"lost-in-the-middle"现象告诉我们相反的事:模型能有效利用的,往往是窗口开头和结尾的部分;中间一大段文字,再多也存在被稀释的概率——而"稀释"不是均匀的稀释,而是"和当前问题更相关的 token 更容易被加权"。这就是为什么"上下文很多"和"上下文有用"之间有一道鸿沟,而上下文工程正是要跨越这道鸿沟。
把这三件事放在一起,就得到一个稳定的工程真理:上下文是"易逝的黄金"。 它贵,所以不能铺张;它慢,所以不能堆砌;它有效,但只在被精心组织过的位置上有效;它一旦超过某个体量,就开始反向吞噬它本应带来的质量收益。这意味着上下文不是"越多越好",而是"在对的位置上有合适的量最好"。
把这道三角约束和上一节的五根支柱对在一起看,我们就能画出整本书的工程坐标系——
横轴是"被维护信息的体量",纵轴是"这些信息被使用的精度"。五根支柱分别在这两条轴上有自己的位置:
- **长上下文** → 提高体量上限
- **压缩** → 提高体量内的密度
- **摘要** → 提升历史信息的可复用精度
- **检索记忆** → 扩展可被即时召回的体量
- **状态管理** → 保证贯穿全程的身份与意图精度
任何一项工程优化都可以被定位到这张坐标系的某个象限里。这也是本书后续每一章回头看时的一种内部坐标系。
炼金术的隐喻:从混乱的 token 流到有意义的语境
最后,我想为这本书定下一个贯穿始终的隐喻——炼金术。
中世纪的炼金术士相信他们能把铅变成黄金。今天的工程师面对一种有点类似、但其实更困难的工作:把一段混乱的 token 流——用户断断续续的话、历史工单的字段、外部文档的段落、模型自己的中间推理——炼成一段"对模型而言有意义、对用户而言负责任"的语境。这段语境不一定是"黄金般的好答案";它是让模型在每一轮都能做出"比上一轮更接近用户真实意图"输出的一种被精心维护的反应介质。
为什么用炼金术作为隐喻,不直接说"上下文管理"? 因为我希望读者把这件事看作一种手艺,而不是一种功能。功能是"调用某个 API、勾选某个开关"的事;手艺是"在材料、温度、压力、时间之间反复试错、在失败和成功之间反复校准"的事。上下文工程毫无疑问属于后者。它没有银弹——没有一种压缩算法可以一劳永逸,没有一种摘要策略可以适应所有场景,没有一种检索架构可以完美处理所有查询。它要求工程师在多个相互冲突的维度之间反复做出折中,并且要为每一个折中承担它带来的后果。
更重要的是,炼金术作为一种隐喻,提示我们一件事——"被提炼出来的东西"和"被丢弃的东西"之间有一种近乎道德的张力。 在上下文工程里,每一次"摘要"、每一次"压缩"、每一次"窗口边界刷新"都在做同一件事——决定哪一部分历史被保留、哪一部分被遗忘。这是一个信息伦理问题。模型"忘了"用户说过的话,从用户角度往往就是"被忽视了"——而"被忽视"是有重量的。在那位第 47 轮的用户眼里,机器人"忘了他的核心诉求"和"不在乎他的诉求"在感受上是难以区分的。
我会在本书的最后一章回到这种张力,并试着给出一份工程团队可以使用的"取舍准则"。但在那之前,我希望读者先把这一章的这个画面记住——
> 上下文工程,是在大模型的反应炉里,把无序的 token 流炼成一种对该模型而言有意义、对该用户而言负责任的语境。
本章小结
让我们快速回看这一章走过的路。
从一个真实的生产事故开始——一台客服机器人在第 47 轮"选择性失忆"了用户的核心诉求,并因此把整段会话拖进了 59 轮的泥潭。这次事故并不是"模型不聪明",而是一种结构性的脆弱:当上下文窗口被撑满,关键信息会被稀释,模型会把权重让给最近的 token。
为了让这件事变成可被工程化解决的问题,我们回溯了一段历史——从 RNN 的 hidden state,到 Transformer 的全连接注意力,再到被工程化为"上下文窗口"这一数据结构的过程。这条线让我们理解到:上下文不是"模型自己记得的",它是"被人类精心安排在窗口里的"。
我们重新定义了"上下文"——它不是输入,而是一种由指令、知识、状态三层动态叠加而成的契约。这三层会彼此竞争 token 预算,而上下文工程关心的就是这种竞争如何被管理。
我们用一张全景图引出了五大支柱——长上下文、压缩、摘要、检索记忆、状态管理——它们共同服务于"上下文质量"这一核心目标,但没有任何一根可以独立完成这个目标。我们也顺手给了五步诊断清单,让读者可以把这一章带回到真实项目里。
我们从成本、延迟、质量三个角,画出了上下文必须服从的三角约束。它解释了为什么上下文是"易逝的黄金"——它既不能无限堆积,也不能被任意裁剪。
最后我们用一个贯穿本书的隐喻——炼金术——为这门新学科定下了调子:它是一种手艺,不是一种功能;它需要工程师在多个相互冲突的维度间反复做出折中;它带有一种信息伦理的张力。
下一章,我们会把镜头从这种宏观视角推进到一台大模型最神秘的微观机制——注意力。我们将以"lost-in-the-middle"为起点,剖析注意力是如何被稀释的,并由此引出我们"上下文工程"在第一层微观战场上要打的仗。