news 2026/10/20 12:18:16

上下文工程实战:从五层结构到文档答疑助手落地

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
上下文工程实战:从五层结构到文档答疑助手落地

说实话,我第一次折腾大模型的时候,有个特别大的误区:以为只要会写提示词,就能让模型乖乖干活。后来实际做了几个项目才发现,真正决定效果上限的,根本不是那句提示词写得多花哨,而是你给模型搭了一个什么样的上下文环境。这个“环境”,就是今天想聊的上下文工程。

上下文工程,简单说是一门“给大模型布置作业环境”的技能。你喂给模型什么信息、按什么顺序喂、喂多少、历史对话怎么管、最后怎么约束输出,这些事加在一起,就构成了上下文工程的全部内容。相比提示工程研究“怎么把一句话问好”,上下文工程研究的是“怎么组织一整段对话和一堆参考资料”,前者解决局部问题,后者解决系统问题。

这篇文章我会尽量按照一个实操者的视角,把我踩过的坑和验证过的方法写出来。哪怕你连完整的项目都没带过,只要能理解“模型看到的东西决定了它说出的话”这个核心逻辑,就能跟着把这套思路落地。如果你正在做智能问答助手、文档总结、AI客服这类应用,或者只是单纯想让自己的提示词更稳定、更可控,这篇内容应该能帮你省掉不少试错的时间。

1. 上下文工程到底是什么,它和“写提示词”有什么本质区别

1.1 提示工程是“一句话的学问”,上下文工程是“整场对话的学问”

很多人会把上下文工程和提示工程混为一谈,我可以给你一个很直白的类比:提示词是菜谱,上下文工程是厨房。

菜谱写得再漂亮,也只决定了“这一道菜怎么做”。但厨房里有没有食材、食材新不新鲜、灶台火力稳不稳、锅碗瓢盆放哪了,这些因素叠加起来,才真正决定一道菜能不能稳定出品。提示词写得好,相当于你手里拿了一份不错的菜谱;但你要是没管理好“厨房环境”——比如塞了一堆过期食材、把菜谱和操作手册混在一起、灶台忽冷忽热——那再好的菜谱也白搭。

放到大模型场景里,这种“环境感”会更加明显。我用同一个模型做过对比实验:同样一句“帮我总结这份文档”,直接把文档原文丢进对话里,和先把文档分好段落、用检索挑出最相关的几个片段、再配合一条系统指令一起送进去,两者的输出质量差距可以用“天差地别”来形容。前者经常答非所问或者丢掉关键信息,后者基本都在点上。

所以,如果你已经在提示词上花了不少功夫,却仍然觉得“模型时好时坏、飘忽不定”,问题多半不出在提示词,而出在你对上下文的组织方式上。上下文工程要处理的,从来不是“一句话该怎么说”,而是“一整段输入应该如何被设计”。

1.2 大模型为什么对上下文这么敏感

要理解上下文工程为什么这么重要,得先搞清楚一个底层逻辑:大模型本质上是“看一段文字,然后接着往下写”的预测机器。它对下一个字的预测,完全建立在它“看到”的那段内容之上。也就是说,你给它什么材料,它就在什么材料的基础上继续生成,上下文的内容和结构直接决定了输出的方向和下限。

这就是为什么同一个问题,配上不同上下文会得到完全不同的答案。甚至同样一批信息,只因为排列顺序变了,模型处理重点就会偏移。我刚开始没意识到这点,总以为“信息放了就行,模型自己会找重点”。后来发现完全不是这样,模型确实能“找重点”,但它找出来的“重点”往往和我想让它关注的重点不一样。这就像考试时给你一堆参考资料,虽然每本资料上都有答案,但你如果没给参考资料的目录和顺序,你的注意力很容易被第一页的无关内容带走。

另一个关键变量是所谓“上下文窗口”,也就是模型能处理的最大输入长度。长窗口现在能轻松做到几万 token 甚至更高,看起来好像很够用,但实际上窗口只是“能装下”,不等于“能用好”。当大量文字一起涌进来时,模型的注意力会被稀释,开头和结尾的信息往往记得更牢,中间一大段就变成了“模糊区”。这就是上下文工程存在的最底层原因:信息不是摆上去就完事,你要帮模型把注意力引到该放的位置上。

