第一章 上下文工程的崛起:从提示词到系统设计
当 AI 系统承担的任务从"写一首诗"跃迁到"管理一个软件项目的全生命周期",我们为它准备的那一串指令——即所谓"提示词"(prompt)——还够用吗?这个问题并非修辞性的,而是过去三年里每一个构建大语言模型(Large Language Model, LLM)应用的工程师都必须直面的工程现实。本章将从这个根本问题出发,回顾提示工程(Prompt Engineering)作为一种设计范式的兴起与失效,引入"上下文工程"(Context Engineering)作为替代范式,并建立起"上下文即基础设施"(Context as Infrastructure)的核心隐喻,为全书四大支柱——记忆、压缩、检索、状态——奠定概念基础。
第一节 提示工程的辉煌与边界
提示工程的黄金时代大致从 2020 年 GPT-3 公布开始,一直延续到 2023 年初。在这段时间里,研究者与开发者发现,通过精细设计的自然语言指令,可以让一个通用语言模型在不修改任何权重的情况下,完成从文本分类到代码生成、从多步推理到角色扮演的广泛任务。这一发现之所以令人震撼,是因为它意味着模型的能力可以被"召唤"出来,而不必通过昂贵的再训练。提示工程迅速成为与"模型微调"(fine-tuning)并列的两大调优路径之一,并且由于其低成本、高可解释性、便于迭代的特点,几乎统治了早期 LLM 应用开发的所有场景。
提示工程的代表性技术构成了一条清晰的知识谱系。零样本指令(zero-shot prompting)以自然语言直接描述任务;少样本提示(few-shot prompting)在指令前后附加若干示例;思维链(Chain-of-Thought, CoT)提示引导模型"一步一步地思考";在此基础上又衍生出自洽性(self-consistency)、思维树(Tree-of-Thought)、ReAct 等更复杂的策略。这些技术在多个公开基准上不断刷新当时的性能纪录,并塑造了"提示词设计是一门手艺"这一行业共识。
然而,从 2023 年中开始,越来越多的应用场景揭示了提示工程的边界。当任务具备以下三个特征中的任意两个时,提示工程就会显现出难以克服的失效模式。
第一,长程性(long-horizon)。任务需要在多次交互、跨越多个会话、涉及大量历史决策的情况下完成。例如,一个编程助手需要理解整个代码库的演化历史,一个研究助手需要追踪过去几个月的阅读笔记,一个客服系统需要维护一个 VIP 用户长达数年的偏好记录。提示词是单次注入的,它在工程上不具备"持久内存"的概念,每一次新的请求都被视为一个独立的偶发事件。
第二,多模态与异构信息(heterogeneous information)。任务需要同时处理文本、代码、图像、表格、对话日志、工具返回结果等多种类型的信息。提示词的物理载体是一段有限长度的字符串,无法对异构信息进行结构化管理,也无法在多种信息类型之间维持引用一致性。
第三,外部工具依赖(external tool dependency)。任务需要调用搜索引擎、数据库、代码执行环境、第三方 API 等外部系统。提示词虽然可以"建议"模型去调用工具,但它无法管理工具调用的状态、重试与错误处理,也无法对工具返回的结构化数据进行二次编排与缓存。
这三条边界并非独立存在,而是相互纠缠:当一个任务同时具备这三个特征时,它已经不再是"提示工程"问题,而是一个"上下文工程"问题。换言之,提示工程适用于"一次性、可表达、无外部依赖"的任务;一旦任务突破这三个边界中的任何一个,工程的重心就必须从"措辞"转移到"信息管理"。这一判断构成了本章论点的核心。
第二节 长程任务中的崩塌:当上下文成为瓶颈
要理解范式跃迁的本质,必须先理解范式崩塌的具体机制。提示工程在长程任务中的失效并非个别现象,而是一组具有共同根因的系统性失败。考察一个真实而典型的场景有助于澄清这一点。
假设我们正在构建一个"代码迁移助手",目标是将一个老旧的 Python 2 代码库迁移到 Python 3,并保留所有原有的业务逻辑。一个朴素的实现采用如下提示词:
> "你是一个代码迁移专家。请将以下 Python 2 代码迁移到 Python 3:[代码片段]。请保留原有的业务逻辑,并解释你的修改。"
在单个文件、几百行代码的范围内,这个提示是有效的。模型可以识别 `print` 语句、`xrange`、`字典的 has_key` 方法等 Python 2 特有语法,并给出合理的迁移建议。然而,当任务规模扩大时,多重问题开始显现。
首先是上下文窗口的物理上限。主流 LLM 的上下文窗口从 2023 年的 4K、8K,迅速扩展到 2024 年的 128K、200K,乃至 2025 年的百万级 token。即便如此,一个真实的中型代码库往往拥有数百个文件、数百万行代码,物理上无法一次性塞入。更糟的是,研究表明,即使上下文长度未触及窗口上限,模型的注意力机制(attention mechanism)也会出现所谓"中间遗忘"(lost-in-the-middle)现象——对位于上下文开头和结尾的内容关注度较高,而对中间部分的内容相对忽视。这意味着即便理论上可以容纳全部代码,模型也可能"看不见"关键的依赖关系。
其次是状态的不连续性。一个代码迁移项目往往需要数十次甚至数百次往返的对话,每一轮都涉及一个具体的子任务——"先迁移这个模块"、"再处理那个依赖"、"再补全对应的测试"。当用户关闭会话、第二天重新打开时,模型的"记忆"被清空了。即便在同一会话中,当对话轮次超过几十轮时,早期建立的共识、用户的偏好、项目的命名规范等"隐式状态"也会逐渐被冲淡,新的轮次不断稀释早期的信号。
再次是工具调用的无序性。现代 LLM 应用往往需要调用外部工具——读取文件、搜索代码、运行测试、查看错误日志。每一次工具调用都产生结构化的结果,这些结果需要被整合到下一步的提示中。然而,工具调用的频率、顺序、参数选择往往是不可预测的。当工具调用序列变得复杂时,简单的提示词拼接会导致上下文迅速膨胀、冗余信息堆积、关键信号被淹没。
最后是多步推理的脆弱性。思维链技术可以引导模型在单次回答中进行推理,但当任务需要跨越多个回合、涉及"计划—执行—反思—重规划"的循环时,模型需要在上下文中维护一个"工作记忆"(working memory),记录当前进度、未完成项、已尝试的方案、错误信息。提示词没有为这种工作记忆提供结构化的承载方式。
一个具体的崩塌案例可以更清晰地说明上述机制的叠加效应。某团队在 2024 年初尝试使用 GPT-4 配合简单的提示词来自动化一个真实的代码迁移项目。该项目包含约 50 个相互依赖的 Python 文件,团队设计了如下流程:每次将当前文件的内容与"项目其他文件的摘要"一起放入提示词,让模型生成迁移版本。最初几个文件进展顺利,但从第十个文件开始,模型开始产生不一致——在某个文件中定义了一个新的工具函数,但在另一个文件中却使用了旧版本的函数签名。深入排查后发现,问题的根源在于"项目其他文件的摘要"这一信息会随着项目进展而变化,但提示词没有机制去强制模型关注这些变化,也没有机制去检测"我已经引用了一个我尚未定义的函数"这种一致性错误。
这个案例揭示了提示工程的本质局限:它把"信息组织"的责任完全推给了模型本身。当信息量超过模型的"无辅助处理能力"时,错误就不可避免地累积起来。正如早期软件工程曾试图用"一条 SQL 语句"去构建一个完整的数据库管理系统一样,提示工程试图用"一段自然语言"去承担"持续运行时系统"的责任——在原理上是不可能的。
第三节 上下文工程的概念:构建模型的认知操作系统
如果我们承认上述边界,那么接下来的问题便是:替代范式应该长什么样?"上下文工程"这一术语最早出现于 2024 年中后期的技术社区与研究文献中,并在 2025 年逐步成为业界共识。它指的是:对输入到大语言模型中的全部信息——包括指令、记忆、工具结果、状态变量——进行系统性设计与管理的工程学科。
这一定义中有三个关键词值得仔细推敲,它们共同界定了这门新学科的研究对象与研究方法。
第一个关键词是"全部信息"(all information)。它强调,提示词只是上下文的一个子集。一个完整的上下文可能包括:系统指令(system prompt)、用户输入、历史对话、外部知识(由检索得到的文档)、工具调用结果(function call results)、环境状态(当前时间、用户身份、会话 ID)、工作记忆(中间推理步骤、待办事项)、格式约束(输出 schema 与 JSON 结构)等。"全部"意味着工程视角的根本转换:我们不再把上下文视为一句话,而视为一个由多种成分组成、随时间动态演化的数据结构。
第二个关键词是"系统性设计"(systematic design)。它强调,上下文不是自然语言写就的段落,而是一个需要被像数据库表、API 接口、缓存系统一样被工程化设计的对象。具体而言需要回答一系列子问题:哪些信息应该被纳入上下文中?以何种结构组织?保留多长时间?如何被检索?如何被压缩?如何避免冲突?如何被更新?每一个问题都对应着一个工程子问题,而非一个"措辞技巧"问题。
第三个关键词是"管理"(management)。它强调上下文是一个动态对象。在一个长会话中,上下文会持续增长、修改、清理、优先级重排。一个设计良好的上下文系统应当具备:选择性记忆(记住重要的、忘记冗余的)、动态检索(按需取用而非全量塞入)、冲突解决(当新旧信息冲突时遵循何种策略)、预算控制(在窗口大小限制下最优利用 token)、可观测性(能追踪每次推理使用了哪些上下文元素)。所有这些都是"管理"层面的职责,而非"撰写"层面的职责。
从软件工程的视角看,上下文可以被理解为模型的"认知操作系统"(Cognitive Operating System)。操作系统(Operating System, OS)这个词并非随意选择,它精确地抓住了上下文在 LLM 应用中所扮演的角色。正如传统操作系统为应用程序提供 CPU 时间、内存空间、文件系统、设备等抽象资源的管理,上下文工程为 LLM 的"思维过程"提供它所需要的全部外部资源的管理。模型本身是这个 OS 上运行的"应用程序",而上下文则构成了它所看见的世界。这意味着,对模型行为的每一次干预,本质上都是对"其运行环境"的一次调整。
这一框架带来三个重要的认知转换。
转换一:从"指令撰写者"到"上下文架构师"。提示工程师关心的是"怎么说",上下文架构师关心的是"给它看什么"。前者关注措辞、示例、格式,后者关注信息流、数据结构、状态机。这一转换并非语义游戏,而是工程责任的重新分配。
转换二:从"一次性设计"到"动态工程"。提示词是一次性写就的静态字符串,上下文是一个随着会话进行而持续演化的运行时对象。这要求工程能力不止于"写好一段话",而是要构建"管理这段话的演化"的机制——包括信息的进入、退出、修改、归档等生命周期事件。
转换三:从"模型中心"到"上下文中心"。在过去几年里,许多团队习惯于把模型视为系统的核心——所有设计都围绕"如何让模型做对"展开。上下文工程的兴起意味着,设计的焦点转向"如何为模型构建一个高质量的认知环境"。这与软件工程中的"以数据为中心"(data-centric AI)思想一脉相承:系统的能力上限往往不取决于模型本身,而取决于模型所接触到的数据的组织方式。
第四节 上下文即基础设施:与软件系统栈的类比
理解上下文工程的另一个有力框架,是把它类比为软件系统的基础设施(infrastructure)。这一类比不仅有助于直觉理解,更能直接借鉴传统软件工程数十年来积累的工程经验。
传统软件系统的栈结构大致分为四层:硬件层(CPU、内存、磁盘、网络接口)→ 操作系统层(进程调度、文件系统、网络协议栈)→ 中间件层(数据库、缓存、消息队列)→ 应用层(业务逻辑、用户界面)。每一层都为上一层提供抽象,同时也对下一层施加约束。整个系统的复杂度是通过分层管理来控制的——上层不必关心下层如何实现其承诺的功能,只须通过既定的接口消费下层提供的服务。
当我们把这一框架应用于 LLM 应用时,可以得到一个富有启发的对照:
| 层次 | 传统软件栈 | LLM 应用栈 | |------|-----------|------------| | 基础设施层 | 硬件(CPU、内存) | 预训练模型权重 | | 运行时层 | 操作系统(进程、文件系统) | 上下文窗口 | | 中间件层 | 数据库 / 缓存 / 消息队列 | 记忆系统 / 检索系统 | | 应用层 | 业务代码 | 提示词与业务逻辑 |
这张表的关键洞察是:上下文窗口扮演了"操作系统"的角色。传统的 CPU 在执行指令时是无状态的——它的"工作状态"由操作系统维护;类似的,LLM 本身在每次推理时也是无状态的——它的"工作状态"完全由输入的上下文决定。这一对照的实用价值在于:所有那些在传统软件工程中发展出来的、关于"如何管理有限资源"、"如何在长会话中维护一致性"、"如何在多个信息源之间进行协调"的工程经验,都可以在新的语境下被重新审视和应用。
进一步,类比可以帮助我们识别一类特殊的失败模式——上下文"内存泄漏"(context leak)。在传统编程中,内存泄漏是指程序未能释放不再使用的内存,导致系统逐渐耗尽资源。在 LLM 应用中,存在一个高度类似的问题:在多轮对话或长程任务中,上下文会不断累积——每一轮的工具结果、每一段历史对话都被塞入下一轮的输入,直到 token 预算耗尽。区别于传统内存泄漏,LLM 应用的"上下文泄漏"通常不会导致系统崩溃,而是会导致模型逐渐"失焦"——它会开始关注不那么相关的内容,忽略真正重要的指令;会出现"指令漂移"——早期的核心目标被后期的细枝末节遮蔽。识别并修复这类问题,正是上下文工程的核心职责。
另一个有用的类比是"调用栈"(call stack)。在程序执行中,调用栈记录了从主程序到当前执行点的所有函数调用,每一层对应一个独立的作用域。LLM 应用的复杂行为也可以被理解为一种"调用栈"——根系统指令在最底层,工具与能力的定义在其上,任务相关的领域知识再上一层,当前会话的临时状态与用户输入在最顶层。一个设计良好的上下文架构应当使这"调用栈"的每一层都清晰可辨、互不污染,并且具备独立的更新与回滚机制。当某一层发生变化时(例如工具列表更新),其他层不应被波及。
类比的第三个价值在于"抽象层级"(abstraction layering)。传统软件栈之所以成功,是因为每一层都提供了恰当的抽象:硬件厂商不必关心操作系统如何调度进程,操作系统不必关心应用程序如何使用数据库。而 LLM 应用栈的现状是,许多团队试图在一个单一的提示词中完成所有抽象——既包含业务规则、又包含工具说明、又包含用户偏好、又包含历史摘要。这就像把整个应用程序都写在内核里——功能上可行,但维护性、可测试性、可演化性都极差。上下文工程的实践正是要重构这种局面:将上下文按抽象层级组织。最低层是模型身份的稳定标识与基础行为准则;其上是工具与能力的定义;再上层是任务相关的领域知识;最上层是当前会话的临时状态。这种分层让每一层都可以独立演化、独立测试、独立替换。
值得强调的是,这一类比不是为了把软件工程的概念生硬地移植到 LLM 领域,而是为了利用一套已经成熟的工程思维范式来组织新的问题空间。读者如果熟悉分布式系统、数据库、操作系统中的任何一个领域,都可以借助该领域的概念来更快地建立对上下文工程的直觉。
```mermaid flowchart TD A[Single Prompt\n单条提示] --> B[Multi-turn Dialogue\n多轮对话] B --> C[Tool-augmented LLM\n工具增强调用] C --> D[Agentic System\n智能体系统] D --> E[Context Engineering\n上下文工程]
A -.-> A1[静态字符串\n无状态] B -.-> B1[对话历史管理\n短期记忆] C -.-> C1[工具结果结构化\n工具状态] D -.-> D1[多步规划与反思\n工作记忆] E -.-> E1[记忆 压缩 检索 状态\n四大支柱协同] ```
第五节 案例研究:从提示词到上下文系统——一个客服对话助手的演进
抽象概念只有落到具体场景中才能被真切地把握。本节考察一个客服对话助手在过去两年多时间里的演进历程,从中提炼上下文工程范式形成的实际路径。
阶段一:单条提示(2023 年初)。最初的客服助手实现为一条精心撰写但固定的系统提示词:
> "你是一个客服助手,请礼貌、专业地回答用户的售后问题。如果用户要求退款,请收集订单号并提交退款申请。如果用户抱怨产品,请先表达同理心再引导解决问题。"
这个实现简单、部署快、维护成本低。但它在两周内就暴露出三个问题:第一,无法跨会话记忆——同一个用户第二天再来时,上次的对话上下文完全丢失,助手反复要求用户提供订单号;第二,对话容易跑题——用户聊到一半开始谈论与产品无关的话题,助手缺乏把对话拉回业务的机制;第三,工具调用混乱——"提交退款申请"实际上需要复杂的内部审批流程,但提示词没有为这一流程提供结构化引导,模型常常产生幻觉性的"已为您提交"答复。
阶段二:多轮对话加外部状态(2023 年中)。第一次改进是引入外部数据库来存储用户画像与历史工单。第二次改进是在提示词中动态注入用户的最近三次交互记录。第三次改进是引入"工具路由"(tool routing)机制——模型输出结构化的工具调用请求,由后端服务执行后再返回结果。这个阶段已经显露出"上下文工程"的雏形,但在架构上是临时拼凑的——上下文管理逻辑散落在多个函数中,缺乏统一模型,token 预算经常超支,调试极其困难。
阶段三:上下文系统化(2024 年)。团队重写了整个系统,引入了一个明确的"上下文组装器"(context assembler)。这个组件负责以下职责:第一,从长期记忆中检索用户画像与历史偏好;第二,从会话记忆中获取当前对话的最新状态;第三,从工作记忆中加载当前任务的中间结果与待办项;第四,将所有这些信息按预定义的层级与优先级拼装成最终提示。系统提示词不再是"一整段自然语言",而是一份"配方"(recipe)——规定了每一类信息的位置、长度上限、更新频率、冲突解决规则。这次重构之后,token 利用率提升了约 40%,用户投诉率下降了 60%。
阶段四:自适应上下文与 agentic retrieval(2025 年至今)。随着模型能力的增强,新一代系统开始引