1. 从Agent的"文本眼"说起:为什么需要视频理解能力

先说个实际场景。我做Agent开发差不多两年,期间接到最多的需求不是"帮我写个插件",而是"这个视频太长了,能不能让AI帮我总结一下"。一开始我都是让他们手动截图、手动复制字幕,后来发现这套流程笨得离谱——一个10分钟的视频,截完图、整理字幕、再喂给大模型,光准备工作就得花掉半小时。

问题出在哪?出在Agent的信息获取通道太窄了。现在的Agent框架再花哨,底层能力基本还是围绕文本转来转去:读文档、写代码、调API。遇到视频这种"流式多媒体",大部分Agent直接抓瞎。你给它一个视频路径,它要么报错,要么就只能读个文件名。这不是模型能力不行,是Agent缺了一只"眼睛"。

所以我把目光放在了Skill上。所谓Skill,简单理解就是给Agent装的一个"技能包",让它在特定任务上具备新的能力,而不需要重写整个Agent框架。claude-video这个Skill的思路也很直接:把视频拆成Agent能理解的成分——视觉帧和音频转写文本——再让大模型基于这些成分做推理。它解决的就是"Agent如何消化视频信息"这个基础问题,适合三类人看:正在做Agent开发、想给Agent扩展多模态能力的人;经常需要批量总结视频内容、剪视频素材的人;以及研究Skill机制、想搞懂"Skill和Agent到底是什么关系"的人。

这篇文章我会把claude-video从设计思路、实现细节到踩坑经验完整拆开讲,不藏着掖着。

2. 视频理解的核心技术路线:抽帧、转写、对齐、压缩

让Agent"看视频",听起来像是要给Agent接一个视觉模型,但实际工程落地上,几乎没有人会直接把整个视频喂给多模态大模型。原因很简单:成本爆炸,且效果不可控。真正成熟的方案是先把视频做"预处理",转成结构化的中间表示,再交给大模型理解。

2.1 抽帧:不是每帧都要看

一个30fps的10分钟视频,一共18000帧。就算多模态模型能承受这么大的输入,计算和token消耗也是离谱的。所以第一步一定是抽帧,也叫采样。采样间隔怎么定很关键:内容本身变化快(比如运动类、游戏演示),间隔要密一些;内容变化慢(比如课程讲解、访谈对话),间隔可以拉长。

我实测下来,默认3-5秒抽一帧是个比较稳的区间。一个10分钟视频,5秒间隔就是120帧,塞给模型处理完全在可接受范围内。如果允许模型根据视频类型动态调整间隔(比如画面差异检测),效果会更好,但那是进阶玩法。

抽帧工具优先用ffmpeg,生态成熟、参数可控。核心命令就一行:

ffmpeg -i input.mp4 -vf "fps=1/5" -q:v 2 frames/frame_%04d.jpg

这里 fps=1/5 表示每5秒输出一帧, -q:v 2 控制输出画质(2比较清晰,文件也不算大)。抽完帧之后可以再跑一个去重:用 ImageMagick 或 ffmpeg 自带的 select 过滤器按画面相似度去重,能把字幕静止、镜头长时间不动的视频帧数再砍掉一半以上。

2.2 音频转写:模型理解视频的"主干道"

说实话,视频里的信息密度,音频往往比画面高得多。一段产品发布会,90%的有效信息都在人说的话里,画面只是辅助。所以ASR(自动语音识别)这一步做得好不好,直接决定这个Skill的上限。

选型上我优先考虑Whisper系列,本地的 openai-whisper 或者更快的 faster-whisper 都行。如果只是部署给自己用, faster-whisper 的 large-v3 模型准确率和速度平衡得最好;如果是轻量使用, base 或 small 模型在CPU上也能跑,但中文识别率会打折。还有一条路是用云端的语音识别API,省本地算力,但我个人做工具类项目习惯本地优先,不依赖外网服务。

转写时的参数也讲究。我一般开启 word_timestamps=True ,拿到每个词的时间戳。这一步很多人会忽略,但它正是后面"画面-文字对齐"的关键。

2.3 对齐:让画面和文字在时间轴上握手

抽帧得到了图片序列,转写得到了带时间戳的文本,但它们是两个孤立的"数据包"。要让模型理解"第3分钟画面里出现的那个人就是发言者",必须把两者按时间戳对齐。

做法很简单:每张帧图都带上一个时间标签(比如 frame_36.jpg 对应 180s ),转写文本的每个片段也带上起止时间(比如 [00:02:55 - 00:03:10] 我们推出了新一代芯片 )。合成到一起,就得到了一个"带时间轴的图文剧本"。这才是最终喂给大模型的东西。

