☰
上下文工程实战:如何让大模型精准理解用户意图
2026/10/13 16:18:44 网站建设 项目流程

1. 从一个真实困境说起:为什么模型总是“答非所问”

我第一次意识到上下文工程的重要性,是在做一个智能客服助手的项目时。当时我们接了一个需求,要让模型根据用户的历史订单、当前咨询的商品、以及过往的退换货记录,给出一个准确的回复。模型本身能力不差,单轮问答测试的时候表现很好,但一放到真实对话流里,问题就全暴露出来了:用户问“这个能退吗”,模型要么答非所问地介绍商品参数,要么给出一个和订单状态完全矛盾的退换货政策。

排查了很久才发现,问题不在模型本身,而在于我们喂给它的上下文是混乱的。历史订单信息、商品详情、退换货规则、用户当前问题,这四类信息被我们一股脑拼接在一起,中间没有任何分隔和优先级标记。模型面对一大段没有结构的文本,只能靠概率去猜哪些信息重要,结果就是该关注的信息被淹没了。

这件事让我彻底明白了一个道理:模型的输出质量,很大程度上不取决于模型本身有多强,而取决于你给它看的上下文有多清晰。这就是上下文工程要解决的核心问题。它不是简单地“把相关信息塞给模型”,而是要对信息进行筛选、排序、结构化、压缩,让模型在有限的上下文窗口里,看到最该看的东西。

这篇文章我想把上下文工程这件事讲透。从它为什么重要,到具体怎么做,再到我踩过的坑和总结出来的实操方法。如果你正在做基于大模型的对话系统、知识问答、Agent应用,或者任何需要模型“理解当前情境”的产品,这些内容应该能帮你少走不少弯路。即使你只是刚接触大模型应用开发,我也会用最直白的方式把原理讲清楚,让你知道为什么同样一个模型,别人用起来效果好,你用起来效果差,差距往往就在上下文工程上。

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

2.1 模型没有记忆,它只看得见你给的那点东西

很多人对大模型有一个误解,觉得它“知道”很多东西。确实,模型在训练阶段吸收了海量知识,但这些知识是压缩在参数里的,是一种模糊的、统计性的记忆。当你在一次对话中问它问题,它并不会主动去“回忆”你之前说过什么,它只能看到当前这次请求里你传给它的所有文本。

这就好比你去咨询一个顾问,但这个顾问每次见你之前都会失忆,你必须在他进门之前把所有相关的背景资料放在他桌上。你放什么,他就看什么;你没放的,他就完全不知道。上下文工程就是决定“桌上放什么资料”的工作。

这个特性带来两个直接后果。第一,你不能指望模型记住上一轮对话的内容,除非你把上一轮的内容也放进当前请求里。第二,上下文窗口是有限的,你不能把所有信息都塞进去,必须做取舍。这两个约束,构成了上下文工程的基本矛盾:信息要足够全,才能让模型做出正确判断;但信息又不能太多,否则会超出窗口限制,或者让关键信息被稀释。

2.2 上下文窗口不是越大越好,信息过载会降低效果

现在很多模型都支持很长的上下文窗口,动辄几十万甚至上百万token。这让很多人产生了一种错觉:既然窗口这么大,那我就把所有能找到的相关文档都塞进去,总不会漏掉关键信息吧。

实测下来,这种做法往往适得其反。我做过一组对比测试,同一个问题,分别给模型提供3条精准信息和30条包含噪音的信息。结果提供3条精准信息时,回答准确率在90%以上;提供30条信息时,准确率反而降到了60%左右。模型被大量无关信息干扰,抓不住重点,甚至会把不同文档里的矛盾信息混在一起,给出一个四不像的答案。

这个现象在学术上被称为“迷失在中间”效应。研究发现,当上下文很长时,模型对开头和结尾部分的信息关注度较高,对中间部分的信息容易忽略。也就是说,你塞进去的大量信息,真正被模型有效利用的可能只是头尾那部分。中间那些你以为很重要的内容,模型可能根本没“看进去”。

所以上下文工程的核心不是“塞更多”,而是“塞更准”。你要像一个编辑一样,从海量素材中挑选出最相关的那几条,用最清晰的方式呈现给模型。

2.3 上下文质量直接决定输出质量的上限

我经常跟团队里的人说一句话:模型的输出质量,不会超过上下文的质量上限。你给模型一堆乱七八糟、自相矛盾、格式混乱的信息,却指望它输出一个逻辑清晰、准确无误的答案,这本身就不现实。

