AI Agent这几天算是彻底出圈了,但很多人用下来都有同一个感觉:Agent 能读文档、能搜网页、能写代码,可一旦遇到视频内容,基本就抓瞎了。上传一段录屏、一个教学视频、一条现场监控,它就只会干瞪眼——因为主流 Agent 的默认输入通道还是文本和图片,视频这种带时间轴的多模态数据,在绝大多数 Skill 体系里都是空白。

我最近做了一个名为 claude-video 的 Skill,专门解决这个痛点,让 Agent 真正具备“看视频”的能力。它不是简单做个视频转 GIF 或者抽几帧图,而是让 Agent 能理解一段视频里发生了什么、在哪个时间点发生了什么、前后逻辑是怎样的。这篇文章我会把这个 Skill 的完整设计思路、核心技术选择、实操步骤以及我踩过的坑全部写出来,内容会很长,但保证每一段都是能直接拿去用的干货。

什么人适合看这篇文章?如果你在用 Claude、Codex 这类支持 Skill 机制的 Agent 框架,或者自己正在开发 Agent 技能插件,又或者你手头有大量视频数据需要做自动化处理和内容提取,这篇文章都值得你完整看一遍。我会从“为什么 Agent 需要看视频”讲起,拆解 Skill 的内部结构,再给出一套可以直接抄作业的实现方案。

1. 为什么要让 Agent“看视频”,以及 claude-video 的整体设计思路

先说一个大实话:让 Agent 学会看视频,并不是什么高端算法竞赛,而是一个很务实的工程需求。现在大量业务信息是沉淀在视频里的——产品演示录屏、线上会议录像、教学直播回放、工厂车间的监控记录、线下的活动记录等等。你让 Agent 去读这些内容的文字稿,很多信息会失真,因为视频里的操作动作、界面变化、现场状态、时间节奏,这些非语言信息是文字稿完全承载不了的。

如果把 Agent 的能力边界比作一个人的感官系统,文本和图片相当于触觉和视觉的静态帧,而视频则是动态视觉加听觉的综合体。没有视频理解能力的 Agent,就像一个只能看照片、不能看电影的人,信息获取的维度直接少了一大截。

1.1 从一张图到一整段视频,核心难点到底在哪

直接说结论:让模型理解视频,最简单的技术路径是“抽帧 + 图像理解”,而不是直接把视频丢给模型。原因太现实了——目前主流模型的多模态能力,都是建立在图像理解基础上的。哪怕 Claude 的上下文窗口已经很大,也不支持直接输入一个完整视频文件。

但“抽帧”这两个字说起来简单,做起来全是细节。抽多少帧?每隔几秒抽一帧?抽出来的帧怎么组织才能让模型理解时间顺序?怎么避免帧数太多把上下文窗口撑爆?这些都是问题。

我见过一些人做的“视频 Skill”,其实就是把视频用 ffmpeg 抽成一堆图片,然后直接发给模型,让模型“看一下”。这种做法在一两分钟的短视频上勉强能用,一旦视频超过三分钟,抽帧数量就会失控——假设每秒抽 1 帧,5 分钟的视频就是 300 帧。就算模型能吞下这些图片,理解质量也会急剧下降,因为模型面对一堆没有时间标签的图片,根本分不清前因后果。

claude-video 的核心设计思路,就是解决上面这一连串问题:用一套精心设计的抽帧策略控制图片数量,用时间戳对齐图片的时间轴,用两阶段的结构让模型先看“全局概览”再看“局部细节”,最后输出带精确时间码的结构化分析结果。

1.2 方案选型:为什么不做端到端视频模型,而是做 Skill 封装

这里有一个很重要的判断:市面上确实已经有一些多模态大模型能直接“看视频”,比如 Gemini 1.5 Pro、GPT-4o 这类,它们原生支持视频输入。但你让这些能力落到 Claude 的 Agent 生态里,是不现实的,因为 Claude 本身不支持视频输入。所以更务实的路线,是在 Claude 的 Skill 机制下,用工程手段去模拟视频理解能力。

我选择做 Skill 而不是做一个独立的视频分析应用,原因也很简单:Skill 是和 Agent 深度绑定的。如果你用 Claude Code 做开发,或者用 Codex 做编码 Agent,你把视频理解能力做成 Skill 之后,Agent 就能在你编写代码、调试程序、处理任务的过程中主动调用视频分析能力。比如你给它一个 UI 操作录屏,它能看出操作流程,然后直接帮你写自动化脚本。这种“Agent 内置能力”的体验,跟“打开另一个工具去分析视频”是完全不同的。

