ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

从提示工程到上下文工程:多模态场景下性能优化实践

从提示工程到上下文工程:多模态场景下性能优化实践

这几年代码侧的东西写了不少,但真正让我觉得“提示词工程”这个岗位开始变质的,其实是从多模态模型大面积铺开开始的。以前调文本模型,核心是措辞、格式、角色设定,那套东西更像“语文题”。但到了多模态时代,输入变成了图片、音频、视频、扫描件、图表混在一起,问题一下就变了:你不再只是“把话问清楚”,而是要设计一套完整的上下文进入模型的方式。换句话说,提示工程正在快速演变成上下文工程。

我这么说是有根据的。我自己维护的几套生产级Agent系统,从纯文本切到图文混合之后,最明显的感受就是不是模型变笨了,而是我的输入方式配不上模型的真实能力。同样的模型、同样的任务,很多人跑来问我为什么效果忽好忽坏,我去看他们的调用日志,发现根本原因几乎都出在上下文组织上:要么图片被压得太狠,要么关键文本被淹没在无关内容里,要么示例排列顺序把模型的注意力带偏了。这篇文章我打算把这大半年踩过、填过、验证过的东西一次性倒出来,围绕上下文工程这个核心,聊聊多模态场景下的性能优化到底该怎么做。

1. 从提示工程到上下文工程:为什么多模态时代需要换一套打法

先说结论:上下文工程不是提示工程换个名字,而是提示工程在输入维度暴增之后的必然升级。纯文本时代,上下文就是一段文字,你组织好开头、中间、结尾,控制好长度和语气,基本就完事了。多模态时代,模型要同时处理图像令牌、音频令牌、文本令牌,不同模态之间还有对齐关系,比如一张截图里某个位置的文字和一个问题里的指代,这种信息密度远超纯文本。

如果你还用老办法,把所有内容统统塞到Prompt里,很快会遇到三个现实问题。第一是上下文窗口被无意义内容占满,比如你给模型塞了一张4K截图,但实际上关键信息只占图里5%的区域,剩下95%全是背景和装饰元素,模型的理解能力被大量无关视觉令牌稀释。第二是模态之间的顺序会影响推理质量,模型在理解一张复杂图表时,如果你先把一堆背景介绍文本丢进去,再给图,它倾向于从文本预期去“脑补”图像内容;反过来先给图再给问题,效果往往更稳。第三是成本失控,多模态令牌的计费比文本贵得多,一张普通图片换算成视觉令牌,可能相当于几千个文本令牌,如果你不加约束地传原图,一次调用的成本开销可能达到纯文本方案的十倍以上。

所以上下文工程的核心目标,我总结下来就一句话:在有限的窗口预算内,把最有价值的多模态信息,用最合理的顺序和结构,送进模型的处理流程。它不是让你背更多咒语,而是让你学会做信息流的导演。你要决定谁先出场、谁待剪辑、谁必须特写、谁直接掐掉。

这个思路适配的人群很广:正在做文档解析应用的开发者,做多模态客服机器人的产品经理,研究视觉语言模型效果的研究员,甚至是用本地多模态模型做自动化脚本的个人玩家,都能从上下文编排里拿到实实在在的收益。我自己是从一套文档问答系统开始切入的,当时正被“模型总答非所问”折腾得够呛,后来把上下文组织方式调了一遍,同样的模型和同样的知识库,准确率直接肉眼可见地上涨,这也是我写这篇东西的初衷。

2. 先看懂上下文窗口:多模态场景下的容量估算与分配

很多人拿到一个长上下文模型就以为万事大吉,实际上上下文窗口是典型的“看着大、用着紧”。以目前常见的多模态模型为例,几万到十几万令牌的窗口听起来不小,但你要知道一张普通分辨率图片进去,视觉编码器会把图片切块映射成视觉令牌,一张图少说几百令牌,复杂一点的图直接上千。你要是对话里带五张图,再配上一段问答历史,窗口余额瞬间见底。

我自己在实际项目里养成了一个习惯,先做上下文预算表再动手写提示词。所谓预算表很简单:你先估一下任务里各部分的令牌消耗,然后按优先级分配。文本指令比如系统提示、任务说明、输出格式,这部分是核心,必须保证;被分析的主材料比如图片、文档截图,按需分配;参考示例按重要性选择性给,旧对话记录能压缩就压缩,能用摘要就用摘要,能丢就直接丢。

