上下文工程范式:从提示工程到系统设计
上下文工程范式:从提示工程到系统设计
当开发者第一次调用大型语言模型时,典型的使用模式是构造一个提示词,发送给 API,获取回复。这个过程简单直接,足以应对许多场景。然而,当应用场景扩展到多轮对话、长期任务执行、跨文档分析或需要模型记住用户偏好时,「写好一个提示词」的思维方式很快就会遇到瓶颈。上下文工程正是在这个临界点上登场——它不是提示工程的替代品,而是一种更高维度的思考框架:如何将上下文视为一个需要被设计、构建和优化的系统。
上下文工程的定义:从单体提示到系统工程
问题的本质:上下文不是免费的
在深入定义之前,有必要先理解为什么上下文处理值得被提升到「工程」的高度。一个常见的误解是:既然大模型的上下文窗口在不断扩大,从 4K tokens 到 128K tokens 再到现在的 200K tokens,似乎只需要把更多内容塞进窗口就能解决问题。这种思路在实践中会遇到三重困难。
首先是成本问题。大模型的计费通常与输入 tokens 数量成正比。以 GPT-4o 为例,每千 tokens 的输入成本远高于输出成本。当系统需要处理大量用户请求时,盲目扩展上下文会导致成本呈线性甚至超线性增长。更关键的是,许多上下文内容是冗余的或低价值的——将整本 PDF 文档塞入上下文窗口,只因为模型需要在第 50 页找一个数字,这种做法在经济学上是不理智的。
其次是效果问题。直觉上,更大的上下文应该带来更好的效果。但研究表明,模型对上下文中不同位置内容的「注意力」并不均匀——通常呈现一种「抛物线」模式,对开头和结尾的内容记忆最深,而中间部分容易成为被忽视的「中间丢失」区域。这意味着简单地填充更多上下文,并不等同于传递更多有效信息。
第三是可控性问题。当上下文内容复杂、来源多样时,模型的行为会变得难以预测。不同的上下文组合可能引发不同的推理路径,而开发者往往无法清楚地知道「模型此刻在参考什么」。这对于需要可复现、可审计的应用场景是不可接受的。
上下文工程的正式定义
基于以上分析,可以给出一个操作性定义:上下文工程是关于上下文表示、传输、存储、检索与更新的系统性设计学科。它关注的核心问题包括:如何选择最相关的上下文内容?以什么形式组织上下文?上下文在系统中如何流动?如何确保上下文的时效性和一致性?
这一定义将上下文工程与几个相关但不同的概念区分开来:
提示工程(Prompt Engineering)关注的是如何撰写单个提示词,使其能够引导模型产生期望的输出。这包括提示词的结构设计、示例的选择、指令的措辞等。上下文工程包含了提示工程,但后者无法替代前者的系统层面考量。
检索增强生成(RAG)是实现上下文工程的一种技术手段,而非上下文工程本身。RAG 解决的是「如何从外部知识源获取相关内容」的问题,但上下文工程还需要解决获取之后如何组织、如何与模型原生能力配合、如何处理多轮交互中的上下文演化等问题。
Agent 架构是上下文工程在应用层的体现。一个 AI Agent 需要持续维护自身状态、处理来自环境的反馈、规划下一步行动,这些都依赖于精心设计的上下文管理机制。但 Agent 架构的合理性取决于底层上下文工程的质量。
上下文工程的四个核心活动
如果将上下文工程视为一个系统,其核心活动可以归纳为四个环节:
表示(Representation)关心的是「上下文应该以什么形式存在」。这不仅包括原始文本,还可能包括结构化数据、向量嵌入、工具调用的历史、当前执行状态、甚至是对模型自身推理过程的记录。不同的表示形式在信息密度、检索效率和模型兼容性上有不同的权衡。
传输(Transmission)关心的是「上下文如何从各个来源汇聚到模型」。这涉及上下文的选择(什么该传)、上下文的压缩(是否需要精简)、上下文的路由(不同类型的请求是否需要不同的上下文策略)等问题。
存储(Storage)关心的是「上下文如何持久化」。对于多轮对话,需要存储对话历史;对于跨会话的场景,需要维护用户画像或项目状态;对于复杂任务,需要记录中间推理结果。存储策略直接影响系统的可扩展性和上下文的一致性。
演化(Evolution)关心的是「上下文如何随时间更新」。真实的应用场景中,上下文不是静态的——用户可能改变意图,环境可能发生变化,已有的上下文可能变得过时。上下文工程需要设计机制来处理这些动态变化,包括何时更新、如何合并冲突、如何优雅地丢弃不再相关的内容。
理解这四个环节,有助于建立一个全局视角。后面的章节会逐一深入每个环节,但在此之前,理解它们如何协同工作至关重要。
上下文处理的演进历程:从固定窗口到动态记忆
第一阶段:固定窗口时代
大语言模型的上下文窗口最初是固定且相对较小的。在 2020 年左右,GPT-3 的 2048 tokens 上下文窗口意味着开发者必须小心翼翼地控制输入长度,稍有不慎就会触发截断。这个阶段的上下文处理策略是朴素的:尽可能在有限的窗口内塞入相关内容的摘要或截取,遵循「近者优先」原则——越近期的对话越可能被模型看到。
这种限制催生了一些巧妙的变通方案。例如,在构建对话系统时,开发者会定期对对话历史进行「蒸馏」,提取关键信息和结论,用更紧凑的形式替代冗长的历史。这种手动维护上下文的方式虽然粗糙,但为后来的自动化上下文管理提供了思路。
固定窗口时代的另一个特征是「上下文即记忆」的隐喻被广泛接受——模型似乎需要记住之前说过什么才能保持对话连贯性。但这种隐喻忽略了重要的一点:模型并没有真正的记忆,它只是在每次调用时重新处理所有输入。这意味着上下文窗口的内容不仅是「模型知道的」,也是「模型必须处理的」。
第二阶段:扩展窗口与压缩焦虑
2022 年末到 2023 年初,模型上下文窗口开始快速扩张。Claude 将窗口扩展到 100K tokens,GPT-4 Turbo 达到 128K,随后 Gemini 1.5 Pro 更是突破了 1M tokens。这种扩张表面上解决了容量问题,但实际上只是将问题推迟了一步。
当窗口扩大到可以容纳整本书籍时,开发者面临的新问题是:如何在海量上下文中定位真正相关的内容?全量输入虽然可行,但会带来显著的成本增加和潜在的注意力分散。「压缩」成为新的关键词——如何用更少的 tokens 表达更多的语义信息?如何让模型「聚焦」在关键内容上?
向量检索是这一阶段的主流方案。通过将文本分块并转换为向量嵌入,可以用语义相似度来选择最相关的上下文片段。这种方法在许多场景下有效,但也暴露出其局限性:向量检索无法捕捉精确的逻辑关系,无法处理需要多步骤推理的问题,也无法很好地处理跨文档的复杂依赖。
第三阶段:动态记忆与主动管理
当前正在展开的第三阶段,代表性特征是「上下文作为显式系统」的理念得到认可。在这一范式下,开发者不再将上下文视为模型的隐式记忆,而是作为需要被独立设计、构建和优化的系统组件。
这个阶段的典型特征包括:
记忆分层。不再将所有上下文平等对待,而是建立层级结构。短期记忆存储当前会话的即时交互,中期记忆记录当前任务或项目相关的关键信息,长期记忆保存跨会话的用户偏好、领域知识等。不同层级的记忆有不同的更新策略和生命周期。
主动检索与被动填充。传统的 RAG 是被动的——在收到请求时才去检索相关上下文。主动管理模式则会根据当前任务状态和执行计划,提前准备可能需要的上下文。这需要系统具备对任务阶段和潜在需求的预判能力。
上下文状态的显式表示。模型不再只是处理文本,而是可以接收关于「当前上下文构成」的元信息。例如,告知模型「以下内容来自你的长期记忆,置信度高」「这部分内容是 24 小时前的,可能需要核实」等。这种元信息帮助模型更准确地使用上下文。
范式转移的深层原因
从固定窗口到动态记忆的演进,背后有三股推动力量。
第一是应用场景的复杂化。早期的对话系统多为简单的问答或多轮闲聊,上下文需求相对简单。随着 AI 应用深入到数据分析、代码开发、复杂推理等领域,任务的上下文需求变得多维、动态且相互关联。简单的线性扩展已经无法满足。
第二是系统成本的显性化。当 AI 应用从实验走向生产,成本成为一个不可忽视的因素。Token 成本、延迟成本、计算成本的叠加,使得「上下文管理」不再只是一个技术问题,而是一个经济问题。这推动了更精细化的上下文策略研究。
第三是可观测性和可调试性的需求。在生产环境中,开发者需要理解模型为什么会做出某个决策,而这通常依赖于对模型所使用上下文的理解。将上下文作为显式系统,使得监控、审计和调试成为可能。
理解这一演进历程,有助于读者理解上下文工程各组成部分的来龙去脉。后面的章节中,无论是向量检索、多模态表示还是记忆系统,都可以在这个历史脉络中找到自己的位置。
上下文工程的三大支柱:表示、传输、演化
前文已经简要提及了上下文工程的四个核心活动:表示、传输、存储、演化。在深入讨论之前,有必要对其中最核心的三个维度——表示、传输、演化——进行更详细的展开。这三个维度之所以被称为「支柱」,是因为它们几乎会影响上下文工程中所有其他的设计决策。
表示:上下文的存在形式
上下文可以用多种形式表示,而每种形式在信息保真度、检索效率、模型兼容性上各有优劣。
文本形式是最直接的表示方式。原始的自然语言文本保持了最大的信息完整性,模型可以像处理普通输入一样处理它们。但文本形式的问题是冗余度高——用自然语言描述一个结构化概念往往需要大量 tokens。此外,文本形式缺乏显式的语义结构,模型需要自行推断不同部分之间的关系。
结构化形式将上下文编码为 JSON、XML 或其他格式化的数据结构。例如,将用户偏好表示为 `{"theme": "dark", "notifications": true}` 而不是一段描述偏好的自然语言。结构化形式更紧凑,也更易于程序化处理,但牺牲了灵活性——它要求开发者事先定义好所有可能的字段,且难以表达复杂的关系网络。
向量形式通过嵌入模型将文本转换为稠密向量。这种形式的最大优势是支持语义检索——通过向量相似度搜索,可以找到与查询语义相关的内容,而不需要精确的关键词匹配。但向量形式是一种「有损」表示,它丢失了精确的语法信息和结构关系,且向量检索的召回精度受限于嵌入模型的质量和分块策略。
混合表示是当前实践中的主流选择。系统会同时维护文本形式(用于最终发送给模型)和向量形式(用于快速检索),并根据上下文类型选择合适的表示。例如,对于需要精确匹配的规则或参数,使用结构化表示;对于需要语义理解的文档内容,使用向量检索后的文本片段。
理解不同表示形式的权衡,是设计高效上下文系统的基础。一个常见的错误是「唯向量论」——将所有内容都转换为向量形式存储,忽视了文本表示在某些场景下的必要性。另一个错误是「唯文本论」——只使用原始文本,放弃了向量检索带来的效率提升。上下文工程师需要根据具体场景,选择合适的表示组合。
传输:上下文的流动路径
上下文从其存储位置到达模型输入的过程,构成了「传输」环节。这个环节的核心问题不是「如何传输」,而是「传输什么」和「以什么顺序传输」。
上下文选择是传输面临的首要挑战。当可用的上下文内容超过模型的接受容量时,必须做出选择:保留什么?丢弃什么?最简单的策略是基于相关性评分,选择得分最高的内容。但这面临一个根本性的困难:相关性是相对于「当前问题」的,而模型的问题往往需要结合之前的交互历史和更广泛的任务上下文才能准确判断。
一种改进策略是基于任务类型的选择。不同的任务类型需要不同的上下文:事实查询需要参考权威文档,对话续写需要近期对话历史,创意生成需要示例和风格指南。通过识别任务类型,可以制定针对性的上下文选择策略。这种策略的局限在于,它依赖于对任务类型的准确分类,而许多实际场景中的任务是混合的或难以分类的。
上下文压缩是传输环节的另一个关键活动。压缩的目标是用更少的 tokens 表达同等或近似的信息。常见的压缩方法包括:
摘要压缩使用另一个模型或专门的摘要算法,将长文本压缩为短摘要。这种方法的优点是保真度较高(如果摘要质量好的话),缺点是引入了额外的计算成本和延迟。
提取压缩直接提取原文中的关键句子或段落,而非生成新的摘要文本。这种方法更快,但可能导致上下文不连贯。
结构化压缩将非结构化文本转换为结构化表示,例如将一段描述转化为表格或要点列表。这种方法对于信息密度高的内容效果较好,但可能丢失上下文中的叙事逻辑。
传输顺序同样重要。心理学研究表明,人类对序列开头和结尾的记忆优于中间部分,这种「位置效应」在语言模型中也观察到了。上下文工程师可以利用这一规律,将最重要或最相关的内容放置在上下文的开头或结尾,而将相对次要的内容放在中间区域。
演化:上下文的生命周期
上下文不是静态的——它在任务执行过程中不断积累、更新和淘汰。「演化」维度关注的是上下文如何随时间变化,以及如何管理这种变化。
上下文增长是自然发生的。当用户与系统进行多轮对话时,对话历史不断累积。问题在于,旧的对话内容可能会「稀释」新内容的重要性——模型可能会被早期的上下文分散注意力,尤其是在长对话的中间区域。一种应对策略是定期对对话历史进行压缩或蒸馏,将核心信息提取为更紧凑的形式。
上下文失效同样需要关注。随着时间推移,某些上下文会变得过时。例如,用户可能改变了偏好,项目的技术栈可能已经升级,行业法规可能发生了变化。上下文系统需要机制来识别和处理失效的上下文,最简单的方式是为上下文设置时间戳和有效期,更复杂的方案会涉及对上下文内容的主动验证。
上下文冲突出现在不同来源的上下文给出不一致信息时。例如,用户在一次对话中表达了某个偏好,但在另一次对话中表达了相反的意思。上下文系统需要决策策略:是采用最新的?采用最频繁的?还是向用户确认?这种决策往往需要结合业务逻辑和用户意图理解。
上下文的优雅退出是常被忽视的问题。当一个会话结束或任务完成后,相关的上下文应该被适当地存档或清理。如果上下文被永久保留,会导致存储成本增加;如果过早丢弃,会丢失可能对未来任务有价值的信息。一个常见的做法是设置不同层级的记忆——短期记忆在会话结束时清理,中期记忆保留一定周期,长期记忆则由用户显式管理。
上下文系统设计的核心权衡:效果、效率与可控性
上下文工程不是一项追求最优解的活动,而是一门在多重约束下寻求平衡的艺术。理解效果、效率与可控性之间的权衡,是设计优秀上下文系统的前提。
效果:让模型真正「理解」上下文
效果维度关注的核心问题是:上下文是否真正帮助模型完成了任务?这听起来是一个理所当然的目标,但实践中许多设计决策会无意中损害效果。
一个典型的效果陷阱是过度压缩。为了节省成本或满足 token 限制,系统可能会过度压缩上下文内容,导致关键信息丢失。例如,在摘要对话历史时,如果摘要算法没有捕捉到用户表达的一个微妙偏好,这个偏好可能在后续交互中被忽略。
另一个常见问题是上下文选择偏差。当使用向量检索选择相关上下文时,检索结果可能与模型最终需要的上下文存在系统性偏差——检索算法优化的目标(语义相似度)与任务优化的目标(帮助模型正确回答)并不完全一致。
上下文污染是另一个危险。当混入的上下文内容与问题无关甚至相悖时,模型可能会被误导。这在多用户或多任务共享上下文的系统中尤其容易发生。
提升效果的关键在于建立效果评估机制。这意味着需要持续监控模型在上下文使用上的表现,包括:模型是否正确引用了上下文中的信息?模型的回答是否与上下文一致?用户满意度是否随着上下文质量的提升而改善?
效率:成本与延迟的现实约束
效率维度关注的是系统运行的资源消耗,包括计算成本、token 成本和响应延迟。效果和效率往往存在张力:最有效的上下文策略通常也是最昂贵的,而最高效的策略可能牺牲了效果。
Token 效率是最直观的效率指标。系统在每次请求中发送的 tokens 数量直接影响 API 成本。更激进的压缩可以降低 token 消耗,但可能损害效果。上下文工程师需要为每个场景找到「足够好」而非「尽可能好」的上下文质量。
计算效率涉及向量检索、上下文组装、压缩计算等环节的处理时间。对于需要实时响应的应用,这些环节的延迟会成为用户体验的瓶颈。一个常见的优化方向是缓存频繁使用的上下文或计算结果,避免重复处理。
架构效率关乎系统的可扩展性。当用户量增长时,上下文系统能否线性扩展?不同的上下文存储方案、检索方案有不同的扩展特性。上下文工程师需要在设计阶段就考虑规模问题,而非在问题出现后再重构。
可控性:系统行为的可预测与可调试
可控性维度关注的是开发者能否理解和控制系统的行为。在生产环境中,这一点至关重要——当模型给出了错误的回答,开发者需要知道「模型看了什么」,以便定位问题。
可观测性是可控性的基础。上下文系统应该记录:当前请求使用了哪些上下文?这些上下文来自哪里?它们被选择的理由是什么?模型对这些上下文的「注意力」分布如何?这些信息对于调试和问题排查是不可或缺的。
一致性保证是另一个可控性要求。在复杂系统中,上下文可能来自多个来源,且这些来源可能独立更新。系统需要确保模型在处理某个请求时使用的上下文是自洽的,而非存在内部矛盾。
回滚能力意味着当上下文策略导致问题时,系统能够回退到之前的状态。这要求上下文系统保留历史版本,并支持快速切换。
三维权衡的实际案例
考虑一个实际的例子:设计一个客服对话系统。系统需要记住用户的历史问题、已尝试的解决方案、用户的设备信息等上下文内容。不同的设计选择会导致不同的权衡结果。
如果采用「全量历史」策略:所有对话历史都保留并发送。效果较好(模型有完整信息),但效率低(成本高、延迟高),且可控性差(难以调试模型到底看了什么)。如果采用「只保留最近 5 轮」策略:效率改善,但可能丢失重要历史信息(效果下降),可控性改善(范围明确)。如果采用「智能摘要」策略:用 AI 总结对话历史,效果和效率可能都较好,但引入了新的复杂性(摘要质量如何保证?如何处理摘要遗漏的信息?)。
没有绝对最优的选择,只有在特定约束下最合适的选择。上下文工程的核心技能之一,就是根据具体场景识别关键约束,并在约束内寻求平衡。
最小化上下文系统原型:系统设计与简单拼接的效果对比
理论需要通过实践来验证。本节将构建一个最小化的上下文系统原型,通过具体案例展示「系统设计」与「简单拼接」在实际任务中的差异。这个原型虽然简化,但它反映了真实系统设计时的核心考量。
任务场景:代码审查助手
选择一个具有代表性的任务场景:代码审查助手。该助手需要分析用户提交的代码变更,给出审查意见。典型的代码变更可能涉及多个文件,每个文件可能有增删改多种操作。审查过程需要