再说说 Skill 这个机制本身。Claude 的 Agent 生态里,Skill 本质上就是一个带说明文档和可执行脚本的目录结构。Agent 在运行时会去读取 Skill 的说明,知道“哦,我有这个能力”,然后按需调用。所以 claude-video 做成 Skill,是在当前技术条件下,让 Claude 系 Agent 获得视频理解能力的最合理路径。它不需要改模型,不需要改 Agent 框架,只需要加一个插件目录。

1.3 适用范围与限制,提前说清楚免得你白折腾

这个 Skill 能处理的视频类型,我实测下来包括:带字幕或画面的教学视频、软件操作录屏、人物访谈(至少能识别出画面内容)、直播回放、短视频平台的内容等。基本覆盖了日常会遇到的绝大多数视频场景。

但有几个硬性限制必须提前说:一是音频信息基本享受不到,除非你同时做音频转写并把这个能力扩展进去,否则模型只能通过画面推断内容;二是视频里的画面如果信息密度极高,比如监控视频里同时有很多区域在活动,模型会出现理解遗漏;三是超长视频(超过 1 小时)需要分段处理,不能全量塞给模型。这些限制我在后面的章节都会给出对应的解决方案。

2. 核心细节解析:Skill 的内部结构、关键机制与实现逻辑

先把我最终做出来的 Skill 目录结构列出来,然后再逐个拆解。这个结构看起来简单,但每一步都踩过坑,我把完整设计思路写出来,你照着搭就能少走弯路。

claude-video/
├── SKILL.md              # Skill 的能力说明文件,Agent 优先读取这里
├── assets/
│   └── example.json      # 示例输出格式,方便模型参考
├── scripts/
│   ├── extract_frames.py # 核心脚本:抽帧+时间码生成
│   ├── probe_video.py    # 视频探测工具
│   └── requirements.txt  # Python 依赖列表
└── references/
    └── prompt_templates.md # 提示词模板,指导模型如何分析

2.1 SKILL.md 一锤定音:先让 Agent 知道它能做什么

Skill 机制的核心就是 SKILL.md ,Agent 在运行时会把它注入到上下文里。这一份文件决定了 Agent 知不知道你有这个能力,以及它在什么场景下会主动调用。

我的 SKILL.md 开头是这么写的:

# claude-video Skill

## 能力定位
本 Skill 赋予 Agent 分析视频内容的能力。当用户提供视频文件路径,或提出与视频内容相关的问题时,Agent 应当调用本 Skill。

## 适用场景
- 用户上传或指定一个视频文件,希望了解视频内容
- 用户需要从视频中提取操作步骤、关键信息、事件时间线
- 用户需要检索视频中特定内容出现的时间点
- 用户需要对视频内容进行总结、问答、分析

## 处理流程
1. 使用 `probe_video.py` 探测视频基本信息(时长、分辨率、编码等)
2. 使用 `extract_frames.py` 按策略抽取关键帧
3. 结合帧图序列和 Prompt 模板,对视频内容进行结构化分析
4. 输出带时间码的结构化报告

这里有一个关键设计: SKILL.md 里不仅写了能力说明,还写了处理流程。因为 Agent 有“按步骤执行”的倾向,你给它一个清晰的流程,它就知道遇到视频文件时先干什么、后干什么,而不是自己瞎猜。这一步直接决定了整个 Skill 的可用性。

2.2 extraction_frames.py 的核心逻辑:抽帧策略和上下文预算控制

抽帧策略是整个 Skill 的技术核心。我先说结论:我的策略是“动态抽帧 + 场景变化检测优先”,而不是“固定间隔抽帧”。

固定间隔抽帧的问题显而易见——如果视频里前 3 分钟一直在播放静态画面,你每隔 5 秒抽一帧,抽出来的 36 张图几乎一模一样,纯属浪费上下文;如果视频后半段快速切换画面,固定间隔又可能漏掉关键瞬间。