我举个例子,一个典型的合同审查任务,对话窗口是32K。我拿到一份三页合同扫描件,外加一段用户提问。如果直接传三张高清扫描图,每张可能吃掉2K令牌,三张就是6K,再加历史对话,窗口余量看起来还够,但模型精细阅读时的效果开始下降。后来我把扫描件按条款区域重新切块,只保留和用户问题相关的几块区域,图片令牌量直接压到1K以内,模型反而读得更准。这个案例说明一个道理:上下文分配永远优先做“减法”,而不是堆“加法”。

这里有几个具体的分配原则是我经过多轮测试总结出来的。系统级指令占整个上下文的5%到10%之间比较合适,再长就是浪费,因为模型对长指令的遵循度并不会线性上升;用户的核心材料占50%到70%,这部分的令牌要给足但不能盲目给,要给到“能看清关键细节又不冗余”的程度;示例和参考占10%到20%,只选和当前任务强相关的;历史对话压缩后控制在10%到20%,这个比例再高就会反噬当前任务。

另外还要提醒一点,上下文窗口不是模型真正的“工作记忆”。即使模型声称支持长上下文,它对放在窗口中间位置的信息记忆和理解效果仍然显著弱于开头和结尾。我做过一个小实验:把一段关键指令分别放在窗口的头部、中部、尾部,中部位置的执行准确率比头尾低了将近两成。这就是所谓“迷失在中间”的现象,多模态场景里同样存在,图片放在中间被忽略的概率也不低。所以重要信息要么放开头,要么放结尾,别让它在中间漂流。

3. 多模态上下文的关键编排:文本、图像、语音怎么进怎么出

3.1 图像输入的预处理与压缩

图像是多模态上下文里最重的一块,也是最容易被忽视的一块。很多人觉得模型既然能看图,直接把图丢进去就行,这是最大的误解。模型的视觉编码器对图像的感知方式和人类不一样,它对小文字、密集表格、低对比度区域的处理能力是有限度的,而预处理能极大改善模型的理解上限。

我处理图像上下文的流程基本固定:先判断图片类型,再决定走哪条路。如果是纯文字截图,比如聊天记录、网页长图,我会先做OCR文字提取,把文字内容结构化后再以文本形式传给模型。这里有个经验:很多多模态模型对OCR文字的理解能力远高于直接看图,因为视觉编码器在读密集小字时经常会漏字或错位。文字提取之后,原图就可以不传了,既省令牌又提准确率。

如果是表格或图表类图片,我的做法是提取表格结构转成Markdown或CSV,图表则尽量让模型配合数据描述一起理解。此处有一个容易踩的坑,有些模型在处理纯Markdown表格时反而丢失空间关系,遇到复杂表头合并的表格,我会把图片和Markdown同时给,让模型交叉验证,不能只依赖一种形式。

如果是自然场景图,比如产品照片、人像、街景,这类图没法用文字完全替代视觉,就只能压缩尺寸和裁剪关键区域。我的经验是,宽边控制在768到1024像素之间,超过这个尺寸会浪费视觉令牌但不会带来明显的理解增益。裁剪就更有讲究了,把主体区域切出来,去掉背景杂物,相当于给模型一个视觉特写镜头。这个操作往往比任何文字提示都管用。

我还测试过不同的图像输入顺序,结论是先给图再给文本指令,比先给一大段文字再配图要稳定。原因可能是先入图能激活视觉模态,再给任务指令时模型带着视觉上下文去理解语言要求,对齐更自然;反过来先给大段文字,模型的注意力容易被语言预期锚定,看图时反而带着偏见。

3.2 时间序列信息(音频与视频)的处理

音频和视频比静态图片更复杂,因为它有时间维度。大多数多模态模型对音频的处理方式是把音频切片转成声学特征令牌,视频则是抽帧加音频对齐。这里最容易犯的错误是把整个过程交给模型自由理解,你传一段长视频进去让它总结,它往往被长上下文拖垮,最后给你一份语义空洞的总结。

我在处理视频类任务时的常规思路是先做分段理解再汇总。比如一段两小时的会议录像,我会先提取语音转文字,按发言人切分,把关键片段摘出来;再把重要的画面帧抽出来,每30秒到1分钟抽一帧,标注时间点;最后把这些材料组织成“时间轴 + 文字总结 + 关键帧描述”的结构化上下文交给模型。这种先粗分再精读的方式能大幅降低视觉令牌压力,也能保留时间线索,模型回答时能引用到具体时间点。

音频单独处理时,转写几乎是必做步骤。直接用音频令牌给模型做翻译或总结,时效性和成本都不划算。但这里有个细节:转写稿丢失了语气、重音和停顿信息,而这些对情感分析任务是有用的。遇到这类需求,我会把转写稿和音频的原始特征一起送进去,让模型结合语气信息做判断。实测下来,这类任务的准确率比只用文字稿高不少。