1.3 上下文工程真正解决的三个问题

做了几个月带着上下文工程思路的实战项目之后,我总结了它真正能解决的三个问题,这三个问题也恰好是普通用户最多抱怨的“AI 不靠谱”来源。

第一个是稳定性问题。同样的任务,上午跑得好好的,下午换一批输入就崩了;或者用户换了个问法,模型就理解不了了。这不是模型变笨了,而是你的上下文里没有足够的“约束锚点”帮模型稳住行为。上下文工程通过系统指令、示例、格式约束这些手段,把模型的输出空间从“自由发挥”收敛到“圈定范围内”,稳定度会明显提升。

第二个是信息过载问题。我见过大量团队做文档问答,第一版方案就是把整本手册塞进提示词里,结果模型要么摘抄废话、要么忽略关键信息。信息过载带来的不是“更多答案”,而是“更糊的答案”。上下文工程要求你克制:只给模型当前任务真正需要的信息,并且把信息按重要程度排好序,必要时用检索手段提前过滤掉无关内容。

第三个是成本问题。token 是按输入输出计费的,上下文越长,每次请求的推理成本就越高。一个每天调用几万次的服务,上下文控制得好不好,长期算下来是一笔非常可观的差距。别觉得这是公司才需要考虑的事,个人开发者也一样,你给模型塞了几千 token 的废话,就等于在替这些废话付费。

2. 把上下文拆开看:五层结构是设计的底层逻辑

实践一段时间之后,我习惯把上下文拆成五个层次来看。这个分层未必是什么标准,但用它可以帮你快速定位“问题出在哪一层”,而不是整个上下文一团浆糊地瞎调。

2.1 第一层:系统层,全局任务定义的锚点

系统层,通俗说就是放在最前面的那段“全局设定”。它告诉模型你是谁、你要做什么、你面对的用户是谁、你用什么语气说话、你绝对不能做什么。你可以把它理解成员工入职时拿到的那份岗位说明书,它不负责具体某个任务怎么做,但它规定了整个工作的边界和基调。

我写系统层提示词时有个很重要的心得:别写“你要友好”“你要专业”这种又空又虚的形容词,而要写可以被检验的行为边界。比如“不要编造文档里不存在的数据”“如果问题超出文档范围,直接回复不知道,并提供人工联系方式”,这些指令模型才能执行得了。“友好”这种词模型听了等于没听,因为它不知道友好长什么样,但“不要编造数据”就是一条清晰的边界。

另一个容易被忽视的细节是系统层的“位置权重”。大模型对上下文开头的内容记忆权重很高,所以系统层的优先级天然比塞在中间的一大段参考资料高。这意味着你可以放心地把最重要的规则放在最前面,它会被模型优先“看到”。反过来,如果你把关键规则埋在长文档中间,大概率会被模型忽略,这不是模型故意偷懒,而是注意力机制天然如此。

2.2 第二层:示例层,少样本演示的力量与边际

示例层,学术点的叫法是少样本示例。就是你在上下文里给模型几个“输入-输出”的配对,让模型照着做。与其描述一百遍“你要用简洁的语言回答”,不如直接给模型看一个“简洁回答长什么样”,效果立刻不一样。

但示例不是越多越好,我自己有很深的体会。一开始我总怕模型理解不了,一口气给了七八个示例,结果效果反而变差。后来我意识到,示例过少模型容易自由发挥,示例过多模型会被带偏,会把示例里的噪音和巧合也学走。这中间的平衡点,我在实际项目里通常控制在“两到四个高质量示例”,并且示例之间要有差异化,尽量覆盖不同的输入场景,而不是重复同一个套路。

还有一点要注意,示例的选择本身就是一种偏见注入。你选的示例如果在某些偏好上是倾斜的,模型就会放大这种倾斜。比如你给客服机器人选示例时,如果所有示例都来自“用户抱怨”场景,那模型面对普通咨询时也可能带着防御性说辞。所以选示例的时候,尽量让示例覆盖真实用户分布,而不是你顺手挑几个看着顺眼的。

2.3 第三层:知识层,外部信息的注入方式