所以我的方案是:

  1. 先探测视频时长和分辨率 ,确定视频的基本参数。
  2. 设置一个上下文预算 ,根据视频时长动态计算抽帧间隔。默认策略:视频总时长除以预算帧数,得到基础抽帧间隔。
  3. 使用场景变化检测 (利用 ffmpeg 的 select='gt(scene,0.3)' 滤镜)找出画面突变点,作为关键帧候选。
  4. 混合策略 :以关键帧候选为主,同时保证时间均匀性,避免某一时间段完全空白。

上下文的控制上,我的经验是一个包含帧图、文字分析的请求,图片部分大约每帧占 500-1000 个 token。默认预算设为 30-60 帧之间,这样的话总上下文占用可以控制在 30000 到 60000 token 以内,给后续分析文本留出足够的空间。

2.3 时间戳对齐:让模型知道“这个画面发生在第几秒”

抽帧之后如果不做时间戳对齐,模型看到的就是一堆无序图片,它只能描述「画面里有什么」,而无法回答「这个动作发生在第几分钟」。为了让 Agent 输出带时间码的分析结果,我在抽帧脚本里做了时间戳标注。

每一张抽出来的帧图,文件名格式是这样的:

frame_000123_000456.jpg

其中 000123 表示第 123 秒, 000456 表示毫秒偏移。同时,抽帧脚本会生成一份 JSON 元数据,把每一帧的时间点精确记录下来。

最关键的一步是,在提示词模板中明确要求模型“以帧图文件名中的时间戳为基准,输出事件时间线”。这样做以后,模型输出的分析报告天然就是带时间轴的,用户可以直接跳转到视频的某个位置验证内容。这一步虽然技术含量不高,但效果提升非常明显——从“模型说了什么”升级为“模型说的事情发生在第几分第几秒”,实用价值完全不同。

2.4 两阶段分析机制:先全局概览,再局部聚焦

刚开始做这个 Skill 的时候,我犯过一个错误:让模型直接对着 40 帧图片输出详细分析。结果就是模型很容易迷失在细节里,看到哪张图就描述哪张图,完全没有时间线概念,输出的内容也是碎片化的,根本没法用。

后来我改成了两阶段分析机制,效果立竿见影。

第一阶段,让模型快速浏览所有帧图,输出视频的全局概览:视频主题是什么、整体结构是怎样的、大概分几个环节、各个环节的核心内容是什么。

第二阶段,基于全局概览,让模型聚焦到具体环节,结合帧图在某一时间段内的连续变化,输出该环节的详细步骤和关键细节。

这个机制非常契合当前模型的工作方式——先建立全局认知,再看局部细节,理解质量高了一大截。具体在 Prompt 模板里怎么实现,我会在实操章节给出可直接复制的完整模板。

3. 实操过程与核心环节实现:从零搭一个完整可用的 claude-video

这一部分是纯实操,我会按步骤带你走一遍完整流程。假设你现在什么都没有,我就从环境准备开始讲。我自己用的是 macOS 环境,但同样的步骤在 Linux、Windows(WSL2)上都能跑通。

3.1 环境准备与依赖安装

首先确保你的机器上有 Python 3.10+ 和 ffmpeg。ffmpeg 是抽帧的核心工具,没有它后面的步骤都做不了。macOS 上直接 brew 安装,Linux 上用 apt 或 yum,Windows 上建议用 WSL2 或者在 ffmpeg 官网下载静态编译版本并配置好全局路径。

# macOS
brew install ffmpeg python@3.11

# Ubuntu/Debian
sudo apt update && sudo apt install -y ffmpeg python3.11 python3.11-venv

然后创建 Python 虚拟环境并安装依赖,我把依赖写在 requirements.txt 里:

mkdir -p ~/claude-video && cd ~/claude-video
python3 -m venv venv
source venv/bin/activate
pip install opencv-python-headless pillow numpy

这里我只用了 opencv 和 pillow,没有装其他重量级依赖,因为抽帧调用 ffmpeg 就能完成大部分工作,opencv 主要是用来做一些补充的图像处理和场景变化检测。装 opencv-python-headless 是因为我们只需要它的图像处理功能,不需要 GUI 相关组件,这样装出来的依赖体积更小、在服务器环境也不会报错。

3.2 probe_video.py:先探测视频基本信息

写这个脚本的目的很简单:让 Agent 在处理视频之前,先拿到视频的基本信息。实际开发中,Agent 经常拿到一个视频路径就直接开始抽帧,结果视频本身损坏、分辨率异常、或者时长特别长,抽帧脚本执行到一半就报错。先探测再处理,可以避免这种低级错误。

