上下文工程导论:为什么 Prompt 已经不够用 / Introduction to Context Engineering: Why Prompts Are No Longer Enough
当我们说"上下文工程"时,我们到底在工程化什么?
在 2023 年,如果你问一个 AI 工程师"如何让大语言模型表现得更好",标准答案是"写一个更好的 Prompt"——精心设计的指令、几个 Few-shot 示例、也许再来一段 Chain-of-Thought 引导。到了 2025 年,答案发生了根本性的变化。Prompt 仍然重要,但它已经从"主角"退化为"配角"。真正决定一个 AI 系统成败的,是一套远比 Prompt 复杂得多的信息组装工程——我们称之为上下文工程(Context Engineering)。
这种转变不是概念炒作,而是被一系列真实的生产事故倒逼出来的。让我们从一个具体的失败案例开始。
1.1 一个真实的失败故事:当 Prompt 工程遇到真实世界
想象一下,你是一家 SaaS 公司的产品负责人。2024 年下半年,你们决定上马一个智能客服 Agent,目标听起来并不激进:
- 客户上传一份 200 页的产品使用手册 PDF
- Agent 需要在多轮对话中回答客户关于产品的任何问题
- Agent 需要记住过去 50 轮对话的上下文
- 当客户询问"我的订单状态如何"时,Agent 要能调用订单系统 API
你的团队里有两位工程师——林工和小李。林工是典型的 Prompt 工程派,他相信"只要把 Prompt 写好,模型自己会搞定一切"。小李则更倾向于工程化思维,主张"模型只是 CPU,Context 才是内存"。
林工的做法:把一切塞进 Prompt
林工的第一版方案看起来非常合理。他写了一段精心设计的 System Prompt:
``` 你是一个专业的客服助手。请遵循以下规则: 1. 你必须基于提供的【产品手册内容】回答用户问题 2. 如果用户询问订单,请调用 get_order_status 工具 3. 请保持礼貌、专业、简洁 ...
【产品手册内容】 [此处插入完整的 200 页 PDF 文本,约 80,000 tokens] ...
【对话历史】 User: 这个产品怎么用? Assistant: ... User: 那如果我想要退款呢? Assistant: ... [此处插入 50 轮对话历史,约 15,000 tokens] ...
【用户当前问题】 请问我的订单 #12345 现在是什么状态? ```
林工把这个 Prompt 喂给了当时最强的模型 GPT-4。结果呢?让我们来诊断这次失败。
失败模式一:上下文窗口溢出。 200 页 PDF 完整内容(80K tokens)+ 50 轮对话(15K tokens)+ System Prompt(2K tokens)+ 工具定义(1K tokens)+ 用户问题(0.5K tokens)= 98,500 tokens。这已经触及 GPT-4 早期 128K 窗口的极限,但更致命的是响应极其缓慢——单次推理超过 30 秒,每小时只能服务 100 多个用户,而业务方要求是 1000+ QPS。
失败模式二:大海捞针式的检索失败。 即使把整个 PDF 塞进去,模型也无法精确回答"第 47 页关于退货流程的说明与第 12 页的保修条款有何冲突"这类问题。模型在 80K tokens 的文本中"找到"相关信息的能力随着位置变化而显著衰减——这正是 2023 年 Liu 等人在 *Lost in the Middle* 论文中揭示的现象。
失败模式三:对话历史爆炸。 第 50 轮对话时,模型需要"看到"前面 49 轮的全部内容来理解"那如果……"中的"那"指代什么。但前面 49 轮里可能有大量无意义的寒暄("你好""谢谢""再见"),这些内容消耗着宝贵的 token 配额,却没有提供有效信息。
失败模式四:工具调用幻觉。 在 50 轮对话的嘈杂背景下,模型偶尔会"忘记"系统指令中关于调用 `get_order_status` 的规则,自行编造一个订单状态——这在客服场景中是不可接受的。
失败模式五:状态丢失。 当用户第二天再次打开对话窗口,模型"什么都不记得"。从模型的角度看,这是一次全新的推理,之前 50 轮对话毫无痕迹。
小李的做法:把 Context 当作工程对象
小李面对同样的需求,提出了截然不同的方案。他的核心思路只有一句话:"不要把所有信息塞给模型,而要根据当前任务,动态地组装最相关的 Context。"
具体来说,他构建了五个相互协作的子系统:
1. 长上下文窗口(Long Context):选用支持 200K tokens 的 Claude 或 Gemini 作为底座,确保在需要时可以"看见"完整文档。 2. 检索(Retrieval):用 RAG(Retrieval-Augmented Generation)将 200 页 PDF 切分成 1000 个 chunk,建立向量索引。回答问题时,只检索 top-5 相关 chunk 注入 Context(仅约 4K tokens)。 3. 压缩(Compression):用更小的模型(如 GPT-3.5)对检索到的文档 chunk 进行预压缩,去除冗余信息。 4. 摘要(Summarization):当对话历史超过 20 轮时,将前 20 轮压缩为 500 tokens 的摘要,保留关键事实和决策。 5. 状态管理(State Management):将用户偏好、订单快照、对话摘要持久化到外部数据库,每次新对话开始时按需加载。
最终的小李版本,单次推理的 Context 控制在 8K-12K tokens(系统指令 1K + 检索结果 4K + 对话摘要 1K + 状态 0.5K + 用户问题 0.5K + 工具定义 1K + 预留缓冲 2K-4K)。响应时间从 30 秒降到 2 秒,QPS 提升了 15 倍,工具调用准确率从 78% 提升到 99.2%,第二天用户回来时 Agent 还能"记得"上次聊到哪。
林工和小李的根本区别在哪里? 林工在工程化 Prompt——一段静态的指令文本。小李在工程化 Context——一个动态组装的信息生态系统。这正是从 Prompt Engineering 到 Context Engineering 的范式跃迁。
> Key Insight: A prompt tells the model *what to do*. A context tells the model *what it needs to know*. The first is a sentence; the second is a system. / Prompt 告诉模型"做什么",Context 告诉模型"需要知道什么"。前者是一句话,后者是一个系统。
1.2 LLM 的无状态本质:每一次推理都是一次"重启"
要真正理解为什么 Context Engineering 如此重要,我们必须先理解大语言模型的一个最反直觉、也最容易被忽视的本质特征:无状态性(Statelessness)。
1.2.1 什么是"无状态"
在计算机科学中,"无状态"意味着系统不保留任何跨调用的历史信息。每一次调用都是一个独立的、自包含的快照。LLM——更准确地说,Transformer 推理本身——正是无状态的典范。
让我们看一个最直观的例子:
```python from openai import OpenAI
client = OpenAI()
第一次调用
response_1 = client.chat.completions.create( model="gpt-4", messages=[{"role": "user", "content": "我叫张三。"}] )
第二次调用
response_2 = client.chat.completions.create( model="gpt-4", messages=[{"role": "user", "content": "我叫什么名字?"}] )
print(response_1.choices[0].message.content) # "你好,张三!" print(response_2.choices[0].message.content) # "抱歉,我不知道你的名字。" ```
第二次调用中,模型"忘记"了用户叫张三。这不是因为模型的记忆力差,而是因为模型从设计上就没有跨调用的记忆。它只看到 `{"role": "user", "content": "我叫什么名字?"}` 这一条消息——至于这条消息之外发生过什么,模型一无所知。
1.2.2 三个层次的"无状态"
无状态性其实渗透在 LLM 推理的多个层次,理解这一点对后续章节至关重要。
层次一:模型权重是冻结的。 当你调用 OpenAI 或 Anthropic 的 API 时,你使用的是全球共享的模型权重。你的对话不会改变这些权重。换句话说,模型"学到"的知识在其发布那一刻就被定格了,它不会因为和你聊天而变得更聪明。
层次二:推理过程是无状态的。 即便模型能"看到"你之前的 50 轮对话,那也不是因为它"记得",而是因为你主动把那些对话塞回了输入。模型本身在两次推理之间没有任何连续性——它不"知道"这是第几次调用、不知道用户是谁、不知道现在是几点。
层次三:服务实例是无状态的。 在云端,你的请求可能被路由到任何一个空闲的 GPU 实例。你上次对话可能在实例 A 上完成,这次却在实例 B 上处理。对模型而言,这两次调用毫无关联。
1.2.3 "状态"到底存在哪里
既然 LLM 是无状态的,那 ChatGPT、Claude 这种产品是如何实现"跨对话记忆"的?答案是:状态由客户端(即应用程序)维护,模型每次推理时都把状态作为输入的一部分重新注入。
具体来说,"状态"通常存储在以下几个地方:
- **客户端内存**:当前会话的消息列表
- **本地存储**:浏览器的 IndexedDB,存储用户偏好、历史会话
- **服务器端数据库**:PostgreSQL、MongoDB、Redis 等,存储用户画像、长期记忆
- **向量数据库**:Pinecone、Weaviate、Chroma 等,存储可语义检索的文档
- **外部服务**:CRM 系统、订单系统、知识库等业务系统
每一次用户发起新对话,应用层都会从这些存储中"取出"相关状态,与用户当前问题一起组装成一个完整的 Prompt,发给模型。模型看到的就是一个"自包含的快照",里面包含了它做出正确响应所需的全部信息。
> Mental Model: An LLM is like a genius with amnesia. Each time you talk to it, you must re-introduce yourself, re-explain the situation, and re-supply every piece of information it needs. Context Engineering is the art of building the perfect "re-introduction packet" for every interaction. / LLM 就像一个患有健忘症的天才。每次你与它对话,你都必须重新介绍自己、重新解释情境、重新提供它所需的每一条信息。Context Engineering 就是为每一次交互构建完美的"重新介绍包"的艺术。
1.2.4 无状态性带来的工程后果
无状态性直接导致了 Context Engineering 的核心挑战:
1. 每次推理都是一次"重启":不存在"模型自己会记住"这回事。任何需要"记住"的东西,都必须由应用层显式注入。 2. Context 是唯一的"记忆载体":模型能"看到"什么,决定了它能"知道"什么。Context 之外的世界对模型来说是不存在的。 3. Context 容量是有限的:无论 128K、200K 还是 1000K,Context Window 都有上限。这意味着我们必须做"取舍"——把什么放进去、把什么排除掉。 4. Context 成本是高昂的:Context 中的每一个 token 都要被处理,token 数量直接决定延迟和费用。处理 100K tokens 比处理 1K tokens 慢几十倍,贵几十倍。
这四个后果合在一起,构成了 Context Engineering 要解决的核心问题:在有限的空间、有限的时间内,为模型组装最相关、最精炼、最有助于完成当前任务的信息集合。
1.3 从 Prompt Engineering 到 Context Engineering:范式跃迁
理解了无状态性,我们就能看清 Prompt Engineering 的局限性,以及为什么必须升级到 Context Engineering。
1.3.1 Prompt Engineering 的辉煌与边界
2022-2023 年,Prompt Engineering 几乎成为 AI 工程师的"必修课"。从最简单的"你是 XX 专家"角色设定,到复杂的 Chain-of-Thought、ReAct、Self-Consistency,Prompt 工程一度被认为是"让 LLM 发挥最大能力的关键"。
这些技巧确实有效。Zero-shot、Few-shot、CoT 等技术的核心思想是:通过精心设计输入文本,引导模型沿着我们期望的推理路径生成输出。 它们在单轮、简单任务上的表现令人惊艳。
但 Prompt Engineering 有一个根本性的假设——任务的所有相关信息都"装得下"在 Prompt 里。
这个假设在 2023 年大致成立。那时候主流任务是:
- 翻译一段文字
- 总结一篇文章
- 回答一个事实性问题
- 写一段代码
- 做一次情感分类
这些任务的特点是:信息边界清晰、信息量小、交互轮次少。 在这种场景下,一个 200 tokens 的 System Prompt + 一个 500 tokens 的 User Prompt 就足够装下全部信息。Prompt Engineering 完美匹配了那个时代。
1.3.2 2024-2025:应用形态的剧变
进入 2024 年,应用形态发生了根本性的变化。LLM 不再只是"对话玩具",而是成为了复杂应用的"大脑"。典型的 AI 原生应用包括:
形态一:长文档问答系统。 用户上传一份 500 页的合同、一本完整的白皮书、整套 API 文档,要求模型基于这份文档回答问题。
形态二:多轮 Agent。 一个能自主完成复杂任务的 AI 助手,比如"帮我规划下个月去日本的旅行,包括订机票、酒店、餐厅、行程"。这种任务可能涉及 30-100 轮的工具调用和决策。
形态三:企业知识库助手。 接入企业内部的 Confluence、Slack、GitHub、CRM,跨多个数据源回答员工问题。
形态四:编程助手。 理解整个代码仓库(可能 10 万行代码),跨文件查找、修改、重构。
形态五:多 Agent 协作。 多个 AI Agent 协同工作,每个 Agent 负责一个子任务,彼此通信。
这些新形态的共同特征是:
| 特征 | 2023 年典型应用 | 2024-2025 年典型应用 | |------|----------------|---------------------| | 输入信息量 | < 2K tokens | 50K-500K tokens | | 对话轮次 | 1-3 轮 | 20-200 轮 | | 工具调用 | 0-1 次 | 5-50 次 | | 数据源 | 单一(用户输入) | 多个(DB、API、文件、向量库)| | 时间跨度 | 一次性 | 持续数天/数月 | | 状态复杂度 | 无 | 用户偏好、任务进度、历史决策 |
注意看"输入信息量"这一行:从 < 2K 跳到 50K-500K,整整两个数量级的跃迁。Prompt Engineering 的所有技巧——角色设定、Few-shot、CoT——都假设信息能装在 Prompt 里。 当信息量超过 Context Window 的 50% 时,单靠 Prompt 设计就无能为力了。
1.3.3 范式跃迁的本质
从 Prompt Engineering 到 Context Engineering 的跃迁,本质上是工程化对象的根本性转变:
- **Prompt Engineering 的工程化对象**:一段精心设计的指令文本(通常 100-2000 tokens)。
- **Context Engineering 的工程化对象**:一个动态组装的信息生态系统(包括指令、检索结果、对话历史、工具定义、状态快照、记忆等,通常 5K-200K tokens)。
更具体地说,五大核心转变:
转变一:从"写指令"到"管理信息流"。 Prompt Engineering 是写作的艺术,Context Engineering 是信息架构的艺术。
转变二:从"一次性输入"到"动态组装"。 Prompt 是静态的,Context 是动态的——每次推理都要根据当前任务、当前状态、当前可用资源重新组装。
转变三:从"模型为中心"到"任务为中心"。 Prompt Engineering 关注"如何让模型更好地理解我的指令",Context Engineering 关注"模型为了完成这个任务需要知道什么"。
转变四:从"通用技巧"到"领域工程"。 Prompt Engineering 倾向于通用技巧(CoT、ReAct),Context Engineering 必然是领域相关的——客服场景的 Context 架构和编程场景的 Context 架构截然不同。
转变五:从"模型能力"到"系统能力"。 Prompt Engineering 的优化对象是模型的输出质量,Context Engineering 的优化对象是整个 AI 系统的综合表现(质量 + 延迟 + 成本 + 可靠性)。
> Paradigm Shift: Prompt Engineering optimizes the *sentence*. Context Engineering optimizes the *system*. / Prompt Engineering 优化的是"句子",Context