知识层是上下文工程里最“硬核”的一层,它解决的是“模型预训练知识里没有的东西怎么办”。现实中要做文档问答、私有知识库、实时信息分析这类应用,模型原本的知识一定是不够用的,必须把外部资料注入到上下文里。

注入方式分两类:一类是简单粗暴的直接塞原文,适合文档很短、内容很集中的场景;另一类是先检索再注入,也就是把大文档切成小块,根据用户问题挑出最相关的几块,拼成一个临时上下文再交给模型。第二种方式需要一些检索模块配合,但对结果质量的提升是直接可见的。

知识层最容易犯的错误,是“宁多勿少”。我见过很多人怕漏掉信息,恨不得把十页资料全贴进去。结果模型接收了一堆相关度不高的文字,真正重要的几个段落被淹没在噪音里。正确的思路是反过来:先想清楚“用户这个问题的答案,可能在哪些文档的哪个段落里”,然后把检索到的那些段落按“从最相关到次相关”的顺序排列,再用清晰的边界符号隔开。

2.4 第四层:记忆层,对话历史和状态的管理

记忆层处理的是多轮对话场景。用户不会只问一句,他会追问、会修改问题、会因为一个话题岔开去问另一个话题。如果你把整段对话历史全部塞进上下文,很快就会把这个话题的“记忆”和上一个话题的“记忆”混在一起,模型分不清你现在在讨论哪件事。

做过客服机器人的朋友一定有这种体验:用户前面聊了半天售后投诉,中间插了一句“顺便问下你们营业时间”,如果你把整个投诉过程都喂给模型,它很可能会一本正经地用投诉的语气回答营业时间问题。这就是记忆层没有管理好。我的做法是给对话历史做“剪枝”:保留对当前问题有影响的关键轮次,把无关的旧话题压缩成一句话摘要,而不是逐字保留。

记忆管理的另一个重点是“状态覆盖”。当用户明确表示要切换话题时,你要主动重置相关记忆状态,让模型知道“现在进入新任务了”。这个动作我一般会通过系统层的一段话来完成,比如“如果用户表达转向新任务,旧任务信息仅保留背景摘要,不得主导回答方向”。很多“模型抽风”的问题,其实就是新旧状态互相干扰的结果。

2.5 第五层:输出层,格式与约束的兜底

输出层是很多人最容易忽略、但我认为是性价比最高的一层。它的作用不是让模型答得更好,而是让模型答成“你想要的形状”。比如你要模型返回 JSON 格式的数据,直接在上下文里写清楚“严格按照以下 JSON Schema 输出,不要输出任何解释性文字”,模型就很少会再给你夹带废话。

为什么输出约束要单独成层?因为它在工程上的价值很大。下游程序解析模型输出时,如果模型偶尔多了一行注释、少了一个右括号,整个流程就会崩。输出层相当于给模型加了一个“交付格式”的护栏,让上游的“天马行空”收敛成下游可用的结构化数据。

但输出约束也不是越强越好。约束写得过于死板,模型的生成自由度被压死,会导致它在内容实质性上偷懒,比如“总结一下”变成“嗯嗯好的”。我的经验是,格式约束只管格式,内容要求还是要单独写清楚。比如你既要 JSON 又要高质量总结,那就在系统层写内容标准,在输出层写格式标准,两层各管各的,不要混在同一句话里。

3. 完整实操:从零搭一个文档答疑助手的上下文方案

光拆结构不够,我拿一个具体项目从头到尾走一遍,这样你就能看到上下文工程是怎么一步步落到实处的。这个项目我虚构化了,就叫它“某项目文档答疑助手”,目标是根据项目内部手册回答用户的提问。

3.1 第一步:先拆需求,再谈上下文

做上下文工程最忌讳上来就写提示词。第一件事应该是拆需求:用户会问什么类型的问题?答案从哪来?答错了会有什么后果?

当时我们把这个“某项目答疑助手”的用户问题分成三类:第一类是文档里明确写了答案的事实型问题,比如“报销流程是什么”;第二类是需要跨多个章节整合出来的综合性问题,比如“新员工入职要办哪些手续”;第三类是文档里没有、需要转人工的边界型问题,比如“我能不能把报销金额提高一倍”。这个分类直接决定了后面的信息组织方式:事实型靠精准检索,综合型依赖上下文里的全局引导,边界型靠输出层的拒答规则。

