1. 多模态 Skill 到底解决什么问题
先说结论:Agent 的技能体系里,纯文本 Skill 是基本功,多模态 Skill 才是真正让 Agent 从“会打字”进化到“会看图、会听声、会读视频”的关键一步。这一篇是这个系列的第 6 篇,我把它定位成“承上启下”——前面聊的是 Skill 的定义、编排、记忆和工具调用,这一篇开始把输入和输出的疆域从文字扩展到图像、音频、视频以及各种传感器数据。
我见过不少团队把多模态 Skill 做成了“调用一个视觉模型接口”的玩具:给定一张图,调用 CLIP 或者某个 VLM 输出一段文字描述,完事。这样不能说错,但离真正可用的多模态 Agent 技能还差得很远。真正的多模态 Skill,必须回答三个问题:不同模态的数据怎么统一表达?上下文窗口怎么装下这些“重量级”信息?以及 Agent 在多轮对话里怎么保持对非文本信息的记忆和引用?
上下文工程(Context Engineering)在这里扮演的角色,甚至比模型选型更关键。文本场景下,上下文工程主要管 token 预算、压缩和检索;多模态场景下,事情复杂得多:一张 1024×1024 的图片放进视觉模型可能是几百甚至上千个 token,一段 10 秒的音频可能是几千个向量,一个视频更是动辄上十万级别的 token 消耗。如果不做上下文工程,一两次工具调用就能把 Agent 的上下文窗口塞满,后面的推理质量断崖式下跌。
这篇文章的结构很简单:先讲多模态 Skill 和普通 API 封装的本质区别,然后拆解一套我在实际项目中验证过的多模态 Skill 设计框架,再重点讲上下文工程里的三个坑位——预算管理、压缩、时序对齐,最后用一个“多模态情感分析”的完整案例串起所有知识点,附上我在踩坑过程里总结的问题速查表。适合正在做 Agent 应用开发、想从纯文本往多模态方向扩展的工程师参考,也适合刚入门但想系统理解这一块的读者。
2. 先搞明白:多模态 Skill 为什么不能当普通 API 封装来写
2.1 Skill 的本质是“带上下文的可复用工具包”
把多模态 Skill 当普通 API 封装写,是很多团队踩的第一个大坑。API 封装是“输入参数-返回结果”的一次性交易,Skill 不是。Skill 在 Agent 体系里是一个“可复用的能力单元”,它需要被编排、被记忆、被组合——同样的一个“读图技能”,在问答场景里输出图片描述,在数据分析场景里输出结构化表格,在情感分析场景里输出情绪标签和置信度,同一套底层能力,面对不同上下文要产出不同形态的结果。
这意味着多模态 Skill 的设计要分成两层:底层是稳定的“模态理解引擎”,上层是灵活的“策略适配层”。理解引擎负责把图像、音频、视频统一处理成中间表示——向量、特征序列、结构化文档都可以;策略适配层拿到上下文后,决定怎么用这批中间表示,是直接生成自然语言,还是压缩成摘要存进记忆,还是拼成新的输入喂给推理模型。
我是在做第一版多模态 Agent 时想明白这件事的。当时直接把所有图片识别逻辑硬编码在一个函数里,结果每次新增场景都要改底层代码,后来花了两个星期重构,才把“理解”和“适配”拆开。如果你现在刚开始设计多模态 Skill,宁可多写一层抽象,也不要把两者耦在一起。
2.2 多模态输入的“重量”倒逼上下文设计
文本世界里,一个词就是一个 token,一个 token 大概是 2~4 字节。切换到视觉和音频的世界,量级发生剧变:CLIP 把 224×224 的图片编码成 1×768 的向量,但这个向量如果不做降维和索引,直接丢进上下文里是没有意义的;更常见的是把图片切成 patch,比如 ViT 的 16×16 分块方式,一张标准分辨率图片会产生 196 个 patch token,换算到上下文里就是 196 个位置的信息。
音频也是同样的逻辑:10 秒的音频经过 Whisper 或类似模型编码,可能对齐出上百个语义片段;如果走的是声学特征路线,如 MEL 频谱序列,那上下文长度直接和采样率、窗口步长挂钩。我做过一个 20 秒语音片段的情感识别 Skill,仅仅是把特征序列接进上下文就吃掉了相当于 2000 多个中文 token 的预算。
这就是上下文工程要介入的第一个点:模态输入进入上下文之前,必须经历“特征化-压缩-结构化管理”三步。特征化是把原始数据变成可计算的东西;压缩是去掉冗余但保留关键信息;结构化管理是让推理模型知道“这段向量对应的是哪张图的哪个区域、哪段音频的哪个时间窗”。不做这三点,再强的模型也会被多模态输入淹没。
表格对比各类模态输入的上下文“价格”:
| 模态 | 典型处理方式 | 上下文成本估算 | 压缩可能 |
|---|---|---|---|
| 短文本 | 直接 token 化 | 1 token/词 | 保留原文 |
| 单张图片 | CLIP patch 编码 | 196+ token | 可降为摘要向量 |
| 10 秒音频 | MEL + Whisper 对齐 | 数百~千级 token | 可降为文本转写或标签 |
| 1 分钟视频 | 抽帧 + 时序编码 | 数千~数万 token | 必须做关键帧选样 |
| 传感器时序 | 波形裁剪 + 归一化 | 与窗口长度相关 | 可降为统计特征 |
2.3 模态之间的对齐是隐含的上下文需求
多模态 Skill 和单模态最大区别,在于数据之间存在“对齐关系”。图像和它的文字描述是对齐的,音频和它的转写稿是对齐的,视频的关键帧和对应的时间轴是对齐的。上下文工程如果忽略这种对齐关系,Agent 会“看得到但理解不了”——比如拿到了图片又能读到文本,但不知道文本描述的是图片里的哪个局部,信息就断了。
我处理过的一个典型案例:给 Agent 一段产品演示视频,配一条客服反馈文本,想让 Agent 判断反馈里说的“按钮不好点”对应视频里的哪个交互细节。如果只做单模态提取,视频摘要和文本反馈会变成两条平行的信息,Agent 只能瞎猜。必须先把视频按时间轴切段,每段提取关键帧和动作描述,再把文本反馈里的“按钮”实体映射到某一帧的某个坐标区域,才能完成真正的跨模态推理。
这种对齐能力通常来自多模态模型的预训练,比如 CLIP 的对比学习就是让图像和文本落到同一个向量空间。但作为 Skill 设计者,你要做的是在上下文里显式地保留对齐索引——时间戳、区域框、向量库 ID——而不是让推理模型自己从原始数据里找规律。后者在小模型上几乎不可行,在超大模型上也是浪费算力。
3. 实战拆解:多模态 Skill 的四段式设计框架
3.1 感知层:模态接入与统一中间表示
设计一个多模态 Skill,第一件事是定义“统一中间表示”。我习惯把所有模态的数据先转成一个结构化的字典,里面至少包含三块:模态类型、内容本体、对齐元数据。内容本体可以是特征向量、文本描述、标签列表;对齐元数据记录时间戳、空间位置、源文件指针等信息。
即使最终要交给推理模型的是自然语言摘要,这一步也不能省。统一中间表示的价值在于:它让你可以在 Skill 内部接不同的底层模型,而不影响上层逻辑。今天用 CLIP,明天换 SigLIP,或者换成某个国产多模态模型,只要输出还是特征向量+文本的双重形态,上层代码一行都不用改。我在实际项目中吃过底层模型强耦合的亏,后来统一了中间表示,模型迁移成本直接降了一个数量级。
以图像输入为例,参考的中间表示结构大概是这样的:
{ "modality": "image", "content": { "embedding": [768, "float32"], "cls_label": "product_photo", "ocr_text": "SN: A100-2024-0032" }, "alignment": { "image_width": 1024, "image_height": 768, "roi_boxes": [[120, 80, 300, 220]], "source": "user_upload_001.jpg" } }这个结构看久了你就知道,真正决定 Skill 上限的不是 embedding 的质量,而是对齐元数据是否完整。没有对齐,embedding 只是一堆浮点数。
3.2 理解层:如何选择和组合多模态模型
统一中间表示定义好之后,理解层的关键是模型选型。多模态模型现在大致有三条路线:双塔编码器路线(如 CLIP、SigLIP),擅长语义对齐和跨模态检索,适合做召回和特征提取;融合编码器路线(如各类 VLM、LLaVA 变体),输入多种模态输出文本,适合做内容理解和问答;专用单模态模型路线(如语音识别、情感分类、OCR),负责把原始信号变成结构化中间产物。
一个成熟的多模态 Skill 通常不是只调一个模型,而是组合调用。比如处理一段包含人物对话和背景音乐的视频:人声转写用 ASR 模型,情感判断用音频情感分类模型,画面内容用 VLM 摘要,音乐类型可用标签模型。四个模型的输出经过统一中间表示层的整理,汇入同一个上下文,Agent 才能基于完整信息做推理。
组合调用的一个技巧是“管道与并行并存”:如果模态之间存在依赖关系(比如要先知道画面里有什么人,才能判断人物关系),就管道式串联;如果模态之间独立(画面和音频通常独立),就并行处理来节省延迟。我一般用 Python 的 asyncio 或线程池做并行,实测下来对吞吐提升非常明显。
3.3 推理层:把多模态信息“翻译”成语义密度高的文本
统一中间表示之后,推理模型看到的仍然是特征向量或 JSON 结构,这样通常不好直接用。我建议在进入 Agent 主语言模型之前,加一层“语义翻译器”,把多模态信息浓缩成语义密度高的文本。
举个例子:一段视频中间表示里有 100 个关键帧的 embedding、30 段转写文本、5 分钟的波形摘要。直接把这些丢给推理模型,几乎必然超窗。但经过一层快照式摘要——比如“视频开头用户展示产品外观,中段操作设置界面时出现报错弹窗,报错文本为 ERR_TIMEOUT,用户情绪评分 0.78(负向)”——上下文成本可能只有原来 10%,关键信息一点没丢。
这层语义翻译器,本质上就是上下文工程里的“摘要引擎”。它可以是一个单独的轻量模型,也可以直接用主语言模型做抽取式摘要。关键是把“信息密度”放在第一位:宁可牺牲细节,也要保证核心实体、时间线、关键结论都在。
3.4 表达层:Skill 的输出必须携带可追溯性
最后是表达层。很多多模态 Skill 的输出是一段流畅的自然语言,看着像模像样,但没有可追溯性——推理模型说“图片里有一只猫”,却不知道是图片的哪个区域、基于什么特征才得出“猫”的结论。这在严肃场景里是不可接受的。
我的做法是:输出文本里嵌入结构化引用标记。比如“[IMG_L: 左上区域] 显示为深色毛发纹理,置信度 0.92,推测为宠物猫”;或者“[AUDIO_T: 3s-5s] 出现高频哭声,音量陡增”。这些标记让 Agent 在后续推理时能回指数据源头,也方便做人工审查和调试。多模态输出没有可追溯性,就等于给 Agent 装了一个幻觉放大器。
4. 上下文工程:多模态 Skill 的不变量
4.1 上下文窗口预算:先把钱花在刀刃上
上下文工程在多模态场景下的第一原则,是“预算前置”。在 Agent 的每轮推理开始前,先算清楚:当前任务需要哪些模态信息?每类信息在上下文里占多少预算?哪些可以压缩、哪些必须完整保留?
我习惯把上下文预算分为三档:核心事实(必须原样保留,比如报错文本、时间戳、实体名称)、支撑细节(可以摘要化处理,比如图片的局部描述、音频的情绪片段)、背景材料(可以降级为锚点或丢弃,比如与当前任务无关的帧内容)。分配比例通常控制在 40%、40%、20%。但具体任务可以调整,比如一个以视觉理解为核心的任务,核心事实的比例要上调到 60%。
实操中的“超窗防护”机制也很关键。我见过太多 Agent 应用在多模态输入一多就直接崩溃,根本原因是没做预算哨兵。我的做法是:每次拼接新的多模态内容之前,先用 token 计数函数估算成本,超过阈值就触发一个“压缩-降级-截断”的三级响应:先压缩语义密度低的段落,再降级支撑细节为摘要,最后截断背景材料。这套机制上线之后,长会话崩溃率基本归零。
多模态上下文预算表的参考模板:
| 预算类别 | 占比 | 策略 | 示例 |
|---|---|---|---|
| 核心事实 | 40% | 原样保留或轻量摘要 | 报错码、时间戳、实体、关键帧 |
| 支撑细节 | 40% | 语义压缩/结构化抽取 | 画面描述、音频转写、局部 ROI |
| 背景材料 | 20% | 降级锚点或丢弃 | 冗余帧、重复叙述、环境噪声段落 |
4.2 多模态压缩:摘要之外还有一条路叫“指针化”
纯文本的压缩基本就是截断和摘要两种,但多模态信息有一种更优雅的处理方式:指针化。不做压缩,而是把“一大堆数据”换成“一个指针+一段描述”,让 Agent 知道信息在哪里、怎么取,而不是把信息本体塞进上下文。
最常见的指针化实现是走向量数据库:图片、音频切片、文档片段都编码成向量入库,上下文里只保留“查询向量+Top-K 命中结果的摘要”。Agent 需要细节时,再通过检索工具去取原始记录。这相当于把上下文从“生产力工具”变成“索引目录”,窗口内始终是轻量的,而重数据放在外部存储里随取随用。
我做多模态 Skill 的早期版本,就是把所有图片按帧向量化,存进一个本地向量库。Agent 每轮只携带“视频主题摘要+关键帧索引列表”,需要看某个具体瞬间,就调用检索工具按时间戳取出那一帧。实测这样处理 10 分钟的视频素材,上下文成本能控制在 3 次调用以内不爆窗,而如果全部原始帧进上下文,第一轮就会爆。
指针化的代价是引入“间接层”,推理模型拿不到全部原始数据,只能依赖检索结果。所以检索质量非常重要。关键词召回+向量召回的混合策略是有必要的,否则很容易出现“索引到了,但检索不到关键帧”的情况。
4.3 时序对齐:多模态上下文里的隐藏主线
多模态数据几乎都有时间属性:视频有帧时间戳,音频有采样时间轴,传感器有记录时刻。上下文工程如果破坏了这个时间轴,Agent 的跨模态推理会变得混乱——它可能先看到结束状态,再看到开始状态,完全丧失因果推断能力。
解决办法是在上下文里显式维护一条“时序主线”。每次写入多模态内容,都必须带上时间戳或序列号。我自己会用前缀标记法:把每个上下文片段标注为 [T-3s],[T-2s],[T-1s],[T0],让推理模型明确看到先后关系。对异步采集的数据,还要做对齐插值,比如音频和视频时间轴存在偏移时,先做时间修正再进上下文。
2026 年华为杯 E 题那种“复杂场景下多模态情感预测”的赛题就是典型的需要时序对齐的场景:观察者的面部表情、声音语调、手势动作往往存在几十毫秒到几百毫秒的错位,简单拼接这些模态特征会让模型学错关联。我在自己的情感分析案例里也是踩了这个坑——一开始不做对齐直接融合特征,准确率只有 52%,基本等于瞎猜;后来把音频和视觉特征按帧级时间戳对齐再融合,准确率直接跳到 78% 以上。时序对齐不是加分项,在多模态 Skill 里是必需项。
4.4 上下文的状态管理:多轮会话里的多模态记忆
多模态信息的另一个特殊性是“不宜回溯”。文本内容随时可以重新读取,但一段视频的关键帧如果不在上下文里,Agent 就“忘记”了那个画面。所以多模态 Skill 需要配合额外的状态管理机制。
我的实践是“三级记忆”:工作记忆(当前轮上下文中保留的多模态关键信息)、短期记忆(最近 3~5 轮的多模态摘要,存放在 Session 中)、长期记忆(重要多模态事件存入向量库+纪要表)。每一轮 Agent 与用户的交互结束时,系统自动把当前轮的多模态信息整理成结构化摘要写进短期记忆,同时把关键事件(比如用户上传的产品图、出现的报错画面)固化到长期记忆。
如果没有这个三级记忆,多模态 Agent 的用户体验非常糟:用户刚才传过一张图片,下一轮提问“图片里的产品型号是什么”,Agent 一脸茫然。有了记忆管理,这种问题基本消失。值得注意的细节是,多模态记忆的摘要也要遵循“核心保留+支撑压缩”的原则,否则 Session 里的短期记忆会失控膨胀。
5. 完整案例:从零实现一个多模态情感分析 Skill
5.1 场景定义与模态拆解
用多模态情感分析作为完整案例,是因为这个场景刚好覆盖我们前面讲的全部要素:多模态输入(视频/音频/文本)、时序对齐、特征融合、上下文压缩、结构化输出。而且它对推理质量的要求直观——别人一眼就能看出你的 Agent 判断得准不准。
场景定义:输入一段人物讲话视频(含音轨),目标是输出演讲者的情感状态曲线,包含 6 类基本情绪标签(高兴、悲伤、愤怒、惊讶、中性、厌恶)以及置信度。
模态拆解如下:视频帧,用于面部表情识别,每 0.5 秒抽一帧;音轨,用于语音情感识别,按 1 秒为窗口提取声学特征;转写文本,用于语义情感判断,按句切分。三个模态各出一路特征,然后在时间轴上对齐,最终形成统一的“时间片-情感分布”表格。
5.2 特征提取与数据集准备
特征提取阶段,我参考了类似 bird1445 这类多模态数据集的处理思路:以时间戳为索引,把图像特征、音频特征、文本语义拼接在同一行里。这里要注意对齐粒度,参数不同结果差异很大。我试过 0.5 秒、1 秒、2 秒三种对齐窗口,1 秒窗口的 F1 最高,0.5 秒会导致片段太碎、模型学不到整体情绪,2 秒又会抹平情绪转折点。
数据集方面,除了公开数据集如 MELD、RAVDESS,我还额外收集了一部分真实访谈视频做补充。这个步骤不能省,因为公开数据集里的人脸角度、录制条件都比较理想,真实场景里侧脸、遮挡、背景噪声是常态。数据增强也很关键:对音频加 Gaussian 噪声,对视频帧做随机裁剪和亮度扰动,能明显提升模型的泛化能力。
特征文件建议统一存成“时间戳-特征向量-来源模态”的三元组格式,方便后面做对齐和融合。我自己用 parquet 格式存储,比 JSON 快很多,而且能直接喂给 DataFrame 操作。
5.3 上下文注入与 Skill 组装
在这个案例里,Agent 的 Skill 是这样组装的:感知层接入视频解析器(OpenCV 抽帧 + 人脸检测)和音频解析器(ffmpeg 切窗 + 声学特征提取);理解层接了三个模型——人脸表情分类(ResNet 微调)、语音情感分类(Wav2Vec 微调)、文本情感分类(预训练中文 BERT);对齐层用时间戳索引把三路输出合并成“情感时间表”。
关键技巧在上下文注入这一步。我没有把整个视频的完整情感时间表一次性塞给 Agent,而是分成两段:先让 Agent 拿到“全局概览”——情感分布统计、总体倾向、几个显著转折点;当用户追问某个时间区间时,再通过检索工具按时间戳取详细片段。这样既保留了全局视角,又控制了上下文成本。
上下文模板参考这个格式:
【视频全局概览】 总时长: 185秒 情绪基调: 悲伤-平静 显著转折点: T-62s(愤怒峰值0.85),T-133s(悲伤峰值0.91) 【详细区间 - 用户请求: T-60s~T-65s】 T-60s: 面部愤怒0.8/语音愤怒0.7/文本中性0.6 T-61s: 面部愤怒0.85/语音愤怒0.75/文本愤怒0.5 ...5.4 调优记录:对齐粒度与融合权重的实验对比
实操中我做了两组关键调优实验。第一组是对齐粒度对照,用 0.5 秒、1 秒、2 秒三档做对比,1 秒窗口的 F1 为 78.2%,明显优于其他两档;第二组是融合策略对照,用了三种方式——特征拼接(Concat)、加权投票、以及跨模态注意力,结果加权投票在简单场景下最稳,跨模态注意力在转折点检测上表现更好但训练成本高了一倍。
我还做过一个有意思的测试:单独用视觉模态、单独用音频模态、以及多模态融合,三者效果差距显著。视觉单独大概是 63% 准确率,音频单独 58%,融合后到 78%。这印证了多模态融合的必要性——不是简单的加法,而是信息互补带来的非线性提升。
这个案例的核心启示是:多模态 Skill 的调优不只在模型层,对齐粒度、融合策略、上下文注入方式,每个环节都有 5~10 个点的性能浮动空间。模型选对了但上下文注入一团糟,效果照样拉胯。
6. 常见问题速查表与独家避坑心得
| 问题现象 | 根因 | 排查思路 | 解决方案 |
|---|---|---|---|
| 上下文窗口频繁爆满 | 多模态原始数据未压缩直接注入 | 检查日志中每轮上下文 token 数是否线性增长 | 引入摘要层+指针化检索,核心事实之外全部摘要化 |
| 跨模态推理答非所问 | 时序未对齐、模态之间没有关联索引 | 检查中间表示里是否有时间戳和引用标记 | 全局时间轴对齐,输出中嵌入 [T-x s]/[IMG_x] 引用 |
| 长会话尾部质量下降 | 早期多模态细节被遗忘 | 查看短期记忆和长期记忆的摘要是否覆盖关键事件 | 部署三级记忆:工作记忆+短期摘要+向量库长期存储 |
| 特征提取慢导致响应延迟 | 模型串行调用浪费时间 | 统计每个模态模型的耗时占比 | 独立模态并行处理,异步合并,必要时用流水线 |
| 小模型多模态表现差 | 上下文注入结构混乱,模型抓不到要点 | 尝试改变注入顺序和格式,A/B 测试 | 统一使用“概览+按需取细节”的两段式注入 |
| 情感分类准确率低 | 模态特征融合粒度不匹配 | 对比不同对齐窗口的指标 | 用 1 秒窗口做对齐,融合策略先试加权投票再试注意力 |
6.1 关于上下文工程,我最想说的三句话
第一,多模态 Skill 的复杂度从来不是模型给的,是上下文给的。模型决定了天花板,上下文工程决定你实际能摸到多高的天花板。
第二,不要迷信“把所有信息塞进上下文”的思路。多模态世界是一个信息过载的世界,好的上下文工程是“少给一点,但给得精准”——你的 Agent 会更聪明,而不是更混乱。
第三,调试多模态 Skill 时,习惯性先看中间表示,再看模型输出。大多数“模型表现差”其实都是“中间表示质量差”,要么对齐丢了,要么摘要丢了关键实体,要么上下文结构混乱。这个排查顺序能帮你省掉大量无效调参时间。
7. 从系列角度补充一些经验和下一步方向
这一篇把多模态 Skill 的设计框架和上下文工程的关键点都覆盖了,但整个 Agent Skills 系列里,多模态只是一个横向切片,后面还有很多可以继续做的事情。我自己正在推进的两个方向,分享出来供参考。
第一个方向是“多模态 Skill 的自动评测体系”。文本 Skill 的评测相对成熟,有标准数据集和评估指标;多模态 Skill 的评测复杂得多,涉及模态覆盖度、对齐质量、上下文成本控制、生成内容可追溯性等多个维度。我目前的做法是建立一套多维评分卡,每项按 0~1 打分,加权汇总,辅助选择方案和定位瓶颈。
第二个方向是“上下文工程的自动化”。现在的上下文预算分配、压缩策略、检索触发,很多是需要人工配置的规则。随着 Agent 任务复杂度提升,规则会越来越多、越来越难维护。理想形态是让 Agent 自己根据任务目标动态决定上下文策略——但这本身就是一个很值得单独开一篇的话题。
最后再分享一个小技巧:多模态 Skill 上线前,我一定先做“模态缺失测试”——分别屏蔽图像、屏蔽音频、屏蔽文本,观察 Agent 的表现在哪些任务上退化、在哪些任务上不受影响。这个测试能帮你准确理解每个模态的贡献度,也能帮你判断数据采集阶段哪个通道更不能出错。踩过几次坑之后,我把它列成了多模态 Skill 上线前的固定动作。