#!/usr/bin/env python3
"""探测视频基本信息"""
import json
import subprocess
import sys


def probe_video(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)
    video_stream = next(
        (s for s in data.get("streams", []) if s.get("codec_type") == "video"),
        None
    )
    if video_stream is None:
        return {"error": "No video stream found"}
    return {
        "duration": float(data.get("format", {}).get("duration", 0)),
        "width": video_stream.get("width"),
        "height": video_stream.get("height"),
        "codec": video_stream.get("codec_name"),
        "fps": eval(video_stream.get("avg_frame_rate", "0/1")),
        "size_mb": round(float(data.get("format", {}).get("size", 0)) / 1024 / 1024, 2)
    }


if __name__ == "__main__":
    path = sys.argv[1]
    print(json.dumps(probe_video(path), ensure_ascii=False, indent=2))

输出示例:

{
  "duration": 372.56,
  "width": 1920,
  "height": 1080,
  "codec": "h264",
  "fps": 29.97,
  "size_mb": 245.8
}

Agent 拿到这些信息之后能做什么?举个实际例子:如果视频时长 372 秒,模型就知道应该把上下文预算分成几段;如果分辨率是 4K,模型就会意识到图片文件比较大,可能需要压缩后再分析;如果时长达到 372 秒,模型会自动判断需要分段处理,而不是一次性抽完所有帧。

3.3 extract_frames.py:抽帧策略的完整实现

这个脚本是整个 Skill 的技术核心,我直接给出完整实现,然后一行一行拆解核心逻辑。

#!/usr/bin/env python3
"""按策略抽帧,并生成带时间戳的帧图"""
import argparse
import json
import os
import subprocess
import sys


def get_video_duration(video_path):
    cmd = [
        "ffprobe", "-v", "quiet", "-print_format", "json",
        "-show_format", video_path
    ]
    result = subprocess.run(cmd, capture_output=True, text=True)
    data = json.loads(result.stdout)
    return float(data.get("format", {}).get("duration", 0))


def extract_frames(video_path, output_dir, max_frames=40, scene_threshold=0.3):
    os.makedirs(output_dir, exist_ok=True)
    duration = get_video_duration(video_path)

    # 计算基础抽帧间隔:保证总帧数不超过预算
    base_interval = max(1, int(duration / max_frames))

    # 使用 ffmpeg 的场景检测过滤器抽关键帧
    select_expr = f"select='gt(scene,{scene_threshold})'"
    cmd = [
        "ffmpeg", "-i", video_path,
        "-vf", f"{select_expr},showinfo",
        "-vsync", "vfr",
        "-frame_pts", "1",
        os.path.join(output_dir, "scene_%05d.jpg")
    ]
    result = subprocess.run(cmd, capture_output=True, text=True)
    # 从 showinfo 输出中解析帧的时间戳
    timestamps = []
    for line in result.stderr.split("\n"):
        if "pts_time:" in line:
            try:
                part = line.split("pts_time:")[1].split()[0]
                timestamps.append(float(part))
            except (IndexError, ValueError):
                continue

    # 如果场景检测抽出的帧太少,用均匀间隔抽帧兜底
    if len(timestamps) < 8:
        cmd = [
            "ffmpeg", "-i", video_path,
            "-vf", f"fps=1/{base_interval}",
            os.path.join(output_dir, "uniform_%04d.jpg")
        ]
        subprocess.run(cmd, capture_output=True, text=True)
        timestamps = [i * base_interval for i in range(int(duration / base_interval))]

    # 生成带时间戳的文件名和元数据 json
    metadata = []
    files = sorted(os.listdir(output_dir))
    for i, f in enumerate(files):
        if f.endswith(".jpg"):
            if f.startswith("scene_"):
                frame_time = timestamps[i] if i < len(timestamps) else i * base_interval
            else:
                frame_time = timestamps[i] if i < len(timestamps) else i * base_interval
            frame_sec = int(frame_time)
            frame_msec = int((frame_time - frame_sec) * 1000)
            new_name = f"frame_{frame_sec:06d}_{frame_msec:03d}.jpg"
            os.rename(os.path.join(output_dir, f), os.path.join(output_dir, new_name))
            metadata.append({
                "file": new_name,
                "timestamp": frame_time,
                "time_str": f"{frame_sec // 60:02d}:{frame_sec % 60:02d}.{frame_msec // 100:01d}"
            })

    with open(os.path.join(output_dir, "frames_metadata.json"), "w") as f:
        json.dump(metadata, f, ensure_ascii=False, indent=2)

    print(json.dumps({
        "total_frames": len(metadata),
        "duration": duration,
        "metadata_file": os.path.join(output_dir, "frames_metadata.json")
    }, ensure_ascii=False, indent=2))


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("video_path")
    parser.add_argument("output_dir")
    parser.add_argument("--max-frames", type=int, default=40)
    parser.add_argument("--scene-threshold", type=float, default=0.3)
    args = parser.parse_args()
    extract_frames(args.video_path, args.output_dir, args.max_frames, args.scene_threshold)