这一步很多人会跳过,但它恰恰决定了上下文设计的骨架。不同问题类型对“信息密度”和“检索策略”的需求是完全不一样的,你没法用一套固定流程同时服务好三种类型。

3.2 第二步:搭“系统层”的骨架

需求拆完之后,我开始写系统的骨架。这版系统层提示词有三个核心段落:第一段写“你是谁”,明确模型身份是文档答疑助手,只依据提供的资料回答;第二段写“你怎么工作”,包括答案需要标明出处、不编造、用户问题不明确时可以反问;第三段写“你绝对不能做”,包括不回答与文档无关的内容、不给出超出文档范围的具体建议。

写系统层时我有个强烈的感受:指令要“可验证”,而不要“可感知”。“可感知”的指令比如“请专业一点”,模型不知道怎么量化执行;“可验证”的指令比如“回答时引用资料编号”,模型知道要用什么格式完成任务。我团队后来复盘时发现,凡是“虚词指令”占比高的系统提示词,上线后行为漂移得都很快,反倒是每条都能验证的指令稳定得多。

这个系统层设定好之后,基本就不用动了。它是整个上下文方案的“地基”,后续知识层、记忆层怎么变,系统层的边界都能把整个回答框在正确的范围内。

3.3 第三步:设计“知识层”的检索注入

知识层是这个项目里最花时间的部分。我们把手册切成了按章节结构组织的片段,每段尽量保持语义独立,然后借用常见的向量检索思路,根据用户的问题把最相关的几个片段捞出来,拼成临时上下文。

拼接的时候有个细节我特别想强调:给每个片段前面加一个元信息头,比如“【章节:报销流程】【片段编号:3-2】”。这个看起来是小事,但实测下来能显著提升引用的准确率。模型在回答时可以看到每个片段属于哪个章节,它就更倾向于把答案和正确的章节绑定在一起,出错的概率大幅下降。

在“给多少片段”这件事上,我们做过一组对照实验。给 3 个片段时,回答精准但偶尔信息不全;给 8 个片段时,信息全了但杂质也开始混进来;给 12 个以上时,模型开始把不相关片段里的细节当成答案输出。最终我们定在“5 到 6 个片段”这个区间,这是我们在精度和完整度之间的平衡点。不同项目最优值可能有差异,但你如果做类似任务,可以从这个数量起步再调整。

3.4 第四步:给记忆层做裁剪

多轮场景下,用户会连续追问。我们策略比较简单:只保留最近两轮完整对话,再往上更早的对话全部压缩成单独一行摘要,放在系统层和最新问题之间。比如“用户之前询问了报销上限,已得到回复”,一句话概括,后面的回答不会再去纠结旧话题。

这样做的原因是:完整对话历史占用的 token 太高,而且它会给模型注入大量“上下文噪音”。你以为把所有历史都给模型,它就能记住上下文,实际上它记住的可能是历史里最激动人心的部分,而不是最近的需求。

记忆裁剪这个操作几乎没有成本,但对长会话体验的改善是立竿见影的。我们上线后观察到一个现象:对话超过十轮之后,模型答非所问的概率反而比不裁剪时更低,因为模型永远不会被“十轮前的抱怨情绪”带跑。

3.5 第五步:用输出层约束格式

输出层我们做了两套方案。第一套是“给用户看”的自然语言回答,这部分的约束比较松,只要求结构清晰、先给结论再给依据。第二套是“给系统看”的日志记录,每次回答的同时输出一个隐藏结构化字段,包含“是否回答成功”“命中的片段编号”“是否转人工”,方便下游做质检。

后者的提示约束要严格得多。我们写了明确的输出示例,并且要求模型只能输出合法 JSON,不允许添加任何前缀后缀注释。测试中发现一个有意思的现象:如果我们不明确写“只输出 JSON”,模型偶尔会给 JSON 加一句“好的,以下是答案”,下游解析器就会因为这段白色文本而挂掉。这是很典型的“你以为模型知道,但它真的不知道”的场景。

