前阵子一直在折腾AI Agent的落地项目,最大的感受是:语言模型越来越聪明,但一旦接上摄像头,很多看似简单的任务就会翻车。同一块区域,早上的逆光和晚上的暖光下检测结果能差一大截;画面里明明竖着三个调料瓶,模型却顺着文本先验回答“灶台左边是醋瓶”——它压根没“看”明白。所谓AI Agent的多模态场景理解与推理,不是把图像丢给大模型然后等一句生成结果那么简单,背后是感知、表示、推理三层问题的交织。这篇内容就把我在这类系统中做的方案、踩过的坑、调出来的经验一次说清楚,适合正在搞智能体落地、做多模态感知的工程师参考。
1. 先搞清楚一件事:Agent需要的不是“看到”而是“想明白”
1.1 单模态“识别”和Agent需要的“理解”差在哪
传统的视觉识别任务,比如图像分类、目标检测、OCR,输出的都是比较“薄”的结果:这张图是客厅、这里有个瓶子、这行字是什么。但Agent做决策时,需要的是“厚”的场景状态。
我做过一个任务:让Agent判断“能不能把水杯放在茶几上”。传统检测模型能告诉你茶几在哪、水杯在哪,但它回答不了“茶几表面有没有被其他东西占满”“水杯现在是否处于悬空状态”。这些信息藏在物体关系、平面区域、遮挡情况里,藏在场景的语义结构里。
很多刚上手多模态Agent的朋友都有个误区:以为目标检测跑得准就够了。结果模型识别率很高,任务完成率却一塌糊涂。后来我把两者的差异整理成一个对比,才意识到问题出在“信息密度”上。
| 信息维度 | 传统单模态识别 | Agent场景理解需要的 |
|---|---|---|
| 类别 | 给出“杯子”“茶几”标签 | 还要知道品牌是玻璃杯还是纸杯、是否透明 |
| 位置 | 给出2D检测框坐标 | 还要知道与其他物体的相对方位、是否被遮挡 |
| 状态 | 不关心 | 门是否打开、灯是否亮着、液体是否在流动 |
| 关系 | 不建模 | 支撑、悬挂、靠近、叠放等语义关系 |
| 空间 | 不知道 | 可行走区域、障碍物边界、深度层次 |
| 事件 | 不感知 | 是否有人经过、是否有物体正在移动 |
如果不把这一层想清楚,后续所有推理都是在“半盲”状态下做,Agent自然显得很蠢。
1.2 场景理解要覆盖的信息层次
我把场景理解拆成了五个层次,做方案设计时按这个层级逐层核对,缺一层都可能导致决策失效。
第一层是目标层,解决“场里有什么”。第二层是属性层,解决“这些东西什么状态、什么材质、什么形状”。第三层是关系层,解决“物体之间怎么连接、谁支撑谁、谁与谁相邻”。第四层是空间层,解决“物体分布在哪个平面区域、哪些区域可通行”。第五层是事件层,解决“当前正在发生什么动态变化”,比如有人刚起身离开座位,或者机械臂正在搬运货物。
一个相对完整的多模态场景理解系统,应当能从这五层中生成一个统一描述。只做第一层或者只做前四层,都只能在受限任务里工作。
1.3 多模态的“多”到底指什么,以及怎么融合
这里的多模态不仅指“图像加文字”,还包括深度图、点云、音频、红外、时序视频帧等。多模态不代表维度越多越好,而是看任务是否需要。判断“前方有没有行人”用普通可见光画面就够;判断“货架上的箱子是否超出边缘”就需要深度信息参与。
模态融合通常分三种时机。早期融合是在输入侧拼接多路原始信号,比如把深度图和彩色图拼成多通道输入;中期融合是在特征层面做对齐,比如用注意力机制让文本特征与视觉特征互相调制;晚期融合是在各模态分别得到结果之后再做统一决策。我做过一次对比实验:早期融合在光照变化大的场景里最不稳定,晚期融合最稳定但容易漏掉细微关系。真正可落地的方案,往往在三个时机都有所涉及,但主要发力点会放在中期和晚期融合上。
2. 端到端还是组合式:多模态场景理解的三条路线与取舍
2.1 三条技术路线的真实差异
现在做多模态场景理解,绕不开三条路线。
第一条是端到端多模态大模型。直接把图像、文本指令、甚至音频一起输入,模型内部完成从感知到推理的全部工作。好处是零样本能力强、开放词汇理解好、不需要为每个具体任务训练专用视觉模块。坏处也很明显:内部不可解释,出了错你根本不知道是哪个环节的锅;推理成本高,把高清视频帧全量喂进去,按token计费的账单会非常难看;幻觉问题目前还没有根治手段。
第二条是组合式感知加语言推理。视觉侧用专门的检测、分割、OCR、深度估计模块各司其职,输出结构化结果;语言侧由大模型基于这些结构化结果做推理决策。这是我目前落地项目里最常用的路线。
第三条是感知与推理双Agent协作。感知Agent和推理Agent分开部署,由调度器决定把任务交给谁。多了一个通信层和路由层,适合任务复杂、模块边界清晰的场景。
| 对比项 | 端到端多模态大模型 | 组合式感知+LLM推理 | 双Agent协作 |
|---|---|---|---|
| 可解释性 | 差,黑箱 | 好,每层可观测 | 较好,但通信层会增加复杂度 |
| 幻觉倾向 | 中等偏高 | 可控,感知层可兜底 | 取决于感知层的质量 |
| 开发成本 | 低,主要靠调Prompt | 高,需开发多个视觉模块 | 最高,还要设计路由逻辑 |
| 推理延迟 | 高,尤其视频输入 | 中,视觉模块可并行 | 最高,多一跳通信 |
| 扩展性 | 差,换任务常要换模型 | 好,按需替换子模块 | 好,但工程负担大 |
| 适用场景 | 快速验证、开放长尾 | 生产系统、强约束业务 | 大型复杂项目 |
2.2 实测下来,生产系统为什么优先组合式
我试过在第一版Demo里直接用端到端多模态模型来处理监控画面,结果陷入一个非常尴尬的循环:任务失败时,无法判断是画面没看清、还是推理跑偏,只能一次次调整提示词碰运气。后来切换到组合式架构,问题定位马上清晰了:感知层输出的场景状态不对就修感知,推理层规划错误就调推理。
组合式的第二个好处是成本可控。视觉模块跑在本地,只需要把结构化结果发给推理模型,Token消耗降了一个数量级。项目里的视频流是持续输入的,如果每一帧都发给端到端模型,成本根本压不住。
第三个好处是可靠。感知层可以用规则兜底,比如检测置信度低于阈值的区域直接标记为“未知”,不允许推理层把它猜测成某个物体。纯端到端模型做不到这种细粒度的约束。只有一种情况我会坚持用端到端:开放世界长尾概念太多,规则无法覆盖,或者任务本身就是跨模态语义检索,而不是严格的空间决策。
2.3 视觉模块与推理模型对接时的设计细节
组合式架构的成败,往往取决于“视觉输出怎么交给推理模型”这个接缝。我见过不少项目,视觉模块跑得不错,但把检测结果塞给大模型时,要么写成自然语言描述,要么把坐标、置信度、类别混在一个乱七八糟的文本串里,导致推理层越看越糊涂。
推荐的做法是定义一个标准化的场景状态结构。检测结果、OCR结果、深度估计结果、场景图三元组,全部转成统一的JSON片段,并明确坐标系的定义。我常用的格式是:所有坐标归一化到0到1之间,以图像左上角为原点,同时附带归一化坐标对应的空间关系描述。
{ "timestamp": 1023, "objects": [ { "id": "obj_01", "category": "cup", "confidence": 0.93, "bbox_norm": [0.42, 0.31, 0.48, 0.38], "attributes": {"transparent": false, "filled_liquid": true} }, { "id": "obj_02", "category": "table", "confidence": 0.97, "bbox_norm": [0.20, 0.50, 0.80, 0.75], "attributes": {"surface_area": "large"} } ], "ocr": [ {"text": "PUSH", "bbox_norm": [0.88, 0.10, 0.95, 0.15]} ], "relation_triplets": [ ["obj_01", "on", "obj_02"], ["obj_01", "left_of", "obj_03"] ] }这里有几个细节值得注意。坐标必须统一且要说明参考系,否则推理层拿到的坐标可能是像素值、可能是中心点、也可能是归一化后的左上角,混乱的坐标系会让后续所有空间推理失真。置信度必须保留,不能只输出类别,因为推理层要根据置信度决定“这是确定事实还是疑似信息”。关系三元组建议单独输出,不要嵌入自然语言描述里,否则Prompt解析成本会高得多。
3. 从感知到行动:把场景状态变成Agent可推理的结构化记忆
3.1 一个可执行的感知管线长什么样
先给你看我经常用的一套感知管线框架。核心思路是:并行执行多个感知单元,然后通过一个统一的后处理模块合并结果,最终生成上一节定义的场景状态结构。
def perceive(frame, task_context=None): with ThreadPoolExecutor(max_workers=4) as pool: det_future = pool.submit(detect_objects, frame) ocr_future = pool.submit(extract_text, frame) depth_future = pool.submit(estimate_depth, frame) motion_future = pool.submit(extract_motion, frame) detections = filter_by_conf(det_future.result(), conf_threshold=0.5) texts = normalize_ocr(ocr_future.result()) depth_map = depth_future.result() motion_regions = motion_future.result() scene_dict = merge_into_scene_state( detections, texts, depth_map, motion_regions, task_context ) return scene_dict这里每个“submit”的感知单元可以单独替换,比如深度估计从单目深度模型切到双目立体匹配,不需要动其他模块的代码。merge阶段是容易被忽视的地方,它才是真正完成“多模态对齐”的环节。检测框、OCR框、深度图各有各的坐标系,必须在merge阶段统一映射,并利用深度信息判断物体之间是否存在遮挡关系。
3.2 为什么用结构化状态而不是直接丢图像给推理层
很多人会问:现在多模态大模型都能直接看图,为什么还要费劲做结构化和透传?我来说几个亲测的原因。
第一是记忆。Agent做多轮任务时,需要记住“上一轮看到什么、做过什么动作”。图像本身不方便存、不方便检索,而结构化状态可以序列化成一条历史记录,放进短期记忆或长期记忆库里,随时取用。
第二是可控性。结构化状态天然是符号化的,方便写规则。比如可以设定:凡是置信度低于0.6的目标,进入推理层时必须附带“low_confidence”标记,且不允许作为“确定事实”参与规划。这种约束在纯图像输入里没办法直接表达。
第三是成本。视频流场景里,一秒钟可能来二三十帧。全量图像传一次可能消耗几千个Token,而结构化状态哪怕加上历史上下文,也只需要原来零头的预算。这直接影响能不能做持续运行的常驻Agent。
3.3 一个走完整流程的案例:模拟项目里的“寻找并确认桌上物品”
我在某家庭服务机器人的模拟项目里,设计过一个“寻找并确认桌上物品”的任务。整个闭环分成感知、推理、行动三阶段。
感知阶段,机器人到达餐桌附近后,先采集一帧俯视画面,并行跑检测、OCR、深度估计。检测模型找到水杯、纸巾盒、花瓶,OCR识别出桌角贴纸上的字,深度模块算出桌面平面区域。merge之后,场景状态里多了一条“水杯在纸巾盒右侧10厘米,均位于桌面平面A上”的关系描述。
推理阶段,任务目标是“确认桌上是否有需要回收的易碎物”。LLM拿到场景状态后,进行三步推理:先看属性列表里是否有“易碎”标记;再看位置关系,判断易碎物是否被其他物体压住;最后生成动作序列。如果花瓶被纸巾盒遮挡了部分视图,场景状态里会有一个“partially_occluded”的标签,推理层就会额外规划一个“绕到侧面再确认”的动作,而不是直接凭先验作出判断。
行动阶段,把LLM生成的动作序列解析成机械臂的具体运动指令,执行一步就回传一次新感知结果,形成闭环。这个案例给我最大的启发是:Agent的“聪明”不来自某个单一模型,而来自感知层是否把不确定性也传递上去了。遮挡信息、置信度信息、深度信息只要有一项缺失,推理层就会开始瞎猜。
4. 幻觉、漏检与标注尺度:实测中绕不开的四个深坑
4.1 多模态幻觉:模型明明没看见,却说“看见了”
幻觉是多模态场景理解里最棘手的问题。我在一个设备状态巡检Agent里遇到过典型病例:监控画面上明明只有一个贴了封条的机柜,推理报告却写“机柜上方有一个散热风扇正在运转”。回查场景状态,发现视觉模块根本没有输出风扇这个目标,问题出在推理层。
根源在于语言模型的先验知识太强。它从训练数据里学到一个模式:机柜附近通常有风扇;所以哪怕当前场景状态里没有风扇,它也会“脑补”。压制幻觉我试过几个有效手段:一是让LLM只允许基于输入的JSON字段推理,凡是场景状态里出现的实体才能参与结论;二是在Prompt里加一句硬约束,要求对未出现在输入里的实体默认回答“未知”;三是增加后置校验器,把推理结果里出现的实体与感知清单做一次比对,发现不在清单里的实体直接拦截。
提示:后置校验器是不少团队遗漏的环节。它不依赖模型本身,用一个简单的集合比对脚本就能挡住大量幻觉输出,性价比非常高。
4.2 级联错误:感知的一个小漏检,被推理放大成离谱结论
组合式架构容易出现一类连锁问题:感知层漏掉一个目标,导致场景状态不完整,推理层在缺失信息上做了自信推理,最终动作完全跑偏。漏检本身是小概率事件,但一旦进入推理层就会被放大。
应对方式有三道防线。第一道防线设置在推理前置过滤:在merge阶段把所有低置信目标标记为“疑似”,推理层不得把疑似目标当作确定障碍物或确定可交互对象。第二道防线是运动规划里的“未知区域保守策略”,把没被感知模型覆盖到的区域一律按障碍物处理。第三道防线是执行反馈闭环:动作执行失败时,不能只在推理层换一个动作重试,而是触发一次重新感知,用新一帧场景状态覆盖旧的错误假设。
这三道防线加在一起,能让级联错误的概率显著下降。单独依赖其中任何一道都不够,尤其是执行反馈闭环,在长期运行的Agent系统里几乎必备。
4.3 数据标注尺度不一致,正在悄悄拉低模型上限
用自有数据微调视觉模块时,最容易忽视的坑是标注尺度不一致。场景理解里的很多标注项天然带有主观性:遮挡比例达到多少算“严重遮挡”?拥挤程度怎么分级?某个区域算“可通行”还是“危险区域”?不同标注员的判断差异很大。
我建议用三类手段控制。第一,标注规范里必须有锚定示例,比如给一组“partial_occlusion”标准图,让标注员比对着打标,而不是靠文字描述理解。第二,同一批数据至少双人标注,自动计算一致性,低一致样本直接进入复核池而不是训练集。第三,能用自动启发规则的不用人工标注,比如遮挡面积比可以写一个基于检测框重叠区域的计算脚本,让人工只负责校验例外情况。
数据质量直接影响视觉模块的可靠性,而视觉模块的可靠性又决定上一层推理的可信度。这一步偷懒,后面所有调优都会事倍功半。
4.4 分层评测体系:只有“最终任务成功率”远远不够
不少团队评测多模态Agent只看最终成功率,任务成功就认为系统没问题,任务失败就一脸懵。这种评测方式掩盖了太多中间层的问题。最后任务失败了,你根本不知道是感知层漏检、对象推断出错,还是LLM规划失误。
我维护一套四层评测体系。第一层是感知层,分别测量检测精度、OCR识别率、深度估计误差;第二层是场景结构层,检查场景图三元组的召回率与正确率;第三层是推理决策层,让专家对决策合理性打分,并统计规则违反率;第四层才是任务执行层,看任务完成率、平均耗时、重试次数。
| 评测层 | 关键指标 | 主要用途 |
|---|---|---|
| 感知层 | mAP、OCR准确率、深度误差 | 定位视觉模块退化 |
| 场景结构层 | 三元组召回率、正确率 | 验证多模态对齐与merge逻辑 |
| 推理决策层 | 专家评分、规则违反率 | 暴露LLM幻觉与规划错误 |
| 任务执行层 | 完成率、耗时、重试次数 | 端到端效果验收 |
这套体系上线后,系统的调优效率提高了很多倍。以前一个Bug要排查半天,现在先看是哪一层的指标掉了,再顺着对应模块排查。
5. 调优心得:从能跑的Demo到一个可落地的多模态Agent
5.1 提示词和上下文设计的几个实用技巧
多模态Agent的推理质量,一半取决于场景状态质量,另一半取决于你怎么把状态交给语言模型。我的几个实践技巧如下。
第一,强制结构化输出。让LLM输出遵循预定Schema的JSON,而不是自然语言。输出后立刻做Schema校验,不符合就重试一次,重试仍失败才转为规则兜底。
第二,少样本示例比长篇指令更有效。在系统提示词里放两三个与业务场景高度相关的输入输出示例,比写一大段“你必须……不要……”有效得多。示例要刻意覆盖边界情况,比如输出“未知”的示例、处理低置信目标的示例。
第三,拆解推理步骤,不要期望一次生成完整方案。把“找目标、判遮挡、定路径、执行”拆成多轮,每一轮只让模型完成一个子任务。虽然总延迟增加了,但每步的准确性明显提升,整体成功率反而更高。
5.2 微调本地视觉模块时,数据优先级怎么排
微调视觉模块的时间有限,数据优先级必须排清楚。我的排序是:自有目标场景的真实数据最高,因为它们包含实际光照条件、传感器视角、目标分布;其次是开放词汇检测数据和OCR混合数据,它们帮助模型保持通用能力;最后才是通用视觉语言指令数据,这部分的边际收益其实很低,适合放在最后补充。
为什么自有数据这么重要?因为分布偏移是真实存在的。公开数据集的画面是“晴朗天气街道”“标准商品摆拍”,而项目现场是“逆光的货架”“油污的机床表面”“昏暗的走廊”。模型在这两类分布上的表现可以差很大。如果时间只够做一件事,那一定是先采集目标场景的几百张标注图,而不是去刷公开排行榜。
合成数据可以用来扩充,但要注意合成与现实的gap。我试过用仿真引擎批量产出的数据训练检测器,某些边缘场景效果不错,但遇到仿真里没有模拟过的反光、雾气,效果立刻衰减。合成数据最好搭配真实数据混合,并且单独验证模型在真实数据子集上的表现有没有退化。
5.3 从Demo到产品:延迟、成本、隐私与可回退之间的平衡
能跑的Demo和可落地的产品之间,差的不是模型,而是工程细节。
延迟方面,感知模块应控制在200毫秒以内,可以采用多线程并行推理。LLM推理层控制在秒级,超过2秒的规划通常意味着推理链路设计得不够好,要么Prompt太长,要么模型在绕圈子。
成本方面,建议做帧采样策略而不是全帧喂入。静止画面不需要每帧重感知,只有画面变化超过阈值时触发重新感知。检测置信度也可以动态调度,目标场景简单时降低调用频率。
隐私方面,尽量在边缘设备完成图像处理,只上传结构化结果。图像数据不出设备,这在很多业务场景里不只是合规问题,也直接决定了方案能不能被采纳。
可回退方面,LLM不可用时不能系统直接罢工。我在管线里保留了一套基于规则的确定性推理作为兜底,遇到大模型服务异常就降级。虽然决策质量会下降,但至少系统还能维持基本运转。
我自己的体会是,多模态场景理解与推理最核心的一点,是把这个任务拆成“感知负责实事求是,推理负责逻辑推演”两个部分。感知层该认未知就认未知,推理层该保守就别发挥。先想清楚每一层负责什么、边界在哪里,再去看模型选型和参数细节,整个系统才不会在真正应用时崩盘。