核心逻辑有几层,我逐个讲清楚。

第一层:基础间隔计算。用 duration / max_frames 算出均匀抽帧的基础间隔,这是兜底策略。比如视频 372 秒、max_frames 是 40,那 base_interval 就是 9 秒,也就是说最坏情况下每 9 秒至少抽 1 帧,保证时间覆盖的完整性。注意 max(1, ...) 这个处理,可以防止视频太短时出现间隔为 0 的情况。

第二层:场景检测抽帧。ffmpeg 的 select='gt(scene,0.3)' 滤镜做的是画面差异分析,超出阈值的画面突变会被标记为场景切换点。这个 0.3 的阈值我调了很久才定下来——设低了(比如 0.1),一个普通的淡入淡出都会被当成新场景,帧数爆炸;设高了(比如 0.5),很多真正的关键画面切换会被漏掉。0.3 是一个相对均衡的值,能识别出大部分用意义的环境变化,同时不会因为画面细微抖动就疯狂抽帧。

第三层:兜底策略。如果场景检测抽出来的帧不足 8 张,说明这个视频画面变化非常少,比如一段纯 PPT 录屏、白板讲解类视频。这种情况下直接用均匀间隔抽帧,保证整个视频的每个阶段都有画面覆盖。

第四层:文件名时间戳化。把抽出的帧统一改名成 frame_{秒}_{毫秒}.jpg ,这一步是整个 Skill 能够输出带时间码分析的关键。帧文件名里的时间戳,会贯穿后续所有的模型分析流程。

使用示例:

python scripts/extract_frames.py /path/to/video.mp4 /tmp/frames --max-frames 40

执行完之后,/tmp/frames 目录下会生成 30-40 张按时间戳命名的 jpg 图片,以及一份记录所有帧时间点的 frames_metadata.json 。

3.4 Prompt 模板设计:决定分析质量的隐藏关键

很多人做 AI 工具只关注代码,觉得提示词是“文科生的事情”。但实际上,在 claude-video 这个 Skill 里,Prompt 模板和抽帧代码同样重要,甚至更容易决定最终输出的质量上限。

我的两阶段分析提示词模板长这样。第一阶段,全局概览:

你正在分析一段视频。以下是一组从视频中抽取的关键帧图片,文件名中的时间戳表示该帧在视频中出现的时间(格式为frame_秒_毫秒.jpg)。

请先快速浏览所有帧图,输出视频的全局概览。概览包含如下内容:
1. 视频主题:用一句话概括视频在讲什么
2. 整体结构:视频大致分为几个环节,每个环节大概在什么时间段
3. 画面类型:是纯录屏、真人出镜、PPT讲解还是混合形式
4. 核心信息:第一遍浏览后发现的关键信息点,按时间顺序列出

注意:这一阶段只需要概括性描述,不用深入细节。

第二阶段,局部聚焦:

基于你刚才的全局概览,现在请聚焦到以下时间段的画面:[起始时间] 到 [结束时间]

请结合这个时间段内的连续帧图变化,输出:
1. 该环节的完整步骤或事件顺序,标明每一步的精确时间
2. 画面中出现的关键文字、关键操作、关键状态变化
3. 如果画面中有 UI 界面,请描述界面布局和操作路径
4. 该环节与前一个环节之间的逻辑衔接关系

输出格式要求:
- 使用时间码标注关键事件,格式为 [mm:ss]
- 内容要具体,不要泛泛而谈

把这两段模板放在 references/prompt_templates.md 文件里,然后在 SKILL.md 中让 Agent“参考模板文件中对视频分析的要求”。这样做的效果是:Agent 在调用 Skill 时,会自动按照模板的逻辑去组织输出,而不是自由发挥。

