ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

上下文工程实战:从概念到落地的完整指南

上下文工程实战:从概念到落地的完整指南

上下文工程这个词这两年被聊得越来越多,但真正动手做过的团队其实没那么多。我最早接触这个概念是在一个对话类产品的迭代里,当时模型换了一版,回答质量反而掉了,排查了整整两天才发现问题不在模型本身,而在我们喂进去的上下文结构变了——历史消息截断策略改了,系统提示词的位置挪了,几个工具描述的顺序也调了。那次之后我才真正意识到,上下文工程不是玄学,它是一套可以拆解、可以复现、可以量化的工程方法。这篇文章我想把这两年踩过的坑、试过的方案、验证过的参数,尽量完整地摊开讲一遍,从概念到落地,从设计到排查,适合刚入门想搞明白这套东西的读者,也适合已经在做但总觉得效果不稳的同行对照着看。

1. 上下文工程到底在解决什么问题

1.1 从提示词工程到上下文工程的认知升级

很多人把上下文工程当成提示词工程的新马甲,这个理解偏差挺大的。提示词工程关注的是"这一句话怎么写",上下文工程关注的是"这一整块信息怎么组织"。打个比方,提示词工程像是你在跟人说话时斟酌措辞,上下文工程则是你在安排一场会议——谁先发言、谁坐哪、材料什么时候发、哪些信息该提前铺垫、哪些该留到最后揭晓,这些都属于上下文工程的范畴。

具体到技术层面,一次模型调用的输入其实由好几块拼起来:系统指令、历史对话、检索到的外部知识、工具定义、当前用户输入、格式约束。这些内容不是简单堆在一起就完事,它们的顺序、篇幅、粒度、冗余度都会直接影响输出质量。我见过太多案例,模型本身没问题,就是上下文塞得太满或者太乱,导致关键信息被淹没。

从提示词工程到上下文工程的转变,本质是从"单点优化"走向"系统设计"。前者是局部技巧,后者是全局架构。你在做上下文工程时,要考虑的不只是这句话怎么说,还要考虑整个信息流的生命周期——什么进、什么出、什么留、什么丢。

1.2 上下文窗口变大为什么反而更难用

现在主流模型的上下文窗口动辄几十万 token,理论上能塞进去的东西越来越多,但实际用下来你会发现,窗口大不等于效果好。这里有个很反直觉的现象:把一堆相关资料全塞进去,模型的表现有时候还不如只给两三段精准内容。

原因在于注意力机制的特性。模型在处理长上下文时,对中间位置的信息关注度会下降,业内常说的"中间遗忘"就是这个现象。你辛辛苦苦检索了二十篇文档全塞进去,结果模型只记住了开头和结尾的几段,中间的关键论据反而被忽略了。所以上下文工程的一个核心任务,不是"能塞多少塞多少",而是"该塞什么、塞多少、怎么排"。

我实测下来的经验是,对于大多数问答和生成任务,有效上下文控制在窗口容量的三到五成是比较稳的区间。留出余量不仅能让模型注意力更集中,也给输出留了空间。当然这不是绝对数字,具体要看任务复杂度和信息密度,但"宁精勿滥"这个原则基本通用。

1.3 哪些场景最需要上下文工程

不是所有场景都值得投入精力做上下文工程。简单的一问一答、单轮翻译、格式转换这类任务,提示词写清楚就够了。真正需要上下文工程发力的是这几类场景:

  • 多轮对话系统:历史消息怎么压缩、怎么摘要、哪些轮次要保留原文,直接决定对话连贯性。
  • 检索增强生成:检索回来的片段怎么排序、怎么去重、怎么标注来源,影响答案的准确性和可追溯性。
  • 工具调用型应用:多个工具的定义怎么描述、按什么顺序排列、参数说明写多细,关系到模型选工具的准确率。
  • 长文档处理:合同审阅、报告分析这类任务,文档怎么分块、块之间怎么建立关联,是成败关键。
  • 角色扮演与人格化应用:人设信息怎么稳定注入、怎么防止多轮之后人设漂移,是这类产品的核心难题。

