给Agent装上“眼睛”:基于Skill的视频理解与时间线分析实践
先说一个我最近反复遇到的痛点:我自己搭的Agent能读文档、能查网页、能调API,但一碰到视频就抓瞎。给它一个课程回放或者会议录像的链接,它要么回一句“无法处理视频内容”,要么只能靠文件名瞎猜。后来我花了两天时间,给Agent装了一个叫
claude-video
的Skill,它终于能“看”视频了——而且是真能看懂:能按时间点说出画面里出现了什么、讲了哪些话、有没有PPT关键词。这篇就是把整个思路、实现细节和踩坑过程完整写一遍,给同样在折腾Agent扩展能力的同学一个参考。如果你正在搭自己的智能体,需要处理短视频、会议录像、网课回放,或者单纯想给Agent加一个“视频理解”的入口,那这篇应该值得你花几分钟看完。
1. 为什么要给Agent一双“眼睛”:视频理解的需求与困境
1.1 当前Agent的“信息盲区”
现在的Agent框架越来越强,文本理解、工具调用、代码执行都已经很成熟,但视频这块一直是明显的盲区。原因很清楚:视频不是一个单纯的文件,它是画面、语音、字幕、时间轴多种信息的叠加体。文本可以被直接切成token喂给模型,图片可以被压缩成图像token,但一段10分钟的视频如果按视频流去理解,计算量和上下文开销都会立刻爆炸。
我一开始也想过直接调用多模态模型接口传视频,但实测下来有几个现实问题:第一是成本,视频每秒钟几十帧,真正有用的关键信息可能只有几个画面,把全部帧都传给模型,费用高得离谱;第二是延迟,长视频接口处理时间经常超过一分钟,等结果等到没脾气;第三是控制力太弱,模型看完一整段视频后,如果只记得一个大概印象,你再问它“第3分钟的画面里写了什么”,它往往答不上来。
所以问题的本质是:Agent缺的不是“看视频”的能力,而是一条能把视频从高维连续信号转换成低维、结构化、可检索信息的管道。只有把视频“翻译”成Agent熟悉的东西,它才能真正理解。
1.2 思路:把视频“翻译”成Agent能读懂的语言
我的解决方案并不复杂:与其让Agent直接看视频,不如先把视频处理成一批带时间戳的“笔记”。这些笔记包括关键帧图片、语音转写后的文字、画面内OCR识别出的文字,以及一个按时间顺序组织好的结构化时间线。最后把这套“笔记”交给Agent,让它基于这些可信内容去回答用户问题。
这个过程有点像请人帮你看一部电影,然后让他写一份带页码和引用位置的读书笔记。你不需要把整部电影塞进脑子里,只要笔记足够准确和详细,你依然能回答出“男主在第几分钟说了什么”、“那个公式出现在哪个画面里”这类细节问题。
模块拆开看,核心就是四步:抽帧、转写、OCR、对齐。抽帧解决“看画面”的问题,转写解决“听声音”的问题,OCR解决“读文字”的问题,对齐解决“时间索引”的问题。四步做完,视频就被压缩成一份几千字的文本加几十张精选图片,Agent处理起来毫无压力,而且每一步都是可控、可单独调优的。
2. claude-video Skill的整体设计:从视频到上下文的转换管线
2.1 Skill在Agent里到底扮演什么角色
在正式讲实现前,有必要先把Skill这个机制说清楚。Skill本质上就是一个带说明文档和可执行脚本的“能力插件”。它不是一个独立运行的Agent,而是一套给Agent使用的工具箱:一个
SKILL.md
描述这个工具什么时候能用、怎么用,几个脚本负责真正干脏活累活,可能还有一些静态资源用来辅助判断。
claude-video
就是这样一个Skill。它的输入是视频文件的路径,或者一个可以直接下载的视频链接;输出是一份带时间戳的视频内容分析,包括画面摘要、语音转写、OCR文字和时间线。Agent在对话中一旦判断“用户需要我理解这个视频”,就会按照
SKILL.md
里的指引,去调用脚本完成视频处理,再把结果作为上下文继续推理。
这种设计的好处是职责分离:Agent还是负责思考和对话,视频处理这种重计算活交给独立脚本。以后如果抽帧算法升级,或者换一个更强的转写模型,只需要改脚本,不用动Agent本身的逻辑。
2.2 视频处理的四个阶段:抽帧、音轨、OCR、时间线对齐
我把整个视频处理流程拆成了四个阶段,每个阶段都有明确产出:
- 抽帧 :用ffmpeg按时间间隔从视频里抽取画面,保存为JPG图片。间隔不是越短越好,短视频5秒一帧,长视频10到15秒一帧,基本能满足绝大多数需求。抽帧解决的是“画面内容”问题。
- 音轨提取与转写 :把视频里的音轨单独抽出来转成WAV,再用whisper模型转成带时间戳的文字。这一步解决“说了什么”的问题,也是视频信息量最大的部分。
- OCR识别 :对抽出来的关键帧做文字识别,提取画面里出现的字幕、PPT文字、菜单、标题等。视频里经常有“一闪而过”的重要信息,声音里不一定提到,但画面里却写了,OCR就是补这个缺口。
- 时间线对齐 :把图片文件名、转写文本、OCR结果统一换算到同一个时间轴上,生成一份结构化的JSON或Markdown。这是后期让Agent能回答“第几分钟发生了什么”的关键。
这四个阶段不是串行依赖,而是可以并行跑的。抽帧和转写互不相干,OCR又依赖抽帧结果。实际实现里,我用上下两个线程分别跑 ffmpeg 和 whisper,处理速度能快不少。
2.3 为什么不用“直接丢视频给模型”的暴力方案
这里我想专门回应一个很常见的问题:为什么不能直接把视频文件丢给AI模型,让多模态模型自己理解?我在调研阶段确实尝试过这条路,但发现至少有三个硬伤:
第一是上下文窗口瓶颈。一段10分钟的视频解出来可能有上千帧画面,即使模型能看图,也不可能一次性塞进去这么多视觉token。强行塞的结果就是模型为了“理解”而牺牲细节,只记得模糊的大意。
第二是成本和延迟不可控。视频理解类API按处理时长计费,长视频跑一次成本不低,而且等待时间很长。相比之下,抽帧和whisper转写用本地开源方案处理,只要机器性能不差,成本几乎是零。
第三是定位困难。假设用户问“这个视频里提到的那个GitHub仓库地址是多少”,如果模型只看了整段视频的概括,很难准确定位到那一帧;但如果先跑完时间线,就能把问题变成“查找第10分钟附近的OCR文本”,准确率高一个数量级。
所以我强烈建议:视频理解类能力,优先走“预处理 + 结构化 + Agent检索”的管线,而不是把原始视频当作模型的输入。你后续在这个管线上加任何功能都方便很多。
3. 核心实现:我把claude-video拆成了这些部件
3.1 Skill目录结构与SKILL.md的写法
先看整体目录结构。这是我的维护版本,你完全可以根据自己的习惯改,但核心结构建议保持一致:
claude-video/
├── SKILL.md
├── requirements.txt
└── scripts/
├── extract_frames.py
├── transcribe.py
├── ocr_frames.py
└── build_timeline.py
SKILL.md
是整个Skill的入口,里面有固定的YAML frontmatter,然后是对Agent的使用说明。我写了一个比较通用的版本:
---
name: claude-video
description: 分析本地视频或视频链接。输入为视频文件路径或URL,输出为带时间戳的图像帧、语音转写文本、OCR文字和结构化时间线。Agent在用户要求理解视频内容时必须使用此Skill。
---
# claude-video Skill
你是一个视频分析助手。你需要调用参数中提供的视频路径,执行以下步骤:
1. 用 extract_frames.py 抽取关键帧,默认每5秒抽1帧,存入临时目录。
2. 用 transcribe.py 提取音频并生成带时间戳的转写文本。
3. 对关键帧做 OCR,提取画面文字。
4. 用 build_timeline.py 把结果合并为 timeline.json。
分析时注意:
- 只依据脚本输出的结构化内容回答问题,不得自行编造视频细节。
- 回答与时间相关的问题时,必须引用具体时间戳。
- 如果视频中没有相关信息,明确回答“未在视频中找到”。
这个文件决定了Agent会不会在正确时机调用Skill,所以
description
一定要写清楚“何时使用”。我在实践里踩过一个坑:描述写得太泛,Agent有时候根本不会触发脚本,还会自己瞎编答案。
3.2 视频预处理脚本:ffmpeg抽帧与音频提取
视频处理离不开ffmpeg,这个工具基本是所有处理管线的地基。抽帧的核心命令我封装在
extract_frames.py
里,但底层调用其实是这条:
ffmpeg -i input.mp4 -vf "fps=1/5,scale=1280:-1" -q:v 3 frame_%04d.jpg
参数解释:
-
fps=1/5:每5秒抽取一帧。转化为fps就是0.2,ffmpeg会按固定时间间隔取帧,保证时间分布均匀。 -
scale=1280:-1:把画面宽度缩放到1280,高度按比例自动计算。过高的分辨率对OCR和图注没有帮助,反而占用空间。 -
-q:v 3:JPG质量参数,范围1到31,数字越小质量越高。3在我的测试里是画质和文件体积的平衡点。
音频提取命令长这样:
ffmpeg -i input.mp4 -vn -ac 1 -ar 16000 audio.wav
-vn
表示不要视频流,
-ac 1
强制单声道,
-ar 16000
是16kHz采样率。whisper模型对16kHz单声道音频兼容性最好,处理速度也更快。
有个细节要注意:如果输入是视频链接而不是本地文件,我会先用
yt-dlp
下载到临时目录,再走同一套处理流程。下载的时候建议直接提取最佳画质和最佳音频,避免转码损耗。
3.3 语音转写与画面识别:whisper + OCR
语音转写我用的whisper系列模型。如果是本地机器性能一般,推荐用
faster-whisper
,它对CPU推理做了大量优化,速度比原版whisper快不少,而且显存占用低。命令层面大概是:
whisper audio.wav --model base --language zh --output_format srt --output_dir output/
这里
--model base
是一个平衡点。base模型对普通话的识别准确率已经能到不错的水平,且速度很快;如果视频里专业术语多,再升级到
small
或
medium
。
--output_format srt
会生成带时间轴的字幕文件,这在后续对齐时间线时非常有用。
OCR部分我建议用PaddleOCR,它对中文支持比Tesseract好不少。核心代码就十几行:
from paddleocr import PaddleOCR
ocr = PaddleOCR(use_angle_cls=True, lang="ch")
def ocr_image(image_path):
result = ocr.ocr(image_path, cls=True)
lines = []
for line in result:
if line is None:
continue
for item in line:
text = item[1][0]
confidence = item[1][1]
if confidence > 0.6:
lines.append(text)
return lines
实际处理时我会把每张关键帧的OCR结果和帧对应起来,存成一个映射表。后续Agent问“屏幕上的字是什么”时,就可以按时间点回去查。
3.4 让结果“可被检索”:时间戳与分段描述
最后一步是汇总。我会让
build_timeline.py
读取抽帧文件名、SRT字幕、OCR结果,统一换算成相对视频开头的秒数,然后输出一个
timeline.json
。结构大致长这样:
{
"video": "meeting.mp4",
"duration_seconds": 3600,
"timeline": [
{
"start": 0,
"end": 5,
"frame": "frame_0001.jpg",
"ocr": ["项目进度", "季度汇报"],
"transcript": "大家下午好,我们开始今天的会议。"
},
{
"start": 5,
"end": 15,
"frame": "frame_0002.jpg",
"ocr": ["实现方案"],
"transcript": "首先看一下上个季度的整体数据。"
}
]
}
这个JSON是整个Skill的“中枢”。后续Agent分析视频时,不是把所有帧图片全部塞进上下文,而是先把timeline里的文本部分读完,再根据问题需要,决定要不要把某个时间点附近的帧图片重新加载进视野。这一步等于做了一次“粗粒度过滤”,上下文占用立刻降下来。
我在实践里发现,这种结构还有一个好处:它可以被向量化。把时间线文本写入向量数据库后,Agent就能用语义检索快速定位到“包含某个关键词的片段”,而不需要逐条遍历。视频变成长达几个小时的课程录像时,这个能力尤其重要。
4. 实操过程:从安装到第一次让Agent“看”视频
4.1 环境准备与依赖安装
如果你也想复现这套方案,先把基础环境准备好。ffmpeg是第一依赖,macOS上执行
brew install ffmpeg
,Ubuntu上用
apt install ffmpeg
,Windows用户去官网下载release包并加入PATH即可。
Python版本建议3.10以上,然后安装核心依赖。我的
requirements.txt
是这样的:
faster-whisper>=1.0.0
paddleocr>=2.7.0
paddlepaddle>=2.5.0
opencv-python>=4.8.0
yt-dlp>=2024.1.0
装完之后跑一下
python -c "import ffmpeg"
检查ffmpeg的Python binding,以及
whisper --help
看看转写命令是否可用。第一次跑PaddleOCR会自动下载模型权重,建议在网络稳定的环境里操作。
4.2 把Skill接入Claude Agent
接入步骤不复杂,我以常见的Agent配置方式为例:先把整个
claude-video
目录放到Agent技能目录,比如
~/.claude/skills/
下,然后确认Agent配置里允许使用技能文件。如果你的Agent框架支持技能热加载,放进去之后重新开启一个对话,Skill就能被发现。
触发方式有两种:一种是用户直接在对话中提出“帮我分析一下这个视频”,Agent从你的提示词里识别出意图,主动去
SKILL.md
查找并执行;另一种是手动指定Skill,调用时给Agent一个明确指令。
这里我想提醒一句:
SKILL.md
里的
description
直接决定了Agent能不能“想起来”用这个Skill。描述写成“分析视频”太模糊,最好是“当用户需要理解视频内容、总结视频字幕、查找视频画面内文字时使用”。越具体,触发概率越高。
4.3 一个完整的调用示例
假设我有一段会议视频,路径是
/data/meeting.mp4
,我想让Agent总结这次会议的决定事项。在我自己的工具链里,调用方式大概是:
claude --skill claude-video --input "/data/meeting.mp4" --prompt "总结这次会议的决定事项,并指出每个决定出现的时间点"
在Agent内部,这个请求会走一遍完整流程:
- 读取视频文件,确认格式和时长。
-
调用
extract_frames.py,生成关键帧。 -
启动
transcribe.py,提取语音转写。 - 对新帧批量OCR。
-
合并出
timeline.json。 - Agent分析时间线,组合回答。
如果你是在代码里调用Agent的API,方式类似:把视频路径放进工具参数,把问题当作用户输入。重点在于:整个视频处理过程对用户来说是透明的,用户只知道“Agent能看视频了”。
4.4 效果调优的关键参数
这部分直接给结论,都是我反复试过的参数组合。
| 场景 | 抽帧间隔 | 分辨率 | Whisper模型 |
|---|---|---|---|
| 30秒短视频 | 1-2秒 | 1280 | base |
| 5-10分钟录屏 | 5秒 | 1280 | base/small |
| 1小时课程/会议 | 10-15秒 | 960 | small/medium |
有几个额外建议:
- 视频里如果主要是静态PPT,抽帧频率可以降低,减少冗余。
- 如果视频里动作变化快,比如教程操作Demo,抽帧间隔要相应缩短到3秒。
-
Whisper的
language参数手动指定会更快,省去语言识别时间。 - 所有图片和临时文件处理完最好自动清理,避免堆积占用磁盘。
5. 常见问题与排查实录
5.1 视频抽帧后图片太多,上下文塞不下
这个是最容易遇到的问题。假设1小时视频每5秒抽一帧,那就是720张图片,如果全塞给模型,任何上下文窗口都不够用。我的解法是“两级检索”:第一级只让Agent读取时间线的文本信息;只有当问题涉及具体画面细节时,才通过脚本按时间范围加载对应帧图片。另外,我会把相邻的若干帧拼成一张“网格图”,比如把一个5分钟片段里的关键帧拼成一张2x3的大图,这样既能保留信息,又只占一次图像token。
5.2 语音转写结果和画面对不上
转写结果和画面不同步的原因一般出在音频时间轴上。ffmpeg从视频中抽取音频时,如果视频本身有编辑过的时间偏移,或者转写模型输出的偏移量判断有误差,就会出现“字幕说第10秒,但画面已经是第15秒的内容”这种情况。排查时先检查生成的SRT时间和实际画面是否一致,如果偏差固定,可以在
build_timeline.py
里加一个校正参数,手动调整偏移量。另外,whisper的
--word_timestamps
参数可以帮助得到更细粒度的时间戳。
5.3 模型幻觉:明明视频里没有,却描述得头头是道
这属于Agent常见的“补全”问题。当Agent觉得信息不足时,它会基于自己的知识库自动补全,这是大模型的固有倾向。为了抑制这种现象,我做了两件事:一是
SKILL.md
里明确写了“只依据脚本输出回答”,并且要求引用时间戳;二是每次提问时,如果问题过于宽泛而从时间线里找不到对应证据,我会要求Agent直接回答“未在视频中找到相关信息”。实测下来,幻觉出现的频率下降了一个档次。
5.4 长视频处理效率失控:一个视频跑了半小时
长视频最影响效率的是whisper转写和OCR。转写这块,如果你有NVIDIA GPU,用
faster-whisper
加CUDA能提速5倍以上;没有GPU的,可以把音频切成长度15秒左右的小段并行转写。OCR部分,不要对全部帧做识别,先筛选出画面变化较大的帧,再做OCR;如果视频画面长时间不变,中间那段直接跳过就行。
我还加了一个兜底逻辑:视频时长超过2小时,脚本会先按章节切分,分块处理,再合并时间线。虽然工程上多写一点代码,但处理长视频时稳定性明显提升。
| 现象 | 可能原因 | 快速排查/解决 |
|---|---|---|
| Agent没有触发Skill | SKILL.md描述太模糊 | 在description里加上“视频、字幕、会议、课程”等关键词 |
| 图片太多上下文爆了 | 抽帧间隔太密 | 调大抽帧秒数,或使用网格图拼接 |
| 转写和画面偏移 | 时间轴未对齐 | 用SRT时间戳重新同步,检查固定偏移量 |
| 模型开始瞎编 | 缺少防幻觉提示 | 修改SKILL.md,要求必须基于时间线作答 |
6. 写在最后:踩过坑之后的一点心得
给Agent做视频理解这个事,最关键的启发来自第一次实际跑通:Agent面对的不应该是“视频文件”,而应该是“一份有时间索引的内容摘要”。把视频转化成结构化的时间线之后,很多后面的问题——检索、摘要、问答、章节切分——都会变得简单不少。
我在这个项目里最大的心得是:不要试图做一个处理所有视频的万能Skill,而是让Skill像一把瑞士军刀,给Agent提供“抽帧”、“转写”、“OCR”、“对齐”这几个基础能力,具体怎么组合、什么时候触发,全交给Agent判断。这样单个模块保持简单,整体能力反而更强。
如果你也在折腾Agent,想把视频能力加进去,我的建议是现在就可以动手。从一个只有二三十行脚本的demo开始,拿一段录屏做测试,等跑通了再慢慢补充细节。等你把整套管线走完,再回头看那段“Agent无法处理视频”的历史,会觉得这段路走得非常值。
更多推荐
所有评论(0)