用 Claude 反推视频提示词:ffmpeg 抽帧 + Opus 5.5 看图

把一段视频交给 Claude Opus 5.5,拿到可以直接用于 Sora、Veo、可灵、Seedance 等视频模型的中英文提示词。一段 10 秒的视频,约 30 秒出结果,费用约 0.05~0.07 美元。

维护:302API · 更新于 2026-10-28

验证说明:2026-10-28 在 macOS 上使用 Python 3.13、Anthropic Python SDK 2.3.0 和 ffmpeg 9.0,对一段 10 秒、3 个镜头的程序生成测试视频抽 8 张画面,通过本站实际调用 claude-opus-5-5 两次:输入均为 6,640 Token,输出分别为 2,107 和 1,458 Token,控制台记录 US$0.0653 和 US$0.0529,每次耗时约 30 秒。实际用量随画面数、视频内容和模型版本变化。

1. 原理:先抽画面,再让 Claude 看图

Claude 不能直接读取视频文件,也听不到声音。做法是先用 ffmpeg 从视频里截取若干张关键画面,按时间顺序连同时间点一起发给 Claude,让它逐个镜头分析,再写出提示词。

步骤做什么用到
抽帧在视频里均匀取 8~24 个时间点,各截一张长边不超过 1024 像素的 JPEGffmpeg
看图分析图片和时间点放进同一条消息,要求逐镜头描述主体、动作、运镜、光线和风格Claude 原生接口、claude-opus-5-5
输出只返回 JSON:分镜表、整体风格、中英文提示词和负面提示词Python 解析后保存

只分析你有权使用的视频。生成的提示词用于描述画面风格,不代表可以复制他人作品中受保护的内容。

2. 准备

需要三样东西:ffmpeg、Anthropic 官方 Python SDK 和 302API 的 Key。

bash
# 安装 ffmpeg(任选其一)
brew install ffmpeg            # macOS
winget install Gyan.FFmpeg     # Windows
sudo apt install ffmpeg        # Ubuntu / Debian

# 安装 SDK
pip install anthropic

在 302API 控制台创建 Key,保存为环境变量 API302_KEY。不要写进代码或发到聊天里。

bash
export API302_KEY="你的 Key"          # macOS / Linux
$env:API302_KEY = "你的 Key"         # Windows PowerShell

3. 最小可运行脚本

保存为 video2prompt.py。脚本不到 100 行:抽帧、发给 Claude、解析结果、打印费用。

python
"""用 Claude 反推视频提示词:ffmpeg 均匀抽取关键画面,Claude Opus 5.5 看图写出可复用的提示词。

用法:python video2prompt.py 视频.mp4 [--frames 12]
准备:安装 ffmpeg;pip install anthropic;设置环境变量 API302_KEY(在 302API 控制台创建)。
结果:打印中英文提示词和本次费用,并把完整结果保存为“视频名.prompt.json”。
"""
import argparse
import base64
import json
import os
import subprocess
import sys
import tempfile
from pathlib import Path

import anthropic

MODEL = "claude-opus-5-5"
PRICE_INPUT, PRICE_OUTPUT = 3.8, 19.0  # 本站价,美元 / 百万 Token;以 302api.com/models 为准

SYSTEM = """你是专业的视频分析师和 AI 视频提示词工程师。用户会按时间顺序给你一段视频的若干关键画面。
请先逐个镜头分析主体、动作、场景、景别与运镜、光线色调和画面风格,再写出能让 AI 视频模型生成类似视频的提示词。
只描述画面中看得到的内容;看不清或无法判断的地方写“无法判断”,不要编造。
严格只输出一个 JSON 对象,不要任何其他文字,结构如下:
{"shots":[{"start":"00:00","end":"00:03","subject":"","action":"","scene":"","camera":"","lighting":"","style":""}],
 "overall_style":"",
 "prompt":{"zh":"","en":""},
 "negative_prompt":{"zh":"","en":""}}"""


def run(*args):
    return subprocess.run(args, check=True, capture_output=True, text=True).stdout