你可以对照一下自己手上的项目,如果命中上面任意两条以上,那上下文工程就值得认真做一轮。

2. 上下文的组成结构与设计原则

2.1 一次调用的上下文分层模型

我把一次模型调用的上下文拆成五层,从外到内依次是:系统层、知识层、历史层、任务层、约束层。这个分层不是官方标准,是我自己在项目里总结出来的组织方式,好处是每一层职责清晰,改动时不容易互相干扰。

系统层放的是角色设定、能力边界、总体行为准则,这部分内容相对稳定,一次会话内基本不变。知识层放检索到的外部信息或者预置的领域知识,这部分是动态的,每次请求可能不同。历史层是对话记录,需要做压缩和摘要处理。任务层是当前这一轮的具体指令,要写得明确具体。约束层是输出格式、长度、语气这些硬性要求。

分层的价值在于可维护性。当输出出问题时,你可以逐层排查:是系统层的人设被稀释了,还是知识层塞了无关内容,还是历史层压缩过度丢了关键信息。不分层的话,所有内容糊成一团,排查起来就是大海捞针。

2.2 信息密度与冗余度的平衡

上下文里最忌讳两种极端:一种是信息太稀,说了半天没说到点子上;另一种是信息太密,每句话都是干货但堆在一起反而让人抓不住重点。模型处理信息的方式和人有点像,需要一定的结构信号来引导注意力。

我的做法是给关键信息加"锚点"。比如在知识层里,每段检索内容前面加一句简短的主题标注,告诉模型这段讲的是什么;在历史层里,对重要的用户偏好做显式标记,而不是指望模型自己从对话里总结。这些锚点会增加一些 token 开销,但换来的是模型定位信息的准确率提升,这笔账算下来是划算的。

冗余度控制上,我一般遵循"同一信息不重复超过两次"的原则。系统层说过的人设,任务层就不用再强调;知识层已经给出的数据,约束层就不用再复述。重复不仅浪费窗口,还会让模型误以为这是重点而过度关注,反而干扰判断。

2.3 顺序编排对输出质量的隐性影响

上下文里内容的排列顺序,影响比大多数人想象的大。有几个我验证过的规律:

重要指令放两端。开头和结尾是模型注意力最集中的位置,核心要求尽量放在系统层开头或者任务层结尾,别埋在中间。

知识在前,问题在后。检索内容应该放在用户问题之前,让模型先"读完材料"再"回答问题",顺序反了容易导致模型先形成判断再找证据,产生确认偏误。

示例紧跟指令。如果你给了输出示例,示例要紧挨着对应的指令,中间别插其他内容,否则模型可能对不上号。

工具定义按使用频率排。工具多的时候,常用的放前面,冷门的放后面,实测能提升工具选择的准确率。

这些规律不是拍脑袋想出来的,是我在不同项目里做 A/B 对比测出来的。当然不同模型可能有细微差异,建议你在自己的场景里也做一轮小规模验证。

3. 核心实操:上下文组装的完整流程

3.1 系统提示词的编写与稳定注入

系统提示词是上下文的"地基",写得好不好直接决定后续所有环节的上限。我总结了一个四段式结构,实测下来比较稳:

第一段定角色和能力边界,说清楚"你是谁、你能做什么、你不能做什么"。第二段定行为准则,比如遇到不确定的信息要怎么处理、遇到超出范围的问题要怎么回应。第三段定输出风格,语气、详略、格式偏好。第四段放少量高质量示例,给模型一个具体的参照。

这里有个细节很多人忽略:系统提示词要防稀释。多轮对话之后,系统提示词在上下文里的占比会越来越小,模型对它的"记忆"会减弱。解决办法是在历史层压缩时,始终保留系统提示词的核心要点摘要,或者在每若干轮之后重新注入一次精简版人设。我一般设置每八到十轮做一次轻量重注入,效果比较明显。