对齐这一步看似不起眼,实际上决定了模型能不能回答"这个产品的发布时间是什么时候"这类需要跨模态关联的问题。没有对齐,模型只能分别描述"画面里有什么"和"声音在说什么",无法把两者打通。

2.4 上下文压缩:控制Token预算

预处理做完,还有一个现实问题:120帧图+全文转写文本,一次塞给模型的token量还是很可观。一个10分钟视频,转写文本大概1500-2500字,还好;但120张图如果都走视觉token,按多模态模型每张图几百个token估算,一轮就要几万token,做多轮对话审计会崩。

压缩策略我常用两个:

一是"帧图拼接"。把抽出来的帧按网格拼成大图,比如6x6拼一张,120帧拼成4张高分辨率合成图。这样视觉token数量直接从120份降到4份,信息损失在大部分场景下可以接受。代码实现用 PIL 就能做:

from PIL import Image
import os

frames = sorted(os.listdir("frames"))[:36]
images = [Image.open(os.path.join("frames", f)) for f in frames]
width, height = images[0].size
grid = Image.new("RGB", (width * 6, height * 6))
for i, img in enumerate(images):
    grid.paste(img, ((i % 6) * width, (i // 6) * height))
grid.save("grid_batch1.jpg")

二是"转写文本摘要"。对很长的转写文本,可以先用一次轻量模型按段落压缩,再去喂主模型。但对于追求准确率的场景,我其实不建议做这层摘要——摘要会丢失细节,而视频理解的常见问题恰恰需要抠细节。

3. claude-video Skill的完整实现:目录结构、核心代码与提示词

概念说完了,直接上实现。claude-video本身不是一个独立的应用程序,而是遵循Agent Skill机制的一组目录+脚本+说明文档。做好之后可以挂进支持Skill的Agent框架里(当前主流的几个Agent框架都有Skill机制,区别只在于调用约定)。

3.1 Skill目录结构:约定优于配置

我按照社区通用的Skill结构来组织:

claude-video/
├── SKILL.md
├── scripts/
│   ├── process_video.py
│   └── requirements.txt
└── assets/
    └── sample_config.json

SKILL.md 是整个Skill的"门面",Agent框架会先读这个文件来判断"这个Skill是干什么的、什么时候该调用、怎么调用"。它里面写清楚触发条件、入参出参、环境依赖。这步做不好,Agent在需要的时候可能根本不会触发这个Skill。

3.2 SKILL.md:让Agent知道何时用、怎么用

---
name: claude-video
description: 解析本地视频文件,提取关键帧与音频转写文本,帮助Agent理解视频内容。当用户提供视频文件路径并要求总结、分析、答疑时使用。
version: 1.0.0
---

# claude-video

## 功能
- 输入:本地视频文件路径
- 输出:视频的结构化解读(画面摘要+文字转写+关键信息提取)
- 适用场景:视频总结、视频问答、会议纪要提取、视频素材检索

## 使用方式
1. 调用 `python scripts/process_video.py <video_path> --interval 5`
2. 读取生成的中间产物(frames/ 和 transcript.json)
3. 基于中间产物进行多模态分析

## 注意事项
- 视频格式建议 MP4/MOV,其他格式需要保证 ffmpeg 可解析
- 长视频(超过1小时)建议分段处理
- 如果视频没有音轨,转写步骤会自动跳过

description 字段值得多花点心思写。Agent的调度器在做技能选择时,就是靠这段描述来判断"当前用户请求是不是这个Skill的活"。写得越具体,触发越准。

3.3 核心脚本:处理管线一次性跑通

process_video.py 是整个skill的引擎,完整跑一遍抽帧、转写、对齐三件事:

import subprocess, json, os, sys
from pathlib import Path

def extract_frames(video_path, out_dir, interval=5):
    Path(out_dir).mkdir(parents=True, exist_ok=True)
    subprocess.run([
        "ffmpeg", "-y", "-i", video_path,
        "-vf", f"fps=1/{interval}",
        "-q:v", "2",
        f"{out_dir}/frame_%04d.jpg"
    ], check=True)

def transcribe(video_path):
    from faster_whisper import WhisperModel
    model = WhisperModel("small")
    segments, _ = model.transcribe(video_path, word_timestamps=True)
    result = []
    for seg in segments:
        result.append({
            "start": seg.start,
            "end": seg.end,
            "text": seg.text
        })
    return result

def align_and_dump(transcript, out_path):
    with open(out_path, "w", encoding="utf-8") as f:
        json.dump(transcript, f, ensure_ascii=False, indent=2)

if __name__ == "__main__":
    video = sys.argv[1]
    interval = 5
    if len(sys.argv) > 2:
        interval = int(sys.argv[2])
    extract_frames(video, "frames", interval)
    transcript = transcribe(video)
    align_and_dump(transcript, "transcript.json")
    # 同时输出一个图文混合的markdown,方便直接喂给大模型
    with open("combined.md", "w", encoding="utf-8") as f:
        f.write("# 视频解析结果\n\n")
        for item in transcript:
            f.write(f"[{item['start']:.1f}s - {item['end']:.1f}s] {item['text']}\n")
    print("done")

这套脚本本身不产生"理解",它生成的是中间产物。真正的"看懂"发生在下一步——把中间产物交给大模型。

3.4 提示词设计:让多模态模型真正"看懂"

有了帧图和转写文本,最后一步就是把它们组合成提示词喂给Claude这类多模态模型。我给claude-video设计了一套结构化的提示词模板,核心是让模型分三步走:先看全局,再抠细节,最后回答/总结。

你是视频理解助手。下面是一个视频的结构化信息:
- 画面帧:以图片形式提供
- 音频转写:按时间戳排列的文本
请依次完成:
1. 用3-5句话概括整体内容
2. 列出视频中出现的核心人物/产品/关键时间节点
3. 回答用户的具体问题(如果有)
回答时请标注信息对应的时间点,方便回溯验证。

设计这套提示词时有个经验:别让模型"自由发挥",要给它明确的任务拆解。多模态模型在自由总结时容易出现"说的都对但什么都没说"的空话,一旦要求它标注时间点、列关键节点,输出质量会明显上一个台阶。

4. 实测评估:能力边界和失败模式

工具做出来不能只看能跑,要看在真实场景里到底顶不顶用。我拿三类视频做了测试:会议录像、产品发布会、带大量字幕的教程录屏。

4.1 测试结果一览

视频类型 时长 帧数(5s间隔) 转写质量 总结准确率 问答能力
会议录像 42min 504 中上 高 中
产品发布会 18min 216 高 高 高
教程录屏 25min 300 高 中 中

会议录像的特点是多人说话、环境音嘈杂,转写质量会掉,尤其说话人重叠的时候。我后来加了 vad_filter=True (语音活动检测)过滤静音段,转写准确率有所回升。教程录屏的问题在于大量信息在画面上(代码、菜单、操作路径),单靠帧图和字幕不足以还原操作细节,问答时容易答不到点子上。

4.2 最常见的失败模式

  • 信息全在画面里,转写近乎为零 :比如一段纯乐器演奏视频,或者"无声编程"录屏。没有对白,模型只能靠抽帧猜,效果完全依赖抽帧质量。
  • 长视频token溢出 :1小时的视频即使5秒抽帧也有720张,拼图策略能缓解但有限。我的做法是分段处理,每10分钟一个段落,各自生成结构化信息后再汇总。
  • 多语言混合内容 :中英夹杂的发布会,如果Whisper模型不是双语优化过的,会丢内容。
  • 时间戳偏差 :帧图上的时间来自 ffmpeg 抽帧的均匀间隔,如果视频本身有剪辑跳跃,帧图时间轴和实际内容会错位。好在对大多数总结场景影响不大。

4.3 和纯文本/纯截图方案的对比

我拿同一段视频分别用"只喂转写文本""只喂截图""claude-video完整方案"试过,差别很直观:纯文本方案能抓事实但完全丢失视觉信息,遇到"界面上有个红色警告是什么"这种问题直接歇菜;纯截图方案能描述画面但记不住连续说的话,对"他是先讲了A还是先讲了B"这类顺序问题几乎无能为力。完整方案在绝大多数问题上都能答,只是偶尔在"细节精确度"上打折扣。这就是多模态信息互补的价值。

5. 接入Agent框架的实战经验:从"工具"到"技能"的关键一步

Skill脚本本身跑通了,还差最后一步:让它通过Agent框架的调度机制被自动调用。这一步决定你是在用"一个视频处理脚本",还是拥有"一个具备视频理解能力的Agent"。

5.1 不同框架的Skill调用约定

市面上主流的Agent框架都支持Skill机制,但命名和加载方式各不一样,本质都是"框架扫描某个目录下的SKILL.md,把描述注册成一个可调用工具"。因此claude-video这个Skill在迁移到不同框架时,核心逻辑不用改,要改的只是 SKILL.md 的字段格式和脚本的 stdin/stdout 约定。

我在实际项目里通常用一个中转层来做适配:Skill脚本统一输出JSON结构( {status, frames_dir, transcript_path} ),Agent框架只需要解析这个JSON,再决定下一步怎么使用多模态能力。这样即使换框架,也只改中转层,不动核心管线。

5.2 异步处理与进度反馈

视频处理是个耗时操作,一个10分钟的视频,抽帧加转写加起来可能要几十秒。如果Agent框架在同步等待,用户体验就是"卡住了"。我踩过这个坑:最初接入时,Agent调用Skill后直接挂起将近一分钟,用户以为坏了。

解决办法是给Skill加上异步回调逻辑:Skill先把任务投进队列,立刻返回一个任务ID;Agent轮询进度接口获取状态。进度反馈也很有必要,一个标准的处理管线至少有"抽取帧中(20%)""转写中(60%)""对齐中(90%)"几个节点,Agent拿到进度后可以给用户一个实时状态播报,体验提升明显。

5.3 缓存设计与成本控制

同样一段视频,用户可能问五个不同的问题。如果每次都重新抽帧、重新转写,纯属浪费。我在Skill里加了一层基于视频文件哈希的缓存:处理完一次就把 transcript.json 、帧图目录、以及第一次生成的 combined.md 缓存起来,后续请求直接命中缓存,秒回结果。

缓存目录结构:

cache/{video_md5}/
├── transcript.json
├── frames/
└── combined.md

这个设计同时解决token消耗的问题。多轮对话中,模型不需要每次都重新"看"所有素材,只需要在首轮加载完整的视频结构信息,后续针对具体问题的追问带上缓存中对应时间段的内容片段即可。

6. 踩坑记录与进阶优化方向

最后聊点实在的。这个Skill从初版到稳定运行,前前后后经历了好几轮修补,有几个坑值得单独记一笔。

6.1 踩坑记录

坑一:ffmpeg版本导致的不兼容。 我本地用的是一个新版本ffmpeg,能正常处理MOV格式;部署到另一台机器上,旧版ffmpeg直接报"Unknown encoder"。最后统一用 apt 固定了版本,并且在 requirements.txt 里注明了最小版本号。这种环境问题不提前锁定,换个机器就翻车。

坑二:内存爆炸。 第一次处理一个1080p的长视频时,把帧图一次性全部读进内存做拼接,直接把进程搞崩了。后来改成批量拼接(每36张拼一批),内存占用从几个GB降到几百MB,稳定多了。

坑三:转写模型在无声片段会输出幻觉文本。 Whisper在背景音乐响但没人说话的时候,有时候会"脑补"出歌词或对话。这个问题通过开启 vad_filter 基本解决,但触发VAD的阈值需要调——阈值设太高会把轻声音挡掉,设太低又挡不住幻觉。

6.2 进阶优化方向

目前版本能解决"Agent看得懂视频"的基础问题,但还有很大的升级空间:

  • 场景检测 :用镜头边界检测算法把视频自动切分成"场景段落",每个段落单独分析,再汇总段落之间的关系。这对长视频尤其有价值,能让模型理解结构而不是线性罗列。
  • OCR叠加 :对画面里出现的文字(PPT、字幕、界面)自动做OCR识别,和帧图一起喂给模型,能解决"信息全在画面上"的教程录屏场景。
  • 流式处理 :当前是"全量处理再理解",如果改成边接收视频流边抽帧转写,就能支持直播场景的实时总结,这是另一个方向的玩法。
  • 接入RAG :处理完的视频信息可以向量化存库,之后用户以自然语言检索"之前看过的视频里,他说过XX那句话是哪段?",直接命中时间戳跳转。

说实话,做这个Skill的最大收获不是脚本代码本身,而是搞懂了"给模型喂数据"这件事的精髓——你不需要让模型直接面对原始视频,只需要把原始视频转化成模型擅长处理的中间形态,问题就解决了大半。Agent的多模态能力扩展,最终拼的不是模型多强,而是数据管线多会"翻译"。

Logo

火山引擎视频云技术社区,是面向 AI 音视频开发者的技术交流平台。这里汇聚源自抖音、豆包等亿级 DAU 产品的 RTC、直播、点播、AI 媒体处理、音视频互动技术,提供接入指南、最佳实践、性能调优、场景案例、Demo 代码、开源项目、白皮书和 API 文档。社区汇聚官方工程师与一线开发者,为 AI 视频通话、数字人、AI 视频处理等应用的开发与落地提供技术支持。

更多推荐