3.3 文本上下文的优先级分层

多模态场景里的文本上下文不能一视同仁,必须做优先级分层。我把文本信息分成三层:核心层、辅助层、噪音层。核心层是直接决定任务成败的信息,比如问题描述、任务指令、关键参考段落,必须完整体现,不能压缩。辅助层是背景信息,比如项目介绍、历史聊天摘要,可以压缩成要点。噪音层是那些“感觉有用但实际不参与推理”的内容,比如礼貌性的寒暄、过时的旧思考过程、冗长的原始文档,能删就删。

操作时我会把噪音层在送入模型前直接过滤掉。有个典型例子是文档问答场景里的“思考链残留污染”:之前版本的系统会把模型的推理过程存进历史记录,下一次问答时又作为上下文传回去,结果模型读到这些推理残片,频繁出现重复和幻觉。后来我把历史记录中的推理过程全部剥离,只保留结论性文本,问题立刻消失。这说明层级过滤不只是省成本,直接关系到输出质量。

还有一种特殊情况要注意:某些任务的上下文不是静态的,而是层级展开的。比如处理一份大文档,你有整体目录,有章节摘要,也有具体段落细节。聪明的组织方式是先给模型目录和摘要让它定位,再根据用户问题动态拉取相关细节,而不是把全文一次塞进去。这就是所谓的“分层上下文策略”,在多模态场景下尤其有用,因为大文档通常还配着插图、表格,全量铺开的成本高到离谱。

4. 少样本示例的取舍:多少合适,怎么排列,放哪里

提示工程里的少样本学习在多模态时代依然有效,但玩法变了。纯文本时代,你给几个问答对做示例,模型就能学格式和风格。多模态时代,示例本身可能就包含图片,那你就要面临一个残酷的取舍:多一个图片示例,多几百上千令牌;示例太少,模型学不会格式;示例太多,上下文直接被吃掉一大块。

我的经验是,示例数量控制在1到3个之间,最多别超过5个。超过这个数,性能增益就开始递减,成本却线性上升。而且示例的质量比数量重要得多,一个和当前任务紧密相关的示例,比五个相关度不高的示例效果都强。我见过有人为了“丰富”给模型塞了十个示例,结果模型反而被示例里那些不相关内容带偏,输出变得四不像。

示例排列顺序也很有讲究。模型对上下文开头和结尾的内容更敏感,所以最重要的示例应该放最前面,用来锚定任务类型;如果你想强调某种格式,把格式最标准的示例放最后,模型收尾时更容易受到它的影响。中间放辅助示例。这个方法我做过对照实验,同样的示例集合,调换顺序后输出准确率能差出十个百分点。

另一个容易翻车的点是示例的内部一致性。示例里如果包含了用户问题模板,你实际提问时就必须严格复用这个模板,连标点都别改。比如示例里问题写的是“请分析这张图片中的产品缺陷”,你实际输入时写“帮我看看图里有什么问题”,模型就有可能模仿示例中的旧格式输出,不仅没帮你完成任务,还白白浪费了上下文预算。这个坑我还看见不少团队踩过,排查半天找不到原因,最后发现是问题模板不一致。

多模态示例的图片选择也要讲究,别拿高清大图做示例,一是浪费令牌,二是示例图片分辨率过高时,模型会把注意力放到无关细节上。示例图片也做预处理,裁剪、压尺寸、标注重点区域,和对待正式输入一个标准,这样才能让模型学到正确的关注模式。我自己做示例时甚至会故意在示例里标注“重点关注区域框”,让模型学会遇到类似图片时优先看那些区域。

5. 基于检索的上下文构建:把整库知识装进有限的窗口

多模态场景里最让我头疼的其实不是单次输入的组织,而是怎么从庞杂的知识库里挑出该进上下文的内容。早期我做RAG时用的是纯文本向量检索,把文档切成块,做embedding,按相似度召回,这套路在文本场景下跑得不错。但一旦文档里混入了图片、表格、流程图,纯文本块召回就开始拉垮了。

问题在于,文本块切分时会丢失视觉信息的位置关系。一张架构图对应的说明文字,和图上画的实际内容,在切块后被拆到了不同的块里,向量检索召回文字块时,模型看不到对应的图,信息就缺了一半。后来我改成了“多模态块”的存储策略:解析文档时先把视觉元素和文本段落绑定在一起,一个图、一张表,配上它的标题和解释文字,作为一个独立的块存储并编码。这样召回时返回的不再是孤零零的文本,而是图文绑定的整体,模型拿到后能看文也能看图,效果提升非常明显。