还有一个坑是系统提示词写太长。我见过有人写了三千字的系统提示,结果模型反而抓不住重点。系统层控制在五百到八百字是比较舒服的区间,超过一千字就要考虑拆分,把部分内容挪到知识层按需注入。

3.2 历史对话的压缩与摘要策略

多轮对话的上下文管理是重灾区。全量保留会撑爆窗口,粗暴截断会丢关键信息,怎么取舍是门手艺。我目前用的是分层压缩方案:

最近三轮对话保留原文,一个字不改,保证即时连贯性。第四到第十轮做轻度摘要,保留用户的核心诉求和模型的关键结论,删掉寒暄和试错过程。十轮以前的对话做深度摘要,只保留事实性信息,比如用户透露的偏好、已经确认的结论、明确排除的方案。

摘要不是让模型随便总结,而是要用固定的模板。我用的模板大概是:"用户在此轮提出了X需求,确认了Y信息,排除了Z方案。"这种结构化摘要比自由发挥的摘要稳定得多,信息丢失率也低。

注意:摘要环节本身也要消耗一次模型调用,如果对话轮次不多,直接截断可能更划算。我一般设置在对话超过十五轮之后才启用摘要机制,短对话用滑动窗口截断就够了。

另外有个实操技巧:在历史层的最前面放一句"以下是历史对话摘要,按时间顺序排列",给模型一个明确的结构信号。别小看这句话,实测能减少模型把摘要内容当成当前指令的误判。

3.3 检索内容的筛选、排序与标注

检索增强场景下,检索回来的内容怎么处理是上下文工程的重头戏。我的处理流程分四步:

第一步去重。不同来源的文档经常有重复段落,用简单的文本相似度做一轮过滤,能砍掉两三成的冗余。

第二步相关性重排。检索系统给的排序不一定适合直接喂给模型,我会用一个轻量模型或者规则做二次排序,把和当前问题最相关的片段提到前面。

第三步分块粒度调整。块太大信息杂,块太小上下文断裂。我的经验是每块控制在三百到五百字,块与块之间保留一句重叠内容做衔接。

第四步加标注。每块前面加一行来源和主题标注,格式大概是"[来源:某文档 | 主题:XX]"这种。标注的作用是让模型知道这段内容的出处和主旨,回答时能引用来源,也方便它判断内容可信度。

排序上我遵循"倒金字塔"原则:最相关的放最前,次相关的放最后,中间放补充材料。这样即使模型只关注了两端,也能拿到最关键的信息。

3.4 工具定义的描述技巧与排列顺序

工具调用型应用里,工具定义的写法直接影响选择准确率。我踩过的坑是工具描述写得太技术化,模型理解不了工具的适用场景。后来改成"功能描述+适用场景+不适用场景"三段式,准确率明显提升。

举个例子,一个查询订单状态的工具,光写"根据订单号查询订单状态"是不够的,要补上"适用于用户询问物流进度、订单是否发货等场景;不适用于修改订单、退款等操作"。把边界说清楚,模型就不会乱用工具。

工具数量多的时候,排列顺序也有讲究。我一般按"高频通用工具在前,低频专用工具在后"排,同时把功能相近的工具放在相邻位置,方便模型对比选择。工具超过十个之后,建议在系统层加一句工具使用总则,告诉模型优先考虑哪几类工具。

参数描述同样重要。每个参数要写清楚类型、是否必填、取值范围、示例值。我见过参数描述只写"订单号"三个字的,模型经常传错格式。补上"字符串类型,必填,格式为纯数字,例如12345678"之后,传参错误率降了一大截。

4. 参数调优与效果验证方法

4.1 关键参数的取值逻辑与计算

上下文工程涉及几个关键参数,取值不能拍脑袋,要有计算依据。我列一下自己常用的几个:

上下文预算分配。假设模型窗口是 128K token,我不会全用满。我的分配比例大概是:系统层占 5%,知识层占 40%,历史层占 25%,任务层和约束层占 10%,剩下 20% 留给输出。这个比例是根据任务类型调的,检索密集型任务知识层可以提到 50%,对话密集型任务历史层可以提到 35%。

