在聊今天这个项目之前,我想先抛一个问题:你的 Agent 现在到底卡在哪?
我做了快一年的 Agent 应用,最深的体会是——文本能力早就不是瓶颈了。Claude、GPT 这些模型处理文档、写代码、跑分析,已经能做到让我觉得“这钱花得值”。但只要你把一段视频丢给 Agent,它基本就麻了。截图?OCR?“我猜一下内容”?统统顶不住。视频是包含时间线、画面、语音、字幕、动作变化的复合信息流,你让它“干看”,它只能干瞪眼。
后来我研究了一段时间 Anthropic 的 Skill 机制,又结合视频处理的老工具链,自己动手搓了一个叫claude-video的 Skill,让 Agent 真正拥有了“看视频”的能力。它可以自动分析一段视频的完整时间线,抽帧、听声、识别字幕、理解画面内容,最后给出一份结构化的“视频解读报告”。你可以拿它去总结一场直播、分析产品演示录屏、审阅监控片段、甚至批量处理会议录像。
这篇内容里,我会把我自己的设计思路、踩过的坑、完整的目录结构、脚本逻辑、测试结果全部分享出来。照着做,你也能给 Agent 装上一双眼睛。
1. 为什么非要用 Skill,而不是 MCP 或微调
在动手之前,我其实纠结了好几天:到底是给 Agent 做 MCP 服务,还是直接写个工具脚本,或者干脆微调一个多模态小模型?最后我选了 Skill,这个决定是有依据的。
1.1 Skill 机制的天然优势
Anthropic 的 Skill 本质上是一个带结构描述文件和可执行脚本的“技能包”。Agent 在运行时会读 SKILL.md,就像人拿到一份使用说明书,知道什么时候该调用它、需要什么输入参数、按什么流程执行。这个机制和 MCP 的关键区别在于:Skill 更轻,更贴近“教一个聪明人干活”,而不是“给一个服务写接口”。
对于视频理解这种流程相对固定的任务,Skill 再合适不过。视频处理的逻辑链条很明确:拿元信息 → 抽帧 → 提取音频 → 转写 → 视觉分析 → 汇总。这个流水线不需要 Agent 即兴发挥,它需要的是“老师傅”的经验沉淀——在什么时候抽帧、用多密的间隔、怎么省 Token、怎么对齐时间戳。Skill 恰好能把这一整套经验固化下来,Agent 只需要按照说明调用即可。
1.2 不选 MCP 和微调的理由
MCP 不是不好,它适合“工具众多、需要 Agent 动态发现”的场景。但视频分析就那几步,引入一个完整 MCP 服务,你还得维护服务注册、工具名录、安全边界,属于用大炮打蚊子。
微调多模态模型就更不现实了。视频帧说白了就是一堆图片按时间排列,多模态模型本身就能看单帧,它缺的不是“视觉能力”,而是“怎么高效地看完一整个视频”的流程管理。微调一个模型来处理这种流程问题,成本和收益差得太远。
所以我的方案是:用 Skill 管流程,用脚本管脏活累活,用 Claude 的视觉能力管画面理解,用 Whisper 管语音识别,最后让 Agent 自己整合所有中间结果。
1.3 这套设计能解决什么真实问题
我在构思 claude-video 时,脑子里想的是几个非常具体的痛点:
- 每次给的视频时长不一样,抽帧间隔总不能是写死的吧?
- 视频里有语音也有画面,光听声音会漏掉视觉信息,光看画面会漏掉台词,怎么同步起来?
- 视频动辄几十分钟,抽出来的帧可能有几百张,直接全塞给模型,上下文窗口直接爆掉。
- Agent 要能回答用户的问题,比如“视频里那个人穿了什么颜色的衣服”“这段会议最后达成了什么决议”,它得知道去哪里找答案。
这些问题不是单一模型能解决的,而是需要一个设计得当的流水线。claude-video 就是围绕这些痛点设计的。
2. claude-video 的整体架构与工作流设计
这套系统的核心设计哲学,我总结成一句话:先拆解,再压缩,最后精准调用。视频的信息量太大了,不可能让模型直接“看完”,必须先把视频拆成模型能理解的单元,再按需调用。
2.1 五步流水线
我把整个视频理解过程拆成了五个步骤,形成一条流水线:
视频信息解析:用 ffprobe 读取视频的时长、分辨率、编码格式、帧率、是否有音轨。这些参数直接决定后面的抽帧策略和音频处理方式。
自适应抽帧:根据视频时长,动态计算抽帧间隔。短视频加密抽,长视频放宽间隔,关键场景额外补帧。
音轨提取与转写:把音频从视频里剥离出来,交给 Whisper 转成带时间戳的文本。这样 Agent 就知道“哪句话是哪一秒说的”。
关键帧视觉分析:对抽出来的帧图,调用多模态模型做画面描述。这一步是 Agent “看见”画面的关键,它能识别出画面中的人物、物体、动作、场景。
时间线汇总与结构化输出:把语音转写文本和画面描述按时间对齐,生成一份完整的视频解读报告,支持按时间线浏览、关键词检索、内容问答。
2.2 为什么抽帧是“关键中的关键”
如果说整个流水线里哪个环节最影响最终效果,那一定是抽帧策略。抽得太密,几百张图把 Token 烧光,分析反而浮于表面;抽得太疏,关键画面(比如广告里的产品名、PPT 里的一页关键数据)可能直接错过。
我的策略是“双层抽帧”:
- 基础抽帧:按时长动态设定间隔,比如 1 分钟以内的视频每 2 秒抽一帧,1-10 分钟的视频每 5 秒抽一帧,超过 10 分钟的视频每 15 秒抽一帧。
- 场景补帧:用 ffmpeg 的场景检测功能,检测镜头切换的位置,在切换点前后各补抽一帧。这样不管视频是静态访谈还是快节奏广告,都不会漏掉关键画面。
2.3 信息压缩与 Token 管理
很多人在做类似工具时容易忽略一个工程问题:Token 预算。一段 10 分钟的视频,按每 5 秒一帧算,是 120 帧;每帧经过视觉模型编码后,即便压缩也有不小的消耗。120 帧全丢进 Claude 的分析上下文,既不经济,也会让模型“看得太多但记不住重点”。
我的处理方式是“两级压缩”:第一级,抽帧后先做一次小模型批次初筛,剔除模糊帧、重复帧;第二级,视觉分析时引导模型用一段紧凑的结构化描述输出每帧内容,而不是随便写一段话。最后再让 Claude 基于全部帧描述和转写文本,生成整体理解。这样 Token 消耗能降低 40%-60%,理解质量反而更高。
2.4 Skill 目录结构与依赖
claude-video 的目录结构完全遵循 Claude Skill 的规范:
claude-video/ ├── SKILL.md ├── scripts/ │ ├── video_info.py │ ├── frame_extract.py │ ├── audio_extract.py │ ├── transcribe.py │ ├── vision_analyze.py │ └── build_report.py ├── runtime/ │ └── (运行中生成的临时文件) └── requirements.txt依赖方面,我尽量精简:ffmpeg 和 ffprobe 是系统级工具,负责所有音视频处理;whisper 负责语音转写;其余就是 requests 和基础的 Python 库。Claude 的视觉能力通过 API 调用,这样我不用在本地跑视觉模型,省下不少折腾。
3. 核心模块实现细节拆解
这一节是全文的精华。我会把每个模块的核心逻辑、关键代码、参数设计思路都讲清楚。你在自己的项目里可以单拎出来用。
3.1 入口设计:SKILL.md 怎么驱动 Agent
SKILL.md 是整个 Skill 的“大脑”。Agent 会先读这个文件,再决定怎么调用脚本。所以在设计时,我把描述写得尽量具体:什么时候用、需要用户提供什么、输出什么格式、限制条件是什么。
我的 SKILL.md 核心部分长这样:
--- name: claude-video description: 分析视频文件内容。当用户提供视频文件路径或URL时,自动执行帧提取、音频转写、场景分析和时间线汇总,生成结构化视频解读。适用于视频总结、内容检索、会议记录分析、产品演示解读等场景。 --- # claude-video 让 Agent 具备视频内容理解能力。输入一个视频文件路径或URL,输出结构化视频分析报告。 ## 输入参数 - `video_path`:必填。本地视频路径或网络视频URL。 - `mode`:可选。`full`(完整时间线分析,默认)、`summary`(仅摘要)、`qa`(仅回答用户关于视频的问题)。 - `question`:可选。当 mode 为 `qa` 时,用户的具体问题。 ## 工作流程 1. 检查视频是否存在,格式是否支持;网络URL先下载到本地。 2. 调用 scripts/video_info.py 获取视频元信息。 3. 根据视频时长计算抽帧参数,调用 scripts/frame_extract.py。 4. 检测到音轨时,调用 scripts/audio_extract.py 提取音频,并调用 scripts/transcribe.py 转写。 5. 调用 scripts/vision_analyze.py 对关键帧做视觉描述。 6. 调用 scripts/build_report.py 整合所有中间结果,生成最终报告。这里有个小技巧:SKILL.md 里的工作流程是给 Agent 看的指令,不是给代码看的。Agent 会根据这个流程自己决定调用顺序。所以我特意写得“像一份任务清单”,而不是“像一段伪代码”。
3.2 video_info.py:拿到视频的“体检报告”
这一步是所有后续操作的依据。没有准确的时长和帧率信息,抽帧参数就全靠猜。
import json, subprocess, sys def get_video_info(video_path): cmd = [ "ffprobe", "-v", "quiet", "-print_format", "json", "-show_format", "-show_streams", video_path ] result = subprocess.run(cmd, capture_output=True, text=True) data = json.loads(result.stdout) info = { "duration": float(data["format"].get("duration", 0)), "size": int(data["format"].get("size", 0)), "has_audio": False, "width": 0, "height": 0, "fps": 0, "video_codec": "", "audio_codec": "", } for stream in data["streams"]: if stream["codec_type"] == "video": info["width"] = int(stream.get("width", 0)) info["height"] = int(stream.get("height", 0)) info["fps"] = eval(stream.get("avg_frame_rate", "0/1")) # 如 30000/1001 info["video_codec"] = stream.get("codec_name", "") elif stream["codec_type"] == "audio": info["has_audio"] = True info["audio_codec"] = stream.get("codec_name", "") return info if __name__ == "__main__": print(json.dumps(get_video_info(sys.argv[1]), ensure_ascii=False))这里需要注意,avg_frame_rate返回的是分数形式的字符串,比如“30000/1001”代表 29.97fps。直接用eval简单粗暴,但胜在可靠。回头我建议你改成fps = int(frame_str.split("/")[0]) / int(frame_str.split("/")[1]),以防 eval 出幺蛾子。
3.3 frame_extract.py:自适应抽帧策略
抽帧是决定最终效果的上限。我在这个模块里做了三件事:基础抽帧、场景补帧、去重压缩。
import subprocess, os, sys def calc_interval(duration): if duration <= 60: return 1.0 elif duration <= 600: return 5.0 elif duration <= 1800: return 15.0 else: return 30.0 def extract_frames(video_path, output_dir, duration, fps): interval = calc_interval(duration) base_pattern = os.path.join(output_dir, "frame_%06d.jpg") # 基础抽帧:按时间间隔 base_cmd = [ "ffmpeg", "-i", video_path, "-vf", f"fps=1/{interval},scale=1280:-2", "-q:v", "2", base_pattern ] subprocess.run(base_cmd, check=True) # 场景补帧:检测镜头切换点 scene_pattern = os.path.join(output_dir, "scene_%06d.jpg") scene_cmd = [ "ffmpeg", "-i", video_path, "-vf", "select='gt(scene,0.35)',scale=1280:-2", "-vsync", "vfr", scene_pattern ] subprocess.run(scene_cmd, check=True) return os.listdir(output_dir)抽帧时我统一把画面宽度缩放到 1280 像素。为什么不保留原分辨率?因为视觉模型吃的是图像的语义内容,不是像素细节,1280 宽已经能识别出画面里的物体、文字、人脸轮廓。分辨率太高只会浪费 API 的图片处理耗时和 Token。
场景检测阈值scene,0.35是我反复试出来的。阈值太高,场景切换会被漏掉;太低,每一帧都像“变化”,补帧就失去意义。0.35 适合大多数视频内容,包括讲话场景和演示录屏。
3.4 audio_extract.py 与 transcribe.py:让 Agent“听”到声音
视频里最重要的信息通道往往不是画面,而是语音。尤其对于会议录像、访谈、课程回放,画面内容反复就那么几个镜头,但语音信息量极大。所以音频转写这一步不能省。
提取音频很简单:
ffmpeg -i input.mp4 -vn -acodec pcm_s16le -ar 16000 -ac 1 audio.wav一个关键设置:采样率统一降到 16000、单声道。这是 Whisper 的标准输入格式,能提高转写速度和准确率。采样率太高反而容易引入噪声、增加文件体积,转写速度也变慢。
转写脚本:
import whisper, json, sys def transcribe(audio_path): model = whisper.load_model("small") # 可根据需要选 base/small/medium result = model.transcribe(audio_path, verbose=False) segments = [] for seg in result["segments"]: segments.append({ "start": round(seg["start"], 2), "end": round(seg["end"], 2), "text": seg["text"].strip() }) return segments if __name__ == "__main__": segments = transcribe(sys.argv[1]) print(json.dumps(segments, ensure_ascii=False))我默认用small模型。base模型中文识别错误率高,medium和large速度太慢且显存要求高。small在中文、英文、中英混说场景下表现均衡,是性价比最高的选择。如果你处理的是纯英文且对速度要求高,可以退回base。
3.5 vision_analyze.py:Agent 的“眼睛”
这一模块是 claude-video 最关键的部分,调用视觉模型对每一帧做结构化描述。我这里的做法是:把每张帧图用 base64 编码,通过 API 发给多模态模型,再让模型返回 JSON 格式的帧内容描述。
import base64, json, requests, os, sys API_URL = "https://api.anthropic.com/v1/messages" MODEL = "claude-sonnet-4-20250514" # 实际环境请按可用模型调整 def encode_image(path): with open(path, "rb") as f: return base64.b64encode(f.read()).decode("utf-8") def analyze_frame(image_path): image_b64 = encode_image(image_path) headers = { "x-api-key": os.environ["ANTHROPIC_API_KEY"], "anthropic-version": "2023-06-01", "content-type": "application/json", } prompt = """请分析这张视频截图,输出如下 JSON: { "scene_type": "室内/室外/屏幕录制/人群/其他", "main_subjects": ["主体1", "主体2"], "actions": ["正在进行的动作"], "visible_text": ["画面中可见的文字"], "visual_detail": "一句话描述整体画面" } 要求简洁准确,不要想象画面中不存在的内容。""" payload = { "model": MODEL, "max_tokens": 600, "messages": [{ "role": "user", "content": [ {"type": "image", "source": { "type": "base64", "media_type": "image/jpeg", "data": image_b64 }}, {"type": "text", "text": prompt} ] }] } resp = requests.post(API_URL, headers=headers, json=payload) data = resp.json() text = data["content"][0]["text"] return json.loads(text)这段代码里的提示词我反复打磨过。关键点在于明确要求模型不要想象不存在的内容——我测试时发现,不写这句,模型会脑补画面里没有的人物和台词,非常致命。
另外,API 地址和模型名称我建议从环境变量读取,不要写死在代码里。不同账号可用的模型不太一样,写死了到时候换模型还得改代码。“claude-sonnet-4-20250514”只是一个示例,你按自己环境的实际模型名替换。
3.6 build_report.py:把多模态信息编成一份报告
最后一步是整合。这一步我不写太多代码,因为核心是“提示词设计”。我会把帧描述、转写文本,按时间戳排序后拼成一个长上下文,然后让 Claude 生成结构化报告。
报告包含几大块:
- 视频概览:总时长、主题、类型、整体内容一句话总结。
- 时间线事件:按时间排序的关键事件,每个事件包含时间段、内容描述、信息类型(视觉/语音/文字)。
- 核心信息提取:涉及的人、提到的数字、出现的产品名、结论性语句。
- 问答索引:告诉 Agent 用户可能问哪些问题,答案去哪个时间段找。
生成报告时,我有一个重要的提示词:
你是视频分析助手。下面是一段视频的抽帧描述和语音转写内容。请整合成结构化报告。注意:语音转写可能有不准确的地方,请优先采用多个信息源相互印证的内容;画面描述可能存在重复,请合并同类信息;对无法确认的内容,标注“无法确认”,不要推测。
这样一份报告,既能让用户直接阅读,也能让 Agent 后续基于它做问答。我把问答能力设计成“检索式问答”而不是“自由发挥式问答”,因为前者有据可依,准确率高得多。
4. 从零部署与实测:拿真实视频跑一遍
光说不练假把式。我拿了一个 12 分钟的“智能家居产品发布会录像”做了完整测试。下面是我的实操过程,包括环境、依赖、命令和实际输出。
4.1 环境准备与依赖安装
我是在 Ubuntu 22.04 服务器上搭建的,8 核 CPU、16GB 内存、无 GPU。这个配置跑 Whisper small 模型完全够用,只是比 GPU 慢一些。
# 安装 ffmpeg 和 ffprobe sudo apt update && sudo apt install -y ffmpeg # 安装 Python 依赖 pip install openai-whisper requests # 克隆项目并安装 git clone https://your-repo/claude-video.git cd claude-video pip install -r requirements.txt # 配置 API Key export ANTHROPIC_API_KEY=sk-xxxx如果你没有 GPU,建议把 Whisper 模型换成base,速度能快一倍以上,识别准确率对清晰的发布会录音来说差距不大。如果是在 Mac 上测试,也可以用whisper的mlx版本,M 系列芯片上跑得飞快。
4.2 执行一次完整分析
我写了一个run_analysis.sh脚本,把这五个步骤串起来:
#!/bin/bash VIDEO=$1 WORKDIR="runtime/$(basename $VIDEO | tr '.' '_')" mkdir -p $WORKDIR/frames $WORKDIR/audio # 1. 视频信息 python scripts/video_info.py $VIDEO > $WORKDIR/info.json # 2. 抽帧 python scripts/frame_extract.py $VIDEO $WORKDIR/frames # 3. 提取音频并转写 ffmpeg -i $VIDEO -vn -acodec pcm_s16le -ar 16000 -ac 1 $WORKDIR/audio/audio.wav -y python scripts/transcribe.py $WORKDIR/audio/audio.wav > $WORKDIR/transcript.json # 4. 关键帧视觉描述(这一步会调用 API,时间较长) python scripts/vision_analyze.py $WORKDIR/frames > $WORKDIR/vision.json # 5. 生成结构化报告 python scripts/build_report.py $WORKDIR/info.json $WORKDIR/transcript.json $WORKDIR/vision.json > $WORKDIR/report.md echo "完成!报告路径:$WORKDIR/report.md"整个过程跑下来大约花了 6 分钟,其中视觉分析占了大头,因为 12 分钟的视频抽了约 200 帧,每帧都要调一次 API。转写用 Whisper small 花了不到 1 分钟。
4.3 实测输出质量评估
我挑几段报告的真实内容,能直观展示效果:
视觉识别:帧图里出现 Smart Lock 产品时,模型成功识别出“指纹识别区域”“门锁主体”“LED 指示灯闪烁”这些视觉细节。这说明只要抽帧没有错过关键画面,视觉模型完全能读懂画面信息。
语音转写:发布会中提到的“HomeKit 兼容”“支持 Matter 协议”“$199 首发价”全部被正确转写。但有一句“AI 加持的自动化”被误转为“AI 加持的自动挂”,这种同音词错误在中文转写里很常见,所以我在汇总报告的提示词里要求:遇到关键数字和产品名,要结合画面文字交叉验证。
时间线对齐:报告里出现了一条很漂亮的时间线:“00:00-00:45 开场介绍品牌理念;00:45-02:30 展示 App 界面;02:30-05:10 发布第一款智能门锁...”这说明抽帧、转写、对齐这一步的精度是够用的。
4.4 问答模式实测
我特意测试了qa模式,问 Agent:“这个视频里智能门锁支持哪些通信协议?”Agent 没有直接看视频,而是先调用脚本生成中间结果,再在报告里检索到“Matter、HomeKit、Wi-Fi、蓝牙”这几个关键词,最后给出答案。这比单纯把视频丢给模型然后硬问,准确率高了一个量级。
5. 常见问题与避坑指南
我在开发和使用 claude-video 的过程中,踩了不少坑。挑几个最典型的分享出来,能帮你省下至少半天的排查时间。
5.1 API 返回 400 错误,提示图片过大
这是最常见的坑。API 对图片大小有限制,超过限制直接报 400。我最初就是直接传原始帧,结果一张 4K 帧 5MB,API 直接拒了。
解决方案是在抽帧时就统一缩放:scale=1280:-2。另外建议用 JPEG 格式、质量参数-q:v 2(高质量),这个组合能在画质和体积之间取得平衡。处理后每帧大约 100-300KB,完全不会触限。
5.2 Whisper 中文转写出现错别字
中文同音字错误太常见了,尤其是人名、产品名、专业术语。我的经验是:
- 如果视频有字幕,优先做 OCR,用字幕内容和语音转写互相校正。
- 在产品名、专有名词出现密集的场景,可以在提示词里给 Whisper 传
initial_prompt,预热专有名词列表。Whisper 支持initial_prompt参数,你可以传入“本视频会提到以下词汇:Matter, HomeKit, 智能门锁,等等”。
5.3 帧数太多,Token 预算爆炸
10 分钟的视频,按每 5 秒一帧抽,是 120 帧;每帧视觉描述要消耗几百 Token,总消耗轻松破万。更麻烦的是,同时送入上下文的图片越多,模型越容易“忽略”部分内容。
我的解法是分两次压缩:
- 第一层:抽帧后立即删除模糊帧(用 OpenCV 的 Laplacian 方差判断,方差低于阈值就删)和画面几乎相同的重复帧(用图像哈希比较)。
- 第二层:视觉分析时直接要求模型输出紧凑 JSON,而不是长篇大论。
实测下来,200 帧可以压缩到 80 帧左右,Token 消耗降低一半以上,而报告质量没有下降。
5.4 直播流或 URL 输入超时
一开始我支持直接传 URL,但测试发现网络视频经常下载超时。后来我改用“优先本地文件,URL 先异步下载”的策略。如果视频很大(超过 500MB),提示用户手动下载会更好,因为 API 环境的下载速度不可控。
5.5 长视频抽帧场景补帧反而重复
场景检测在视频拍摄质量较差、镜头抖动明显时,会把每一帧都判定为“场景变化”,导致补帧数量爆炸。这个问题的解法是给 ffmpeg 的场景检测加一个最小间隔限制:在select='gt(scene,0.35)*not(char(1,T))'这类表达式里做手脚比较麻烦,更简单的方式是设置-vsync vfr和min_interval=1,或者干脆把场景阈值从 0.35 调到 0.5。
5.6 最后的排查速查表
| 症状 | 可能原因 | 解决方案 |
|---|---|---|
| ffprobe 解析不到时长 | 视频格式特殊或未完成下载 | 用ffprobe -show_format确认;损坏文件建议重新转码 |
| 抽帧结果为 0 张 | 视频编码 ffmpeg 不支持 | 先统一转码为libx264 |
| Whisper 转写为空 | 视频没有音轨或音轨损坏 | 检查has_audio;手动播放音频确认 |
| 视觉模型返回空 | 图片格式不支持或 base64 编码错误 | 确认 media_type 与图片格式一致 |
| 报告时间线乱序 | 脚本并发处理导致顺序混乱 | 汇总前按时间戳排序,不要按文件名字典序 |
| Token 消耗过高 | 压缩层失效 | 检查去重阈值,适当提高帧删除标准 |
6. 后续我打算怎么做
第一次把 claude-video 跑通、看到那份完整报告的时候,说实话有点兴奋——不是因为它多复杂,而是因为整个 Agent 的能力边界一下子被拓宽了。之前 Agent 只能处理能“读”的信息,现在它能“看”、能“听”了,这意味着会议纪要、内容审核、视频检索这些需求,都可以在上面快速搭建。
我接下来想做的有三件事:第一,把音频转写模型换成更适合中文的 FunASR,进一步降低错别字率;第二,给视觉分析加上“主人公跟踪”能力,让 Agent 能回答“这个人什么时候出现的”这类问题;第三,把 claude-video 封装成可以直接给其他 Agent 复用的 CLI 工具,做成一个独立可安装的 Python 包。
最后再啰嗦一句实践中的感受:视频理解这个方向,最大的坑不是模型能力不够,而是工程流程太随意。抽帧节奏、存储管理、Token 预算、提示词约束,每一个细节都决定上线的效果。claude-video 这个 Skill 是我目前比较满意的一版方案,希望你能拿它做起点,做出更适合自己场景的视频理解工具。