检索结果进入上下文时的顺序,也值得动脑子。我一般会把命中分数最高、和问题最相关的块放在正文前面,但紧接着会放一到两个相关性略低但信息互补的块。这个“主块在前、互补块在后”的排列,比单纯按分数排序效果更稳。因为模型在开头形成的上下文锚定,往往决定了后续理解的方向,你把它最需要的答案先给它,它回答时就更不容易跑题。

还有一类特殊的检索,是针对图像的反向检索,也就是用户提问时给出了参考图,你需要从库里找出相似的图来做对比。这种场景纯靠文本描述匹配是没法做的,得走图像特征向量。我在做某个质检系统时遇到过这个需求,当时是把产品图片做特征提取,建立图像向量索引,用户上传缺陷图时先做向量召回,把相似的历史缺陷图都拉出来,组装成一组对比上下文。这个方案跑通后,质检员肉眼比对的工作量降了一大半,效果比单纯让模型看单张图要好得多。

上下文长度动态调整方面,我一般会设一个软阈值,比如总上下文用到窗口的70%时触发压缩策略。压缩策略不是简单截断,而是把检索回来的块做二次筛选:移除重复信息、合并相似段落、把长列表转成摘要。这一步我通常会搭一个小工具链,先跑一遍文本相似度去重,再用摘要模型把冗长材料压成要点。整个过程跑下来,上下文能从接近满载降到40%左右,模型理解效果反而回升。

6. 对话连续性与状态管理:多轮场景下的上下文组织策略

多模态模型一旦进入多轮对话,上下文管理就上升到了状态机的高度。很多人单轮测试时效果一流,一到多轮就开始崩,原因在于对话历史里的多模态信息在日积月累地堆积。每一轮用户发来的图片、模型给出的带图回答,都会成为下一轮的历史上下文。如果你不管理,三五轮之后,上下文窗口就满了。

我的做法是给对话维护一个“动态上下文包”,而不是把所有历史都原样保留。每一轮结束后,系统会做一次总结,把这一轮的关键信息提炼成文字摘要,其中包含图片怎么办?我会把图片的指纹、核心描述、识别出的关键要素存下来,但不保留原图。下一轮开始时,动态上下文包主要由三块组成:系统指令、压缩后的历史摘要、当前轮的新输入。只有当前轮涉及对之前某张图的追问时,我才会把那张原始图重新拉进上下文。

这套策略在客服机器人场景下效果很好。用户发一张产品故障图,客服先回答初步判断;用户追问“那这个位置能不能修”,系统把上一轮的故障图重新挂载,再配上当前提问。模型的回答就能准确引用图片细节,而不是凭空猜测。如果系统没有重新挂载机制,模型面对“这个位置”这种指代,往往只能模糊应答,用户体验很差。

对话轮数多了以后,还有一个问题是指代消解。用户说“刚才那张图”、“你上一条说的问题”,这些指代需要系统在进入模型前就解析好。我自己设计了一个轻量的引用解析模块:每一轮的消息都带上自增ID,图片也带独立的资源ID,用户在提问时如果引用了资源,系统就自动把对应资源ID的内容附加到当前上下文里。这套机制比让模型自己去历史里翻找要可靠得多,因为它不消耗额外的推理令牌,也不会翻到错误的位置。

多轮场景还有一个容易忽略的细节:模型的输出格式也会影响下一轮上下文质量。如果你在输出格式里让模型每轮都输出“总结本轮关键信息”,那这些总结会在下一轮作为结构化历史进入上下文,模型就能基于自己做过的总结延续回答,稳定性大幅提升。我在实践里确实发现,加了这行格式要求之后,日常多轮对话的连贯性好了不少。不夸张地讲,这一句话的改革是全篇文章里见效最快的一个手段,强烈建议大家先试。

7. 常见问题与排查思路:多模态上下文翻车现场实录

这一节我整理一下过去大半年调试过程中反复出现的高频问题,每条都是我或者身边同事实际踩过坑之后才总结出来的,建议收藏当排查手册用。

第一个高频问题是“模型忽略图片细节,只根据文字作答”。这种现象通常是因为图片被压缩得太狠,视觉令牌信息量不足;或者是图片放在上下文的中间位置,被“迷失在中间”效应影响;还有一种可能是图片与文字的关联不够强,模型没意识到图片和任务相关。排查路径是:先检查传入图片的分辨率是否达标,再检查图片在上下文中的位置,最后检查文字指令里是否明确指代图片中的具体元素。别用“请分析这张图”这种模糊指令,换成“请分析图片右上角的红色区域标注的内容”,模型就更容易聚焦。

