☰
上下文工程实战:从检索到拼装,让大模型精准回答
2026/10/13 21:02:40 网站建设 项目流程

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

1.1 从一个真实翻车现场说起

去年我帮一个团队调一个智能客服系统,模型用的是当时第一梯队的大语言模型,接口参数调得漂漂亮亮,提示词也反复打磨了十几版。测试环境里回答得滴水不漏,一上生产就出洋相:用户问“我上周买的那双鞋能退吗”,模型一本正经地回答了退货政策,却完全不知道这个用户上周到底买没买鞋、买的是哪双、订单状态是什么。问题不在模型笨,而在于我们喂给它的东西里,压根没有“这个用户上周的订单信息”。

这就是上下文工程要解决的核心矛盾:模型本身的能力是固定的,但你能让它“看到”什么是可以设计的。很多人把精力全砸在提示词措辞上,却忽略了更根本的一件事——模型在生成回答的那一刻,它的“视野”里到底有哪些信息。上下文工程,说白了就是一门“在正确的时间,把正确的信息,以正确的格式,放进模型视野”的手艺。

1.2 上下文不等于提示词

这里必须先厘清一个常见误解。很多人把“上下文工程”和“提示词工程”当成一回事,其实两者是包含关系,而且上下文工程的范畴要大得多。

提示词工程关注的是“怎么说”——措辞、语气、指令结构、少样本示例的排列。上下文工程关注的是“给什么”——哪些信息该进、哪些该出、以什么顺序进、占多少空间、什么时候动态替换。打个比方,提示词工程像是你写一封求职信的文笔,上下文工程则是你决定随信附上哪几份证明材料、按什么顺序装订、哪些无关的证书干脆不放。

我见过太多团队在提示词上抠字眼,却让整个对话历史无脑全量塞进去,结果模型被几十轮无关闲聊淹没,关键信息反而被稀释。这就是典型的“提示词很精致,上下文很粗糙”。

1.3 为什么现在特别值得聊这件事

三个现实原因叠加,让上下文工程从“锦上添花”变成了“生死攸关”。

第一,上下文窗口变大了,但没大到可以浪费。现在主流模型动辄支持几十万甚至上百万token的上下文,听起来很爽,但实测下来,塞得越满,模型对中间部分信息的注意力越弱,这就是业内常说的“迷失在中间”现象。窗口大不等于可以乱塞,反而更需要精挑细选。

第二,检索增强生成成了标配,但检索质量参差不齐。很多系统接了个向量库就以为万事大吉,结果召回一堆语义相似但实际无关的片段,模型被带偏。检索只是手段,把检索结果工程化地组织进上下文才是目的。

第三,成本敏感。token是要花钱的,上下文越长,延迟越高、费用越贵。一个设计良好的上下文,可能只用三分之一的信息量就达到更好的效果,这在规模化场景下是实打实的成本差异。

1.4 这篇文章适合谁看

如果你正在做智能问答、知识库助手、代码辅助、文档分析这类应用,或者你只是想让日常用模型处理复杂任务时效果更稳,那这篇内容都对你有用。我不打算讲空泛的理论,而是把我在实际项目里踩过的坑、总结的取舍逻辑、能直接抄的配置方法都摊开讲。基础概念我会用生活化的例子解释,有经验的读者可以直接跳到实操章节。

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

2.1 一个完整上下文里通常有哪些块

把模型每次收到的内容拆开看,大致可以分成这么几层,从外到内、从稳定到动态:

层级内容类型变化频率典型作用
系统层角色设定、行为边界、输出格式要求极低定基调,约束模型行为
指令层当前任务的具体要求中告诉模型这次要干什么
知识层检索到的文档、数据库记录、工具返回结果高提供事实依据
记忆层历史对话摘要、用户画像、长期偏好中维持连贯性
输入层用户当前这句话极高触发本次生成

这个分层不是学术定义,而是我在工程实践中总结的“心智模型”。它的价值在于:每一层的更新策略、压缩策略、优先级都不一样。系统层可能一个月才改一次,知识层每次请求都要重新检索,记忆层需要定期做摘要压缩。如果你不把它们分开管理,就会陷入“一锅乱炖”的困境。

2.2 设计上下文的第一原则:相关性优先于完整性