输出层这块,我建议你一定要加“负面约束”,比如“不要输出与 JSON 格式无关的任何文字”。大模型对“不要做什么”指令的执行力,理论上不如“做什么”指令强,但在输出格式这种场景里,负面约束仍然能大幅降低异常率。

3.6 效果验证:我是怎么判断方案好坏的

任何上下文方案,都需要一套可量化的评价标准。我们的验证集是人工标注的 80 个典型问题,答案里包含“是否准确”“是否完整”“是否引用正确”三个评分项。每调整一次上下文方案,就用这 80 个问题重新跑一遍,对比整体通过率。

这里我想给个非常真诚的建议:不要靠“感觉变好了”来迭代上下文。你自己手工试五个问题就说“好像不错”,这种主观判断会在统计意义上骗你。因为你很容易只试那些“模型擅长的题”,而忽略了那些“模型会犯错的题”。

第三轮迭代时,我们把知识层的片段数量从 4 调到 6,准确率从最初的 62% 提升到 81%,综合完整度到 86%。再往后调系统层的指令措辞,只提升了 4 个百分点。这个数据告诉我们:上下文工程的收益上限,主要取决于信息组织,而不是语言措辞。别把时间花在反复打磨“用词”上,多花时间在“喂什么”上。

4. 常见问题与排查技巧实录

实操过程中踩过的坑,我整理成几个高频问题,每个都配上当时的排查思路和最终解决方案,你如果也遇到过类似情况,可以直接对照着看。

4.1 上下文太长,模型顾头不顾尾

这是最普遍的问题。用户问一个需要大量背景信息的问题,你把整本手册都塞进去了,模型却只盯着开头几段发挥,后面的关键信息全被忽略。

排查思路其实很简单:把上下文分段包裹上明显标记,然后让模型“只基于【片段3】回答”,如果它还答错,说明不是片段找错了,而是注意力分配出了问题。这时候你要压缩整体长度,或者把关键片段人工移动到更靠前或更靠后的位置。开头和结尾是模型注意力的“高权重区”,中间是“洼地”,重要的信息要么放最前,要么放最后,中间只放辅助信息。

4.2 多条信息互相冲突,模型开始“精神分裂”

这种情况常见于文档本身前后矛盾,或者检索到的多个片段存在版本差异。模型收到冲突信息后,会试图“融合”它们,结果给出一个两边都不沾的奇怪答案。

我当时的解法是:在系统层加了一条规则,“当不同片段信息冲突时,以片段编号更靠前的章节为准”,而且在知识层拼接时把“高优先级资料”明确标注出来。有些场景可能反过来要以“最新修订版”为准,但你只要定义了明确的冲突裁决规则,模型就能给出稳定结果,而不是和稀泥。

4.3 加了示例反而效果更差

有段时间我为了让模型输出“更规范”,给了它很多不同任务的示例。结果模型的内容规范了,但回答的信息量缩水了,变得特别“模板化”。这其实是示例层过载的表现。

排查后发现,我的示例全部集中在同一个任务形态上,模型学到的是“这类问题应该这么答”,但遇到稍微偏离这个形态的问题时,它会硬套模板。解决办法是精简示例数量,并增加示例间的形态差异。如果示例本身没问题,但你怀疑是示例“教坏”了模型,你可以先删掉所有示例,看看模型回答的基准表现,再逐个加回来,对比哪个示例真正在起作用。

4.4 换模型或换版本,效果漂移

大模型产品迭代很快,今天跑得好好的方案,明天换了个新版模型,效果可能就掉下去了。这种情况不是你的上下文方案坏了,而是不同模型对指令的遵循能力和语气理解不同。

排查方法是用你的验证集跑一遍新旧模型,然后逐个观察“哪种类型的问题掉分了”。如果掉分集中在“综合推理”,新模型可能需要更长的推理步骤提示;如果掉分集中在“格式遵守”,你可能需要重写输出层的描述。没有一个提示词模板可以通吃所有模型,模型变了,上下文方案必然要跟着微调,这是行业常识。

4.5 排查问题速查表

我把高频现象、可能原因、快速验证三列整理成一张表,你可以直接抄走。