分块大小计算。分块大小 = 目标知识层预算 ÷ 预期块数。比如知识层预算 50K token,预期放 100 块,那每块就是 500 token 左右,换算成中文大概三百多字。这个计算能保证块数和预算匹配,不会出现块太大放不下或者块太小信息碎的问题。

摘要压缩比。轻度摘要保留原文 40% 到 50% 的信息量,深度摘要保留 15% 到 20%。这个比例是通过对比摘要前后关键信息召回率测出来的,低于这个比例信息丢失明显,高于这个比例压缩效果不够。

这些数字不是金科玉律,但提供了一个起点。你在自己场景里可以先按这个比例跑一轮,再根据实际效果微调。

4.2 用对照实验验证上下文改动

上下文工程的改动最怕"感觉变好了"。感觉这东西不靠谱,必须用对照实验说话。我的做法是建一个小型评测集,覆盖典型场景,每次改动上下文结构就跑一轮对比。

评测集不用很大,三五十条就够,但要覆盖不同难度和不同类型。每条样本记录改动前后的输出,人工或者用规则打分。打分维度我一般看四个:准确性(信息对不对)、完整性(该说的说了没)、相关性(有没有跑题)、格式合规(输出格式对不对)。

跑对照实验时有个原则:一次只改一个变量。同时改顺序又改分块大小,出了问题根本不知道是哪个引起的。我吃过这个亏,后来老老实实单变量测试,虽然慢一点但结论可靠。

4.3 上下文长度与响应质量的实测曲线

我在一个问答项目里做过一组实测,把上下文长度从 2K token 逐步加到 64K token,观察响应质量的变化。结果挺有意思:

上下文长度准确性完整性响应延迟
2K中等偏低快
8K较高中等较快
16K高较高中等
32K高高偏慢
64K略降高慢

可以看到,质量在 16K 到 32K 之间达到峰值,再往上加反而准确性略降,延迟还明显上升。这个曲线的拐点因任务而异,但"存在最优区间"这个结论是通用的。盲目堆上下文长度是性价比很低的做法。

5. 常见问题排查与避坑实录

5.1 输出跑偏的逐层排查法

输出跑偏是最常见的问题,排查时我按分层模型从外往内查:

先看系统层,人设有没有被稀释,行为准则有没有被后续内容覆盖。再看知识层,检索内容里有没有和问题冲突的信息,有没有无关内容干扰。然后看历史层,摘要有没有丢关键信息,有没有把旧指令当成新指令。最后看任务层,当前指令写得够不够明确,有没有歧义。

这个排查顺序的逻辑是"从稳定层往动态层查",因为稳定层出问题的概率低但影响大,动态层出问题的概率高但影响局部。大部分跑偏问题最后都定位在知识层和历史层。

5.2 上下文超限的应急处理

上下文超限报错是家常便饭,应急处理有几个手段,按优先级排:

第一优先是压缩历史层,这是最不影响效果的。第二是精简知识层,去掉相关性低的块。第三是缩短系统层,把非核心内容挪走。最后才是截断任务层,这个要慎用,容易丢关键指令。

我一般会在组装上下文之前先做一次预算检查,预估 token 数,超了就按上面的优先级自动降级。这个预检查机制能避免大部分超限报错。

5.3 多轮对话中的人设漂移修复

人设漂移是多轮对话的顽疾,聊到十几轮之后模型就开始"出戏"。修复手段我试过几种,效果最好的是周期性重注入:每八到十轮,在历史层末尾插入一段精简人设摘要,格式是"提醒:你的角色是X,核心行为准则是Y"。这段内容不占太多 token,但能有效把人设拉回来。

另一个辅助手段是在摘要模板里固定保留人设相关字段,每次摘要都带上,防止人设信息在压缩过程中被丢掉。两个手段配合使用,人设稳定性提升很明显。

