教程
通过 hiapi API 使用 minimax-h3-max 进行多角度图生视频提示词编写
没有镜头角度参数——下面这套提示词写法可以把一张照片变成多镜头片段。
minimax-h3-max 能把一张照片变成 5–15 秒的视频,但其端点根本没有“镜头角度”“多视角”或“镜头数量”参数——参数参考列出的受支持输入字段为 prompt、first_frame_image、last_frame_image、duration、resolution 和 aspect_ratio。请把镜头指令写进 prompt,该字段最多接受 7,000 个字符。
所以,一个“多角度”片段——从一个广角建立镜头切到特写,再切到跟拍镜头——并不是你设置的参数,而是你写出来的提示词。本教程涵盖请求结构、在一个生成片段内要求多个镜头角度的提示词模式,以及围绕它的生产级流程。
用这段提示词生成 → 选择 10 秒和 768P。若做图生视频,请添加你的帧图像并省略 aspect_ratio。
前置条件
从你的 hiapi 控制台获取 API 密钥。下面每次调用都使用 Authorization: Bearer YOUR_API_KEY 对共享异步任务端点 POST https://api.hiapi.ai/v1/tasks 进行认证。
最小示例:一张照片,三个镜头角度
first_frame_image 锚定主体和场景,让模型有具体内容可以切走再切回。提示词把每个镜头描述为明确的编号切镜。这个摩托车提示词是代码示例,未附带渲染样例。
一辆红色复古摩托车停在黄昏时分潮湿的城市街道上,霓虹灯招牌映在路面上。镜头 1 - 低角度广角:摄像机保持静止广角镜头,拍摄整辆摩托车,细雨轻轻落下。切到镜头 2 - 特写:摄像机向镀铬前灯和前轮紧密推进,缓慢向右跟拍。切到镜头 3 - 俯视顶拍:摄像机从上方垂直向下俯拍摩托车,缓慢旋转。无文字或字幕。
用这段提示词生成 → 添加你自己的可公开访问的摩托车图像,选择 10 秒和 768P,并省略 aspect_ratio。
import os
import requests
import time
API_KEY = os.environ["HIAPI_API_KEY"]
BASE = "https://api.hiapi.ai/v1"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
payload = {
"model": "minimax-h3-max",
"input": {
"prompt": (
"A red vintage motorcycle parked on a wet city street at dusk, neon signs reflecting on the pavement. "
"Shot 1 - low wide angle: the camera holds a static wide shot of the full motorcycle, rain lightly falling. "
"Cut to Shot 2 - close-up: the camera pushes in tight on the chrome headlight and front wheel, tracking slowly right. "
"Cut to Shot 3 - overhead top-down: the camera looks straight down at the motorcycle from above, slowly rotating. "
"No text or subtitles."
),
"first_frame_image": "https://your-cdn.example.com/motorcycle-source.jpg",
"duration": 10,
"resolution": "768P",
},
}
# Frame images and aspect_ratio are mutually exclusive.
def response_data(response):
response.raise_for_status()
body = response.json()
if not isinstance(body, dict) or body.get("code") != 200:
raise ValueError("Expected a JSON object with code=200")
data = body.get("data")
if not isinstance(data, dict):
raise ValueError("Expected data to be an object")
return data
# Submit once. An uncertain POST response is not a reason to submit again.
try:
resp = requests.post(
f"{BASE}/tasks", headers=HEADERS, json=payload, timeout=(10, 60)
)
created = response_data(resp)
task_id = created.get("taskId")
if not isinstance(task_id, str) or not task_id.strip():
raise ValueError("Missing data.taskId")
except (requests.RequestException, ValueError) as exc:
raise RuntimeError(
"Could not confirm task creation. Check task records before resubmitting."
) from exc
print("taskId:", task_id, flush=True)
deadline = time.monotonic() + 600
while time.monotonic() < deadline:
try:
resp = requests.get(
f"{BASE}/tasks/{task_id}", headers=HEADERS, timeout=(10, 60)
)
task = response_data(resp)
except (requests.RequestException, ValueError) as exc:
raise RuntimeError(
f"Query failed; retain taskId={task_id} and resume GET for this ID."
) from exc
state = task.get("status")
if state == "fail":
raise RuntimeError(f"Task {task_id} failed: {task.get('error')}")
if state == "success":
outputs = task.get("output")
if not isinstance(outputs, list):
raise RuntimeError(f"Task {task_id}: missing output array")
video = next(
(item for item in outputs
if isinstance(item, dict) and item.get("type") == "video"
and isinstance(item.get("url"), str) and item["url"]),
None,
)
if video is None:
raise RuntimeError(f"Task {task_id}: no video URL in output")
print(video["url"])
print("expireAt (Unix seconds):", video.get("expireAt"))
break
if state not in {"queued", "handling", "archiving"}:
raise RuntimeError(f"Task {task_id}: unexpected status {state!r}")
time.sleep(min(3, max(0, deadline - time.monotonic())))
else:
raise TimeoutError(f"Polling timed out; continue GET with taskId={task_id}")
把 first_frame_image 换成真实、可公开访问的 HTTPS 图像 URL,或者完全去掉它,仅凭文本生成同样的多角度序列——minimax-h3-max 通过同一个 prompt 字段同时处理文生视频和图生视频。
编写提示词:编号镜头模式
三个选择能让你的目标镜头序列清晰明确:
- 一句建立场景的话,然后是编号镜头。先描述一次主体和场景,再把每个镜头角度作为各自标注的节拍列出:
镜头 1 - [构图]:[动作]。切到镜头 2 - [构图]:[动作]。“切到镜头 N”会明确要求改变镜头角度。 - 使用标准电影摄影词汇。
低角度广角镜头、俯视顶拍镜头、特写、缓慢推镜、侧向跟拍镜头、手持——这些词能指明你想要的构图和运动。像“另一个酷炫角度”这样含糊的措辞会产生不一致的结果。 - 按时长规划镜头数量。
duration是 5 到 15 秒的整数。两到三个镜头比较现实;把五个不同角度塞进 5 秒片段里,每个镜头不到一秒,往往看起来像故障而不是切镜。
first_frame_image 和 last_frame_image 都是可选的,并且彼此独立——可以设置一个、两个或都不设置。提供其中任意一个时,请跳过 aspect_ratio:这些字段互斥,帧图像决定输出的宽高比。另请注意,跨角度并不能保证严格的三维或几何一致性——这是生成式视频模型,不是摄影测量管线,所以即使有首帧锚定,也要预期主体细节在切镜之间会有轻微漂移。
让它适合生产环境
- 一旦超出脚本规模,优先使用回调而不是轮询。在请求体中加入
"callback": {"url": "https://your-domain.com/hiapi/callback", "when": "final"},当任务到达终态时 hiapi 会 POST 通知,而不是由你在循环中轮询GET /v1/tasks/{taskId}。 - 按
taskId去重。轮询响应和回调都以同一个taskId为键——把同一 ID 的重复终态通知视为幂等,而不是多个独立任务。 - 立即下载输出。成功的任务会返回
data.output[].url用于临时存储。选择type为video的输出项,并检查其expireAtUnix 时间戳(秒)。在处理终态的同一个处理程序中,将其重新上传到你自己的存储。 - 处理两种失败形态。错误的请求体(不支持的字段、超出范围的
duration、格式错误的图像 URL)会在提交时快速失败并返回400 INVALID_REQUEST。认证问题返回401,并带有error.code: "permission_denied"。生成过程中失败的任务则会在轮询/回调响应中报告"status": "fail"——在信任output之前先检查status。
相关阅读
- minimax-h3-max 模型页面——实时定价、参数,以及可在浏览器中测试提示词再接入代码的演练场。
- minimax-h3-max API 文档——完整参数参考和回调细节。
- H3 和 H3 Max 定价——分辨率费率和短视频预算。
- hiapi 定价——每个模型的当前每秒费率。
常见问题
minimax-h3-max 有专门的多角度或多机位参数吗?
没有。文档中的输入字段是 prompt、first_frame_image、last_frame_image、duration、resolution 和 aspect_ratio。请在 prompt 内描述多个镜头,而不是添加镜头角度字段。
可以把 aspect_ratio 与 first_frame_image 或 last_frame_image 一起设置吗?
不可以。aspect_ratio 与任一帧图像字段互斥。提供的图像决定输出比例。只要传入任一帧图像,就省略 aspect_ratio。
多角度片段可以多长?
duration 接受 5 到 15 的整数秒。两到三个不同镜头角度可以较从容地容纳在这个窗口内;超过这个数量往往会把每个镜头压缩到几分之一秒。
主体会在不同角度之间保持完美一致吗?
不保证。minimax-h3-max 是生成式视频模型,所以即使 first_frame_image 锚定了开场帧,细节(精确纹理、细微比例)也可能在切镜之间漂移。请把它视为强创作一致性,而不是像素级精确的多视角重建。
我可以仅凭文本、没有源图像生成多角度片段吗?
可以——省略 first_frame_image 和 last_frame_image,并在 prompt 中描述场景和镜头。纯文本生成使用默认横向画布。若要纵向构图,请提供纵向帧图像且不设置 aspect_ratio,或在编辑器中裁剪/填充保存的视频。
认证失败是什么样子?
HTTP 401,响应体类似 {"error":{"code":"permission_denied","type":"hiapi_error","request_id":"..."}}。请仔细检查 Authorization: Bearer 请求头,以及密钥是否已从控制台撤销。
复制摩托车镜头序列,并用这段提示词生成 → 选择 10 秒和 768P;添加你的帧图像以引导开场构图。