举个具体的例子。假设你要让模型根据一份合同条款回答用户的问题。如果你直接把整份合同PDF转成文本丢给模型,里面夹杂着页眉页脚、表格错位、条款编号混乱,模型很可能找错条款。但如果你先把合同拆解成结构化的条款列表,每条包含条款编号、条款标题、条款正文、适用范围,然后再根据用户问题筛选出最相关的3条,模型回答的准确率会大幅提升。

同样的模型,同样的合同内容,仅仅因为上下文的组织方式不同,效果差距可能达到一倍以上。这就是上下文工程的价值所在。它不改变模型本身的能力,但它决定了模型能力能被发挥出多少。

3. 上下文工程的核心构成要素

3.1 系统指令:给模型定好角色和边界

系统指令是上下文中优先级最高的部分,它决定了模型以什么身份、什么风格、什么约束来回答问题。很多人写系统指令很随意,就写一句“你是一个有用的助手”,这其实浪费了系统指令的价值。

一个好的系统指令应该包含几个要素。第一是角色定义,明确模型是谁,比如“你是一个电商平台的售后客服专员”。第二是能力边界,说明模型能做什么、不能做什么,比如“你只能依据提供的订单信息和退换货政策回答问题,不能自行编造政策”。第三是输出格式要求,比如“回答控制在100字以内,先给结论再给依据”。第四是异常处理方式,比如“如果提供的信息不足以回答问题,直接告知用户需要转人工,不要猜测”。

我自己的习惯是把系统指令写成一个结构化的模板,每次调用时只替换其中的变量部分。这样既能保证指令的稳定性,又能灵活适配不同场景。系统指令写得好,后面用户问题和参考信息的组织压力就会小很多,因为模型已经知道该怎么“演”了。

3.2 对话历史:不是越长越好,要学会做减法

多轮对话中,把全部历史记录都传给模型是最简单的做法,但往往不是最优的。历史记录里包含大量寒暄、重复、无关内容,这些都会占用宝贵的上下文空间,稀释真正重要的信息。

我的做法是对对话历史做分层处理。最近3到5轮对话保留完整内容,因为这部分和当前问题关联最紧密。更早的对话只保留关键信息摘要,比如用户之前提到过的订单号、产品型号、问题类型,把这些抽取出来以结构化形式放在上下文里。再早的对话如果没有特殊需要,直接丢弃。

具体怎么判断哪些历史该保留、哪些该丢弃?我通常看两个维度。一是时间近度,越近的对话越重要。二是信息密度,包含具体事实(订单号、日期、金额)的对话比寒暄更重要。你可以写一个简单的规则引擎来做这件事,也可以用一个小模型来对历史对话做摘要和关键信息抽取。实测下来,把20轮对话压缩成“最近3轮完整+历史关键信息摘要”的形式,效果比直接传20轮完整对话要好,而且token消耗能降低60%以上。

3.3 知识片段:精准检索比全量投喂更有效

在知识问答场景中,从知识库检索相关片段是上下文工程的关键环节。这里最容易犯的错误是检索策略太粗糙,比如只用关键词匹配,或者只取相似度最高的那一条。

我的经验是采用“多路召回+重排序”的策略。先用多种方式召回候选片段,比如向量相似度召回、关键词召回、基于元数据的过滤召回。然后对召回结果做重排序,综合考虑相似度分数、信息新鲜度、来源权威性等因素。最后只取排名最高的3到5条放入上下文。

每条知识片段在放入上下文时,要加上清晰的标记。比如用方括号标注来源,用换行分隔不同片段,用加粗标出关键句。这样模型能快速识别每条信息的边界和重点。我试过不加任何标记直接把5条片段拼接在一起,模型经常把不同片段的内容混在一起,加了标记之后这种情况明显减少。

3.4 用户输入:原始问题也需要预处理

用户输入往往是不完整的、口语化的、甚至带有错别字的。直接把原始输入传给模型,模型需要花额外精力去理解用户到底在问什么。更好的做法是在传入之前做一轮预处理。

预处理包括几个动作。第一是意图识别,判断用户是在咨询、投诉、还是闲聊。第二是实体抽取,把用户提到的订单号、产品名、日期等关键实体提取出来。第三是查询改写,把口语化的问题改写成更规范的查询语句,用于后续的知识检索。第四是补充默认值,比如用户没提订单号,但系统能从会话上下文中推断出当前订单,就自动补上。