3.5 完整调用链路:从视频文件到结构化分析报告

所有模块就位之后,Agent 处理视频的完整链路是这样的:

  1. 用户给出视频路径,比如“帮我分析一下 /tmp/demo.mp4 这个录屏视频”。
  2. Agent 读取 SKILL.md ,识别到这是视频分析任务,决定调用 claude-video。
  3. Agent 运行 probe_video.py /tmp/demo.mp4 ,拿到视频时长 245 秒、分辨率 1920x1080、大小约 80MB 等信息。
  4. Agent 根据时长计算抽帧策略,运行 extract_frames.py /tmp/demo.mp4 /tmp/frames --max-frames 35 。
  5. Agent 读取 /tmp/frames 下的帧图和元数据,结合 Prompt 模板进行两阶段分析。
  6. Agent 输出带时间轴的结构化分析报告,包含视频主题、完整时间线、每一步操作的精确时间。

这条链路里有几个环节是 SKILL.md 的提示词在暗中引导的。比如 Agent 默认会在抽帧前先跑一下 probe_video.py ,因为 SKILL.md 里明确写了这一步;Agent 默认会去看 references/prompt_templates.md ,因为 SKILL.md 里说明了“按照 Prompt 模板的要求组织分析结果”。

这就是 Skill 机制的强大之处——你通过文件结构和提示词,把一个复杂的处理流程“教”给了 Agent,之后 Agent 在遇到同类任务时会自动复现这个流程,完全不需要你每次再去手动指挥。

4. 常见问题与排查技巧实录

前面讲的是设计和实现,这一章我整理了在开发和使用这个 Skill 过程中遇到的最典型问题,每一个都是真实踩过的坑,你直接照着我给的解决方案去排查就行。

4.1 抽帧数量超出上下文预算,上下文被撑爆

这个问题刚上线时就遇到了。一个 40 分钟的视频,我的抽帧脚本在场景检测模式下抽出了 200 多帧,每一帧是一张 1920x1080 的图片,Agent 处理时直接把上下文窗口撑爆了,或者处理速度慢到不可接受。

排查过程:我先去看了抽帧脚本输出的日志,发现场景检测阈值 0.3 在画面切换频繁的视频上完全不够用——镜头每 5 秒切一次,一次 40 分钟的发布会视频,就可能抽出 500 多个场景帧。

解决方案是在脚本层面增加两道保险。第一道是抽帧时把分辨率降下来,默认将帧图压缩到宽度 768 像素、高度自适应,这样单帧 token 占用从 1000+ 降到 400 左右。第二道是上下文预算硬性控制——抽帧后如果帧数超过设定的 max_frames ,就按时间均匀地丢弃一部分帧,只保留时间分布均匀的 N 帧。这个逻辑我后来直接固化到了 extract_frames.py 里,新增了一个自动缩放参数,不需要 Agent 每次手动指定。

实际效果:40 分钟的视频现在默认只保留 35 帧左右,而且时间覆盖仍然均匀,分析质量没有明显下降。这个经验的核心是:抽帧数量和质量不是线性关系,超过一定数量之后,模型的分析能力不会提升,反而会因为上下文被图片挤占、文字推理空间不足而明显变差。

4.2 模型把帧图当成“一组独立的图片”,输出结果没有时间轴

这个问题也是最容易让人困惑的——代码看起来没问题,帧图也抽出来了,文件名也带了时间戳,但模型输出的分析报告完全不带时间码,就是干巴巴的描述“画面中出现了一个界面,界面上有登录按钮”。

根因几乎可以锁定在提示词上。模型默认不会主动去读文件名里的时间信息,你必须显式告诉它“帧文件名中的时间戳很重要,请根据这个时间戳组织输出”。一开始我以为只要文件名里带时间戳,模型就能自动识别,事实证明这个假设完全不成立。

我把这个要求强化到了 SKILL.md 能力和处理流程里,并且放了两三次重复强调。具体做法是在 SKILL.md 里写清楚“每一帧图的文件名都包含该帧在视频中的时间位置,分析时必须参考该时间戳,并在输出中标注 [mm:ss] 格式的时间点”。同时,在 Prompt 模板的输出格式要求里再次强调。双管齐下之后,模型输出的时间轴信息就稳定出现了。

4.3 长视频(超过 1 小时)处理效果不佳

