上下文工程这个词这两年被聊得很多但真正动手做的时候大部分人还是停留在“把提示词写长一点、把资料多塞一点”的阶段。我最早也是这样觉得上下文嘛不就是往输入框里堆东西堆得越全模型答得越好。后来在一个知识问答类的小项目里连续踩了几次坑才发现上下文工程远不是“堆料”这么简单它更像是在给模型布置一个恰到好处的工作现场给多了它抓不住重点给少了它答非所问给错了它一本正经地胡说。这篇就把我对上下文工程的理解、拆解和实操经验完整讲一遍从它到底解决什么问题到怎么一步步搭出一套能复用的上下文组织方案尽量让刚入门的朋友看完能直接上手也让已经做过一些提示词工作的同行能对照着查漏补缺。1. 上下文工程到底在解决什么问题1.1 从“提示词写得好”到“上下文组织得好”的认知转变很多人入门大模型的第一课是学提示词学怎么加角色设定、怎么加输出格式约束、怎么用少样本示例。这套东西确实有用但它有个隐含前提模型需要的信息你都能在一段话里说清楚。现实项目里这个前提经常不成立。比如做一个内部文档问答用户问“上季度的报销流程有没有变化”模型要答对至少得知道三件事当前生效的报销流程是什么、上一版流程是什么、两者差异在哪。这三块信息分散在不同文档里靠一句提示词根本装不下。提示词工程关注的是“怎么把一句话说漂亮”上下文工程关注的是“怎么把一堆相关信息组织成一个模型能消化的结构”。前者是修辞问题后者是信息架构问题。我自己的体会是提示词技巧在简单任务上收益明显但任务一旦涉及多源信息、长文档、多轮对话决定效果上限的往往是上下文怎么组织而不是那句话写得多巧妙。这也是为什么同样一段提示词有人用起来很稳有人用起来时好时坏——差别常常在喂进去的上下文上。1.2 上下文窗口变大之后为什么“多塞”反而更糟现在主流模型的上下文窗口动辄几十万 token看起来“塞得下”已经不是问题。但我实测下来窗口大不等于效果好。有个很典型的现象叫“中间遗忘”当关键信息被放在一长段上下文的中段时模型对它的利用率明显低于放在开头或结尾。这不是玄学而是注意力机制在长序列上的固有特性——距离越远、干扰越多权重越容易被稀释。我做过一个粗糙的对照实验同一份 3 万字的产品手册一次整篇塞进去问一个细节问题一次只截取相关章节塞进去问同样的问题。整篇塞的那次模型答对了大方向但细节张冠李戴截取章节的那次答案精准得多。这说明上下文工程的核心矛盾不是“容量够不够”而是“信噪比高不高”。塞得越多噪声越多模型越容易在无关信息里迷路。所以真正要练的本事是在有限窗口里放最该放的东西而不是把窗口填满。1.3 一个判断标准模型答错时先问“它是不是没看到”我现在排查模型答错的问题第一步不是改提示词而是问自己答对这道题所需的信息模型在上下文里真的看到了吗如果没看到改再多提示词也是白搭如果看到了还答错才轮到提示词和推理能力的问题。这个判断顺序能省掉大量无效调试。很多所谓的“模型不行”拆开看其实是“上下文没给对”。把这条当成习惯之后我调问题的效率高了不少也更清楚哪些问题该靠检索解决、哪些该靠提示词解决、哪些是模型能力边界。2. 拆解上下文的几个组成部分2.1 系统指令、任务描述与约束条件的分层一份组织良好的上下文我习惯把它拆成三层。最上层是系统指令定的是模型的角色和总体行为边界比如“你是一个严谨的技术文档助手不确定时明确说不知道”。中间层是任务描述说清楚这一次具体要干什么比如“根据下面的文档回答用户问题”。最下层是约束条件管的是输出形态比如“回答不超过三句话引用原文时标注章节号”。这三层分开写的好处是改任务的时候不用动系统指令调格式的时候不用碰任务描述维护起来清爽很多。很多人把这三层揉成一大段结果每次微调都得通读全文改一处可能影响另一处。分层之后还有个隐性收益系统指令可以复用任务描述可以按场景切换约束条件可以做成配置项。我在一个多场景助手里就是这么干的同一套系统指令配不同的任务模板切换成本很低。这里的关键不是格式多好看而是让每一层职责单一出问题时能快速定位是哪一层没写对。2.2 检索内容、示例与对话历史各自扮演的角色除了指令层上下文里通常还有三类“素材”检索来的外部知识、少样本示例、以及多轮对话的历史。这三类东西的作用完全不同混在一起塞容易互相干扰。检索内容提供的是事实依据示例提供的是输出风格和推理范式的参照对话历史提供的是当前会话的连贯性。我一般会把它们用明确的分隔标记隔开让模型清楚哪段是“参考资料”、哪段是“示范”、哪段是“之前聊过的”。举个具体的坑早期我把示例和检索文档混在一起给模型结果模型有时候会把示例里的虚构内容当成事实引用出来。后来加了清晰的分隔和一句“示例仅用于说明格式不作为事实依据”这个问题就基本消失了。对话历史也一样轮次多了之后旧信息会和新问题打架我的做法是对历史做摘要压缩只保留和当前问题相关的部分而不是无脑全量带上。这三类素材各司其职上下文才干净。2.3 上下文里最容易被忽略的“元信息”还有一类东西经常被漏掉我管它叫元信息时间、来源、版本、适用范围。比如一份流程文档是哪个版本、什么时候生效、适用于哪个业务线这些信息如果不给模型很可能拿旧版本当现行版本答。我在一个政策问答场景里就吃过这个亏模型引用了已经废止的条款答案看着有理有据实际是错的。后来我在每段检索内容前面都带上“来源版本生效时间”的元信息头模型引用时也会带上可追溯性强了很多。元信息还有个作用是帮模型做取舍。当检索回来多份内容互相矛盾时如果每份都标了时间和版本模型就有依据判断哪份更新、更该采信。没有元信息模型只能瞎猜或者把矛盾原样抛给用户。所以别小看这几行标注它往往决定了答案的可信度。我的经验是凡是会随时间变化的信息元信息必须给足这是上下文工程里性价比很高的一步。3. 把上下文组织起来的实操方法3.1 先做信息分层哪些必须给、哪些可以给、哪些坚决不给动手组织上下文之前我习惯先列一张信息清单把候选信息分成三档必须给、可以给、坚决不给。“必须给”的是答对问题不可或缺的比如用户问题的直接依据“可以给”的是有帮助但非必需的背景比如相关但非核心的章节“坚决不给”的是容易引发误导的比如过时版本、相似但不同场景的案例。这个分档动作看着简单但能逼着你想清楚每份信息到底为什么进上下文。分档之后上下文长度往往能砍掉一大半效果反而更好。我做过一次对比把一份两万字的资料按“必须给”筛到三千字问答准确率不降反升。原因很直白噪声少了模型注意力集中了。这里有个实操建议分档时多问一句“如果删掉这段答案会不会变错”如果不会它大概率属于“可以给”甚至“不给”。用这个标准过一遍上下文会精炼很多。3.2 结构化排版用分隔符和标题让模型“看得清”模型读上下文不像人那样有天然的段落感所以排版要显式一点。我常用的做法是用清晰的分隔符把不同区块隔开每个区块加一个小标题比如“参考资料”“历史对话”“输出要求”。分隔符用简单的等号线或者井号标题都行关键是前后一致让模型能稳定识别区块边界。实测下来结构化排版对长上下文的稳定性提升很明显尤其是多份资料并列的时候。除了分隔顺序也有讲究。我一般把最关键的指令和问题放在靠前或靠后的位置把大段参考资料放中间这样能一定程度上缓解中间遗忘。如果参考资料本身很长我会在开头先给一个目录或者摘要让模型先建立全局印象再往下读细节。这个技巧在长文档问答里特别好用相当于给模型一张地图它读起来不容易迷路。排版这件事不花什么成本但对效果的影响比想象中大。3.3 动态组装按问题类型切换上下文模板固定一套上下文模板应付所有问题早晚会不够用。我的做法是按问题类型准备几套模板运行时根据问题特征动态选。比如事实型问题走“检索为主、示例为辅”的模板创作型问题走“示例为主、约束为辅”的模板多轮追问走“历史摘要当前问题”的模板。这样每类问题拿到的上下文都是为它定制的信噪比更高。动态组装的关键是有一个靠谱的问题分类逻辑。简单点可以用关键词规则复杂点可以用一个小模型先做意图识别。我在一个客服场景里用的是规则加轻量分类的组合先按关键词粗分再让模型确认准确率够用且成本可控。模板本身要留好插槽检索内容、历史摘要、用户问题各占一个位置组装时按需填充。这套结构搭好之后新增场景基本就是加一套模板的事扩展性比一坨硬编码的提示词好太多。4. 实测中踩过的坑和应对4.1 检索内容互相矛盾时模型会“和稀泥”最典型的一个坑检索回来两份文档一份说流程 A一份说流程 B模型没有判断哪份对而是把两个都列出来让用户自己选。表面看很“全面”实际是把矛盾甩给了用户。根因是上下文里没给足判断依据。我的应对是给每份内容加元信息版本、时间、适用范围并在指令里明确“当资料冲突时优先采信版本更新、适用范围更匹配的那份并说明取舍理由”。加了这条之后模型的和稀泥行为少了很多。还有一种情况是资料本身没错但适用场景不同模型没区分就混着用。比如面向新员工和面向管理层的两套流程细节有差异。这时候光靠元信息不够还得在指令里点明“根据提问者身份选择对应资料”。所以遇到矛盾别急着怪模型先看上下文里有没有给它判断冲突的依据。给了依据还出错才考虑是不是模型推理能力的问题。4.2 长对话里旧信息“污染”新问题多轮对话做久了历史越堆越长旧话题的信息会干扰新问题。我遇到过一个情况用户前面问过退款后面问发票模型答发票时还把退款政策带出来了。原因是历史全量塞进去模型分不清哪些还相关。解决办法是对历史做滚动摘要每几轮压缩一次只保留和当前话题相关的要点同时给摘要标注时间让模型知道哪些是“过去时”。摘要本身也有坑压得太狠会丢关键约束压得太松等于没压。我的经验是摘要里至少保留三类东西用户明确表达过的偏好或约束、已经确认过的事实、尚未解决的问题。其余的寒暄和过程性内容可以大胆砍掉。另外当前问题如果明显切换了话题我会在上下文里显式提示“以下进入新话题此前历史仅供参考”帮模型做区隔。这个提示很小但效果立竿见影。4.3 示例给多了反而带偏输出少样本示例是好东西但给多了会出问题。我有一次为了追求格式稳定塞了五六个示例结果模型开始机械模仿示例的措辞遇到示例没覆盖的情况也不会变通。后来减到两三个并且特意让示例覆盖不同难度和不同边界情况泛化性反而好了。示例的作用是“示范范式”不是“穷举答案”给太多等于把模型的发挥空间压没了。还有个细节是示例的摆放位置。放在指令之后、正式问题之前模型更容易把它当成参照放在问题之后有时候会被当成问题的一部分。我一般固定在指令和问题之间并用分隔符隔开标注“以下为格式示例”。另外示例本身要干净别有错别字或者和当前任务无关的内容否则模型会连噪声一起学。这些细节看着琐碎但都是实测出来的经验能少走不少弯路。5. 一套可复用的上下文组装流程5.1 从需求到上下文的五步落地法把前面的经验收拢一下我平时落地一套上下文方案基本走五步。第一步明确任务和评判标准先想清楚“答成什么样算对”这决定了后面所有取舍。第二步列信息清单并分三档必须给、可以给、不给砍掉噪声。第三步设计上下文结构分层写指令、分隔素材、安排顺序。第四步准备动态模板按问题类型分几套。第五步拿真实问题跑一轮看答错的是“没看到”还是“看到了没用好”分别对应检索问题和提示词问题。这五步里第一步最容易被跳过但恰恰最重要。评判标准不清楚后面分档和排版都是拍脑袋。我现在的习惯是先攒二三十个真实问题作为测试集每改一版上下文就跑一遍看准确率和稳定性变化。有了这个测试集调优就有了依据不至于改了半天不知道是变好还是变坏。这套流程不复杂但坚持走下来上下文质量会稳定很多。5.2 用测试集验证上下文改动的实际收益上下文调优最怕“感觉变好了”。我吃过这个亏凭感觉改了一版上线后才发现某些边缘问题反而变差了。后来固定用测试集做回归每次改动都跑全量记录准确率、引用准确率、拒答合理性几个指标。指标不用多但要能反映你关心的维度。比如知识问答场景我会重点看“答案正确率”和“引用是否真实存在”后者能抓出模型编造来源的问题。测试集本身也要维护随着业务变化补充新问题淘汰过时的。我一般保持测试集里有一半是常见问题、一半是边界和刁钻问题这样既能看整体水平也能看鲁棒性。跑测试的时候别只看总分要逐条看错例很多时候总分没变但错的地方换了说明改动引入了新问题。这套验证习惯建立起来之后上下文工程就从“玄学调参”变成了有据可依的工程活。5.3 上下文长度与成本的平衡取舍上下文不是免费的长度直接关系到调用成本和响应速度。我的原则是“够用就好留一点余量”。具体做法是先按必须给的信息组装跑测试集看效果如果准确率达标就不再加如果不达标再逐档加入“可以给”的信息每加一档测一次找到收益拐点就停。这样能避免无脑堆料带来的成本浪费。实测下来很多场景在信息精简后效果不降成本却降了不少。还有个省成本的技巧是缓存。系统指令和固定示例这类不常变的部分很多平台支持缓存复用能省掉重复计算。动态部分检索内容、历史摘要才每次重新组装。另外长文档场景可以考虑先做一轮粗筛再精排把候选范围缩小再进上下文而不是把所有候选都塞进去让模型自己挑。这些手段组合起来能在保证效果的前提下把成本控制住。上下文工程不只是效果问题也是成本问题两者要一起考虑。6. 几个容易混淆的概念澄清6.1 上下文工程和提示词工程不是替代关系经常看到有人把两者对立起来说“提示词工程过时了现在讲上下文工程”。我的理解是两者是包含和协作的关系不是替代。提示词工程解决的是“怎么把指令写清楚”上下文工程解决的是“给模型看什么、怎么组织”。指令本身就是上下文的一部分写得好不好依然重要。只不过当信息量变大、来源变多时光靠指令技巧不够得靠上下文组织来兜底。实际项目里两者是配合使用的。上下文结构搭好之后每个区块里的指令怎么写、示例怎么措辞还是提示词工程的活。我一般先把上下文骨架搭起来再逐块打磨措辞。反过来只练提示词不练上下文组织遇到多源信息任务就会卡壳。所以别被“新概念取代旧概念”的说法带偏把两者当成一套组合拳来练效果最好。6.2 上下文工程和检索增强的边界在哪检索增强解决的是“从哪里把相关信息找出来”上下文工程解决的是“找出来之后怎么组织给模型”。两者衔接紧密但职责不同。检索没做好上下文里全是无关内容再怎么组织也救不回来检索做好了但组织得乱模型照样用不好。我排查问题时会把这两段分开看先看检索回来的内容相不相关再看组织方式有没有问题定位清楚再动手改。实践中这两块经常要一起调。比如检索返回的片段太长上下文里塞不下就得在检索侧做片段切分和精排反过来上下文结构设计得好也能容忍检索结果稍微粗一点。我的经验是先把检索的召回率和相关性做到及格再重点打磨上下文组织因为组织方式的优化空间往往更大、成本更低。两边都到位了整体效果才稳。7. 我个人的几条实操心得第一条别迷信“万能模板”。上下文模板一定要结合具体场景调别人的模板拿过来只能当起点真实问题跑一轮才知道哪里要改。第二条养成“先看上下文再改提示词”的排查习惯能省掉大量无效调试。第三条元信息别省来源、版本、时间这几行字往往决定了答案可不可信。第四条测试集是上下文工程的命根子没有测试集就是在盲调。第五条长度和成本要一起算账够用就好别为了心理安慰堆料。还有个小技巧分享下调上下文的时候我会把组装好的完整上下文打印出来自己当模型读一遍看能不能顺畅找到答对问题所需的信息。如果我自己读着都觉得绕、觉得信息打架模型大概率也读不明白。这个“人肉自检”花不了几分钟但经常能提前发现结构问题。上下文工程说到底是在替模型整理信息站在它的角度想问题很多坑就能提前避开。
阅读完成 · 觉得有帮助?