- AI 应用
- 人工智能
- AI Agent
- 本地部署
- 前端
- 后端
- 工作流自动化
【免费下载链接】ekko-studio
Ekko Studio is a local-first AI workspace for multi-agent chat, coding, and visual workflows, available on desktop and the web.
导读
video-frames是 Ekko Agent 内置的一组可复用 Skill,用于从本地视频中按时间戳或精确解码帧序号抽取单张图片,供模型做画面检查、生成缩略图与视觉调试。本文以 SKILL.md 为核心骨架,逐条拆解其前置条件、三种抽帧命令、输出格式选择与输出校验要求,并结合仓库中 frame.sh 的完整实现讲解底层 ffmpeg 命令构造与参数校验逻辑,最后深入 skills.ts 与 runtime.ts,说明skill_view、skill_list的调用协议以及 Skill 自动路由机制。读完本文,你可以直接在自己的 Ekko Agent 会话中执行抽帧命令,也能复刻一个健壮的本地视频帧提取脚本,并理解 Skill 在被模型调用前后的完整生命周期。
一、Skill 是什么:为什么视频抽帧需要做成 Skill
Ekko Agent 将“可复用的、非显而易见的操作指引”封装为 Skill,每个 Skill 至少包含一个带 frontmatter 元数据的SKILL.md(参考 skill-creator/SKILL.md 中定义的 Skill 结构规范)。video-frames正是这类封装的典型:抽帧不是一句“用 ffmpeg 截一张图”就能稳定完成的,它涉及:
- ffmpeg 是否安装的前置检查;
- 按时间戳定位(
-ss)与按帧序号精确定位(select=eq(n\,N))两种截然不同的寻址语义; - 输出目录创建、输出格式(JPEG/PNG)取舍;
- 结果文件存在性校验。
把这些易错点固化进 Skill,模型每次只需按指令调用配套脚本即可得到一致结果。在 README.md 的内建 Skill 清单中,video-frames与image-gen、grok-image-to-video等一同随包自动安装到每个 Profile。
二、前置条件:先探测 ffmpeg,绝不静默安装
Skill 文档明确要求先执行环境探测:
command -v ffmpeg- 若命令存在,
command -v会输出 ffmpeg 的绝对路径,探测通过; - 若 ffmpeg 缺失,不允许静默安装,而应向用户说明需要安装什么(如系统包管理器中的 ffmpeg 包)、为什么需要它,由用户决定是否安装。
这一约束同样体现在 frame.sh 的运行时防线中:脚本在真正执行 ffmpeg 之前再次做command -v ffmpeg检查,失败则以“ffmpeg not found in PATH”退出并返回非零状态码。脚本前置检查与 Skill 文档指引形成双重保险——文档层约束 Agent 的行为习惯,脚本层约束每次执行的安全性。
三、三种抽帧模式:命令、语义与适用场景
Skill 的核心操作是调用其捆绑脚本frame.sh。skill_view在返回 SKILL.md 内容的同时会给出该 Skill 的baseDirectory(即 Skill 所在目录),因此文档中的命令都以<baseDirectory>/scripts/frame.sh形式给出。共支持三种模式:
3.1 抽取首帧(默认模式)
<baseDirectory>/scripts/frame.sh /path/to/video.mp4 --out /tmp/frame.jpg不指定--time也不指定--index时,脚本取视频第 0 帧。底层对应 ffmpeg 的select=eq(n\,0)滤镜:n是当前帧序号,eq(n,0)在序号等于 0 的帧上输出一次,配合-frames:v 1只输出一帧(详见 frame.sh)。适合快速确认视频封面、分辨率、画幅比例。
3.2 按时间戳定位(推荐用于“某时刻发生了什么”)
<baseDirectory>/scripts/frame.sh /path/to/video.mp4 --time 00:00:10 --out /tmp/frame-10s.jpg--time接受HH:MM:SS格式(如00:00:10表示第 10 秒)。底层使用 ffmpeg 的input seeking方式:-ss "$time"放在-i之前,让 ffmpeg 先快速跳转到目标时间附近的 keyframe,再做精确解码,-frames:v 1输出一帧(frame.sh)。因为 seek 发生在解码之前,这种方式定位快,适合调查某个时间点前后发生了什么,例如:
- 定位一条告警或事件发生的时刻;
- 检查视频中间段的画面内容;
- 从长视频中快速取样多帧。
3.3 按精确解码帧序号定位
<baseDirectory>/scripts/frame.sh /path/to/video.mp4 --index 120 --out /tmp/frame-120.png--index接受非负整数,表示精确的第 N 帧(从 0 开始计数)。底层使用select=eq(n\,120)滤镜,ffmpeg逐帧解码直到命中目标帧序号(frame.sh)。由于必须从头部完整解码到第 N 帧,帧序号越大耗时越长,但定位是帧精确的,不受 keyframe 间隔影响。适合:
- 调试视频处理流水线时复现“第 N 帧”的问题画面;
- 对按帧生成的内容(如动画、逐帧渲染结果)做精确检查;
- 配合测试场景做确定性复现。
场景选择建议:关心“某一时刻附近”的画面用
--time(快);关心“精确第 N 帧”用--index(准)。这也是 SKILL.md 中“Prefer a timestamp when investigating what happens around a moment”一句的用意。
四、frame.sh 脚本实现深度解析
frame.sh 是 Skill 的实际执行载体,采用#!/usr/bin/env bash并开启set -euo pipefail。其设计要点可以提炼为以下四条可复用的健壮性范式:
4.1 参数解析与帮助
- 无参数、
-h、--help时打印 Usage 到 stderr 并以退出码 2 结束; - 第一个位置参数固定为输入视频路径,其余选项通过
while循环解析:--time、--index、--out各取下一个参数值,未知参数报错并进入 usage。
4.2 四道输入校验
| 校验项 | 失败处理 | 对应实现 |
|---|---|---|
输入文件存在(-f判断) | File not found,退出码 1 | frame.sh |
--out必须提供 | Missing --out,退出码 2 | frame.sh |
--time与--index互斥 | 提示Use either --time or --index, not both,退出码 1 | frame.sh |
| ffmpeg 可用性 | ffmpeg not found in PATH,退出码 1 | frame.sh |
互斥校验非常关键:如果同时传入--time与--index,脚本的 if/elif 分支会静默优先命中--index,导致结果与用户预期不符;显式报错把歧义挡在执行之前。
4.3 输出目录自动创建
mkdir -p "$(dirname "$output")"在执行 ffmpeg 之前先递归创建输出文件的父目录(frame.sh)。这消除了“目标目录不存在”这一类最常见的失败原因,保证--out /tmp/xxx/frame.jpg这类深层路径也能直接成功。
4.4 ffmpeg 命令构造
三种分支统一使用以下公共参数,保证输出干净、可重入:
-hide_banner:隐藏版本横幅,减少日志噪音;-loglevel error:只在出错时输出日志,正常成功保持静默;-y:覆盖已存在的同名输出文件,支持重复执行;-frames:v 1:仅输出 1 帧视频帧,立即结束,避免整段视频被处理。
三条 ffmpeg 命令分别为(与 3.1~3.3 节一一对应):
# --index N:帧精确寻址 ffmpeg -hide_banner -loglevel error -y -i "$input" \ -vf "select=eq(n\,${index})" -frames:v 1 "$output" # --time HH:MM:SS:输入侧 seek,快速定位 ffmpeg -hide_banner -loglevel error -y -ss "$time" -i "$input" \ -frames:v 1 "$output" # 默认:首帧 ffmpeg -hide_banner -loglevel error -y -i "$input" \ -vf "select=eq(n\,0)" -frames:v 1 "$output"注意select滤镜表达式中逗号,需在 bash 双引号内转义为\,,否则会被 ffmpeg 当作滤镜链分隔符——这是手写 ffmpeg 命令时最容易踩的坑,脚本通过-vf "select=eq(n\,${index})"的转义写法规避了它。
4.5 执行成功信号
脚本最后echo "$output"将输出文件绝对路径打印到 stdout(frame.sh)。Agent 可通过解析该输出来获取实际产物路径,作为后续返回给用户的依据。
五、输出格式与结果校验:JPEG 还是 PNG
SKILL.md 给出两条明确的输出约定:
- JPEG 用于快速分享:体积小、加载快,适合聊天内展示、预览图、缩略图等场景,如
frame.jpg; - PNG 用于清晰的界面截图:无损、无压缩伪影,适合需要放大查看的 UI 细节、文字界面、图表画面,如
frame-120.png。
两条约定可以进一步推导为:需要“看个大概”用 JPEG,需要“看清楚”用 PNG;而输出文件扩展名(.jpg/.png)会同时影响 ffmpeg 的封装格式与编码器选择,因此--out的扩展名就是格式声明。
Skill 还强制要求在把结果返回给用户之前验证输出文件确实存在。结合脚本设计,验证方式可以是:
ls -l /tmp/frame-10s.jpg # 确认文件存在且大小非 0这一步把“命令执行成功”与“产物真实可用”区分开——ffmpeg 以 0 退出码结束时,仍可能因滤镜或 seek 边界问题产生异常产物,存在性检查是最后一道质量闸门。
六、在 Ekko Agent 中的调用链路:从 skill_list 到 skill_view
理解了脚本本身,再看 Skill 是如何被 Agent 加载和调用的。video-frames的调用协议定义在 skills.ts:
- skill_list:按名称、描述、keywords 做不区分大小写的检索,返回 Skill 清单(SkillListTool)。SKILL.md 中
metadata.keywords声明的video frame、extract frame、ffmpeg frame正是此类检索与精确匹配的输入; - skill_view:加载某个 Skill 的完整 SKILL.md 内容(或
references/、templates/、scripts/、assets/下的支持文件),并在返回内容中携带baseDirectory=${skill.directory}(SkillViewTool)。这就是文档中“skill_viewreturns this Skill'sbaseDirectory”的来源——模型先调用skill_view拿到baseDirectory,再把<baseDirectory>/scripts/frame.sh代入具体命令执行。
skill_view的返回体还包含characters、sha256、validationStatus等字段,其中filePath支持scripts/frame.sh这类相对路径,读取范围被严格限制在四个支持目录内(见SUPPORT_DIRECTORIES定义 skills.ts 与resolveSupportFile的路径白名单校验 skills.ts)。
在运行时层面,runtime.ts 会把skill_view作为内建工具注入工具列表(runtime.ts),并且当用户消息与某个 Skill 的名称或 keywords 精确匹配时,会自动发起skill_view调用把 SKILL.md 载入上下文(runtime.ts)。也就是说,用户只要说出“抽一帧”“提取视频第 10 秒的画面”这类与video-frames匹配的意图,Agent 就可能自动加载该 Skill 并按其指引执行frame.sh。
七、Skill 的启用与配置:profiles、externalDirectories 与 disabled
video-frames属于随包自动安装的内建 Skill,一般无需手动配置即可使用。但在多 Profile 场景下,Ekko 支持细粒度控制(见 README.md):
{ "skills": { "enabled": true, "reviewEveryToolCalls": 0, "profiles": { "work": { "externalDirectories": ["~/shared-skills", "$TEAM_SKILLS"], "disabled": ["weather"] } } } }externalDirectories:为某个 Profile 追加只读的 Skill 根目录,这些目录不会被复制进 Ekko 存储,本地同名 Skill 优先于外部同名 Skill;disabled:隐藏指定 Skill 名称,使其不进入提示注入与自动路由;- 内建 Skill 的安装策略是:启动时补齐缺失项,仅更新未被修改的 Ekko 安装副本,用户编辑过或已存在的同名 Skill 不会被覆盖。
需要说明的是,video-frames的素材改编自 OpenClaw 的 skill 集(见 THIRD_PARTY_NOTICES.md 的声明),属于包内置 Skill,因此不受删除操作影响——按照 skill-creator/SKILL.md 的约束,内建 Skill 不可删除;如需调整其行为,应通过skill_manage的patch/edit在 Profile 本地副本上进行受管理的修改。
八、扩展与自定义:把抽帧能力演进为专属 Skill
如果你需要更复杂的能力(例如批量抽多帧、输出时间戳水印、按场景切分镜头),可以参考 skill-creator/SKILL.md 的规范创建新的 Skill:
- 用
skill_list查重,避免重复造轮子; - 名称限 64 字符内的小写字母/数字/连字符/下划线,首尾须为字母数字;
SKILL.md必须包含标量name、description和非空metadata.keywords,正文为完整 Markdown 指令;- 用
skill_manage的action=create创建,用action=write_file添加scripts/支持脚本(这正是video-frames的组织方式:SKILL.md+scripts/frame.sh); - 创建后依次用
skill_list确认发现、用skill_view检查元数据、并用terminal_exec在安全输入上实际执行脚本验证。
对于video-frames本身,一个常见扩展方向是增加“批量时间点抽帧”能力:在frame.sh中支持多个--time或多个--index,循环调用现有的 ffmpeg 命令模板即可,同时保留现有的--out命名约定与输出存在性校验。
九、最佳实践清单
综合 SKILL.md 文档、frame.sh 实现与 Ekko 工具协议,可总结出如下可直接套用的实践:
- 先探测后执行:任何依赖外部二进制(ffmpeg 等)的任务,先
command -v,缺失时向用户说明而非静默安装; - 按语义选模式:调查时间点附近用
--time(快),复现精确帧用--index(准),快速预览用默认首帧; - 按用途选格式:分享预览用 JPEG,界面细节与文字画面用 PNG;
- 校验产物:返回结果前确认输出文件存在且非空;
- 保持命令可重入:复用
-hide_banner -loglevel error -y -frames:v 1这套参数组合,保证输出干净、可覆盖、单帧即停; - 尊重受管目录:Skill 支持文件只放在
references/、templates/、scripts/、assets/下,路径访问受 skills.ts 的白名单约束。
十、关键文件索引
| 文件 | 作用 |
|---|---|
| packages/ekko-agent/skills/video-frames/SKILL.md | Skill 的指令本体:前置条件、三种抽帧命令、格式与校验约定 |
| packages/ekko-agent/skills/video-frames/scripts/frame.sh | 抽帧实现:参数解析、四道校验、三条 ffmpeg 命令模板 |
| packages/ekko-agent/src/tools/skills.ts | skill_list / skill_view / skill_manage 工具实现与路径安全约束 |
| packages/ekko-agent/src/runtime/runtime.ts | skill_view 工具注入与按 keywords 的 Skill 自动路由 |
| packages/ekko-agent/README.md | 内建 Skill 清单、profiles 配置与安装策略 |
| packages/ekko-agent/skills/skill-creator/SKILL.md | Skill 创建/修改规范与结构约定 |
以上路径均可在当前仓库中直接打开对照阅读,结合源码理解本文所述的每一处校验与命令构造细节。
- AI 应用
- 人工智能
- AI Agent
- 本地部署
- 前端
- 后端
- 工作流自动化
【免费下载链接】ekko-studio
Ekko Studio is a local-first AI workspace for multi-agent chat, coding, and visual workflows, available on desktop and the web.
相关推荐
OpenClaw 视频抽帧技能实战:用 video-frames + ffmpeg 精准截取单帧与预览缩略图
OpenClaw 视频抽帧技能实战:用 video frames + ffmpeg 精准截取单帧与预览缩略图 导读 video frames 是 OpenCla
AI 应用AI Agent交互助手后端即时通讯网关python-mini-projects 实战指南:基于 OpenCV 的 Capture Video Frames 视频帧提取
python mini projects 实战指南:基于 OpenCV 的 Capture Video Frames 视频帧提取 本文以 python mini
示例工程Coil 视频帧提取实战指南:使用 coil-video 将视频帧作为图片加载
Coil 视频帧提取实战指南:使用 coil video 将视频帧作为图片加载 导读 coil video 是 Coil(coil3)生态中专门用于 从视频中提
移动开发图像处理缓存抽象
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考