详细解释
上下文工程(Context Engineering) 指的是在每一次模型调用之前,系统性地决定「这一轮到底要把哪些信息拼进 上下文窗口」,并对这些信息做筛选、压缩、排序与预算分配的一整套工程方法。它和 提示词工程 处于不同层次:提示词工程管的是「话怎么说」——怎么把指令写清楚、怎么给示例、怎么约束输出格式;上下文工程管的是「喂什么信息、喂多少、以什么顺序喂」——系统提示占多少 Token、检索回来几段资料、历史对话保留哪几轮、长期记忆要不要注入。两者不是替代关系而是叠加关系:措辞写得再好,如果关键资料没被放进去,模型也答不对;反过来资料齐全但指令含糊,输出同样不可用。
从来源看,进入上下文的信息可以归为四大类:一是系统提示与工具定义,决定模型是谁、能做什么;二是检索片段,来自 RAG 召回的外部知识;三是对话历史,即当前会话此前的轮次;四是长期记忆,跨会话沉淀下来的用户偏好与事实。这四类信息共享一个固定的窗口容量,因此核心工作就是做预算分配:哪一类多给一些,哪一类超预算时先砍。工程上有两类典型失效模式必须防住:其一是信息位置问题,被放在上下文正中间的关键约束容易被模型忽略,这种现象被称为 Lost in the Middle(关于长上下文位置敏感性的系统研究见 https://arxiv.org/abs/2307.03172 );其二是信息总量问题,也就是所谓上下文腐化(context rot)——当上下文里塞入大量与当前任务无关的历史或噪声后,模型表现会不升反降,历史越长反而越差。这两类失效的共同根源在于模型的注意力是一种有限的资源,它并不会平等地对待窗口里的每一段文字:位置靠中间或语义较弱的片段容易被忽略,而存在矛盾或已经过时的内容又会被不加区分地采纳。因此上下文工程的核心动作其实是做减法——不是把找得到的信息都塞进去,而是反复判断「哪一段信息对这一步推理真正有用」,把有限预算花在刀刃上。
落地方案上,团队通常会把这套逻辑收敛到 Agent 网关里统一实现。唯元智创 在网关侧做上下文组装与压缩:先按任务类型套用预算模板,再对 检索片段 做去重、重排与分级 上下文压缩,对对话历史按相关性裁剪并做摘要,对跨会话的 智能体记忆 只注入与当前任务相关的条目,最后按前缀缓存的友好顺序把稳定内容排在前面、动态内容排在后面。整个过程都会记录每一轮实际拼进去的成分与 Token 预算 占用,做到可回放、可定位,避免出了问题只能靠猜。对于检索密集的问答场景,还会额外记录召回条数、去重前后的片段数、压缩比以及最终真正注入的 Token 数,把不同上下文策略的收益做成可比指标。
提示词工程与上下文工程对比表
| 对比维度 | 提示词工程 | 上下文工程 | 关注层次 | 主要产出物 | 典型失败模式 | 优化手段 |
|---|---|---|---|---|---|---|
| 核心目标 | 把单条指令写清楚、写准确 | 让进入窗口的全部信息恰到好处 | 措辞与指令对比信息架构 | Prompt 模板与 System Prompt | 指令含糊、模型不听话 | 迭代措辞、补充示例 |
| 处理对象 | 一段提示的文本表述 | 系统提示、工具定义、检索片段、历史与记忆 | 单点表达对比全局装配 | 动态上下文组装流水线 | 信息缺失或信息过载 | 召回、去重、压缩、重排 |
| 时间尺度 | 开发期打磨,上线后相对稳定 | 每次请求动态组装,随会话状态变化 | 静态对比动态 | 每轮的上下文计划 | 长会话后期质量衰减 | 预算分配、动态裁剪 |
| 失败模式 | 模型理解偏差、格式不合规 | 关键信息被忽略、上下文腐化 | 局部对比系统 | 可观测的上下文日志 | Lost in the Middle 与噪声干扰 | 双端强化、去噪精选 |
| 成本视角 | 提示词变长会直接推高输入 Token | 通过压缩与缓存控制总预算 | 表达成本对比预算成本 | 上下文预算分配表 | 成本失控、撞上窗口上限 | 前缀缓存、分级压缩 |
| 团队分工 | 产品与业务同学即可独立迭代 | 需要工程、检索、平台多方协同 | 单人对比协作 | 可复用的上下文中间件 | 各自为战、过程不可观测 | 统一网关与全程埋点 |
上下文预算分配参考表
| 上下文成分 | 建议预算占比 | 超预算时的处理顺序 | 可压缩性 | 是否可缓存复用 | 典型失效后果 |
|---|---|---|---|---|---|
| 系统提示与工具定义 | 15% 至 25% | 最后才裁剪 | 低,多为固定文本 | 可,稳定前缀能命中前缀缓存 | 裁剪过度会导致指令遵循下降 |
| 检索片段 | 20% 至 35% | 先去重重排再截断 | 高,可做摘要与压缩 | 部分可缓存 | 噪声过多会显著拉低准确率 |
| 对话历史 | 15% 至 30% | 优先按相关性裁剪 | 高,可摘要可丢弃 | 前缀部分可缓存 | 无关历史越长表现越差 |
| 长期记忆 | 5% 至 15% | 优先裁剪低相关条目 | 高,可摘要与合并 | 可,稳定记忆块可缓存 | 记忆过时会导致错误沿用 |
| 用户当前输入 | 5% 至 15% | 不可裁剪 | 极低 | 否 | 裁剪会直接丢失任务约束 |
| 预留输出空间 | 15% 至 25% | 绝不占用 | 不适用 | 不适用 | 占满会导致长回答被截断 |
| 唯元智创 上下文网关 | 按任务动态分配,默认落在上表推荐区间 | 自动按优先级逐级裁剪 | 内置分级压缩 | 支持前缀缓存友好排序 | 配置不当会压缩过度 |
实战经验与避坑
-
不要把上下文窗口塞满到接近上限,必须留出足够的输出空间:很多团队为了「让模型知道得更多」,把检索到的资料、全部历史、全部工具定义一股脑塞进去,把窗口用到九成以上,结果模型刚要生成较长的回答就被迫截断,输出到一半戛然而止。正确做法是把窗口明确切成输入区与输出区,输出区至少预留 15% 至 25%,长回答场景还要更多;当输入超预算时,按「先砍低相关历史、再砍低分检索片段、最后才动系统提示」的顺序逐级裁剪。宁可少喂一点噪声,也不要让回答被腰斩。
-
关键约束不能只放在上下文开头或正中间,要做双端强化:Lost in the Middle 现象表明,模型对上下文两端的信息记得最牢,对中间位置的召回最弱。因此像输出格式、禁止事项、必须遵守的业务规则这类硬约束,绝不能只写在长长的上下文开头就完事。稳妥的做法是双端强化:在系统提示的开头说一遍,在用户输入前再简明强调一遍,让同一个约束同时出现在两端;对于工具定义与检索片段,也要把最重要的那条排在第一段或最后一段,而不是随手埋在中间。更细的做法是给约束编号,在上下文两端引用同一个编号,这样即使中间塞了很长的资料,模型也能明确知道两端说的是同一条规则。
-
历史对话要按相关性而不是时间顺序筛选,只保留与当前任务相关的轮次:直接保留最近 N 轮看似简单,但长会话里用户往往中途换过话题,「最近」未必「最相关」。更糟的是无关历史会占用预算并引入干扰,让模型把旧话题的条件误套到新任务上。正确的做法是按相关性筛选:先用向量或关键词把历史轮次与当前问题做匹配,只保留强相关的若干轮,其余丢弃或压成一句摘要;对已经解决的子任务明确标记为已完成并移除细节,只留结论。
-
检索片段必须先做去重与重排,塞入 20 段往往不如精选 3 段:检索召回的原始片段里重复、近似、低质的内容很多,如果不加处理直接拼进上下文,等于把噪声喂给模型。工程上的标准动作是:先用相似度去重合并近似片段,再用重排模型按与问题的相关性重新排序,最后只取分数最高的 3 至 5 段。实测的经验区间是,把片段数从 20 段精简到 3 至 5 段后,答案准确率通常不降反升,因为模型不再被自相矛盾或离题的内容牵制。
-
上下文组装过程必须可观测,要记录每次请求实际拼进去的成分与 Token 数:上下文工程最大的坑是「出了问题是黑盒」。如果无法知道这一轮到底注入了哪些检索片段、保留了哪几轮历史、各类成分各占多少 Token,就只能靠猜来调参。因此必须在组装环节埋点,记录每一类成分的条数、Token 占用与被裁剪的部分,保留最近一段时间可供回放。有了这份日志,才能快速判断某次答错究竟是召回错了、历史污染了、还是预算把关键信息挤掉了。条件允许时,把这份记录与最终答案、用户反馈对齐,就能反推出什么样的上下文组合更容易得到正确答案。
-
稳定前缀要放在前面以便命中前缀缓存,动态内容放后面:上下文里有一部分内容是跨请求稳定的,比如系统提示、固定的工具定义、不变的规则说明;另一部分每次都变,比如用户当前输入、最新检索结果。由于提示词缓存(前缀缓存)依赖请求前缀的完全一致,把稳定内容排在前面、动态内容排在后面,就能让每次请求复用同一段缓存前缀,命中部分的输入成本可降至零头,延迟也明显下降。业界实测的典型收益是,长系统提示加长工具定义的场景下,缓存命中可让输入成本下降三成到六成,是性价比极高的一条优化。要拿到这个收益,前提是稳定部分的文本逐字节完全一致,任何一处标点或空格的变动都会让缓存整体失效,因此模板里的动态变量必须严格放在稳定前缀之后。