新手最容易犯的错,是追求“信息越全越好”。我早期也这样,觉得多给点总没坏处。实测下来完全相反:无关信息是负资产。

举个具体例子。做一个法律咨询助手,用户问“租房押金不退怎么办”。如果你把整部相关法律条文全塞进去,模型确实“什么都知道”,但它生成回答时会平均用力,把大量无关条款也带出来,回答又臭又长,重点反而不突出。正确做法是只召回与“押金”“租赁合同解除”“违约责任”强相关的几个条款,模型反而能给出精准、可操作的建议。

判断相关性的实操标准很简单:如果这段信息删掉,模型的回答会不会变差?如果不会,那它就是冗余的,应该拿掉。这个“删除测试”是我做上下文裁剪时最常用的判断方法。

2.3 第二原则:结构清晰优于堆砌

模型对信息的组织方式很敏感。同样三条信息,平铺直叙地堆在一起,和用清晰的结构标记出来,效果差别很大。

我做过一个对比测试,让模型根据几段产品信息回答用户问题。A组把信息直接拼接,B组用明确的标签分隔,比如“【产品参数】……【用户评价】……【售后政策】……”。结果B组的回答准确率明显更高,而且模型更少出现张冠李戴的情况。原因不难理解:结构标记相当于给模型画了地图,告诉它每块内容是什么性质,它就不容易混淆。

所以我在组织上下文时,习惯给每一块内容加上轻量的“信封”——用简洁的标记说明这块是什么、来源是什么、时间是什么。这些标记本身也占token,但带来的准确性提升完全值得。

2.4 第三原则:动态组装优于静态模板

很多团队的做法是写一个巨大的提示词模板,把能想到的内容都占好位置,每次请求填空。这种方式在简单场景能用,一旦信息源变多就崩了。

更合理的做法是把上下文当成一个动态组装的流水线:先判断这次任务需要哪些类型的信息,再去对应的数据源取,取回来做相关性过滤和压缩,最后按优先级排序拼装。这个过程每次请求都可能不一样。比如用户只是打招呼,那知识层和记忆层可能都不需要触发;用户问具体业务问题,才去检索。

这种“按需组装”的思路,既省token又提准确率。实现上通常需要一个调度逻辑,判断当前意图,决定激活哪些信息通道。这部分我在第4章会展开讲具体实现。

3. 核心环节拆解:从检索到拼装的全流程

3.1 信息召回:怎么把“该看的”捞出来

召回是上下文工程的第一道关口,捞错了后面全白搭。常见的召回手段有三类,各有适用场景。

向量语义检索适合处理“意思相近但用词不同”的情况。比如用户问“怎么退钱”,文档里写的是“退款流程”,字面不匹配但语义一致,向量检索能捞出来。它的短板是对精确匹配不敏感,比如订单号、产品型号这类,向量检索经常抓瞎。

关键词检索(比如基于倒排索引的方案)擅长精确匹配,订单号、专有名词、代码符号这类它最靠谱。但它对同义表达无能为力。

结构化查询针对数据库、表格类信息,直接按字段条件查,准确率最高,但前提是信息本身是结构化的。

我的实战经验是:混合召回,然后重排。先用向量和关键词各召回一批,合并去重,再用一个重排模型按相关性打分排序,取前若干条。这套组合拳下来,召回质量比单用任何一种都稳。重排这一步很多人省掉,觉得麻烦,但它恰恰是过滤噪声的关键,我强烈建议不要省。

3.2 信息压缩:窗口有限,怎么塞得下

召回回来的内容往往很长,直接塞会爆窗口。压缩是必须的,但压缩方式有讲究。

最粗暴的是截断,取前N个字符。这种方式简单但危险,关键信息可能正好在被截掉的后半段。我一般只在信息结构均匀、前后重要性一致时才用。

更聪明的是抽取式压缩,从长文档里挑出与问题最相关的句子或段落。实现上可以用句子级的相关性打分,保留高分句。这种方式能保住关键信息,但可能破坏上下文连贯性,读起来跳跃。

还有生成式压缩,让模型先把长文档总结成短摘要再喂给主模型。这种方式压缩率高,但引入了额外的一次模型调用,有信息损失风险,而且增加延迟。我通常只在文档特别长、且对细节精度要求不高的场景用。

