什么是上下文?——大模型世界的'工作记忆'
第 1 章 什么是上下文?——大模型世界的"工作记忆"
当你打开一个聊天框,敲下第一句话,然后看着屏幕另一端的 AI 给出流畅的回答时,你有没有想过一个看似简单的问题:这一刻,这个 AI 究竟"看到"了什么?
普通人会把这个问题的答案归结为"对话历史"——也就是你说的话和它之前的回答。但在大语言模型(Large Language Model,简称 LLM)的内部世界,这个答案远远不够。本章要解决的核心问题是:"上下文"(context)对一个 LLM 究竟意味着什么? 我们将用四步拆解这个看似平常、实则深刻的概念,并通过一个真实失败案例,让你直观感受到:上下文,正是理解大模型行为的第一把钥匙。
---
1.1 从"对话历史"到"工作记忆":重新理解上下文
1.1.1 普通人心中的"上下文"
让我们从一个日常场景开始。
假设你打开 ChatGPT,先问:"你好,我叫 Alice,是一名产品经理。"模型友好地回应:"你好 Alice,很高兴认识你。"
接着,你问了第二个问题:"我下周要做一个新功能发布,有什么建议吗?"模型给出了几条建议,看起来很专业。
然后,你抛出了第三个问题:"你觉得这个功能的命名应该注意什么?"模型继续作答。
到这里,如果你被问到"AI 是怎么知道你在做产品经理工作的",你大概率会回答:"因为它看了前面的对话,知道我是 Alice、是产品经理。"
这个直觉是对的,但只是冰山一角。
1.1.2 认知科学中的"工作记忆"
在进入模型世界之前,我们先去借一个来自认知心理学的概念:工作记忆(working memory)。
心理学家 Baddeley 和 Hitch 在 1974 年提出了一个经典模型,把人脑的记忆分成了两大类:
- **长期记忆**(long-term memory):容量近乎无限,存储着我们学过的知识、经历、词汇、概念。它的特点是"持久",从你三岁学会说话,到昨天读到的新闻,都可以存在这里。
- **工作记忆**(working memory):容量非常有限,通常只能同时处理 4 到 7 个"信息块"(chunk)。它的作用是**当下**——当你和别人对话、解一道数学题、或者在厨房里同时煮面和炒菜时,你脑子里那个"暂时的工作台"。
一个关键比喻:工作记忆就是意识的"桌面"。你的长期记忆像是一个巨大的档案柜,而工作记忆就是桌面上摊开的那几张纸。你正在思考的事情、所有需要同时调用的信息,都必须先"摊"到这张桌面上。一旦桌面被占满,旧的东西就会被挤掉,或者你必须把它们重新归档,等需要时再调出来。
1.1.3 把这个类比搬到 LLM 世界
现在,让我们做一次大胆但有用的类比:
| 人类认知 | 大模型 | 作用 | |---|---|---| | 长期记忆(档案柜) | 模型参数(训练好的权重) | 存储语言知识、世界知识、推理能力 | | 工作记忆(桌面) | 上下文窗口(context window) | 模型在一次推理中能"看到"的全部信息 | | 注意力(eyes-on-desk) | 注意力机制(attention) | 决定桌面上哪些信息被重点关注 |
这张表是理解后续所有章节的基石。我们先把它记住。
需要立刻指出的是:工作记忆这个类比非常有解释力,但并不完美。 人类的长期记忆会被遗忘、会被扭曲,而且人类的"工作记忆"和"长期记忆"之间有复杂的交互。而大模型的"参数"一旦训练完成就固定下来(在没有微调的前提下),它的"工作记忆"——也就是上下文——则每次推理时都是临时组装、推理结束就丢弃。两者各有不同,但在"当下能看到什么,就只能基于什么来思考"这一点上,它们惊人地相似。
1.1.4 一个关键洞察
理解了这个类比,你立刻就能得到一个贯穿全书的洞察:
> 大模型在任何一个时刻的"思考",都严格受限于它当下能"看到"的信息总量。
这听起来像是废话,但它的推论是深刻的——如果模型"看到"的信息被裁剪、被压缩、被错误地组装,那么它接下来的所有推理、所有回答、所有"智能表现",都会建立在那个残缺的现实之上。这就是为什么我们说"上下文是第一把钥匙":它不是对话历史那么简单,它是你和模型之间那座唯一的桥。
---
1.2 token、序列与窗口:上下文的物理形态
1.2.1 上下文不是一个"段落",而是一串 token
上一节我们用了类比,这一节我们要揭开物理现实。
在大模型内部,所谓"上下文",不是一个段落、一段对话、或者一个文档。它是一串 token(token sequence)。
什么是 token?它是模型处理文本的最小单位,介于"字符"和"词"之间。模型并不直接认识汉字或英文单词,它认识的是 token。
举几个例子:
- 英文 "Hello world" 会被切成大约 2 个 token。
- 中文 "你好,世界" 会被切成大约 4 到 6 个 token(因为中文每个字往往对应一个或多个 token)。
- 一段 emoji 或代码,token 切分方式会更加不同。
具体的切分规则由分词器(tokenizer)决定。不同模型使用不同的分词器,例如 GPT 系列常用 BPE(Byte Pair Encoding),而许多中文模型使用专门优化过的中文 tokenizer。但无论规则如何变化,有一点是不变的:
> 模型的输入永远是 token 序列,而不是人类可读的"文本"。
当你敲下"你好,Alice",模型看到的是类似 `[你, 好, ,, Ali, ce, ...]` 这样的一个 ID 列表。每个 ID 对应一个固定大小的向量,这些向量被送入神经网络,开始一场规模惊人的计算。
1.2.2 序列的"窗口":上下文长度的硬约束
token 串起来,就构成了序列(sequence)。但这个序列不是无限长的——它有一个窗口(window)。
上下文窗口(context window),就是模型在一次推理中能够处理的最大 token 数量。这个数字是模型的"先天属性",由它的架构决定,在训练完成后基本固定。
以几个常见模型为例:
- 早期 GPT-3:2,048 个 token(约 1,500 个英文单词,或 1,000 个中文字)。
- GPT-3.5:4,096 或 16,384 个 token。
- GPT-4:8,192 或 32,768 个 token。
- Claude 2:100,000 个 token。
- Claude 3 / GPT-4 Turbo:200,000 个 token 量级。
- Gemini 1.5 Pro:达到 1,000,000(100 万)token 量级。
你可能注意到了:这个数字近年来在快速增长。但无论它怎么增长,它终究是一个有限的数字。一百万 token 已经能装下一本中等厚度的书,但仍然装不下一个人一辈子的对话、一个公司的所有产品文档、或者整个互联网。
1.2.3 注意力机制:为什么"窗口"如此重要
为什么窗口大小如此关键?因为决定模型"看见什么"的,是它的注意力机制(attention mechanism)。
Transformer 架构的核心,就是所谓的"自注意力"(self-attention):序列里的每一个 token 都会和序列里的所有其他 token 计算关联强度。换句话说,理论上,序列里的每个 token 都能"看到"序列里的其他所有 token。
但这正是代价所在:计算复杂度是序列长度的平方,即 $O(n^2)$。如果序列长度翻倍,计算量大约翻四倍。这在工程上是昂贵的,也是为什么"上下文窗口"会成为一个硬约束——不是不能做大,而是做大要付出算力代价。
反过来想:窗口的存在,也意味着窗口之外的世界对模型来说完全"不存在"。如果某条信息没有出现在上下文里,模型不会"想起"它——它就像从未发生过一样。这和人脑的遗忘有相似之处,但更绝对:人脑的"长期记忆"还可能在某些刺激下被唤起,而模型的参数化知识则永远不会因为上下文变化而动态更新。
1.2.4 一个具体示例:上下文长度的"切身感受"
为了让你对 token 数量有直觉,我们做几个估算:
- 一条微信消息:大约 20-50 个 token。
- 一篇 1,500 字的微信公众号文章:大约 2,000-2,500 个 token。
- 一份 30 页的产品需求文档:大约 15,000-20,000 个 token。
- 一本 300 页的平装书:大约 80,000-100,000 个 token。
- 一份完整的法律合同(几十页):可能 30,000-50,000 个 token。
这就解释了为什么"长上下文"会成为大模型的一个卖点——能用 AI 读完整本书,或者一次性处理一份完整合同,这在过去是不可想象的。
但即便如此,100 万 token 也不是无限。它有边界、有成本、有注意力衰减(随着序列变长,模型对细节的关注会下降),这些我们会在后续章节展开。
---
1.3 上下文决定行为:为什么同一个模型能"判若两人"
1.3.1 一个违反直觉的事实
很多初学者会以为,模型的"性格""知识""能力"是固定的,就像一个被训练好的助手。但事实是:模型在出厂时几乎没有任何"性格"——它的"性格"完全由上下文决定。
让我用一个实验来说明。假设我们使用同一个底层模型,比如 GPT-4,然后设计三种完全不同的"上下文"(也就是输入给它看的信息):
情境 A:默认助手 ``` System: You are a helpful assistant. User: 今天心情不太好。 ```
情境 B:苏格拉底 ``` System: You are Socrates. Answer in the style of ancient Greek philosopher, using questions. User: 今天心情不太好。 ```
情境 C:脱口秀演员 ``` System: You are a stand-up comedian. Be funny, slightly self-deprecating. User: 今天心情不太好。 ```
同一个模型,同一个问题,三种回答会截然不同。情境 A 可能会温柔安慰你;情境 B 会反问你"心情不好是什么?""为什么心情会不好?";情境 C 可能会先抖一个包袱,然后用幽默化解。
模型没变,变的是上下文。
1.3.2 上下文是"指令",也是"约束"
这个例子告诉我们一个重要事实:上下文既是给模型的信息,也是给模型的指令。 模型在推理时,不会去翻自己的"出厂手册"(因为根本不存在),它只会老老实实地基于上下文来预测下一个 token。
这意味着:
1. 你给什么,模型就用什么。 不在上下文里的知识,模型要么靠训练时的"参数化记忆"(也就是参数里学到的常识),要么就只能"猜"(也就是产生所谓的"幻觉",hallucination)。 2. 你给的格式,模型就照着做。 如果你给模型一个表格,它倾向于继续表格格式;如果你给模型一个 JSON 例子,它倾向于继续 JSON。 3. 你给的语气,模型就会模仿。 如果上下文中充满敬语,模型也会敬语;如果上下文中全是代码,模型也会开始写代码。
这就是为什么我们说"上下文即行为边界"——模型能做什么,不能做什么,会说什么,不会说什么,几乎完全由这一时刻的上下文决定。
1.3.3 三个常见的"上下文失效"场景
为了把这个观点落到实地,让我们看三个真实场景:
场景一:忘了之前说过什么。 你问:"刚才我提到的那份合同在哪里?"但因为之前的对话已经被裁剪出了窗口,模型根本看不到"那份合同"的任何信息。它的回答要么是"抱歉,我不记得"(如果模型被对齐过),要么是凭空捏造一个文件路径(幻觉)。
场景二:答非所问。 你问:"这份销售报告 Q3 增长了多少?"模型回答:"销售报告通常包括以下几个部分......"。原因是上下文中只有销售报告的目录结构,而没有实际数据。模型看到的是"问题 + 目录",于是它学会了"回答目录"。
场景三:同一个问题,不同答案。 你连续问同一个数学题,只是中间插入了一些无关对话。两次的答案可能不同,因为上下文的微小变化(噪声、措辞、上下文长度)都会影响模型的采样过程。这不是模型"不稳定",而是模型严格地按照"看到的内容"做出了不同的合理推断。
1.3.4 一个根本性的判断标准
通过上面的分析,我们可以提炼出一个判断标准,在后续的章节中你会反复用到:
> 当一个 LLM 应用出现"奇怪"的表现时——比如遗忘、幻觉、答非所问、风格突变——第一反应不应该是"模型不够好",而应该是"上下文可能有问题"。
这是上下文工程的第一性原理:先检查上下文,再考虑模型。 在大多数实际项目中,问题的根源都不在底层模型的能力,而在上层上下文的组装方式。
---
1.4 失败案例解剖:一个客服机器人是怎么"变笨"的
理论铺垫已经够多。现在让我们看一个真实场景,把"上下文"问题具象化。
1.4.1 场景描述
某 SaaS 公司上线了一个 AI 客服机器人。它的功能是帮用户解答产品使用问题。设计团队的方案很简单:
1. 把公司所有 FAQ 文档存进向量数据库。 2. 用户提问时,用问题去检索相关 FAQ 段落。 3. 把"检索到的段落 + 用户问题"作为上下文,送给 LLM。 4. LLM 生成回答。
上线第一天,效果惊艳。用户问"如何重置密码",机器人准确地从 FAQ 中检索到步骤,给出了清晰回答。团队欢呼。
上线第三天,客服主管收到用户投诉。
1.4.2 投诉内容还原
用户 Alice 的对话记录如下(简略版):
> Alice (第 1 轮): 你好,我叫 Alice,我们的公司订阅了你们的企业版。 > Bot: 你好 Alice,很高兴为你服务。请问有什么可以帮您? > > Alice (第 2 轮): 我们团队有 50 个人,如何批量导入账号? > Bot: 批量导入的步骤是:1. 登录管理后台;2. 进入"团队设置"...... > > Alice (第 3 轮): 如果导入过程中某个账号格式错误,会怎么样? > Bot: 系统会跳过错误账号,并在导入完成后给出报告...... > > Alice (第 4 轮): 那导入完之后,我能看到每个账号的登录记录吗? > Bot: 可以,在"用户管理"页面点击"登录历史"...... > > Alice (第 5 轮): 我刚才说的那 50 个人,他们登录之后会自动同步到我们的企业 SSO 吗? > Bot: 抱歉,我没有看到您之前提到过具体的数字。请问您是指哪些用户?
1.4.3 问题诊断
如果你刚才认真读了 1.3 节,你可能已经能看出问题:在第 5 轮,机器人"忘了"Alice 在第 2 轮提到过 50 个人这个信息。
为什么会这样?我们来排查可能性。
可能性 1:底层模型能力不足。 排除。GPT-4、Claude 3 这类模型在正常配置下完全具备多轮对话的记忆能力。
可能性 2:上下文窗口太小。 排除。整个对话加起来不到 1,000 个 token,远未触及任何主流模型的窗口上限。
可能性 3:RAG 检索没召回相关信息。 部分相关。但即便 RAG 没召回,模型也应该"看到"前面的对话历史。这里真正的问题更深一层。
真正的根本原因:系统设计者没有把"对话历史"纳入上下文。
这是一个真实项目中非常常见的架构错误。设计者的思路是:
> "每次用户提问时,我用问题去检索 FAQ,然后把检索结果送给模型。至于用户之前说过什么——让模型自己根据对话窗口去'理解'就好。"
这个思路的问题在于:向量检索是以"当前问题"为查询的,它不会主动去召回历史对话中的事实。 当用户问"我刚才说的那 50 个人",模型看到的上下文是:
``` [系统提示: 你是客服助手] [检索结果: 关于 SSO 集成的 FAQ 段落] [用户当前问题: 我刚才说的那 50 个人,他们登录之后会自动同步到我们的企业 SSO 吗?] ```
注意,Alice 之前说过的所有内容都没有出现在上下文里。模型眼前的世界里,根本不存在"Alice""50 个人""企业版"这些信息。所以它的回答是诚实的——它确实"没有看到"。
1.4.4 问题的层次
这个案例展示了一个非常典型的上下文工程失败模式。问题不是出在模型上,而是出在上下文的组装策略上。具体来说,有三个层次的问题:
层次 1:上下文遗漏。 "对话历史"本应是上下文的天然组成部分,但被设计者忽略了。
层次 2:上下文检索策略不当。 即使用户问的是 SSO 这种具体技术问题,如果系统能把"Alice""50 个人""企业版"这些用户级元信息也送进上下文,模型的回答也会更准确。
层次 3:上下文与系统状态的解耦。 更严重的是,即便我们把对话历史都送进去,如果用户的"姓名""公司名""订阅版本"这些结构化的用户档案信息没有被持久化,每次会话都是"失忆"的。
1.4.5 这个案例的教训
这个案例给我们的教训是多重的:
1. 上下文不是"自动"的。 它需要被显式地设计、组装、管理。 2. "用户档案"是一种上下文。 它需要被独立存储,并在每次对话开始时被注入。 3. "对话历史"是另一种上下文。 它需要按窗口长度截取,或者经过压缩后注入。 4. "检索到的知识"是第三种上下文。 它需要根据当前问题动态召回。
这三类上下文如何协同工作,如何取舍,如何在窗口预算内实现最优组合——这正是"上下文工程"这个名字的由来。
---
1.5 动手小练习:三句话窗口的取舍困境
理论再多,不如亲手试一次。现在让我们做一个纸笔练习,让你站在模型的角度,亲身体会"上下文受限"的痛苦。
1.5.1 规则说明
请你假装自己是一个"AI 助手"。但和真正的 LLM 不同的是,你的工作记忆极其有限——你只能同时记住最近的 3 句话(上下文窗口=3)。
具体规则:
1. 用户会和你进行多轮对话。 2. 你只能"看到"最近的 3 句话(包括用户的话和你之前说过的话)。 3. 当对话超过 3 句时,最早的句子会被"挤掉",你再也看不到它。 4. 你