导论:为什么我们需要"模型上下文工程"
导论:为什么我们需要"模型上下文工程"
如果让你用一句话描述过去三年大模型应用开发中最让人崩溃的问题,你会怎么回答?
我猜很多人会选"幻觉"——模型一本正经地胡说八道。但我要给出一个也许不那么直觉的答案:遗忘。具体来说,是模型对我们说过的话、做过的事、关心过的问题,表现出一种近乎"金鱼记忆"般的遗忘。这种遗忘不是某个模型的 bug,而是一种系统性、结构性的工程难题。本书要解决的,正是这个难题——我们如何把"让模型记住该记住的东西"这件事,变成一门可被设计、可被实现、可被维护的工程学科?我把它叫做模型上下文工程(Model Context Engineering,简称 MCE)。
这一章是全书的第一站。我们不会马上写代码、列公式、画架构图。我们要做一件更重要的事:把问题看清楚。看清真实业务中那些让人抓狂的"上下文失败"到底长什么样,理解它们的根因,然后建立一套完整的思维框架——六大技术支柱——来系统性地应对它们。只有把问题的轮廓看清了,我们后续的技术方案才不会变成"头痛医头、脚痛医脚"的局部补丁。
1.1 真实业务中的三大上下文失败场景
工程师对"上下文失败"这四个字的理解,往往是从一次具体的故障开始的。我把过去几年最常见的失败归纳为三类:长对话遗忘、信息过载、上下文溢出。让我们一个一个来看。
场景一:陪聊机器人 3 小时后忘记用户名字
这是我亲身经历的一个真实案例,也是本书反复会用到的一个"教学标本"。某智能客服团队为了让机器人"更有人情味",把整个对话历史都塞进了 Prompt。系统上线初期一切顺利,机器人能记住用户说过"我儿子今年高考"、"我不太懂电脑"这种细节。但用户连续使用 3 小时后,问题出现了:
> 用户:*你还记得我儿子今年要高考吗?* > 机器人:*抱歉,我不太清楚您之前提到过的事情,您能再说一遍吗?*
用户在工单里愤怒地写道:"这机器人是不是有阿尔茨海默症?三个小时前刚说的事就忘了!"
团队的排查结果让人哭笑不得:模型本身没问题,Token 计数也远没到上限。问题出在他们自己写的一段"对话压缩逻辑"上——这段逻辑每 20 轮触发一次,会把"看起来不重要的对话"剪掉。所谓的"看起来不重要",是用一个简单的关键词过滤器判断的,而"高考"、"儿子"这类带情感色彩但不在高频词表里的词,恰好被判定为"不重要的闲聊"。结果就是:技术上的"压缩",在用户体验上变成了"遗忘"。
更深一层去看,这个团队的失败不是某个开发者的疏忽,而是对"什么信息重要"这件事缺少系统的工程化判断。他们用"关键词频率"代替了"语义重要性",用一个静态的规则代替了一个动态的评估机制。当业务复杂度提升后,这种粗糙的判断迅速崩塌。
这个故事揭示了上下文工程的第一个核心矛盾:模型的"工作记忆"不是无限大的,但我们又不能简单地扔掉任何东西。
场景二:把 10 份合同塞进 Prompt 后,模型答非所问
第二个场景来自一个法律科技团队。他们试图用大模型做合同审查:把 10 份、每份 50 页左右的并购合同一起丢给模型,让它回答"哪份合同的违约责任最重"。结果模型给出的答案与法务人工审核的结果大相径庭,更糟糕的是,它经常给出一些"似是而非"的引用——看起来像是在引用合同原文,但其实是在胡编。
这就是典型的信息过载。模型不是不能处理长文本,而是当文本长度逼近上下文窗口上限时,模型的"注意力"会显著分散。有研究专门量化了这种现象:在一个 32K 上下文窗口的实验中,把关键信息放在文本开头或结尾时,模型的召回率约为 80%;但把同样的关键信息塞到中间位置,召回率会跌到 30% 左右。学术上把这种现象叫做 "Lost in the Middle"(迷失在中间)。这个发现在多个独立团队的实验中得到了重复验证,已经成为上下文工程领域最广为人知的实证规律之一。
更有意思的是,这个团队后来在生产环境做了一次 AB 测试:当输入从 10 份合同精简到 3 份合同 + 一个明确的"违约责任摘要"时,模型的回答准确率从 41% 提升到了 78%。他们学到的教训很宝贵:少即是多。当你不知道哪些信息真正有用时,一股脑全塞进去,往往比只塞最相关的几条更糟糕。
场景三:上下文溢出导致整条生产线宕机
第三个场景是发生在某电商大促期间的"事故级"故障。他们的商品推荐机器人会在每轮对话中,把用户的浏览历史(最多 200 条)、购物车(最多 50 件)、当前会话上下文(最多 50 轮)、以及系统 Prompt(约 2000 字)全部拼接起来,作为输入送给模型。当大促流量峰值来临,对话轮次和浏览历史同时增长,模型的输入 Token 数突破了 API 的最大限制。直接结果:整个推荐服务返回 400 错误,前端体验雪崩。事故复盘时,技术负责人说了句让人印象深刻的话:"我们以为我们在用 AI,其实我们在自杀。"
这种上下文溢出看似是一个简单的"加个 try-catch"就能解决的问题,但在真实的工程实践中,它暴露出的是更深层的架构缺陷——上下文管理没有和系统的可观测性、可降级、可恢复能力耦合在一起。当一个问题出现时,我们需要的不是一个补丁,而是一套完整的工程方法论。一个健康的上下文系统,应该像一辆配备了刹车、防抱死系统、备用油箱的汽车——不仅能跑得远,更能在各种异常情况下安全可控地停下来。
这三个场景看似独立,其实指向了同一个根因:我们一直在用"写作文"的思维处理上下文,而不是用"建系统"的思维。我们习惯于想"我应该把哪些信息告诉模型",而不是想"我应该如何设计一个上下文系统,让它在容量、相关性、成本、可恢复性之间保持动态平衡"。这正是"模型上下文工程"这个新学科要解决的问题。
1.2 为什么简单地把信息塞进 Prompt 不够
看到这里,有些读者可能会想:现在主流大模型的上下文窗口已经做到 128K、200K 甚至 1000K 了,我直接把全部信息塞进去,不就行了?
这种直觉很自然,但有三个隐藏的成本,让它在大规模应用中几乎不可行。
第一,信息密度的边际递减效应。模型的注意力机制并不会因为你多塞了一倍文本,相应地"多理解一倍"。事实上,当你把 10 条相关信息一起喂给模型,模型对每一条的注意力都会下降——这是注意力机制的物理特性,不是某个模型的 bug。我们可以把它想象成一个会议:与会者越少,每个人发言的权重越高;与会者越多,每个人发言的"存在感"越低。上下文工程要做的,就是确保每一次"会议"都只有最相关的"与会者"在场。
用一个更技术化的表述:模型对上下文的"有效利用率"会随着上下文长度的增加而单调递减。这不是工程问题,而是数学问题——当注意力的总和固定为 1 时,分给每个 Token 的注意力就是 1/N。把上下文长度从 1K 增加到 10K,分给每个 Token 的注意力就从 1/1024 降到 1/10240,刚好差一个数量级。这也是为什么很多团队会发现,把同样的 Prompt 从短版本改成长版本,模型的回答质量不升反降。
第二,"Lost in the Middle"——位置对注意力的强烈影响。多项独立研究都表明:大模型对上下文窗口开头和结尾的内容,注意力明显强于中间部分。这意味着,即使你把所有信息都塞进去了,如果关键信息落在"中间地带",它的实际影响力会被大幅削弱。这是一个反直觉的事实:多塞信息不仅不一定有帮助,甚至可能有害。
工程上有一个简单但非常实用的对策:永远把最重要的信息放在开头或结尾。如果你有几条核心指令、几个关键事实、几个必须被引用的数据,把它们放在 System Prompt(最开头)和最近一轮的对话(最结尾),模型记住它们的概率会显著提高。这是一个零成本的优化技巧,但它对最终效果的影响往往超出想象。
第三,成本与延迟的现实约束。Token 不是免费的。一个 100K Token 的输入,调用一次主流大模型的成本可能在几毛钱到几块钱之间,而且响应延迟会显著增加(因为 Prefill 阶段的计算量与输入长度近似线性)。当我们把上下文管理做得粗糙时,等于在每一轮对话中都在浪费钱、浪费时间。在企业级应用中,这种浪费会被放大成百万、千万级别的成本。
更重要的是,延迟对用户体验的影响是乘性的。如果一次对话的平均响应时间从 1 秒增加到 3 秒,用户感知的"慢"不是 3 倍,而是 10 倍(心理学上叫"韦伯-费希纳定律")。当你的应用是实时对话类(陪聊、客服、导购)时,这种延迟的恶化会直接转化为用户流失。
把信息塞进 Prompt 不是"够不够"的问题,而是"代价是否值得"的问题。一个好的上下文工程,应该像是一个精明的物流系统:不是把仓库里所有东西都搬到客户面前,而是只把当下这一单最需要的东西精准地送到。
1.3 理解上下文窗口:模型的"工作记忆"
要真正理解上下文工程,我们必须先理解它的物理基础——上下文窗口(Context Window)。我倾向于把它比作模型的"工作记忆"。
我们人类的短时记忆一次大约能记住 7±2 个项目,几秒钟后就开始模糊,几十秒后大部分细节消失。大模型的"工作记忆"也有类似的限制,但实现机制完全不同:它是一个固定长度的 Token 序列,模型在每一轮推理时,只能"看到"这个序列里的内容。当序列超过训练时见过的最大长度,模型的注意力分布就会变得不稳定——这就像让一个习惯了 7 个项目记忆的人去记 70 个项目,结果可想而知。
为了把这个抽象的概念具体化,我们来看几个关键事实:
Token 是什么? 在大多数主流大模型的实现里,Token 不是字符,也不是单词,而是模型自己定义的"子词单元"。粗略地估算,1 个 Token ≈ 0.75 个英文单词 ≈ 1.5 个中文字符。但这只是平均值,实际比例随文本内容、所用分词器不同会有显著差异。例如,专业术语密集的英文文本,Token 化效率较低;闲聊口语的英文文本,Token 化效率较高。一份 10 万字的中文小说,输入到主流模型时,Token 数可能在 13 万到 18 万之间。
我经常被问到一个问题:"我应该用字符数还是 Token 数来预估我的输入长度?"答案是:永远用 Token 数。因为模型的限制是基于 Token 的,不是基于字符的。一个常见的错误是用文本编辑器的字数统计来估算长度,结果上线后发现 Token 数远超预期,导致频繁触发溢出。几乎所有主流大模型都提供 Token 计算工具,你应该把它集成到开发流程的每一步。
上下文窗口是"输入+输出"的总和。很多初学者会忽略这一点。模型宣称的"128K 上下文窗口",指的是输入和输出加起来不能超过 128K。如果你计划让模型输出 4000 Token 的长答案,那输入就只有 124K 的余量。这个细节在长对话、长文档处理中至关重要。一个常见的 bug 模式是:开发者在本地测试时输入较短,没有考虑输出长度;当生产环境触发长回答时,输出预留不足导致输入被截断或整体报错。
位置编码与注意力的关系。Transformer 架构通过位置编码(Positional Encoding)让模型"知道"每个 Token 在序列中的位置。但位置编码本身有"有效范围"的限制——超出训练时见过的最大长度后,模型的注意力分布会变得不稳定。这也是为什么早期模型把上下文窗口从 2K 扩展到 32K、128K 时,需要专门的训练技巧(如 RoPE 外推、ALiBi 偏置)来保证质量。
窗口内的所有 Token 都参与每一步推理的计算。这一点非常重要:模型的计算复杂度与上下文长度的关系是近似 $O(N^2)$,其中 N 是序列长度。这意味着输入翻倍,计算量大约翻 4 倍。这就是为什么长上下文模型的响应时间不是线性增长,而是超线性增长。一个经验法则是:上下文长度翻倍,Prefill 阶段的时间会变成 3-4 倍。
上下文窗口不是"装得越多越好"的容器,而是一个有着严格物理约束的资源。把它当作工作记忆,意味着我们要像管理内存一样管理它——精打细算、按需分配、动态回收。这是上下文工程最底层的思维方式。
1.4 上下文工程:六大技术支柱
理解了失败场景和上下文窗口的本质后,我们终于可以正面回答"上下文工程到底是什么"这个问题。
我给出一个工作定义:模型上下文工程,是一门设计、实现、维护"模型工作记忆"的工程学科。它的目标是在有限的上下文窗口内,动态地为模型提供最相关、最紧凑、最经济的输入信息,从而在准确性、成本、延迟、用户体验之间取得最优平衡。
这个定义里有几个关键词:设计、实现、维护——这是工程学科的三要素;动态——说明它不是一次性配置,而是持续运行的过程;最相关、最紧凑、最经济——这是三个可以量化、可以优化的目标。
为了把抽象的定义变成可操作的工具,我把这门工程学科拆解为六大技术支柱。它们不是相互独立的模块,而是一个相互配合的系统:
支柱一:长上下文(Long Context)。这是最"硬"的支柱——它处理的是模型的物理极限。当上下文窗口不够用时,我们能否通过架构创新(稀疏注意力、状态空间模型、循环记忆等)让模型"看"得更远?这是学术界和工业界投入最密集的方向。从 2023 年的 4K、8K,到 2024 年的 128K、200K,再到 2025 年开始出现的 1M、10M 量级,长上下文技术的进步直接重塑了上下文工程的可能性边界。但这并不意味着我们就可以"无脑塞"——超长上下文的有效利用率仍然是一个未解的开放问题。
支柱二:压缩(Compression)。如果说长上下文是"扩容器",那压缩就是"减压器"。它研究的是如何在不丢失关键信息的前提下,把上下文变得更紧凑。代表性技术包括 Prompt 压缩(如 LLMLingua 系列方法)、Token 级压缩(如去除停用词、合并重复表述)、语义级压缩(如基于嵌入相似度的合并)。压缩的核心难点在于保真度评估——你怎么知道你压缩后的 Prompt 还能让模型给出和原始 Prompt 一样质量的回答?这是一个需要建立专门的评测体系才能回答的问题。
支柱三:摘要(Summarization)。压缩和摘要听起来很像,但有一个关键区别:压缩是无损或近似无损的(基于规则或模型去除冗余),摘要是有损的(基于语义重要性做取舍)。摘要技术在长对话管理中扮演核心角色——开头那个"陪聊机器人 3 小时后遗忘"的案例,本质上就是摘要策略失败。一个成熟的摘要系统,应该能够识别对话中的"目标"、"偏好"、"事实"、"情感"四类关键信息,并对它们采取不同的保留策略。例如,目标类信息几乎不应该被丢弃,情感类信息可以适度抽象但要保留情感倾向,事实类信息可以做去重合并。
支柱四:检索(Retrieval)。也叫 RAG(Retrieval-Augmented Generation)。当我们无法把所有信息都塞进上下文时,就需要一套"按需取用"的机制。检索研究的是:给定当前对话,从庞大的外部知识库中找出最相关的几段文本。它的核心技术包括向量检索、关键词检索、混合检索、重排序等。RAG 在 2023-2024 年成为整个行业的事实标准,但它的工程化挑战远没有"看上去那么简单"——召回率、相关性、时效性、多跳推理等问题,每一个都需要专门的方案。
支柱五:记忆(Memory)。这是一个相对较新的概念,专指跨会话、跨用户、跨时间的长期信息保留机制。它和"上下文窗口内的临时信息"相对,强调的是模型需要"记住"用户的历史偏好、长期目标、个人画像等。这正是当前 Agent 系统设计的核心挑战之一。记忆和检索的区别在于:检索是基于当前查询的临时性信息获取,记忆是基于用户身份的长期性信息保留。一个简单的类比:检索是"我现在需要查什么",记忆是"我是谁、我过去做过什么"。
支柱六:状态管理(State Management)。这是六大支柱中最容易被忽视、但工程上最关键的一环。它研究的是:在多 Agent、多工具、多轮交互的复杂系统中,上下文如何在各个组件之间流转、共享、隔离、同步。一个没有良好状态管理的上下文系统,就像一个没有调度算法的多线程程序——看起来在工作,实则随时可能死锁。当前的 Agent 框架(如 LangGraph、AutoGen)已经把状态管理提升到了产品级的高度,但如何设计"状态契约"、"状态快照"、"状态回滚"等机制,仍然是一个需要深度工程经验的领域。
为了让你直观地看到这六大支柱如何相互配合,我画了一张关系图:
```mermaid flowchart TB A[模型上下文工程] --> B[长上下文] A --> C[压缩] A --> D[摘要] A --> E[检索 RAG] A --> F[记忆] A --> G[状态管理] B --> H[统一的输入序列] C --> H D --> H E --> H F --> H G --> H H --> I[大模型推理] ```
这张图想传达的核心信息是:六大支柱不是六个独立的工具,而是六种不同的"原材料加工方式",它们共同的目标是把信息整合成一份"喂给模型的输入序列"。状态管理扮演"调度者"的角色,决定什么时候、用哪种加工方式处理什么样的信息。
这里有一个常见的误解,我想特别澄清:很多团队把上下文工程理解为"只要做好 RAG 就够了"。这是不对的。RAG 解决的是"从外部知识库找信息"的问题,但它解决不了"系统化的状态流转"、"跨会话的记忆"、"摘要策略的优化"、"长上下文的物理扩展"这些独立但同等重要的挑战。一个成熟的上下文工程方案,一定是六大支柱的有机组合。
为了让你理解六大支柱如何协同工作,我举一个具体例子。假设你正在设计一个"AI 投资顾问"产品,它需要和用户进行多轮对话,理解用户的风险偏好、财务状况、投资目标,然后给出个性化的资产配置建议。这个产品的上下文系统至少需要:
- **长上下文**:单次对话可能涉及 50-100 轮交互,需要稳定的 32K+ 上下文窗口支持;
- **压缩**:去除用户重复表述、合并相似问句,把对话历史压缩到可管理范围;
- **摘要**:每 20 轮触发一次摘要,提取"用户偏好"、"已讨论的产品"、"未决的问题"三类核心信息;
- **检索**:当用户询问某只具体基金时,从金融知识库中检索最新的基金报告、行情数据;
- **记忆**:跨会话保留用户的风险测评结果、历史持仓、长期投资目标;
- **状态管理**:管理"风险测评→方案讨论→执行确认"等多个阶段的上下文流转。
你会发现,没有任何一个支柱能独立解决这个产品的上下文挑战。它们必须作为一个系统共同工作。
1.5 实践:用一句话定义"上下文工程",并开始你的工程思维
在结束本章之前,我想给你两个可以立即使用的"工具"。
**第一个工具:一句话定义*