第二个高频问题是“图片内容识别正确但输出格式混乱”。比如让模型从截图里抽表格数据,它识别对了,但输出一会儿是Markdown,一会儿是JSON,格式不稳定。解决办法是给强格式约束,最好给一个精确的输出模板示例。这里要特别注意:格式示例和真实任务不要靠太近,把示例放在系统指令区域,用明确的“输出格式规范”包裹起来,别让模型把示例内容当成真实数据。

第三个高频问题是“明明已经给了足够信息,模型还是回答得泛泛而谈”。这种多半是上下文里噪音太多,把关键信息淹没了。比如你传了一张产品图,又传了整页官网介绍文字,模型被多余文字干扰,无法聚焦图里的缺陷。我处理这个问题时通常会做信息密度审计:模拟一遍模型视角,数一数上下文里哪些内容对答案有直接贡献,哪些只是背景。凡是背景,要么移到最后作为“可忽略参考”,要么直接删掉。

还有一类典型问题属于多模态特有的“模态冲突”:文字描述和图片内容矛盾时,模型倾向于采信文字。这在自动化标注场景是致命的。比如某张图纸上标注的文字尺寸和图形比例明显不匹配,模型会按文字输出,导致分析出错。应对方法是建立“图文验证指令”,明确要求模型在回答时对比图文信息,遇到不一致时指出来。不过这条在大模型上的表现还不够稳定,更稳妥的办法是在预处理阶段就做一致性校验,发现问题提前拦截。

最后一个常见大坑是“上下文工作区没有边界”。比如系统提示里塞了语气要求、安全规范、示例、历史遗留内容,用户当前的问题反而后面才出现,模型前向注意力已经被占满,响应自然不如预期。这里建议给上下文设定明确的任务边界,把系统指令、资料区、历史区、当前任务区用分隔标记显式隔开。多模态模型对结构化边界的感知能力通常优于无结构文本,你把这些边界写得越清晰,模型的调用效果就越稳定。

8. 上下文工程工具箱:适合多模态场景的落地配置建议

文章最后这部分,我按自己当前项目还在用的配置思路,整理一套可落地的上下文工程基线。这套配置不是万能答案,但至少能让你在拿到一个多模态任务时有一个合格的起点,而不是从零开始乱试。

第一层是输入预处理层。所有图片进入系统前,先做统一规格化:最长边压到1024像素,格式统一成高兼容性格式,大文档自动分块,OCR文本提取结果和原图并行保存。这一层做得越扎实,后面的上下文组织就越省力。

第二层是上下文组装层。设计统一的组装模板,用分隔标记区分系统指令、资料上下文、示例、问题区域。系统指令控制在稳定输出格式和任务目标层面,资料上下文按“图文块”的方式组织,示例只保留最相关的1至3组,问题区域放在最后并明确指代需要关注的视觉元素。

第三层是动态管理机制。每轮对话结束后触发上下文整理,将历史摘要写入持久存储,视觉资源按需挂载而非全程驻留。上下文临近上限时,按“重复内容优先压缩、细节描述优先降级、图像资源优先移除”的顺序做内存回收。

第四层是效果观测层。记录每次调用的输入令牌构成和输出质量结果,定期复盘哪一类任务在什么上下文结构下表现最好,形成你自己的调优经验库。我会建议每次调用都打印一张令牌构成条,看一眼就能发现哪块在吞噬预算,这对持续优化非常有帮助。

这套基线在多模态文档问答、质检报告生成、会议纪总结、图文对齐校验等场景下我都有实际落地。以文档问答为例,基线上线之后,单次调用的平均令牌消耗下降了大概四成,而端到端任务准确率反而提升了一到两成。这就是上下文工程的价值:它不是花更多钱买更多算力,而是把已有的模型能力通过信息结构设计压榨出来。

最后分享一个我自己的使用心得。做多模态上下文优化,最忌讳的是把模型当成黑盒去反复碰运气,你会发现一会儿好一会儿坏,根本找不到规律。我后来建立了一套“故障复现 + 令牌日志 + 结构对比”的调试闭环,每次效果异常都把这一轮的上下文完整结构记录下来,和正常样本做对比,很快就能定位到问题在哪一段。上下文工程这个东西,本质上就是带着显微镜去看信息流的过程,你越能看到细节,模型给你的反馈就越稳定。希望这篇内容能帮你少走一些弯路,多省一些预算。

RELATED READING

延伸阅读

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