5.4 常见问题速查表

问题现象可能原因排查方向解决手段
输出跑偏知识层有冲突信息检查检索内容相关性去重+重排+过滤
人设漂移系统层被稀释检查轮次和人设占比周期性重注入
上下文超限预算分配失衡检查各层 token 占比按优先级降级压缩
工具选错工具描述不清检查描述三段式完整性补适用/不适用场景
信息遗漏摘要压缩过度检查摘要保留比例提高压缩比或改模板
响应变慢上下文过长检查总 token 数压缩到最优区间

这张表是我从实际项目里攒出来的,遇到问题先对照着查一遍,大部分情况能快速定位。

6. 工程化落地与持续优化

6.1 上下文模板的版本管理

上下文结构一旦确定,就要当成代码一样管理。我的做法是把上下文模板抽成配置文件,每个版本打标签,改动走评审流程。这样做的原因是上下文改动的影响很隐蔽,今天改个顺序明天可能才暴露问题,有版本记录才能回溯。

模板配置里我一般包含这几块:各层的模板文本、参数默认值、压缩策略配置、工具定义列表。改任何一项都要记录改动原因和预期效果,跑完对照实验再合并。这套流程听起来重,但比出了问题抓瞎强得多。

6.2 监控指标与迭代节奏

上线之后要有监控。我关注的指标分两类:质量指标和成本指标。质量指标看输出准确率、用户反馈、重试率;成本指标看平均 token 消耗、响应延迟、调用次数。

迭代节奏上,我不建议频繁大改。上下文结构稳定下来之后,小步快跑地调参数就行,大结构调整一个季度做一次顶天了。频繁大改会让效果波动,用户感知很差,排查起来也困难。

6.3 不同模型间的上下文适配差异

换模型是上下文工程最容易翻车的时候。不同模型对上下文顺序的敏感度、对指令的遵循程度、对长上下文的处理能力都不一样。我的经验是换模型之后必须重跑一遍对照实验,重点看三个地方:系统提示词的遵循度、知识层的利用效率、工具选择的准确率。

有些模型对结构化标注更敏感,有些模型对自然语言描述更买账。适配的时候不要照搬,要根据模型特性调整。比如某个模型对列表式指令遵循得好,那就把关键要求改成列表;另一个模型对示例学习效果好,那就多放几个示例。这些调整都要靠实测确定,没有通用答案。

7. 我个人的几条实操心得

做上下文工程这两年,有几个体会比较深,分享出来供参考。

第一,别迷信窗口大小。窗口大是好事,但用不好反而是负担。我见过太多项目把窗口当垃圾桶,什么都往里塞,结果效果还不如精简版。上下文工程的核心是取舍,不是堆砌。

第二,结构化比自然语言更稳。同样的信息,用固定模板和标注组织起来,比一段流畅的自然语言描述效果稳定得多。模型对结构的敏感度比我们想象的高,给它清晰的骨架,它填内容的质量就高。

第三,对照实验是唯一真理。所有"我觉得这样更好"的判断都要用数据验证。上下文改动的影响很微妙,直觉经常骗人。建个小评测集,养成改完就跑的习惯,长期下来能省很多返工。

第四,留余量是门艺术。上下文预算、压缩比例、摘要保留度,这些参数都要留缓冲。卡着极限用,遇到边界情况就崩。我一般所有预算都留两成余量,稳字当头。

第五,人设和指令要防衰减。多轮对话里,越靠前的内容越容易被"遗忘"。重要的东西要么周期性重注入,要么放在靠后的位置。这个规律在几乎所有模型上都成立,值得当成常识记住。

上下文工程这个方向还在快速演进,新的模型、新的能力会不断改变最佳实践。但底层的那套思路——分层组织、预算分配、对照验证、持续迭代——短期内不会过时。把这几条吃透,换什么模型都能快速适配。后续如果要做更细的展开,工具调用的上下文优化和长文档的分块策略是两个值得深挖的方向,我后面有机会再单独聊。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进