1. 上下文工程到底在解决什么问题
1.1 从提示工程到上下文工程:关注范围的迁移
过去两年大家聊得最多的是提示工程(prompt engineering),写提示词、设计指令、调格式,把模型性能压榨到极限。但用到今天,稍微复杂一点的落地项目都会发现一个尴尬现实:提示词写得再花哨,只要喂进去的材料组织得乱七八糟,输出质量照样崩。提示工程只解决了"怎么问"的问题,而真正卡住大规模 LLM 应用脖子的是另一件事——在有限上下文里,把最有用的信息放到最合适的位置,并且让模型能够真正利用起来。
这就是上下文工程(context engineering)这个概念的由来。它不等同于提示工程,也不只是 RAG 的代称。我的理解是:上下文工程是一整套关于"如何为语言模型构建、组织、维护和优化输入上下文"的方法论。范围包括选什么信息进去、不选什么、按什么顺序排、怎么压缩、怎么缓存、跨多轮对话怎么延续、检索结果怎么融入主干上下文。它是比提示词更底层、也更接近系统架构层面的工作。
为什么这两年这个词冒出来了?直接原因是模型上下文窗口越做越大,从最初的 4K、8K,一路卷到 128K、200K,有些甚至声称支持百万级 token。窗口大了,大家的第一反应是"那我全塞进去不就行了"。实践下来发现,塞得进去不等于用得起来。信息一多,模型的注意力被稀释,关键内容淹没在无关文本里,质量和延迟双双恶化。于是大家被迫认真思考:上下文不是下水道,不能什么都往里倒,它本身就是需要设计和管理的资源。
上下文工程要解决的问题,用一句话概括:在给定的 token 预算内,让模型拿到它最需要的信息,并用模型最能理解的方式组织起来。这个问题横跨信息检索、序列组织、压缩策略和评估体系,比单纯优化一条提示词复杂得多。
1.2 模型没那么"长情":长上下文的天花板效应
所有上下文工程的出发点,都是先承认一个反直觉的事实:大模型并没有我们想象中那样善于利用长上下文。
业界已经有很多公开研究讨论"lost in the middle"现象——当关键信息位于上下文中间位置时,模型的表现明显低于信息出现在开头或结尾的情况。为什么?这和 Transformer 的注意力机制有关。开头部分通常包含系统指令和任务描述,模型天然给予高权重;末尾部分距离生成位置最近,残差连接和注意力模式也更容易保留这些信息;而中间的内容,既没有位置优势,又没有指令权重,在长序列里很容易被稀释。
另一个限制是注意力计算本身的特性。虽然稀疏注意力、滑动窗口等机制缓解了计算压力,但模型对长距离依赖的建模能力依然是有限度的。信息之间的关联如果跨越的序列距离太远,模型往往无法有效"回忆"并融合。换句话说,你给了模型一部百科全书,它却只擅长读开头几页和最后几页。
这给上下文工程划了一条红线:不能指望模型自己从海量材料里"大海捞针"。虽然在一些刻意构造的"大海捞针"测试里模型表现不错,但那是单点事实检索,真实业务里需要的往往是多个信息点的综合推理——某条规则、某个历史记录、某条用户偏好要同时被命中并整合。这种复合需求在长上下文下非常脆弱。
所以上下文工程的第一性原理是:把模型当作一个"读题能力有限、但执行能力很强"的助手。你要做的不是把资料全部堆在桌上,而是把资料整理成它一眼就能看到重点的简报。这条原则贯穿后面所有技术细节。
2. 上下文工程的核心方法论
2.1 上下文四要素:信息、结构、顺序、预算
我做了几个实战项目之后,把上下文工程拆成四个可量化的要素,每次设计上下文方案都对着这四件事过一遍。
第一是信息选择。这是最基础也是最重要的一层:哪些信息必须进上下文,哪些可以留在外部存储里,哪些根本不该进来。很多失败案例的根源不是模型不行,而是我们把大量噪音和非必要信息塞了进去。一条经验是,每一条进入上下文的材料都问一句:模型如果没看到它,输出结果会有实质差异吗?如果答案是不确定,那就别放。上下文是稀缺资源,哪怕窗口再大,信息密度直接决定输出质量的上限。
第二是结构组织。同样一段信息,平铺和结构化呈现,模型的理解效率完全不同。JSON 块、XML 标签、Markdown 分段、表格、条目式描述,不同格式在模型眼里权重和可解析性不一样。我的经验是,涉及多实体、多属性的复杂信息时,结构化成表格或键值对比自然语言段落更可靠;涉及流程步骤时,有序列表比连续段落更不容易丢步骤。但这没有绝对标准,不同模型的解析偏好有差异,需要针对性测试。
第三是顺序设计。结合"lost in the middle",关键信息尽量前置到系统提示之后不久的位置,或者后置到离生成位置近的地方,尽量避免沉在中间。对于多路信息源,优先级排序要按任务的相关程度来,而不是按数据源本身的顺序。交互式场景里还有一个首因效应:模型对最早出现的行为约束(比如角色设定、回复规范)最敏感,所以最重要的指令必须放在最前面。
第四是预算分配。把上下文想象成一个预算有限的舞台,系统指令占多少、用户当前请求占多少、历史对话占多少、检索出来的参考资料占多少、示例占多少,每一块都要预先规划。预算分配不是拍脑袋,而是根据任务复杂度和模型对各类信息的依赖程度来动态调整。预算管理是上下文工程里最容易被忽视、但对系统稳定性影响最大的一环。
2.2 构建优先,压缩兜底:质量守恒的思路
上下文工程圈子里有一个我一直很认同的原则:优先通过"构建"来解决上下文质量问题,把"压缩"作为最后手段。
构建的意思是,从源头开始就把上下文组织好——检索阶段做精细召回,引入时做去重和排序,对话中做关键信息提取,始终让进入模型的信息本身就是高质量的。压缩则是对已经选定的信息做进一步瘦身——摘要、抽帧、缓存历史。为什么压缩要排后面?因为任何压缩都是有损的,摘要会丢细节,截断会丢完整性。除非上下文真的装不下,或者成本压力逼着你非压缩不可,否则不要优先做有损操作。
举一个实际的对比:同样是给模型提供一份产品操作手册,A 方案直接把 200 页 PDF 全文塞进去,让模型在 128K 上下文里自行查找;B 方案先离线抽取 FAQ、关键参数表、故障代码表,只把这三类提炼后的结构化信息送进上下文。两个方案的实际效果差距非常明显,B 方案在回答准确率和响应稳定性上全面占优。这不是模型能力差异,而是进入模型之前的信息加工差异。上下文工程做得好不好,很大程度上取决于你愿不愿意在这一步下功夫。
质量守恒还有一个含义:如果信息源本身质量参差不齐,上下文工程无法点石成金。垃圾进,垃圾出。所以上下文工程不只是模型侧的工程,它倒逼你把数据治理、知识库建设这些"上游脏活"做扎实。我见过太多项目在提示词层面反复折腾,最后发现问题是知识库里原文就有错——再好的上下文方案也救不回来。
3. 实操:一个完整上下文工程的搭建过程
3.1 场景定义与信息盘点
下面用一个虚拟场景走一遍完整流程:假设我们要给某公司的内部客服做一个基于大模型的智能助手,回答员工关于报销制度、差旅标准、设备申领流程等问题。
第一步不是写代码,也不是调模型,而是把场景需求彻底摸清。这个助手需要处理什么问题类型?是单轮事实问答,还是需要结合多个文档的复杂推理,还是需要多轮澄清?不同类型对上下文的组织要求差异很大。事实问答只需要精准检索;多文档综合推理需要跨文档信息融合和规则比对;多轮交互则需要额外处理会话记忆和指代消解。
信息盘点要输出一份清单,列出所有可能进入上下文的资源:报销制度文档、差旅标准明细、历史咨询工单、用户身份信息(部门、职级、成本中心)、用户当前提问。每一个资源都要标注更新频率、可信度、结构化程度。比如差旅标准是半年度更新的表格,可信度极高;历史工单噪声大,但里面包含典型问题和历史处理结果。
这个阶段还要定评估基准。没有评估的上下文工程等于闭眼开车。至少要准备三类测试样本:一是经典问题集,覆盖高频咨询场景,比如"出差住宿上限多少";二是混淆问题集,考察边界情况,比如"去海外出差适用哪套标准";三是长会话压力集,模拟连续问答几十轮之后模型还能不能保持准确。评估基准在后续每一轮调优中都要复用。
3.2 检索增强与上下文构造
客服助手必须走检索增强生成(RAG)路线,因为制度文档总量超出单次上下文承载能力。但 RAG 不是简单的"拼接答案",检索器和上下文构造必须配合设计。
文档切分是关键预步骤。一刀切按固定字符数切块的做法我踩过坑:把一条完整的报销流程从中间切断,导致检索回某个故障代码时只有半截解释。合理的做法是"结构感知切分",优先按文档本身的标题、表格、列表边界切,保持语义完整;块与块之间保留重叠区域,防止检索时边界信息丢失。块大小影响很大,太大则检索精度下降、引入噪声,太小则语义不完整,常见做法是 300 到 800 个 token 之间,按文档类型微调。
检索策略上,向量检索加关键词检索的混合方式,比任何一种单通道都稳。向量检索擅长语义匹配,但容易在专有名词密集的领域翻车——"PR 单"、"PO 号"这些词向量匹配很不稳定;BM25 类关键词检索恰好擅长精确匹配,两条通道结果做加权融合,再用重排序模型(reranker)对候选集做精细排序。重排序这一步强烈建议保留,它能把排在后面的正确结果提到前面,对最终答案准确率提升非常明显。
检索进入上下文后,别忘了标注来源。每条检索结果前加上元信息,比如"来源:《差旅管理制度》2024 修订版,第 3.2 条"。这有两个好处:模型能依据来源权重判断信息优先级,还能在回答中引用出处,方便用户核实。我通常会要求模型在输出中带上来源编号,这既降低幻觉概率,也方便审计。
3.3 压缩、缓存与记忆管理
多轮对话场景下,历史信息会快速占据上下文。每轮答复几百 token,几十轮下来就是几万 token,全量保留既不经济也没必要。记忆管理要做分层设计。
短期记忆保留最近几轮的原始对话,保证连贯性;中期记忆用摘要方式保存——每经过一个阶段(比如十轮),把前面的对话压缩成一段结构化要点,包括已确认的事实、用户诉求、未解决的问题;长期记忆则沉淀到外部存储,比如把用户的核心诉求和历史偏好提取出来,存成结构化档案,下次会话开始时加载。这三层各有分工,可以类比成"临时便签、会议纪要、个人档案"。
压缩策略里有个技巧:摘要尽量保持"可检索性",即摘要里保留关键实体和数值。我的做法是让摘要模型输出固定格式,包含用户目标、关键约束、已确认信息、待办事项四个栏目。这样摘要不仅是一次性叙事,还能支撑后续检索。
KV Cache 重用在长文本场景是一个容易被忽视的优化点。如果系统提示和静态参考资料在一次会话里不会变化,可以把这些前缀的 KV Cache 缓存起来,每轮重复请求时跳过重复计算。实测在长前缀场景下,单次请求延迟能降低 30% 到 50%,成本也同步下降。主流推理框架和服务平台都开始提供前缀缓存能力,这是上下文工程里"运维颗粒度"的优化,但收益非常直接。
3.4 效果评估与调优
一轮完整搭建之后,进入迭代调优,这一步没有捷径,就是拿评估集反复跑、看失败案例、调整策略。
我建议每轮实验记录四个量化指标:答案准确率(人工标注或用强模型打分)、上下文命中率(模型是否引用了我们放进上下文的信息)、失败模式分布(幻觉、漏信息、格式错误各占多少)、端到端延迟。其中"上下文命中率"是我自己很看重的一个指标——如果模型输出内容跟上下文信息对不上,说明上下文组织方式有问题,而不是模型能力问题。
调优的常见杠杆按优先级排序:先调信息选择——过滤低相关检索结果,看看去掉之后效果是变好还是变差;再调顺序——把关键规则从中间挪到开头几个位置,观察准确率波动;然后调格式——把规则从纯文本改成结构化列表;最后才考虑增加示例或改模型。每调整一个变量只测一个维度,避免多个变量同时变动导致无法归因。
还有个细节:不同模型对同样的上下文结构响应不同。同一个项目从模型 A 切到模型 B,之前精心设计的提示和结构可能需要调整。所以在选型评估阶段,就应该把上下文工程策略作为选型测试的一部分,而不是拿到模型再临时设计。
4. 常见问题与排查技巧实录
4.1 信息明明给了,模型就是"看不到"
这是上下文工程里最常遇到的问题。排查思路不能只盯着模型,按以下顺序逐层查。
先查信息是否真的进入了模型输入。我遇到过不少次,检索服务返回了正确结果,但拼装上下文时代码有 bug,某个字段被截断或覆盖了。在调试模式打印完整的模型输入,确认目标信息确实存在。
再查信息位置。如果关键信息在上下文中段,那就是典型的"lost in the middle"。解决方案是调整顺序策略,把关键信息移到上下文前部或末尾;或者在系统提示里显式给出"阅读指引",告诉模型哪些位置有重要材料,比如"报销标准在参考资料前两段,差旅规则在最后一段"。
接着查格式。模型对文本格式的敏感性被严重低估。有一次,我把每个条目标记为"1."开头的编号列表,模型只能正确识别前两三条,后面的条目生成时会遗漏;改成 Markdown 表格后问题立刻消失。排查看格式,本质是排除"模型无法稳定解析"的可能。
最后才是怀疑模型参数问题,比如温度设置过高导致输出偏离上下文。温度调到 0 或 0.1 以下,大部分"上下文没生效"的幻觉问题会缓解。
4.2 上下文中信息互相打架
多来源信息冲突是复杂场景的常态。制度文档更新后旧版本没下线、不同部门的规则表述不一致,模型面对矛盾信息时行为不可预测,可能选错,可能胡编,也可能直接说"资料冲突"。
治本方案是信息源治理:在文档入库时就做版本控制,给每个文档挂生效日期和状态标签,检索时只召回当前生效的版本。这属于检索上游的工程改造,但效果立竿见影。
治标方案是在上下文层做冲突前置检测。把检索回来的结果先做一次交叉比对,识别字段级冲突,比如两个来源的住宿上限分别是 600 和 800,就不直接喂给模型,而是显式标注冲突并请求用户确认。这能在没有上游治理能力时保住底线。
4.3 成本与延迟失控
上下文越大,生成延迟和成本涨得越快。有个项目一开始图省事全量塞历史对话,发现每轮请求平均 8 秒,用户完全无法接受。压缩历史到短摘要、只保留最近三轮原文之后,延迟降到 2 秒以内,准确率几乎没有变化。
成本控制的关键是砍掉"无效上下文"。每轮对话都重复加载的大段静态资料,如果与当前问题无关,就是纯浪费。一个做法是"分段加载":把知识库按主题分区,根据当前问题意图只加载相关分区,而不是把所有内容都作为常驻前缀。加上前缀 KV 缓存复用,长会话场景的成本能再降一截。
延迟优化要留意的另一个点是:重排序和压缩如果串在一个 pipeline 里,本身也消耗时间。在高延迟要求下,可以做级联设计:先向量检索取回 Top 50,关键词通道取回 Top 20,重排序只对合并去重后的 Top 50 跑,压缩只对最终进入上下文的 Top 8 执行。每一层的数据量越小,整体耗时越可控。
4.4 上下文工程问题速查表
| 现象 | 常见原因 | 排查/解决动作 |
|---|---|---|
| 关键信息未生效 | 信息位于上下文中间 | 调整顺序,前置关键材料 |
| 输出与上下文矛盾 | 温度过高 | 温度降至 0 或 0.1,开启严格跟随 |
| 同一问题答案摇摆 | 多来源信息冲突 | 上游做版本治理,或显式标注让用户确认 |
| 长会话后质量下降 | 历史压缩丢失关键约束 | 摘要保留实体和数值,固定四栏结构 |
| 延迟过高 | 静态前缀反复计算 | 前缀 KV Cache 复用 + 分段按需加载 |
| 检索结果命中但答错 | 块切分破坏语义 | 结构感知切分,块间重叠重查 |
| 格式错误频繁 | 平铺文本难解析 | 切换为 JSON/表格/列表结构化格式 |
5. 工具生态与选型参考
5.1 框架类工具的取舍
上下文工程领域已经有非常多的框架和工具出现,覆盖文档加载、切分、向量化、检索、上下文拼装、记忆管理全链路。拿 Java 和 Python 生态来说,像 LangChain 这类早期框架五脏俱全,但封装过厚,调试难度高,很多开发者反而被框架的抽象层挡住,看不清楚自己发出去的真实请求长什么样。
我的原则是:最初跑通概念时可以用重量级框架,尽快验证可行性;进入生产落地阶段,则建议核心链路自研或采用轻量组件拼接。自研的上下文拼装逻辑其实不难,难的是规范和可观测性——你需要在每一步都能看到"最终发出去的是什么"。框架的黑盒抽象在这点上很吃亏。
可观测性工具近两年逐渐成熟,能可视化单次请求的 token 分布、逐块成本、检索命中情况。强烈建议在上下文工程质量调优阶段引入这类工具,否则你只能靠猜测优化,效率很低。有一次我通过可视化发现某个知识点引用了 23 次,而另一个核心规则只引用了 1 次,这个信息差直接决定了后续调优方向是裁剪重复引用而非扩充资料。
5.2 自研还是上框架:一个务实的判断标准
这个问题的判断标准就一条:你的上下文路径是否足够特殊,标准框架的默认行为能不能满足。
如果业务只是简单的"文档问答 + 单轮查询",用现成框架完全没问题,默认的切分、检索、拼接能覆盖 80% 场景。但一旦涉及多轮依赖、多数据源冲突、按用户身份差异化加载策略,框架的默认行为就会成为绊脚石,你花费在绕过框架限制上的时间会远超自研成本。
另外提醒一句:不要过早抽象。先把完整流程跑通,再用代码固化流程,最后才考虑抽象成通用组件。顺序反了,很容易造出一个难以维护的"大泥球"。我自己吃过亏,项目刚开始就设计了复杂的上下文编排抽象层,结果业务调整后抽象层成了最大的重构负担。
6. 最后分享一点个人体会
上下文工程这个领域还很年轻,很多经验都还散落在各个团队里,没有形成系统教材。我做完几个项目之后最深的感觉是:它表面上是在跟模型打交道,实际上是在跟信息打交道。把上下文工程做好的人,往往同时也是信息梳理的高手——懂得什么信息值得被模型看到,什么信息只是噪音。
最后再分享一个实用小技巧:建立自己的"调试用黄金问题集"。选 20 个覆盖你业务各维度的经典问题,每次改动上下文策略、更换模型版本、调整检索参数,都先跑一遍这 20 个问题,对比输出差异。这个习惯能让你快速发现回退和劣化,省下大量在线上出问题后再排查的时间。上下文工程没有一劳永逸的配置,它是一套持续维护的工程实践——保持好奇,保持对"模型为什么这样回答"的追问,你会越做越顺。