def duration_of(video):
    return float(run("ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "csv=p=0", str(video)))


def extract_frames(video, count, folder):
    """在视频里均匀取 count 个时间点,各截一张长边不超过 1024 像素的 JPEG。"""
    total = duration_of(video)
    frames = []
    for i in range(count):
        at = total * (i + 0.5) / count
        out = Path(folder) / f"frame_{i:02d}.jpg"
        run("ffmpeg", "-v", "error", "-y", "-ss", f"{at:.2f}", "-i", str(video), "-frames:v", "1",
            "-vf", "scale='min(1024,iw)':-2", "-q:v", "4", str(out))
        frames.append((at, out))
    return total, frames


def main():
    parser = argparse.ArgumentParser(description="用 Claude 反推视频提示词")
    parser.add_argument("video", type=Path)
    parser.add_argument("--frames", type=int, default=12, help="抽取的画面数,默认 12,最多 24")
    args = parser.parse_args()
    if not os.environ.get("API302_KEY"):
        sys.exit("请先设置环境变量 API302_KEY")

    with tempfile.TemporaryDirectory() as folder:
        total, frames = extract_frames(args.video, max(1, min(args.frames, 24)), folder)
        content = []
        for i, (at, path) in enumerate(frames, 1):
            content.append({"type": "text", "text": f"画面 {i},{int(at // 60):02d}:{at % 60:04.1f}"})
            content.append({"type": "image", "source": {"type": "base64", "media_type": "image/jpeg",
                                                        "data": base64.b64encode(path.read_bytes()).decode()}})
        content.append({"type": "text", "text": f"视频总时长 {total:.1f} 秒。请按要求输出 JSON。"})

    client = anthropic.Anthropic(api_key=os.environ["API302_KEY"], base_url="https://api.302api.com",
                                 timeout=600, max_retries=3)
    # 流式接收,长回复也不会因为等待太久而断开
    with client.messages.stream(model=MODEL, max_tokens=4000, system=SYSTEM,
                                messages=[{"role": "user", "content": content}]) as stream:
        message = stream.get_final_message()

    text = "".join(block.text for block in message.content if block.type == "text").strip()
    if text.startswith("```"):
        text = text.split("\n", 1)[1].rsplit("```", 1)[0]
    result = json.loads(text)
    out = args.video.with_suffix(".prompt.json")
    out.write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8")

    usage = message.usage
    cost = (usage.input_tokens * PRICE_INPUT + usage.output_tokens * PRICE_OUTPUT) / 1e6
    print("中文提示词:\n" + result["prompt"]["zh"] + "\n")
    print("English prompt:\n" + result["prompt"]["en"] + "\n")
    print(f"共 {len(result['shots'])} 个镜头,完整结果已保存到 {out}")
    print(f"用量:输入 {usage.input_tokens} Token,输出 {usage.output_tokens} Token,约 ${cost:.4f}(以控制台账单为准)")


if __name__ == "__main__":
    main()

新旧版本的 Anthropic SDK 都可以直接使用,不需要额外设置请求头。

4. 运行并查看结果

bash
python video2prompt.py 我的视频.mp4 --frames 8

下面是验证时的实际输出(测试视频依次为分形缓慢放大、生命游戏元胞演化和旋转的渐变色)。中文提示词:

程序化生成艺术短片,三段硬切。第一段:曼德博集合分形,主心形和圆形内部呈红橙绿放射状渐变,边缘布满蓝橙绿细密分形枝杈,背景为粉紫色并带绿蓝光晕,画面缓慢平稳放大。第二段:纯黑背景上的康威生命游戏像素细胞自动机,青绿色像素组成十字、菱形、环形和成团结构,逐帧演化并留下暗蓝色残影,固定俯视画面。第三段:橙色与蓝色的平滑双色渐变,中间过渡为紫粉色,渐变分界从垂直方向缓慢旋转为斜向。二维平面、高饱和、干净锐利的数字图形,无实拍元素。

English prompt:

A procedural generative-art montage with three hard cuts. Shot 1: a Mandelbrot set fractal, the main cardioid and bulbs filled with radial red, orange and green gradients, edges covered in fine blue-orange-green fractal filaments, on a pink-violet background with a green and blue glow, slowly and smoothly zooming in. Shot 2: a Conway's Game of Life style pixel cellular automaton on pure black, cyan-green pixels forming crosses, diamonds, rings and clustered structures that evolve frame by frame and leave dark blue trails, static top-down flat view. Shot 3: a smooth orange-to-blue two-color gradient with a purple-pink blend in the middle, the dividing line slowly rotating from vertical to diagonal. Flat 2D, highly saturated, clean and crisp digital graphics, no live-action elements.

命令行最后两行:

输出
共 3 个镜头,完整结果已保存到 sample.prompt.json
用量:输入 6640 Token,输出 1458 Token,约 $0.0529(以控制台账单为准)

完整结果保存在同名的 .prompt.json 文件里,包含每个镜头的起止时间、主体、动作、运镜、光线和风格,可以挑出单个镜头的描述单独使用。

最后到控制台的调用记录核对:模型为 claude-opus-5-5,输入、输出 Token 和金额与脚本打印的一致,才说明请求确实经过本站并已结算。

5. 费用估算

本站 claude-opus-5-5 价格为输入 $3.8、输出 $19(美元 / 百万 Token),实时价格见模型与价格。一张 1024×576 的画面约 750 个输入 Token,分析结果通常在 2,000 个输出 Token 左右。

抽取画面数输入 Token(约)输出 Token(约)单次费用(约)
8 张(实测两次)6,6401,458~2,107$0.053~0.065
12 张9,7002,100$0.077
24 张18,8002,100$0.11

12 张和 24 张为按实测结果推算的估计值,实际扣费以控制台账单为准。画面越多,细节越准,但费用也越高;镜头变化少的视频 8~12 张通常就够用。

6. 让 Codex 帮你写完整版工具

最小脚本适合试用。需要镜头切换检测、按目标模型(Sora、可灵等)调整写法、批量处理、本地缓存和费用统计时,可以把下面这段指令交给 Codex、Claude Code 等 AI 编程工具,让它从零写一个完整的命令行工具。

展开完整指令(可直接复制)
指令
请帮我从零写一个“视频反推提示词”工具:输入一个视频文件,输出可直接用于 AI 视频生成模型的提示词。模型通过 302API 调用 Claude Opus 5.5。302API的接入文档:https://302api.com/docs

## 一、接口信息(必须照此实现)

- 动手前先阅读本站接入文档 https://302api.com/docs (Anthropic 格式示例、`max_tokens` 预扣费、错误码都在里面)和错误排查页 https://302api.com/guides/api-errors/ 。接口地址、参数和报错处理以文档为准;如果文档与本指令不一致,按文档实现,并在完成时告诉我哪里不一致。
- 使用 Anthropic 官方 Python SDK(`anthropic`),走 Anthropic 原生格式 `/v1/messages`。
- `base_url` 固定为 `https://api.302api.com`(不要带 `/v1`,SDK 会自动拼接)。
- 模型名:`claude-opus-5-5`。
- API Key 只从环境变量 `API302_KEY` 读取,不得写进代码、配置文件或日志;提供 `.env.example`,并把 `.env` 加入 `.gitignore`。
- 客户端超时设为 600 秒,开启 SDK 自带重试(max_retries=3);遇到 429 或 5xx 时按 `Retry-After` 退避。
- 使用流式输出(`client.messages.stream`),避免长回复超时。
- `max_tokens` 默认 8000,可通过 `--max-tokens` 调整。网关会按这个上限预扣费用,设得过大时余额不足会被拒绝。

## 二、关键限制

- Claude 不能直接读取视频,也听不到音频。必须先把视频转成若干张关键画面(JPEG),以 base64 图片的形式放进同一条 user 消息里,并在每张图前附一段文字标注它的时间点,例如“画面 3,00:07.5”。
- 每次请求最多发送 24 张画面;画面长边缩放到 1024 像素以内,JPEG 质量 80;单张图片不超过 5MB。

## 三、功能要求

1. 命令行用法:`python -m video2prompt 输入视频.mp4 [选项]`
   - `--frames N`:最多取多少张画面,默认 16,上限 24。
   - `--target`:目标视频模型风格,可选 `general`(默认)、`sora`、`veo`、`kling`、`seedance`、`runway`,不同目标的提示词写法放在独立的模板文件里,方便后续增改。
   - `--lang`:输出语言,`zh`、`en` 或 `both`(默认 both)。
   - `--out`:输出目录,默认与视频同目录。
2. 抽帧:调用系统里的 ffmpeg / ffprobe(启动时检查是否已安装,没有就给出中文安装提示)。
   - 先用镜头切换检测(`select='gt(scene,0.3)'`)找出镜头变化点;
   - 镜头数不足时按时长均匀补帧,超过上限时均匀抽样;
   - 始终包含第一帧和最后一帧;
   - 记录每张画面的时间点和所属镜头序号。
3. 可选字幕或对白:如果视频带字幕轨,用 ffmpeg 提取成文本一并发给模型;没有就跳过。不要自行接入语音识别。
4. 发给模型的系统提示词要求它:
   - 先逐个镜头分析:主体、动作、场景、光线、色调、景别、机位与运镜(推拉摇移跟、手持、航拍等)、转场、节奏、画面风格(写实、动画、胶片感等)、时长;
   - 再给出整体风格总结;
   - 最后按 `--target` 对应模板写出最终提示词:一段完整的整体提示词 + 每个镜头一段分镜提示词 + 负面提示词;
   - 只描述画面里看得到的内容,看不清的地方明确写“无法判断”,不要编造;
   - 严格只输出 JSON,结构见下。
5. 输出 JSON 结构(用 pydantic 定义并校验,校验失败时把错误信息发回模型重试一次):

   ```
   {
     "video": {"file": "", "duration_sec": 0, "shots": 0},
     "shots": [{"index": 1, "start": "00:00.0", "end": "00:03.2", "subject": "", "action": "", "scene": "", "camera": "", "lighting": "", "style": ""}],
     "overall_style": "",
     "prompt": {"zh": "", "en": ""},
     "shot_prompts": [{"index": 1, "zh": "", "en": ""}],
     "negative_prompt": {"zh": "", "en": ""}
   }
   ```

   同时生成一份便于阅读的 Markdown 报告(附带抽出的画面缩略图)。
6. 用量与费用:从响应的 `usage` 读取输入、输出 Token,按每百万 Token 输入 3.8 美元、输出 19 美元估算本次费用,在命令行末尾打印。价格写成配置项,不要写死在逻辑里。
7. 缓存:以“视频文件哈希 + 参数”为键,把抽帧结果和模型结果缓存到本地 `.cache/`,同一视频同一参数不重复调用接口;提供 `--no-cache` 跳过缓存。
8. 批量:支持传入文件夹,逐个处理,单个失败不影响其他文件,最后汇总成功和失败清单。

## 四、工程要求

- Python 3.10+,依赖写进 `requirements.txt`(anthropic、pydantic、python-dotenv 等,尽量少)。
- 代码结构:抽帧、调用模型、提示词模板、输出写入分别放在独立模块。
- 所有给用户看的提示和报错用简体中文。
- 写单元测试:抽帧逻辑用一段用 ffmpeg 现场生成的测试视频;模型调用部分用假客户端,不真实请求接口。
- README(中文)写清:安装 ffmpeg、配置 `API302_KEY`、使用示例、费用估算方法、常见错误(401 是 Key 不对;402 是余额不足以预扣本次费用,需要充值或调小 `--max-tokens`;429 是请求太快)。

完成后先用一个 10 秒左右的视频、`--frames 8` 实际跑一次,确认图片能正常发送并返回合规 JSON,再告诉我结果。

常见问题

401 / 认证失败

检查环境变量 API302_KEY 在当前终端是否可见、Key 是否已在控制台停用。

402 / 余额不足

请求前会按 max_tokens 上限临时冻结费用,余额不够时返回 402。充值,或把脚本里的 max_tokens 调小。

413 / 请求太大

减少 --frames 数量,或把截图的长边从 1024 像素再调小。

描述不准或漏掉镜头

均匀抽帧可能错过很短的镜头。增加画面数,或改用第 6 节完整版里的镜头切换检测。

能识别对白和背景音乐吗?

不能,Claude 只看画面。视频自带字幕轨时,可以用 ffmpeg 把字幕提取成文本一起发送。

参考与下一步

Claude 原生接口说明 · Claude 模型价格 · 报错排查 · 提示缓存省钱指南 · ffmpeg 官方文档