实际操作中,我倾向于分层压缩:先按段落做粗筛,把明显无关的段落整段丢掉;剩下的段落内部再做句子级筛选。这样既保住了结构,又去掉了冗余。

3.3 信息排序:位置决定注意力

前面提过“迷失在中间”现象,这意味着信息放在上下文的什么位置,直接影响模型对它的重视程度。

实测规律大致是:开头和结尾的信息,模型注意力最强;中间部分最容易被忽略。基于这个规律,我的排序策略是:

  • 最关键的指令和约束放开头
  • 最重要的知识证据放结尾(紧挨着用户问题)
  • 次要的、辅助性的信息放中间
  • 用户当前问题永远放最后

这个顺序不是拍脑袋,而是大量测试后总结的。你可以自己做个对照实验:把同一条关键信息分别放在开头、中间、结尾,看模型回答的准确率差异,大概率能复现这个规律。

3.4 格式选择:让模型好读,而不是让人好读

一个反直觉的点:给模型看的上下文,格式标准和人阅读的舒适度不完全一致。

人喜欢排版精美、有图表有颜色的文档。模型对纯文本的结构标记反而更敏感。我测试下来,简洁的标记符(比如用方括号、井号、短横线做分隔)比复杂的Markdown表格更稳,因为表格在token化时容易产生歧义,模型偶尔会读错行列对应关系。

另外,重复关键信息有时是有效的。比如一个重要的约束条件,在系统层说一遍,在用户问题附近再强调一遍,模型遵守的概率会提升。这看起来冗余,但在关键约束上值得。当然,别滥用,否则上下文会变得啰嗦。

提示:格式设计没有万能模板,最好的办法是拿你的真实数据做A/B测试。同一个问题,换两种上下文格式各跑几十次,对比准确率和稳定性,用数据说话比凭感觉靠谱得多。

4. 实操:搭一套可复用的上下文组装流水线

4.1 整体架构长什么样

我把自己项目里用的上下文组装逻辑抽象成一个通用流程,大致分五步:

  1. 意图识别:判断用户这句话属于哪类任务,决定需要激活哪些信息源
  2. 并行召回:从向量库、关键词索引、数据库、会话记忆里同时取信息
  3. 重排过滤:对召回结果打分排序,砍掉低分项
  4. 压缩组装:按预算压缩每块内容,按优先级拼装成最终上下文
  5. 预算校验:检查总token是否超限,超了就按优先级继续砍

这套流程的核心思想是把上下文当成一个有预算的资源来管理。每次请求都有一个token预算,各个信息块按重要性竞争这个预算,重要的多占,次要的少占或不占。

4.2 意图识别怎么做才不鸡肋

意图识别不需要搞得很复杂。早期我用过专门的分类模型,后来发现对于大多数场景,直接用主模型做一次轻量判断就够了,甚至可以在同一次调用里让模型自己决定要不要检索。

一个实用的做法是:在系统提示里明确告诉模型“你有以下工具可用”,然后让模型在需要时主动发起检索请求。这种方式叫“工具调用”或“函数调用”,现在主流模型都支持。它的好处是省掉了单独的意图识别环节,模型自己判断需不需要外部信息。

但要注意,不能让模型完全自主决定。我遇到过模型该检索时不检索、凭记忆瞎编的情况。所以我会加一层兜底规则:涉及具体事实、数字、时效性信息的问题,强制触发检索,不给模型偷懒的机会。

4.3 召回参数怎么定

召回环节有几个关键参数,定不好直接影响效果。

召回数量:向量检索一般取top 10到top 20,关键词检索取top 10,合并后可能有三四十条。这个数量不是越多越好,因为后面还要重排。我通常让召回阶段宽松一点,重排阶段严格一点,这样既不漏掉潜在相关项,又能保证最终质量。

相似度阈值:向量检索会返回相似度分数,设一个下限能过滤掉明显不相关的。但这个阈值因模型而异,不能照搬别人的数值。我的做法是拿一批标注好的问题-文档对,跑一遍看分数分布,取一个能平衡召回率和准确率的点。通常0.7到0.8是个常见区间,但一定要自己验证。

重排保留数量:重排后一般保留3到8条进入最终上下文。具体几条取决于单条信息的长度和总预算。如果每条都很长,就少留几条;如果每条很短,可以多留几条。