高频现象可能原因快速验证方法
模型忽略上下文中间的关键信息注意力被大量低相关文本稀释单独把关键信息移到开头或结尾再试
回答内容与文档不一致系统层缺少“不编造”的硬边界在系统层追加“不得输出资料外内容”再测
回答格式不稳定,偶发多出注释输出层没写负面约束加入“只输出结构化内容,不输出任何解释”
多轮对话后跑偏旧记忆未裁剪,带偏当前话题把旧轮次压缩成摘要再测试
换了模型后效果大幅下降模型间指令遵循能力差异用固定验证集逐个对比,定位掉分题
提示词已经很详细但依然不稳定信息组织无序,缺少结构化边界按“系统层-知识层-输出层”重新排布

这张表不是什么金科玉律,但大部分上下文工程问题,最终都能落到这三件事上:信息有没有放对位置,边界有没有说清楚,记忆有没有管干净。你带着这个思路去排查,基本不会跑太偏。

最后再分享一个我自己的体会。上下文工程不像写代码,没有那种“写对了就必然跑通”的确定性,它更像是在调一盆土:太干了植物蔫,太湿了根会烂,你只能根据植株的反应一点点微调配比。但好消息是,这个“配比”是可以总结出规律的,五层结构就是我从无数次“蔫了又缓过来”里总结出的配方。你把这个框架装进脑子,再遇到模型表现诡异,先别急着骂模型蠢,去看看它的“土”是不是出了问题,答案往往就在里面。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/20 12:16:11

计算机网络第一章课后题解析:时延计算与分组交换考点全拆解

翻开《计算机网络:自顶向下方法》第一章的朋友,十有八九正在为课后习题抓耳挠腮。刚看完协议栈、时延公式、分组交换这些概念时觉得自己懂了,合上书面对课后题却不知道从哪下笔,尤其是夹杂着单位换算和存储转发细节的计算题&#…

作者头像 李华
网站建设 2026/10/20 12:14:05

Delphi XE10 Android开发:java2op实现JAR转PAS桥接指南

简介:Java2OP for XE10 是面向 Embarcadero Delphi 开发者的跨语言桥接工具,核心用途是把 Java 的 jar 包转换为 Delphi 可直接引用的 PAS 单元文件,让 Delphi 项目无需重写代码即可调用 Java 类库。它针对 XE10.2 版本做了兼容性优化&#x…

作者头像 李华
网站建设 2026/10/20 12:14:01

Nano Banana API实测:低成本AI图像生成方案与批量调用优化指南

最近AI图像生成这块动静不小,不少群里都在传一个叫Nano Banana API的服务。名字听着挺随意,但价格确实有点扎眼:官方同款品质,单张只要约0.10,不到官方价格的一半。我第一时间去试了试,也跟同行聊了一圈&am…

作者头像 李华
网站建设 2026/10/20 12:12:39

阵列信号处理前三章案例复现:波束形成与MUSIC避坑指南

简介:这份资源是鄢社锋《优化阵列信号处理》前三章关键案例的Matlab实现代码,面向正在学习阵列信号处理、波束形成与DOA估计的高年级本科生、研究生及工程技术人员。压缩包共27个文件,以26个m脚本和1个txt说明为主,整体约36KB&…

作者头像 李华
网站建设 2026/10/20 12:12:08

贝叶斯优化超参数调优:从高斯过程到Python实战

简介:这份资源面向机器学习与深度学习方向的学习者和开发者,聚焦用贝叶斯优化解决超参数调优耗时、试错成本高的问题。压缩包共4个文件,约10.96MB,包含2个Python脚本、1个CSV数据集和1个NPZ数据文件:脚本分别演示机器学…

作者头像 李华
网站建设 2026/10/20 12:11:51

EF Core MongoDB Provider 让部分子类独立成集合——中间基类拆分 TPH

用 MongoDB.EntityFrameworkCore Provider 时,一个继承层次默认会整棵落进同一个集合,靠 _t 鉴别器区分具体类型(类似关系库的 TPH)。但有时需求是混合的:一部分子类共用一个集合,另一个子类要单独成集合。直接给子类指定集合不生效,需要动类型层次本身。 问题现象 领…

作者头像 李华