ysr666/dsh-vision-router
ysr666/dsh-vision-router为纯文本 Agent 提供视觉能力:内置免 Key 视觉链 + 像素级视觉工具(看图问答、定位、裁剪、像素对比、取色、OCR、矢量化、抠图、截图);粘贴图片即可用。
给纯文本 agent 补上视频理解:抽帧并返回带时间码的帧文件、只在场景切换时采样、导出 GIF 片段、探测元数据、并对关键帧做一次性分析。五个工具,通过 argv 数组调用本机 ffmpeg 而非拼接 shell 字符串。
插件 v0.1.0 · 实测 dsh 0.1.6-alpha.2
DSH (DeepSeek Harness) 插件:给纯文本 agent 补上视频理解能力——抽帧、场景切换检测、GIF 片段、元数据探测。
底层通过 child_process 调本机 ffmpeg / ffprobe,参数走 argv 数组(无 shell 注入)。不需要 Python / PIL / 外部服务。
⚠️ dsh 处于开发者预览阶段,破坏性变更频繁,插件的可用性随 dsh 版本变化。换 dsh 版本前先对下表。
| dsh 版本 | 状态 | 说明 |
|---|---|---|
0.1.6-alpha.2 | ✅ 实测通过 | 2026-09-22,源码构建(build:native-system + build:lib:host)后实跑,5/5 工具通过 |
0.1.1-rc.2 | ⚠️ 未实测 | 插件按其编译,peerDependencies 标称兼容;未在新环境实跑 |
下面两个坑与 dsh 版本无关,属插件自身缺陷,已修复。修复前把编译产物换回修复前那版、在 0.1.6-alpha.2 上复跑,两个都原样复现——所以这两个修复对两个 dsh 版本同样成立。
inject 需声明全部依赖服务。video_analyze 走 ctx.attachments + ctx.llm,Cordis 的 inject 是必需语义(不是提示),未声明即访问会抛
cannot get property "attachments" without inject。其余四个工具只用 ctx.tools,所以该问题只在 video_analyze 上暴露。deepseek-v4-flash-vision-exp 已不在服务端模型列表中,
模型解析失败后图像会降级为占位文本(模型只看到附件哈希,回复"无法查看关键帧")。
当前默认改为 deepseek-flash(deepseek-official 上与 deepseek-v4-pro 并列,是唯一 inputModalities 含 image 的)。
换 provider / 账号前,建议先用 ctx.llm.listModels(provider) 确认可用的视觉模型 id。| 工具 | 说明 |
|---|---|
video_frames | 合并版统一入口。mode=uniform | scenes | gif | info | frames 一个参数切换全部能力 |
video_info | 元数据探测:时长、分辨率、帧率、编码、是否有音轨 |
video_scenes | 场景感知抽帧:检测镜头切换点,每个切点抽一帧,跳过静态帧 |
video_gif | 掐一段视频转成 GIF(静态帧表现不了的动态) |
video_analyze | 抽帧 → 存 image attachment → 走 ctx.llm 调视觉模型,返回对画面内容的概括 |
| 参数 | 说明 |
|---|---|
path | 视频文件路径(必填) |
mode | uniform(默认,每 N 秒一帧)| scenes(镜头切换)| gif(动图)| info(元数据)| frames(uniform 但输出独立文件) |
interval | uniform 抽帧间隔秒数(默认 5) |
threshold | scenes 模式场景阈值 0-1,越高越只认明显切镜(默认 0.3) |
from / to | 时间段(秒),只处理某一段 |
duration | gif 模式片段时长(秒),优先于 from/to |
fps | gif 帧率(默认 10) |
width | gif 宽度,高度自适应(默认 480) |
out_dir | 抽出的帧保存目录 |
out | gif 输出路径(mode=gif 必填) |
拆分版 video_scenes / video_gif / video_info 参数与上面对应项一致,适合 agent 按需调用单个工具。
| 参数 | 说明 |
|---|---|
path | 视频文件路径(必填) |
mode | uniform(默认,每 N 秒一帧)| scenes(镜头切换点抽帧,跳过静态帧) |
interval | uniform 抽帧间隔秒数(默认 5) |
from / to | 时间段(秒),只分析某一段 |
prompt | 自定义分析指令(默认:概括场景/人物动作/关键信息) |
max_frames | 喂给模型的帧数上限(默认 60),控 token 的旋钮 |
max_tokens | 模型输出上限(默认 2000) |
provider / model | 视觉模型路由(默认 deepseek-official / deepseek-flash,走 DEEPSEEK_API_KEY) |
要求:Node >= 18,且 ffmpeg / ffprobe 在系统 PATH 中。
通过 DSH 的 bundle patch 机制挂载:
# cordis.patch.yml
- insert:
- id: dsh-video-frames
name: 'dsh-video-frames'
或按 DSH 插件市场的常规方式注册本包。完整 DSH 插件开发说明见 deepseek-harness docs/cookbook/adding-a-tool。
fps=1/N(按墙钟时间,不受关键帧影响);时间段用 -ss(input 定位)+ -t(时长),避开 -to 在 seek 后时间戳重置的坑。select='gt(scene,THRESH)',showinfo 从日志解析 pts_time,再按时间戳精抽单帧。r_frame_rate → avg_frame_rate(concat 拼接视频常是 0/0)→ 数帧/时长推算。video_analyze 目前把抽出的帧一次全喂给视觉模型,不做分批滚动。这是故意的取舍:
max_tokens 烧光吐空内容(概率性,不是必然)。控制手段在参数上:interval 调稀、mode=scenes 跳过静态帧、max_frames 封顶、max_tokens 给足余量。提示词默认也刻意避开"数一下/逐张描述"这类诱导数图的写法。
等 DSH 正式版稳定后,再按"粗筛小批滚动"的方式做分批优化——那套(抽帧分批 + 批间摘要滚进下一批)在 video-frames skill 里已经趟过路。
MIT