4.4 组装顺序的代码化表达

下面这段伪代码展示了我常用的组装顺序逻辑,用Python风格写出来方便理解:

def build_context(user_query, intent, budget=8000): context_blocks = [] # 1. 系统层:角色和约束,永远放最前 context_blocks.append(system_prompt) # 2. 记忆层:会话摘要,帮助维持连贯 if intent.needs_memory: context_blocks.append(compress_memory(session_summary)) # 3. 知识层:检索结果,按相关性排序 if intent.needs_retrieval: docs = retrieve(user_query) # 混合召回 docs = rerank(docs, user_query) # 重排 docs = docs[:intent.top_k] # 取前k条 for doc in docs: context_blocks.append(format_doc(doc)) # 4. 输入层:用户问题放最后 context_blocks.append(user_query) # 5. 预算校验与裁剪 return enforce_budget(context_blocks, budget)

这段逻辑里,enforce_budget是关键。它的策略是:先算总token,如果超了,从优先级最低的块开始砍。优先级排序通常是:系统层 > 用户问题 > 知识层 > 记忆层。也就是说,宁可牺牲历史记忆,也要保住当前问题的相关证据。

4.5 一个具体的参数计算示例

假设你的模型上下文窗口是128K token,但你不应该用满。我的经验是实际使用不超过窗口的60%到70%,留出余量给模型生成回答,也避免注意力稀释。

按70%算,可用预算约90K token。再往下分配:

  • 系统层:约500 token(固定)
  • 记忆层:约2000 token(会话摘要)
  • 知识层:约80000 token(大头)
  • 用户问题:约200 token
  • 生成预留:约7000 token

知识层这80K token,如果单条文档平均2000 token,理论上能放40条。但前面说了,放太多会稀释注意力,所以我实际只会放5到10条高质量文档,剩下的预算宁可空着。空着比塞满好,这是很多人想不通但实测有效的一点。

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

5.1 模型答非所问,怎么定位是上下文的问题

排查这类问题,我有一套固定的检查顺序。

第一步,看上下文里到底有没有答案所需的信息。把实际发给模型的完整上下文打印出来,人工读一遍。如果信息压根不在里面,那就是召回环节的问题,跟模型无关。这一步能排除掉一大半“模型背锅”的情况。

第二步,如果信息在,看它放在什么位置。如果关键信息被埋在一大堆无关内容中间,很可能被模型忽略了。试着把它挪到开头或结尾,再测一次。

第三步,看信息格式是否清晰。如果多块信息混在一起没有标记,模型可能张冠李戴。加上明确的分隔标记再测。

第四步,看是否有冲突信息。有时候召回回来的多条文档互相矛盾,模型不知道该信哪个,就会给出模棱两可的回答。这时候需要在重排阶段做去重和冲突检测。

这套排查流程走下来,绝大多数“模型不行”的抱怨都能找到真正的原因。

5.2 召回总是不准,有哪些提升手段

召回不准是最常见也最头疼的问题。我整理了几个见效比较快的手段,按投入产出比排序:

手段投入效果适用场景
加混合召回(向量+关键词)低明显几乎所有场景
加重排模型中明显召回量大时
优化文档切分粒度中明显长文档场景
补充同义词/别名表低中等有领域术语时
微调嵌入模型高明显领域差异大时
加查询改写中中等用户表达口语化时

其中文档切分粒度最容易被忽视。很多人把文档按固定字数切,结果一句话被切成两半,语义断裂。更好的做法是按语义边界切,比如按段落、按小节,保证每块内容自洽。切分粒度太大,召回不精准;太小,上下文碎片化。我一般控制在200到500字一块,具体看文档类型。

查询改写也值得一试。用户问“那个东西怎么弄”,直接拿去检索肯定不行。可以先让模型把口语化问题改写成规范的检索查询,再拿去召回,效果提升明显。

5.3 上下文太长导致变慢变贵,怎么优化

优化方向有三个:减量、缓存、并行。

减量就是前面说的压缩和裁剪,把不必要的token砍掉。这是最直接的省钱手段。

缓存针对的是那些每次请求都重复的稳定内容,比如系统提示、固定的知识库片段。很多模型服务商支持上下文缓存,重复部分可以打折甚至免费。把稳定内容放前面、动态内容放后面,能最大化缓存命中率。