这些预处理步骤看起来增加了系统复杂度,但它们能显著提升最终的回答质量。因为模型拿到的是一个清晰、完整、规范的问题,而不是一个需要它去猜的模糊输入。

4. 实操:搭建一个上下文工程流水线

4.1 整体架构设计

我以智能客服场景为例,讲一下完整的上下文工程流水线怎么搭。整个流程分为五个阶段:输入接收、信息检索、上下文组装、模型调用、输出后处理。

输入接收阶段负责拿到用户原始问题,并做初步的意图识别和实体抽取。信息检索阶段根据用户问题去查询订单系统、知识库、历史对话记录,拿到候选信息。上下文组装阶段是核心,把系统指令、对话历史、知识片段、用户问题按照优先级和格式要求拼装成最终传给模型的上下文。模型调用阶段就是发请求拿结果。输出后处理阶段对模型输出做格式校验、敏感词过滤、必要时触发转人工。

这个架构看起来简单,但每个阶段都有很多细节要注意。下面我重点讲上下文组装阶段的具体做法,因为这是上下文工程最核心的部分。

4.2 上下文组装的具体步骤

第一步是确定上下文的整体结构。我通常采用“系统指令→对话历史摘要→相关知识片段→当前用户问题”的顺序。这个顺序是有讲究的。系统指令放最前面,因为它是全局约束。对话历史摘要紧随其后,让模型了解当前对话的背景。知识片段放在用户问题之前,这样模型在读到问题时,相关背景信息已经在“脑子里”了。用户问题放最后,作为触发回答的直接输入。

第二步是给每部分加上明确的标记。我用的是类似XML标签的方式,比如<system>...</system>、<history>...</history>、<knowledge>...</knowledge>、<question>...</question>。这些标签能帮助模型清晰地区分不同来源的信息,避免混淆。实测下来,加标签比不加标签,模型回答的准确率能提升10%到15%。

第三步是控制每部分的长度。系统指令控制在200字以内,对话历史摘要控制在300字以内,每条知识片段控制在200字以内,总共不超过5条。这样整个上下文能控制在2000字左右,远低于大多数模型的窗口限制,同时保证信息密度足够高。

第四步是做最终的格式检查。检查有没有标签未闭合、有没有特殊字符、有没有重复内容。这一步看起来琐碎,但能避免很多低级错误。

4.3 参数选择与效果对比

在上下文工程中,有几个关键参数需要根据场景调整。我整理了一个对比表格,方便你参考。

参数保守设置激进设置我的推荐理由
知识片段数量1-2条8-10条3-5条太少信息不足,太多噪音干扰
对话历史轮数1轮全部最近3轮+摘要平衡信息完整性和token消耗
单条片段长度50字500字150-250字太短信息不完整,太长重点不突出
系统指令长度50字500字150-250字太短约束不足,太长浪费空间
上下文总长度500字8000字1500-2500字信息密度和窗口占用的平衡点

这些推荐值不是绝对的,需要根据你的具体场景做调整。比如如果你的知识片段本身就很短,那可以多放几条;如果你的系统指令需要很复杂的约束,那可以适当放宽长度限制。关键是要有意识地去测试不同参数下的效果,而不是拍脑袋决定。

我自己的做法是准备一组测试问题,然后针对每个参数做A/B测试。比如固定其他参数,只改变知识片段数量,看回答准确率怎么变化。通常你会发现一个拐点,超过这个点之后,增加信息量反而会降低效果。这个拐点就是你的最优参数。

5. 常见问题与排查技巧

5.1 模型忽略关键信息怎么办

这是最常见的问题。你明明把关键信息放进了上下文,但模型回答时就是不用。排查思路分几步。

先检查关键信息的位置。如果它被放在上下文的中间部分,很可能被模型忽略了。试着把它移到开头或结尾,看看效果有没有改善。我遇到过好几次,同样的信息,放在知识片段列表的第一条和第三条,效果完全不同。

再检查关键信息有没有被标记出来。如果它混在一大段文字里,没有任何加粗、换行、标签等视觉区分,模型很容易把它当成普通内容。试着用加粗或单独成段的方式突出它。

最后检查有没有矛盾信息干扰。如果上下文里同时存在两条相互矛盾的信息,模型可能会选择错误的那条,或者干脆两条都不用。这时候需要做信息去重和冲突检测,确保上下文内部是一致的。

5.2 上下文太长导致响应变慢

上下文长度直接影响模型的推理时间。如果你发现响应明显变慢,首先要做的是统计上下文的token数量。很多时候你以为只有1000字,实际因为格式标记、重复内容,可能已经超过3000字了。

