为什么需要上下文工程:从一次失败的对话说起
1.1 一次让人哭笑不得的客服对话
2024 年夏天,某跨境电商公司的客服负责人李岩在周会上播放了一段录音。那是一段人机对话的实录,AI 客服代表的是他们花了三个月时间、调用了某顶级大模型、号称"智能升级"的全新客服系统。录音时长 11 分钟,对话轮次 22 轮。在前 8 轮里,AI 表现得体——它能识别用户口音中的"退货"意图,能从订单号中解析出商品信息,甚至能在用户抱怨"快递太慢了"时主动安抚。但从第 9 轮开始,事情开始变得奇怪。
第 9 轮,用户问:"那个蓝色款还有货吗?" AI 回答:"您好,请问您要咨询哪款商品呢?"
第 12 轮,用户已经解释过两次:"我说的是我第 3 轮提到的那双蓝色运动鞋,42 码。" AI 回答:"亲,建议您提供更详细的商品信息,比如颜色和尺码哦。"
第 17 轮,用户明显带着怒气:"你到底有没有在听我说话?" AI 礼貌地回复:"非常抱歉给您带来困扰,请问您是想了解……"
第 22 轮,用户说"算了我要投诉",AI 回答的是:"亲,我们已为您下单,请耐心等待发货。"
周会上的 12 个人听完这段录音,沉默了 30 秒。然后李岩说出了所有人的心声:"这不是模型不够聪明,是模型'失忆'了。"
这个场景不是孤例。在 2023 年到 2025 年之间,几乎所有把大模型接入真实业务场景的团队,都遭遇过类似的问题。一个在单轮问答里表现得像博士的模型,一旦塞进 20 轮、50 轮、上百轮的真实业务对话,就暴露出"金鱼般的记忆"。它不记得 5 分钟前说过的话,不记得用户已经报过三次的订单号,不记得自己上一轮给出的承诺。
这种现象的根源,并不是模型"笨",也不是模型"没有训练好"。它的根源,在于一个被绝大多数初学者长期忽视的工程问题——上下文工程(Context Engineering)。
这一章,我们就从这次失败的对话开始,逐层揭开"上下文"的神秘面纱,定义清楚我们这本书要解决的核心问题,然后让你亲手写出一段调用大模型 API 的代码,打印出你所用模型的"上下文窗口大小"。在章节末尾,我会把全书 15 章的路线图完整呈现给你,让你知道从这一章到最后一章,我们将走过一条怎样的工程化道路。
1.2 上下文窗口:LLM 的"工作记忆"为什么会有上限
要理解上下文工程,我们首先必须理解一个物理事实:大语言模型并不是在全网知识上做"无状态"的问答,它的所有回答,都只能基于一个有限大小的输入。这个输入,在工程上叫做"上下文窗口(Context Window)"。
你可以把上下文窗口想象成模型的"工作记忆"。当一个厨师要做一道菜,他必须把"当前这道菜的原料、配料、烹饪步骤、火候要求"全部摆在自己的案板上。案板有多大,他能同时处理的信息就有多少。模型也一样:上下文窗口有多大,它在一次推理中能"看到"的信息就有多少。
那么这个"案板"有多大呢?我们用 token 来衡量。token 是大模型处理文本的最小单位,一个汉字通常对应 1 到 2 个 token,一个英文单词通常对应 1 到 1.5 个 token。当前主流模型的上下文窗口大小如下(数据截至 2025 年初):
- GPT-3.5 系列:4K 或 16K token
- GPT-4 系列:8K、32K、128K token
- Claude 3 Sonnet:200K token
- Claude 3.5 Sonnet:200K token
- Gemini 1.5 Pro:1M(100 万)token
- 通义千问 Qwen2.5:32K、128K、1M token
- 文心一言 4.0:8K、128K token
- DeepSeek-V3:64K token
- Llama 3.1:128K token
- 智谱 GLM-4:128K、1M token
看起来,1M token 似乎已经足够大——它大致相当于 150 万个汉字,或者一本中长篇小说的全文。但请注意,1M token 并不是"无限大"。它有明确的物理上限,而且这个上限是硬约束:当输入超过窗口大小时,模型要么直接报错,要么会静默截断——只保留最后若干 token 的内容。
这就解释了李岩团队遇到的问题。客服对话系统的 prompt 通常包含这样几部分:
1. 系统提示词(system prompt):定义 AI 是客服、应该怎么说话 —— 约 500-2000 token 2. 知识库检索内容:商品信息、售后政策 —— 约 2000-10000 token 3. 工具调用结果:订单查询结果 —— 约 500-3000 token 4. 对话历史:用户和 AI 此前的每一轮对话 —— 线性增长
如果每一轮对话平均 200 token,那么到第 50 轮时,单是"对话历史"就累积到 10000 token。加上系统提示和工具结果,总量早就超过早期 8K 模型的窗口。模型在这种情况下,常见的"应对方式"是:
- **静默截断**:把最早的几轮对话丢掉,假装那些对话不存在
- **指令漂移**:早期轮次中用户提供的关键信息(比如订单号、商品颜色),因为被截断而丢失
- **自我矛盾**:不知道自己上一轮承诺过什么,行为前后不一致
- **幻觉补全**:为了对话的连贯性,模型会"猜"——而这种猜测往往就是胡说
回到李岩的案例。那个 AI 之所以在第 9 轮开始"听不懂人话",极有可能是因为对话历史已经超过 8K,系统悄悄丢掉了第 3 轮用户提到"蓝色运动鞋"的那次发言。到了第 22 轮,AI 回答"已为您下单"更是一种典型的"幻觉补全"——它可能从对话的某处抓到了"下单"两个字,就顺势编造了一个订单。
这就是我们这本书要解决的核心问题:当模型的上下文窗口这个物理限制无法回避时,我们如何通过工程手段,让模型在有限的窗口里,始终"记得"它应该记得的东西?
1.3 给"上下文"一个精确的工程定义
在大众语境里,"上下文"是一个模糊的词,可以指"语境"、"环境"、"背景"。但在大模型工程领域,我们必须给它一个精确的、可操作的定义。否则,我们无法设计系统、无法评估效果、无法交流方案。
在本书中,我给"上下文"下这样的定义:
> 大模型的"上下文",是指在某一次推理调用中,作为输入送给模型的所有信息的总和。
这个总和,由以下四类信息组成:
第一类:系统提示词(System Prompt) 也叫做"系统消息"或"角色设定"。它告诉模型"你是谁、你能做什么、不能做什么、应该遵循什么规则"。比如,客服场景下的系统提示词可能长达 2 页,包含品牌话术、退换货规则、禁用词清单、情绪安抚策略等。系统提示词的特点是:它是相对静态的,每次推理都基本相同。
第二类:对话历史(Conversation History) 也就是用户和模型此前的所有轮次。在 ChatGPT、Claude 这类产品里,对话历史会作为"messages"数组传给 API。它是动态增长的,是导致上下文爆炸的"头号元凶"。
第三类:检索内容(Retrieved Context) 当模型需要回答"贵公司最新的退货政策是什么"时,单纯依靠模型自身的训练数据是不够的——信息可能过时。工程上常见的做法是:先用用户的 query 去向量数据库或搜索引擎检索相关内容,把检索到的文档片段作为"上下文"的一部分塞给模型。这就是 RAG(Retrieval-Augmented Generation,检索增强生成)。检索内容的特点是:由用户的实时 query 决定,每次推理都不同。
第四类:工具调用结果(Tool Results) 当大模型可以调用外部工具(比如查订单、查天气、执行 SQL)时,工具的返回结果也是上下文的一部分。工具调用结果的特点是:结构化、体积可能很大(比如一张查询结果表)。
我们用一个具体的请求来举例,假设用户问"我上周买的那双蓝色运动鞋什么时候发货?":
``` [系统提示词] 约 1500 token 你是一个专业的电商客服,名叫小蜜。你需要: 1. 用亲切的语气回答用户问题 2. 严格遵守公司《售后政策 v3.2》 3. 不得承诺超出权限的退款额度 4. 在用户情绪激动时优先安抚 [省略约 1300 token 的细则...]
[对话历史] 约 800 token 用户:你好 AI:您好,请问有什么可以帮您? 用户:我想问一下我上周的订单 AI:好的,请提供您的订单号 用户:20240815-XK-3392 AI:已为您查到,订单包含一双蓝色运动鞋 42 码 用户:什么时候发货? [当前问题] 约 30 token 我上周买的那双蓝色运动鞋什么时候发货?
[工具调用结果] 约 200 token 工具:order_query 输入:order_id="20240815-XK-3392" 输出:{ "order_id": "20240815-XK-3392", "status": "shipped", "ship_time": "2024-08-16 14:23:11", "tracking_number": "SF1234567890" }
[检索内容] 约 500 token [来自《发货时效说明 v2.1》] 下单后 24 小时内发货,周末顺延…… [来自《物流时效说明》] 顺丰陆运 1-3 天到达…… ```
总计:约 3030 token。这次请求轻松装得下。
但请注意:在真实的客服系统里,对话历史会持续累积。如果用户在这个问题之后又聊了 30 轮,每轮 300 token,那对话历史就膨胀到 9000 token。加上其他三类内容,单次推理的输入很可能突破 12000 token。如果模型上限是 8K,系统会悄悄截断;如果上限是 16K,会越来越接近极限,推理成本和延迟也会显著上升。
这就是为什么"上下文工程"如此重要。我们不能只让模型"看到"原始的、未经整理的信息,我们必须主动地、有策略地把四类信息组装起来,让最关键的内容保留在窗口里,让无关紧要的冗余被裁剪掉。
1.4 模型能力边界 vs 上下文工程边界:工程能做什么,不能做什么
理解上下文工程,有一个常见的误区必须澄清:"上下文工程"不是要替代模型能力,而是要和模型能力协同工作。
我把它总结成一句话:
> 模型能力决定"模型能做什么",上下文工程决定"模型在这一次具体调用中实际能做好什么"。
让我用几个例子来说明这个区别。
场景一:模型能解一元二次方程,但给错了公式版本
- 模型能力:会解方程
- 上下文工程责任:必须在系统提示词中明确告诉模型"使用求根公式 $x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$,不要使用韦达定理"
场景二:模型能写 SQL,但不知道你们公司的表结构
- 模型能力:会写 SQL
- 上下文工程责任:必须在检索内容中提供数据库的 schema、字段说明、命名规范
场景三:模型能识别用户情绪,但不知道用户的历史问题
- 模型能力:能分类正面/负面/中性
- 上下文工程责任:必须把用户过去 7 天的咨询记录检索出来,作为上下文的一部分
场景四:模型能做数学推理,但算错 759 × 841
- 模型能力:会做四则运算
- 上下文工程责任:要么通过提示词要求"使用代码工具计算",要么将工具结果作为上下文的一部分(让代码执行器算好后,把结果返回给模型)
可以看到,在所有这些例子里,模型本身的能力是足够的,但模型的"工作记忆"里缺乏它完成任务所需的信息。上下文工程的核心工作,就是把模型完成任务所需的所有信息,在每一次推理调用时,精打细算地装进上下文窗口里。
这也意味着,上下文工程有一个清晰的边界:
- 它不能改变模型的知识截止日期
- 它不能突破上下文窗口的物理大小
- 它不能弥补模型在某个领域的能力短板
但它能做的是:
- 让模型在有限的窗口里,看到最有价值的信息
- 让长程对话保持连贯,不丢失关键信息
- 让外部知识、工具结果、历史状态有机融合
- 让推理成本(token 消耗)和响应延迟可控
理解这个边界,对初学者尤其重要。很多人以为"上了一个 GPT-4 什么问题都能解决",结果发现真实业务中表现糟糕;其实问题不出在模型,出在上下文没有管理好。也有一些人走向另一个极端,认为"只要 prompt 写得够好,模型就无所不能",结果在面对长程多轮任务时束手无策。真正成熟的工程实践,是把模型能力和上下文工程当作一对双螺旋来设计。
1.5 同一问题,两种命运:20 轮对话的对比实验
为了让你直观感受"有无上下文工程"的体验差异,我设计了一段 20 轮的对比实验。两个对话系统的"大脑"是完全相同的同一个大模型(比如 GPT-4 或 Qwen2.5),唯一的区别是上下文组装策略。
系统 A(无工程化上下文):
- 系统提示词只有 3 行:"你是一个客服,请礼貌回答用户问题。"
- 每次推理只保留最近 5 轮对话历史
- 不调用任何工具
- 不接入任何知识库
系统 B(有工程化上下文):
- 系统提示词 1500 token,包含品牌话术、退换货规则、情绪处理流程
- 对话历史根据相关性动态保留(重要的早期信息不丢失)
- 实时调用订单查询工具
- 接入 RAG 知识库,检索"发货时效"、"退换货政策"等文档
下面是 20 轮对话的浓缩版(实际对话会更长,这里只展示关键节点):
第 1-5 轮:开场与信息收集
| 轮次 | 用户 | 系统 A(无工程) | 系统 B(有工程) | |------|------|------------------|------------------| | 1 | 你好,我上周买的那双蓝色运动鞋(订单号 20240815-XK-3392)什么时候能到? | 您好!请问您要咨询哪款商品呢? | 您好!已为您查到订单 20240815-XK-3392,商品为蓝色运动鞋 42 码。该订单已于 8 月 16 日 14:23 发出,顺丰单号 SF1234567890,预计 1-3 天送达。 | | 2 | 好的谢谢。那如果我不想要了,可以退货吗? | 您好,请问您想了解什么? | 亲,根据《售后政策 v3.2》,商品已发货但未签收的情况下,您可以拒收,运费由我们承担。如已签收 7 天内不影响二次销售也可退货。 | | 3 | 我想换一个黑色的,42 码的还有货吗? | 抱歉,我无法查询库存。 | 已为您查询,42 码黑色款当前库存 17 双,可以为您换货。换货流程需要您先…… |
第 6-10 轮:出现矛盾点
| 轮次 | 用户 | 系统 A(无工程) | 系统 B(有工程) | |------|------|------------------|------------------| | 6 | 我现在比较急,能不能加急配送? | 抱歉,我们没有加急服务。 | 亲,目前该订单已发货,无法拦截加急。但您可以联系顺丰客服 95338 申请加急派送。或者下次购买时选择我们的"次日达"服务。 | | 8 | 我之前问的那个蓝色运动鞋,不是 42 码,是 41 码,你刚才说错了。 | 抱歉给您带来困扰,请问您要咨询什么? | 非常抱歉!让我重新核对一下……订单 20240815-XK-3392 显示是 42 码。请问您是记错了,还是您想确认 41 码的库存? | | 9 | 算了,我先不换了。但我想问一下你们店有没有专业的运动袜卖? | 抱歉,我无法查询商品。 | 有的,我们目前在售的有三款运动袜……(以下是 3 款商品的介绍、价位、用户评价摘要) |
第 15-20 轮:情绪与承诺
| 轮次 | 用户 | 系统 A(无工程) | 系统 B(有工程) | |------|------|------------------|------------------| | 15 | 你们家的服务真是太差了!我要投诉! | 非常抱歉给您带来困扰。 | 亲爱的顾客,真的非常抱歉让您有这样的体验。我已经为您记录了这次不愉快的咨询,会反馈给主管。我们会……(调用情绪安抚话术模板) | | 18 | 你刚才说会送我一张 50 元代金券,是真的吗? | 抱歉,我无法确认您说的内容。 | 亲,让我重新核对……我此前的回复中没有提到代金券哦。不过鉴于您这次不太愉快的体验,我为您申请了一张 20 元无门槛代金券(券号 ABC123),有效期 7 天,希望能让您满意。 | | 20 | 好的吧,那这次就先这样。 | 感谢您的咨询,再见。 | 非常感谢您的理解!我们会持续改进。再次为给您带来的不便致歉。期待下次为您服务~ |
对比分析:
在第 1 轮,系统 A 完全不知道用户已经提供了订单号,需要用户重复一次才能查询;系统 B 直接调用工具,把"何时发货"这个问题一次答完。
在第 6 轮,系统 A 用一句"没有加急服务"打发用户;系统 B 提供了真实可执行的方案(联系顺丰)。
在第 8 轮,系统 A 已经完全"失忆",不知道 5 轮之前聊的是什么;系统 B 不仅记得订单的尺码,还能主动核对,反过来给用户提了个醒("订单显示是 42 码")。
在第 15 轮和第 18 轮,差异最戏剧化。系统 A 面对投诉只会机械道歉;系统 B 启动了情绪安抚流程,并且更重要的是——它没有"承诺"自己没有承诺过的东西。当用户说"你刚才说送 50 元代金券"时,系统 B 实事求是地说"我没说过",同时主动申请了一张 20 元代金券作为补偿。这种"诚实 + 主动服务"的行为,正是优秀的上下文工程才能养成的。
注意:模型 A 和模型 B 底层是同一个大模型。它们表现差异完全来自"送进模型的上下文"不同。这就是上下文工程的威力。
1.6 第一个 Hello World:让你的代码说出"上下文"
理论讲完了,是时候动手了。我将带你写第一段 Python 代码,调用一个大模型的 API,并打印出你所用模型的上下文窗口大小。这是每一个上下文工程师的"Hello World",也是后续所有章节的起点。
第一步:准备工作
在开始之前,你需要准备两样东西:
1. 一个 LLM API 的访问凭证。可以选择 OpenAI、Anthropic、DeepSeek、智谱、月之暗面、阿里云百炼等任意一家。注册账号、获取 API Key 的具体流程每家略有不同,但本质都是在他们的开发者平台上创建一个 key。新用户通常有免费额度。 2. 一个 Python 环境。推荐 Python 3.10 或更高版本。可以用 pip 安装对应的 SDK。
第二步:安装 SDK
我们以 OpenAI 兼容的协议为例(绝大多数国产模型都兼容这个协议,只需替换