上下文工程 | Chris Loy
上下文工程
“给工程师看的机器学习”系列文章之一

上篇回顾:向量的距离度量
我们对大语言模型 (LLM) 的用法已经变了。以前,我们主要拿它当聊天机器人玩;现在,它正成为复杂系统中做决策的核心部件。这么一来,我们与它“沟通”的推理 (inference) 方式也必须跟着进化。
过去那种“提示词工程” (prompt engineering)——也就是靠精确措辞“求”LLM 给个好答案的做法——局限性太大了。因此,一种更高级、更通用的玩法正在取而代之。我们开始用一种更动态、更有针对性、更深思熟虑的方式,来“喂”给 LLM 每一个它需要处理的信息单元(也就是词元,token)。
这种更拓展、更结构化的新方法,就是我们今天要聊的——“上下文工程” (context engineering)。
接下来,我们会用一个简单的小例子来贯穿全文:想一想,LLM 是怎么帮我们回答“哪部是史上最佳科幻电影?”这种主观问题的。
上下文窗口
大语言模型 (LLM) 是一种机器学习模型。在它眼里,语言不是别的,就是一连串的“词元” (token)(你可以粗略地理解为单词或汉字)。它通过在海量数据里学习这些词元共同出现的模式来“理解”含义。
然而,模型一次能“看”的词元数量是固定的,这个容量上限(可能多达几十万个)就叫作上下文窗口 (context window):

LLM 是怎么训练的呢?就是让它一遍遍地“阅读”连贯的词元序列——通常是从互联网上扒下来的海量文本数据库。
训练好了,我们就可以用它了。这个使用过程叫“推理” (inference)(也就是让它做预测)。它会根据序列里已经出现的所有词元,来预测下一个最可能出现的词元。这个“已经出现的词元序列”,就是我们过去常说的提示词 (prompt):

推理过程就是这样不断地把高概率的词元一个一个加到序列末尾,把“话”接下去。
比如,你给它一句话“史上最好的科幻电影是...”,它预测出的最高概率的几个词元可能是
也许、星球和大战。
LLM 最早的用途就是这种“补完”模式:你给个开头,它帮你写下去。这在当时已经很惊艳了,但限制也很多。比如,你很难精确地告诉它 到底 要按什么风格或要求来补完。
聊天格式
为了解决这个“不好指挥”的难题,模型开发商想了个新办法:他们在训练数据里加入了一种特殊的“聊天格式”。
他们用一些特殊词元来标记对话的“回合”,比如谁是用户、谁是 AI。模型在学习补完时,也学会了复制这种“聊天”的格式。这么一搞,模型突然就变得非常适合聊天了,指挥起来也容易多了:

这时候,宝贵的上下文窗口开始被各种新东西“塞满”了——不仅有用户的提问和 AI 自己的回答(也就是聊天历史),还有一种特殊的“系统消息” (system message)(这是给 LLM 下的“铁命令”,告诉它该扮演什么角色或遵守什么规则)。
有了聊天格式,我们就可以在“提问”前先指挥它:“你是一个影评人”。然后再问它最好的科幻电影是啥。这次,我们得到的回答词元可能就变成了
银翼和杀手,因为它在扮演一个更懂行、更注重评论界共识的“专家”角色,而不是只会附和大众。
这里最关键的一点是:LLM 的底层架构根本没变——它还是那个只会“猜下一个词”的机器。
但它的“世界观”变了。它的训练数据里,所有信息都被框定在了“你来我往”的对话格式里。因此,它在生成回答时,也会死心塌地地遵循这个格式。
提示词工程
在这种模式下,想让 LLM 发挥最大功力,就意味着你得找到“完美”的提示词序列,好“引诱”它给出最好的补完结果。
这就是所谓的“提示词工程” (prompt engineering) 的诞生。不过说实话,在实践中,这玩意儿“工程”的成分很少,靠“试错和瞎猜”的成分居多。
整个过程常常感觉不像是严谨的工程,倒更像是“念咒语”,嘟囔几句神秘的咒语,然后祈祷奇迹发生。这跟真正的工程学所推崇的深思熟虑、严格应用的系统思维简直是背道而驰。
我们可能会试着用更“聪明”的系统提示词来恳求 AI:“你是一位知识渊博、公平公正、熟知电影奖项历史的影评人”。我们希望这能“忽悠”LLM 给出更准的答案。但这种希望完全是建立在语言概率上的,没有任何保证。
上下文学习
随着 LLM 变得越来越聪明、越来越可靠,我们开始给它“喂”更复杂的词元序列,包括各种结构化和非结构化的数据。
这让 LLM 能够根据提示词里给出的新结构来生成补完,而不仅仅是依赖训练数据里的旧记忆模式。
这种给 LLM '现场'喂示例的方法,就叫作上下文学习 (in-context learning)。因为它看起来就像是 LLM 纯粹仅靠上下文窗口里的几个例子就“学会”了如何生成我们想要的输出(注意:它的模型参数并没有真的改变,这是一种“瞬时”学习)。
这种玩法一出现,我们能塞进提示词里的东西就“爆炸”了:
硬编码的例子:从我们的知识库里(比如文档、人类写的好答案、AI 生成的好答案、小例子)拿一些范本,“教会”AI 按我们的格式输出。
非文本数据:代表图像、音频或视频的词元。它们要么被直接塞进上下文窗口,要么先转成文字再塞进去。
工具和函数调用:定义一些外部函数,LLM 可以“告诉”调用者去执行这些函数,以此来获取外部数据或进行计算。
文档和摘要:通过 “RAG” (稍后会讲) 从数据库里检索出来的,或是用户上传的,用来给 LLM 补充训练数据之外的“新知识”。
记忆和对话历史:把以前的聊天信息浓缩一下,让“聊天机器人”能在多次对话中“记住”同一个用户。
回到我们的科幻电影例子。我们现在可以在提示词里塞一大堆东西来帮助 LLM:历史票房收入、各种出版物的百佳电影名单、烂番茄评分、奥斯卡完整获奖史,等等。
突然之间,我们那 10 万+ 的上下文窗口好像也不怎么够用了。我们从四面八方把各种词元都塞了进去:

这种上下文的“大膨胀”,不仅挤占了用来生成答案的宝贵空间,还增加了 LLM 在任意时刻需要“集中注意力”的负担和复杂度。
这反过来又增加了它“犯病”的风险,比如产生幻觉(也就是一本正经地胡说八道)。
因此,我们必须用更精妙的手段来“搭建”这个上下文——要考虑简洁性、相关性、时效性、安全性等等因素。
到了这个地步,我们所做的,早已不是“提示词工程”那么简单了。我们已经开始在“工程化”地管理 AI 生成答案所依赖的整个上下文。
从“神谕”到“分析师”
语言不仅编码了知识,它还编码了意义、逻辑、结构和思维。
因此,训练一个 LLM 去编码“世界上有什么”的知识,并让它能生成描述这些知识的语言,同时也等于创造了一个能够模拟思考的系统。
这,才是 LLM 的真正效用所在。想利用好这一点,我们就必须彻底转变使用它的心态。
拥抱“上下文工程”,就意味着我们不再把 LLM 当作一个神秘的“神谕”——你跑去“神庙”,虔诚地念叨几句“咒语”(提示词),然后忐忑地等待“智慧”降临。
相反,我们开始把它当作一个能干的“分析师”:我们会把所有相关信息都带给他,让他去筛选;我们会清晰准确地定义他要完成的任务;我们会把可用的工具都列好文档;我们绝不依赖他那过时、残缺的“训练记忆”。