优化的方向有几个。一是压缩对话历史,把早期对话做摘要。二是精简知识片段,只保留最相关的部分,去掉冗余描述。三是优化格式标记,用更短的标签,去掉不必要的换行和空格。四是考虑分级处理,先用小模型做一轮筛选,把最相关的信息挑出来再传给大模型。

我做过一个优化,把一个客服场景的上下文从平均3500token压缩到1800token,响应时间从3.2秒降到1.8秒,而回答准确率只下降了不到2个百分点。这个 trade-off 在很多场景下是值得的。

5.3 多轮对话中上下文丢失

多轮对话中,用户经常用代词指代之前提到过的东西,比如“它”、“那个”、“刚才说的”。如果上下文里没有保留足够的历史信息,模型就不知道用户在指什么。

解决这个问题的关键是在对话历史摘要中保留实体信息。具体做法是,每轮对话结束后,抽取其中的关键实体(产品名、订单号、日期、金额等),以结构化形式存储。下一轮组装上下文时,把这些实体信息放在对话历史部分,即使原始对话已经被压缩掉了,实体信息仍然保留。

另外,在用户问题预处理阶段,可以做一轮指代消解。把用户问题中的“它”替换成具体的实体名称,这样即使上下文里没有完整历史,模型也能理解用户在问什么。

5.4 常见问题速查表

问题现象可能原因排查动作解决方案
模型答非所问上下文缺少关键信息检查知识片段是否覆盖问题补充检索或调整检索策略
模型忽略关键信息信息位置不佳或被淹没检查信息在上下文中的位置移到开头或结尾,加标记突出
模型编造信息上下文信息不足或矛盾检查上下文是否自洽补充信息,去除矛盾内容
响应速度慢上下文过长统计token数量压缩历史,精简片段
多轮对话指代不清历史信息丢失检查历史摘要是否保留实体增加实体抽取和指代消解
输出格式不稳定系统指令约束不足检查系统指令是否明确格式补充格式要求和示例

6. 我踩过的坑和总结的经验

6.1 不要试图一次性解决所有问题

刚开始做上下文工程时,我总想设计一个完美的方案,把所有可能的情况都考虑到。结果就是上下文组装逻辑越来越复杂,维护成本越来越高,效果却没有明显提升。

后来我转变了思路,先做一个最小可用的版本,只处理最常见的场景。上线之后收集bad case,针对性地优化。每次只解决一类问题,逐步迭代。这样做的效率反而更高,因为你能清楚地知道每次改动带来了什么效果。

6.2 测试集比调参更重要

很多人把大量时间花在调整上下文参数上,却忽略了测试集的建设。没有好的测试集,你根本不知道调参有没有效果。

我的做法是维护一个包含至少100个问题的测试集,覆盖各种典型场景和边界情况。每次改动上下文工程逻辑后,跑一遍测试集,看准确率、响应时间、token消耗的变化。只有数据变好了,才认为改动是有效的。这个习惯让我避免了很多“感觉变好了但实际变差了”的情况。

6.3 上下文工程不是一劳永逸的

业务在变,用户在变,知识库在变,上下文工程策略也需要持续调整。我见过一些团队,上线初期效果很好,过几个月就慢慢变差了,原因就是没有持续维护上下文工程逻辑。

我的建议是建立一个定期review机制,比如每两周检查一次bad case,每月做一次全面的效果评估。同时监控关键指标,比如回答准确率、用户满意度、平均响应时间,一旦发现指标下降,及时排查原因。

6.4 一个实用的小技巧:给模型“划重点”

在组装上下文时,我会在关键信息前面加上“重点:”这样的前缀。比如“重点:该订单已超过7天无理由退货期”。这个简单的动作,能让模型对关键信息的关注度明显提升。实测下来,加了“重点:”标记的信息,被模型正确使用的概率能提高20%以上。

这个技巧的原理很简单,就是利用模型在训练时学到的语言模式。“重点:”这样的标记在训练数据中通常出现在需要特别关注的内容前面,模型已经学会了这种模式。你利用这个模式,就相当于给模型了一个明确的信号:这条信息很重要,别忽略。

上下文工程这件事,说到底就是站在模型的角度思考问题。模型没有记忆,没有常识推理能力,它只能基于你给它的文本做概率预测。你给它的文本越清晰、越相关、越结构化,它的输出就越准确、越可靠。这个道理听起来简单,但真正做到位,需要在每一个细节上反复打磨。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询