遇到一小时以上的视频,比如培训课程录像,整套流程就会出问题。原因有两层:一是抽帧数量即使控制到 40 帧,对于一个 60 分钟的视频来说,平均每 1.5 分钟才一帧,信息密度不足以支撑模型理解细节;二是即使能抽出足够的帧,单次请求塞进所有帧图也会让上下文接近上限。

解决方案是分段处理。我的做法是在 SKILL.md 里给 Agent 加了一段处理长视频的策略说明:对于超过 30 分钟的视频,先按 10 分钟一段进行切割,每段单独抽帧、单独分析,最后把各段的分析结果拼接成完整报告。

切割操作可以直接用 ffmpeg,代码很简单:

ffmpeg -i input.mp4 -ss 00:00:00 -to 00:10:00 -c copy part_01.mp4

分段处理还有一个额外的好处:每一段的画面内容更聚焦,模型的分析精度反而更高。整个报告拼接起来之后,虽然不像单次处理那样流畅,但每 10 分钟范围内的时间轴精度从“分钟级”提升到了“秒级”,实用性大大增强。

4.4 画面中的文字内容(字幕、界面文字、PPT文字)识别不准

帧图是图片,模型对图片中文字的识别能力明显弱于对纯文本的直接读取。视频里的字幕、PPT 上的小字、界面中的微小数码文字,模型经常识别错误或者干脆忽略。

针对这个问题,我做了两个优化。第一个是分辨率控制——抽帧时把宽度从 768 提升到 1024,代价是单帧 token 占用增加一些,但文字识别的准确率明显提升。第二个是可选的 OCR 辅助——如果用户明确要求提取视频中的文字内容,我会建议 Agent 先用 OCR 工具(比如 PaddleOCR、Tesseract)把帧图中的文字提取成文本,再把文本作为辅助信息交给模型分析。

OCR 辅助这个方案用起来效果很好。遇到那种“PPT 翻页教学视频”,画面里全是文字,模型直接看图片会漏掉大量信息,但先过一遍 OCR、把文字转成纯文本,模型对文字内容的理解精度直接上一个台阶。不过 OCR 也有适用范围——它只能提取静态文字,画面中的动态变化、动作逻辑还是要靠模型结合帧图去理解。

这里我还想提一个场景,就是视频里是真人操作界面,比如鼠标移动、点击、拖拽这类操作。模型看静态帧图时,经常分不清操作的前后顺序。我的方案是让抽帧脚本在时间维度上更加密集——不是每隔 9 秒抽一帧,而是在算法检测到画面变化激烈的时间段,自动把抽帧间隔缩小到 2-3 秒。这个“动态加密抽帧”功能我也加到了脚本里,它对操作类视频的效果提升非常明显。

4.5 问题速查表

问题现象 可能原因 解决方案
帧数太多,上下文被撑爆 场景检测阈值过低或视频切换频繁 调高 scene_threshold 到 0.4,或降低 max_frames
模型不输出时间码 Prompt 未显式要求按帧文件名时间戳输出 在 SKILL.md 和 Prompt 模板里重复强调时间码输出要求
长视频分析质量差 帧数稀疏,上下文不足 按 10 分钟分段抽帧分析,最后拼接结果
帧图里文字识别不准 分辨率不足或者文字太小 提高抽帧宽度到 1024,必要时加 OCR 辅助
画面变化相关动作看不清 抽帧间隔太长,关键动作被漏掉 开启动态加密抽帧,在变化激烈时段缩小抽帧间隔
ffmpeg 执行报错,找不到命令 环境变量未配置 安装 ffmpeg 并确保全局路径可用

5. 从“能看视频”到“看懂视频”:几种进阶用法

基础版本做完之后,我花了些时间继续扩展它,这里分享几个我实测效果很好的玩法,你可以按需自取。

5.1 给代码 Agent 增加界面操作理解能力

这是我觉得最有价值的一个扩展方向。在开发 Agent 工具链的时候,经常需要让 Agent 分析 UI 操作录屏。比如给 Agent 一段“用户操作某 App 完成注册”的录屏,它需要理解操作者的每一步操作顺序、点击了哪些区域、输入了什么内容,然后基于这些信息自动化生成 UI 自动化脚本。

claude-video 在这个场景下的表现非常不错。因为录屏画面通常是静态界面加鼠标动作,场景变化检测会聚焦在用户点击和页面跳转的关键瞬间,抽帧密度天然集中在操作发生时。模型结合帧图序列,基本能还原完整的操作链路,然后我再用一个定制 Prompt 引导它输出成自动化测试脚本的格式。