并行指的是召回环节。如果要从多个数据源取信息,串行执行会很慢,改成并行能显著降低延迟。这个在工程上不难,但很多人一开始没意识到。

5.4 几个我踩过的坑

坑一:把历史对话全量塞进去。早期我图省事,把整个对话历史原样拼进上下文。结果聊到二十轮以后,模型开始答非所问,因为早期无关内容占了大量空间。后来改成滚动摘要,只保留最近几轮原文加更早内容的摘要,效果立刻好转。

坑二:检索结果不去重。混合召回时,向量和关键词经常召回同一条文档,如果不去重,同一条信息在上下文里出现两三次,模型会误以为它特别重要,过度强调。加一步去重很简单,但效果明显。

坑三:忽视时间信息。知识库里的文档有新旧之分,如果不标注时间,模型可能拿过时的信息回答时效性问题。我现在强制给每条召回结果打上时间戳,并在提示里说明“优先参考较新的信息”。

坑四:格式标记用得太花哨。有段时间我用复杂的嵌套标记,觉得结构清晰,结果模型经常解析错误。后来简化成最朴素的方括号加短横线,稳定性反而上去了。给模型看的格式,简单就是美。

注意:上下文工程没有一劳永逸的配置。业务在变、数据在变、模型也在迭代,今天调好的参数过几个月可能就不适用了。建议把关键参数做成可配置项,定期用真实数据回归测试,别把参数写死在代码里。

6. 进阶思路:让上下文自己进化

6.1 从静态配置到动态学习

前面讲的都是人工设计的规则。更进一步的做法,是让系统根据反馈自动优化上下文策略。比如记录每次请求的上下文组成和最终回答质量,用这些数据训练一个策略模型,预测“对于这类问题,什么样的上下文组合效果最好”。

这个方向门槛较高,需要足够的反馈数据积累。但即使不做完整的策略学习,简单的A/B测试机制也值得搭。把不同的上下文配置做成实验组,用真实用户反馈或人工评分做对比,逐步淘汰效果差的配置。这种“小步快跑”的优化方式,比一次性设计完美方案更现实。

6.2 记忆的长期管理

会话记忆不只是“记住之前说了什么”,更包括用户偏好的长期积累。一个设计良好的记忆系统,应该能区分短期上下文(本次对话)和长期画像(这个用户一贯的偏好、习惯、历史问题)。

长期记忆的管理难点在于写入和读取的策略。不是什么信息都值得记,记太多会污染后续上下文。我的做法是只记录那些“跨会话仍然有效”的信息,比如用户明确表达的偏好、反复出现的问题类型。读取时按当前任务相关性筛选,不是无脑全取。

6.3 多模态上下文的组织

当上下文里开始出现图片、表格、代码等非纯文本内容时,组织方式又不一样了。图片需要配文字说明,表格需要转成模型好理解的格式,代码需要标注语言和用途。

我目前的做法是给每种模态的内容加统一的描述性信封,比如图片用“【图片:内容描述】”包裹,表格转成简洁的键值对或列表形式。核心原则不变:让模型一眼能看出这块是什么、跟当前问题什么关系。

6.4 评估体系怎么建

没有评估,优化就是盲人摸象。上下文工程的评估,我建议至少覆盖三个维度:

  • 准确性:回答是否基于上下文中的正确信息
  • 完整性:该用到的信息是否都用上了
  • 简洁性:是否引入了无关信息导致回答冗长

评估方法上,小规模可以用人工评分,大规模需要自动化。自动化评估可以构造一批“问题-标准答案-应召回文档”的测试集,检查系统是否召回了正确文档、回答是否与标准答案一致。这套测试集要持续维护,随着业务变化更新。

我在实际项目里的体会是,上下文工程的投入产出比往往比调模型本身更高。同一个模型,上下文组织得好,效果能上一个台阶;组织得差,再强的模型也救不回来。这件事没有太多玄学,更多是细致的工程活——把信息管好、排好、压缩好,模型自然就能“看到该看的东西”。最后分享一个小技巧:每次上线新策略前,先拿历史失败案例跑一遍,看新策略能不能修复那些老问题,这比随机测试更能暴露真实短板。

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

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

立即咨询