在实践中,我们与 LLM 的集成方式,从“精心打磨完美提示词”转向了“精确构建完成任务所需的那一套词元”。
管理上下文成了一个工程问题,而 LLM 则被重新定位为一个“任务解决器”,只不过它的输出是自然语言罢了。
为“智能体”行为而工程化上下文
我们来看一个你可能想问 LLM 的简单问题:
英国电影院每周的平均票房收入是多少?
在“神谕”模式下,LLM 会很开心地从它训练数据(截止到某个日期)的“旧记忆”里给你扒一个值出来:
截至 2019 年,英国票房平均每周收入约为 2400 万英镑。
这个来自 GPT 4.1 的答案虽然(在当时)是准的,但现在看既不精确又已过时。通过上下文工程,我们能做得好得多。
想想看,在让它生成第一个回答词元之前,我们可以在上下文窗口里多“喂”点什么料:
当前日期,这样它就知道要用最新的统计数据(GPT 4.1 以为“现在”是 2024 年 6 月)
真实发布的数据,比如这篇 BBC 新闻文章
告诉它如何“指挥”调用者去计算两个数的除法
有了上面这些“材料”,LLM 就应该知道怎么做了:它会去找 2024 年的数据;从文档里抓取 9.79 亿英镑的总额;然后调用一个外部函数,来精确地计算总额除以 52 周。
假设调用者(也就是我们的程序)真的执行了计算,然后带着所有的上下文(包括旧的、它自己刚输出的、还有计算结果)再次调用 LLM,我们就能得到一个准确的答案了:
在 2024 年全年,英国票房平均每周收入为 1880 万英镑。
看,即使是这么个小例子,也涉及到了多种在生成答案前“工程化上下文”的手法:
声明当前日期和期望的结果;
搜索并返回相关文档;
清晰说明可用的计算工具;
用中间计算结果来扩充上下文。
好消息是,我们不需要每次都从零开始发明轮子。
这不就是 RAG 吗?
检索增强生成 (RAG - Retrieval-augmented generation) 是目前一种非常时髦的技术,它专门用来在“推理”时(也就是 AI 回答问题时)往上下文窗口里“注射”外部知识。
先不管实现细节(比如怎么找到“正确”的文档),我们能很清楚地看到,这显然是上下文工程的一种特定形式:

对于那些需要用到“训练数据之外”的知识的场景,RAG 是一种非常有用且显而易见的办法。
要想得到一个“正确”答案,我们的应用程序必须知道最新的影评、评分和奖项,才能追踪模型训练日期之后的新电影和新舆论。通过把相关的摘要塞进上下文窗口,我们就能让 LLM 用今天的数据来生成补完,避免它“张口就来”编造事实。
要做到这点,我们可以搜索相关文档,然后把它们包含在上下文窗口里。如果这听起来在概念上很简单,那是因为...它确实很简单。
不过,要可靠地实现它可不简单,这需要扎实的工程能力。
复杂的系统往往很“脆”(易碎),而且“黑盒”(不透明),不好搭建。我们需要一种方法,既能扩展系统的复杂性,又不会牺牲我们维护、调试和理解代码的能力。幸运的是,传统软件设计早就用一种思想解决了同样的问题。
我们可以把 RAG 看作是众多“上下文工程”设计模式 (design patterns) 中的第一个。
就像其他软件工程设计模式一样,未来我们会发现,大多数复杂系统都必须灵活运用这些模式的变体和组合,才能发挥最大效能。
组合优于继承
在软件工程中,设计模式 (design patterns) 是什么?它是对常见设计问题的一套经过验证的、通用的解决方案,目的是促进软件复用。
它们推崇“组合优于继承”(意思是多用“搭积木”的方式构建系统,而不是用“层层继承”的死板方式)。这能让你的代码库更灵活、更易于测试、更易于维护或扩展。它们是软件设计工具箱里的关键法宝,能帮助工程师构建出可以随时间推移而不断扩展的大型功能代码库。
软件工程设计模式的一些例子包括:
Factory(工厂模式): 标准化对象创建,让单独测试更容易Decorator(装饰器模式): 不修改原始代码,扩展其行为Command(命令模式): 把工作当作一个值来传递,类似 lambda 函数Facade(外观模式): 用简单的接口隐藏内部复杂性,促进抽象Dependency injection(依赖注入): 使用配置从外部“装配”模块
这些模式是历经很长时间发展起来的,其中许多最早是在一本书(即大名鼎鼎的《设计模式:可复用面向对象软件的基础》)中被系统性地整理出来的。
上下文工程还是个新兴领域,但我们已经看到一些常见的模式涌现出来,它们能让 LLM 很好地适应特定任务:
RAG(检索增强生成): 根据用户意图的相关性,注入检索到的文档Tool calling(工具调用): 列出可用工具,并将其结果注入上下文Structured output(结构化输出): 为 LLM 的补完固定一个 JSON/XML 格式Chain of thought / ReAct(思维链 / ReAct): 在回答前,先“出声”思考,把推理步骤也作为词元放进上下文Context compression(上下文压缩): 把冗长的历史记录压缩成关键事实Memory(记忆): 跨越多个会话(session)存储和回忆突出的事实
在我们上面的例子中,其实已经用到了其中一些模式:
用
RAG来获取影评、影评人名单和票房数据用
工具调用来准确计算每周收入其他一些技术,比如
ReAct(思维链的一种),可以帮助我们的 LLM 更仔细地构建和验证它的回答,以此来平衡它从训练数据中学到的那种“语言概率”的惯性(防止它顺嘴胡说)。
当我们把每一种方法都看作是一个上下文工程的设计模式时,我们就能为手头的任务挑选正确的模式,把它们组合成一个“AI 智能体” (AI Agent),同时又不会牺牲我们测试和理解代码的能力。
扩展到多智能体
那些依赖 LLM 做决策和行动的“生产级”系统,很自然地会向“多智能体”演进,每个 AI 智能体 (AI Agent) 都有不同的专长:
比如安全护栏、信息检索、知识提炼、人类交互等等。
每一个智能体都是一个“组件”,它先理解一个任务,然后返回一个词元序列,这个序列可能代表要采取的行动,也可能是检索到的信息,或者两者兼有。

对于我们的“多智能体电影排名器”,我们可能需要好几个智能体:
聊天机器人智能体:负责和用户保持对话
安全智能体:检查用户是不是在恶意捣乱
偏好智能体:“记住”用户是否想忽略某些影评
影评人智能体:综合所有信息源,做出最终决定
每一个智能体都为特定任务而“特化”,但这种“特化”完全可以通过工程化它们所消费的上下文来实现,包括消费系统中其他智能体的输出。
这些“输出”在系统里传来传去,变成了其他智能体的“上下文窗口”的一部分。
在每一步,最关键的都是要考虑词元序列生成的模式,以及一个智能体的输出将如何被用作另一个智能体的上下文来完成它自己的任务。
这种“交接棒”的词元序列,实际上就是智能体之间交互的“合同”。你应该像对待软件架构中任何其他 API 一样,严格地对待它。
总结
“上下文工程”是一个刚刚起步但至关重要的学科,它决定了我们如何能有效地引导 LLM 解决我们扔给它们的任务。
作为软件工程的一个子领域,它从系统思维和设计思维中获益良多。我们可以借鉴应用设计模式的经验,来生产模块化的、健壮的、可理解的软件。
因此,在与 LLM 共事时,我们必须:
把 LLM 当作分析师,而不是神谕。给它解决任务所需的一切材料。
对整个上下文窗口负责,而不只是系统提示词和用户提示词。
使用可组合、可复用的设计模式,这些模式可以被独立设计和测试。
把智能体之间的“交接”视作它们上下文窗口之间的 API 合同。
通过做到这些,我们就能用和其他工程软件同样的严谨性来控制“上下文学习”。
原文链接: https://chrisloy.dev/post/2025/08/03/context-engineering