实测下来,对操作类录屏,模型还原操作步骤的准确率相当高,至少在生成初版自动化脚本这个场景里足够实用。当然,复杂操作(比如带有深层菜单跳转、拖拽排序这类动作)还是需要人去做校验,指望完全无人接手不现实。

5.2 结合音频转写,做多模态视频内容提取

之前我提到纯 claude-video 只能分析画面,音频信息基本享受不到。在 A 面很多视频里,音频才是核心信息,尤其是访谈、会议、课程这些场景。

后来我加了音频转写模块,把视频中的音频抽出后转成文本,再和画面分析结果合并。合并的方式不是简单拼接,而是以时间戳为锚点对齐——画面分析的结果按时间轴排列,音频转写的结果也按时间轴排列,模型的综合分析报告就是音画同步的。

这个扩展让 claude-video 从“画面理解”升级成“多模态内容理解”。举个例子:一个产品发布会视频,光看画面你能知道发布了什么产品,但结合音频转写,你能知道官方讲的口径是什么、重点强调了哪些特性、有没有隐藏的信息点。合并后的分析报告,信息密度完全不是一个量级。

音频转写方案我用的开源工具,比如 faster-whisper,速度和精度都够用。如果你想走这条路线,记得抽帧脚本里的时间戳体系和音频转写的时间戳体系要严格对齐,否则后续模型分析会出乱子。

5.3 定时任务跑批,批量处理视频内容

如果只是偶尔分析一两个视频,手敲命令也没问题。但如果是运营团队需要批量处理大量补充素材,或者媒体团队需要对历史视频库做一次全面的内容盘点,你就需要把整套逻辑封装成定时任务或批处理脚本。

我的做法是写了一个 batch_process.py,输入一个视频目录,遍历所有视频文件,依次执行抽帧和两阶段分析,最终输出一个汇总报告目录。整个批处理流程跑完,每个视频都会生成一份独立的 Markdown 分析报告,目录下还有一个索引文件,列出所有视频的主题、时长、核心内容、关键词。

批处理模式里我加了一些容错逻辑——单个视频处理失败不会中断整个任务,而是在索引文件中标记为“处理失败+失败原因”,等全部跑完再统一排查。这些容错机制,在真正面对几十上百个视频的批量场景时非常关键。

6. 我的体会:Agent Skill 的本质是“经验沉淀”,不只是“功能封装”

最后说一点项目之外的思考。做 claude-video 这个 Skill 的过程让我意识到,Agent Skill 这种模式,它的意义不在于“给 Agent 加一个功能”,而在于“把处理某个问题的完整经验沉淀成一个 Agent 可复用的能力单元”。

你看这个 Skill 里最有价值的东西是什么?不是抽帧脚本本身,抽帧脚本只是几十行调用 ffmpeg 的代码,网上随便搜都有。真正有价值的是隐藏在脚本和 Prompt 里的那些决策:为什么阈值要设 0.3 而不是 0.5,为什么抽帧数量要控制在 40 帧而不是 100 帧,为什么两阶段分析比一次性分析效果好,为什么文件名里的时间戳必须反复在 Prompt 中强调。

这些决策,是从一次次测试、一次次看模型输出、一次次发现“这里不对、那里不行”的过程中沉淀出来的。而 Skill 这种机制,刚好提供了一个把这些经验打包、让 Agent 直接继承的载体。以后不管是自己换新项目,还是分享给同行复用,这些经验都能被完整地传递下去。

如果你也在折腾 Agent 相关的东西,我的建议是这样的:与其去追捧那些看起来很宏大的 Agent 框架,不如从一个小而具体的场景入手,把一个能力打磨透,做成一个 Skill。 claude-video 只是我众多尝试中的一个,但它让我体会到,Agent 能力的边界,往往不是模型决定的,而是你愿不愿意花时间去把一个场景的细节打磨到可落地的程度。

这套技能,现在就可以去试试。找出你手头一个一直觉得“如果能自动分析就好了”的视频,把它丢给带了 claude-video 的 Agent,看看它给你输出什么结果。你会发现,Agent 距离真正“看懂世界”又近了一步——而这一步是你亲手推过去的。

Logo

邀请您加入社区

更多推荐