HiAPI
博客

教程

如何通过 HiAPI API 使用 kling-3.0-omni/image-to-video:curl 与 Python 指南

2026年11月3日
扁平液状色块交叠的抽象封面图,墨色底上点缀青瓷绿与石板灰,用于该教程文章。

image-to-video 端点做什么

视频生成不是一次同步调用。在 HiAPI 上,kling-3.0-omni/image-to-video 遵循先提交后轮询的模式:你发送一个描述任务的 HTTP 请求,API 接受请求并返回一个任务标识符,然后你查询该任务,直到视频生成完成。

这种两步式结构有其充分理由。从静态图像渲染视频需要时间——远超一个典型 HTTP 请求应保持打开的时间。如果端点阻塞直到文件就绪,你每次调用都会遭遇网关超时和连接中断。通过立即返回任务句柄,API 让你的代码自行决定等待多久、多久检查一次,以及出错时如何处理。

一个典型任务包含三个逻辑部分:

  • 输入媒体——为镜头提供锚点的图像(或图像集)。
  • 生成设置——提示词文本、运动相关选项、音频和分辨率选择。
  • 生命周期——包含任务 ID 的提交响应,随后是最终产出视频 URL 或失败原因的状态检查。

把这个端点想象成一个带收据的队列会很有帮助。提交调用是你递交工单;收据是任务 ID;轮询调用是你询问柜台订单是否就绪。提交时不会返回任何视频本身,因此你编写的任何客户端从一开始就应围绕这两个阶段来组织。

由此产生两个后果。第一,你的错误处理必须分开:被拒绝的请求体是提交时的问题,可在代码中修复;而渲染失败是任务级的问题,无论重试同一请求体多少次都无法解决。第二,你需要某个地方保存任务 ID——日志行、数据库行、脚本中的变量——因为它是你掌握进行中工作的唯一句柄。

建立这个心智模型后,本指南其余部分主要是管道工作:构造请求,然后正确地等待它。

构建可用的 curl 请求

从尽可能最小的、能工作的请求开始,确认它被接受,然后再添加选项。确切的 JSON 字段名和端点路径定义在 HiAPI 上该模型的参考页面中——请查阅该页面以获取当前 schema,而不是相信来自博客文章(包括本文)的片段。

提交调用有可预测的结构。你在 Authorization 头中使用 HiAPI API key 进行认证,声明 Content-Type: application/json,并发送 JSON 请求体。概念上请求体如下所示:

{
  "model": "kling-3.0-omni/image-to-video",
  "input": {
    "image": "https://your-cdn.example.com/frame-first.jpg",
    "prompt": "A slow dolly-in as morning light crosses the room"
  }
}

结构比字面值更重要:模型标识符、携带源图像引用和提示词的输入对象,以及生成选项的空间。在硬编码每个键之前,请对照当前文档进行验证。

等效的 curl 调用遵循常见模式:

curl -sS -X POST "$HIAPI_BASE_URL/v1/video/generations" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d @request.json

将请求体保存在 request.json 中而不是内联,能让迭代轻松得多——你编辑的是文件,而不是 shell 历史记录条目。

读取响应

成功的提交刻意很平淡。预期会得到一个任务标识符和一个表示已接受但未完成的状态。解析它、打印它并保存它:

curl -sS -X POST "$HIAPI_BASE_URL/v1/video/generations" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d @request.json | tee submit.json

首先检查 HTTP 状态码。4xx 表示请求本身被拒绝——错误的 key、格式错误的 JSON、不支持的选项、无效的图像 URL。2xx 表示任务已进入系统,从那一刻起,有用的信号来自任务状态,而不是 HTTP 状态码。

在编写任何轮询代码之前,先手动运行一次提交。确认你可以在输出中看到任务 ID。这次手动往返能尽早发现大多数配置错误。

处理首帧和末帧输入

你发送的图像不仅仅是参考——它约束镜头的起点。这个区别决定了本节中的大部分设计决策。

仅首帧

当你提供单张图像时,将其视为开场帧。提示词随后描述从该帧开始应该发生什么:镜头运动、主体动作、光照变化、节奏。因为模型必须创造首帧之后的一切,描述轨迹的提示词比描述静态场景的提示词效果更好。“保持静止,看起来很美”没有给模型任何可动画化的内容;“她转向窗户,光线也随之变暖”给了它方向。

添加末帧

末帧输入将任务变成插值问题。你提供两个端点,模型在它们之间生成路径。这对于受控过渡非常强大——产品从前视图旋转到后视图,角色在两个姿势之间切换——因为终点是被固定的,而不是被想象的。

三点实用说明:

  • 保持两帧兼容。 相似的取景、透视和主体比例使插值可信。差异巨大的构图会迫使模型发明一座可能不存在的桥梁。
  • 重写提示词。 有两帧时,你的文本应描述它们之间的运动,而不是终点。命名一种运动风格和持续时间,比叙述你已经提供的最终状态更自然。
  • 预期请求结构会改变。 末帧是一个单独的输入字段,因此你的请求体会多出一个键。构建请求对象时要让可选字段可以在不重构一切的情况下添加。

在代码中,将帧视为一小组命名输入,而不是松散的 URL 集合:

inputs = {"image": first_frame_url}
if last_frame_url:
    inputs["last_frame"] = last_frame_url

这个条件判断让单帧和两帧调用保持在同一代码路径上,一旦你开始生成变体,这一点就很重要。

最后,将帧托管在可靠可达的地方。任务中途过期的 URL 会在提交后失败,而且错误看起来像是生成失败,而不是资源问题。

配置音频和输出分辨率

音频和分辨率是请求级选择,不是全局设置。你按任务决定它们,这意味着它们应放在提示词旁边的请求体构建器中,而不是放在某个设置一次就忘记的配置常量里。

音频

音频选项通常允许你随视频请求配乐,或省略它。将其视为与用例相关的建模决策:社交短片可能受益于生成的音频,而你要在编辑器中配乐的片段可能不应携带音频。请求你从不使用的音频会浪费生成时间,毫无益处。

由于音频行为是最可能演变的领域之一,不要硬编码关于音轨内容或其与提示词关系的假设。阅读当前模型文档,然后用一个真实任务验证输出,再围绕它设计任何下游流程。

分辨率

更高分辨率意味着每帧更多像素和显著更多计算量。一个实用工作流是:在调整提示词和帧选择时以适中分辨率迭代,然后以最终分辨率重新生成胜出的配置。重新生成一个好的结果比最后发现你最好的结果是在错误设置下产生的要便宜得多。

几个值得养成的习惯:

  • 提交前验证。 如果模型不支持你传入的分辨率,你希望得到清晰的提交时拒绝,而不是几分钟后令人困惑的任务级失败。
  • 将设置与任务一起存储。 当你比较多个结果时,你需要知道每个结果是由哪个分辨率和音频选择产生的。
  • 不要假设帧之间对等。 使用大源图像的两帧任务可能与单帧任务行为不同;测试两条路径。

这些在代码中应放在哪里

将生成选项分组到它们自己的对象中,使意图显而易见,并让你能在一处替换默认值:

options = {
    "resolution": "1080p",   # confirm supported values in the model docs
    "audio": True,
}

为依赖文档值的行添加注释。未来的你——或队友——应该能立即分辨哪些字段是安全的,哪些需要在发布前对照参考页面检查。有疑问时,HiAPI 上的模型文档是事实来源,而不是示例请求体。

在 Python 中轮询完成状态

轮询代码是大多数集成悄然变得脆弱的地方。目标是构建一个小型、可复用的客户端,提交任务并以合理限制等待它,而不是每次复制粘贴并编辑的脚本。

最小客户端结构

import os, time, requests

BASE = os.environ["HIAPI_BASE_URL"]
KEY = os.environ["HIAPI_API_KEY"]
HEADERS = {"Authorization": f"Bearer {KEY}"}

def submit(payload: dict) -> str:
    r = requests.post(f"{BASE}/v1/video/generations",
                      headers=HEADERS, json=payload, timeout=30)
    r.raise_for_status()
    return r.json()["id"]

def wait(task_id: str, interval: float = 5.0, max_wait: float = 600.0) -> dict:
    deadline = time.monotonic() + max_wait
    while time.monotonic() < deadline:
        r = requests.get(f"{BASE}/v1/video/generations/{task_id}",
                         headers=HEADERS, timeout=30)
        r.raise_for_status()
        job = r.json()
        if job["status"] in ("succeeded", "failed"):
            return job
        time.sleep(interval)
    raise TimeoutError(f"task {task_id} not finished in {max_wait}s")

那段代码中有四个细节承载了大部分重量:

  • 每次 HTTP 调用都设置显式超时。 挂起的请求比失败的请求更糟。
  • 最大等待时间。 无界循环会把卡住的任务变成卡住的服务。
  • 终止状态检查。 只在状态真正最终时才停止;将其他一切视为仍在运行,包括你不认识的状态。
  • 返回整个任务对象。 调用方通常需要输出 URL 和任何错误详情,而不仅仅是一个布尔值。

轮询节奏

对于视频任务,五秒是合理的默认值。每 200ms 轮询一次会消耗配额且毫无益处,因为渲染无论如何都需要几分钟。如果你提交许多任务,将轮询循环放在 worker 中而不是请求处理器中,并对长时间运行的任务稍作退避。

将它们连接起来

payload = {
    "model": "kling-3.0-omni/image-to-video",
    "input": {"image": first_frame_url, "prompt": prompt},
    "options": options,
}

job = wait(submit(payload))
if job["status"] == "failed":
    raise RuntimeError(job.get("error") or "generation failed")
print(job["output"]["url"])

因为 submit 返回普通字符串,而 wait 返回 dict,你可以独立测试每一半:模拟提交响应来测试轮询逻辑,单独调用 submit 来对照实时 API 检查请求体。

调试常见故障

故障集中在三个层次,识别层次就是诊断的大部分工作。

第 1 层:提交时拒绝

这些在任何任务存在之前以 4xx 响应出现。常见原因:

  • 认证——缺少头、bearer token 格式错误、key 未导出到你认为的环境中。
  • 请求体结构——字段拼写错误、数组位置用了字符串、选项嵌套层级错误。
  • 不支持的取值——模型不接受的分辨率或音频组合。
  • 资源问题——无法访问、已过期或不是图像的文件 URL。

阅读错误响应体。API 通常会指出有问题的字段,最快的修复通常就是消息所建议的。在 curl 中复现失败的调用,以确认问题出在请求体而不是客户端代码。

第 2 层:传输问题

网络位于你的代码和任务生命周期之间。提交调用上的超时和连接重置是模糊的:任务可能已创建,也可能没有。如果你盲目重试,可能会产生重复任务。

安全的模式是只重试幂等安全操作,并记录每次提交尝试及其响应。如果你确实无法判断提交是否成功,请在重新提交前检查任务历史。在轮询方面,单次状态检查失败不等于任务失败——捕获异常、等待、再次询问。

第 3 层:任务级失败

这些以终端状态到达一个原本被接受的任务。它们通常意味着内容无法按指定方式生成:两帧差异太大无法插值、提示词与源图像无法协调,或渲染期间内部错误。

在代码中清晰区分它们,因为补救措施不同。4xx 意味着修复请求。失败的任务意味着更改输入或重试——如果同一请求体两次失败,停止重试并改变一些东西:简化提示词、选择更接近的帧、降低分辨率。

快速分诊清单

  1. HTTP 状态是 4xx 吗?修复请求体或凭据;不要轮询。
  2. 提交成功但任务从未完成?你的等待限制太短或任务卡住了——记录任务 ID 并调查,而不是永远循环。
  3. 任务失败了吗?阅读错误详情,然后调整输入,而不是原样重新提交。
  4. 成功但视频不对?那是提示词和帧选择问题,不是 API 问题。

发布:生产检查清单

在面向用户之前,加固那些会静默失败的部分。

配置。 将 base URL 和 API key 保存在环境变量或密钥管理器中,绝不要放在源代码里。如果任一缺失,在启动时快速失败,而不是在第一个用户请求时。

超时和重试。 每次出站调用都需要显式超时。只在重复可容忍的地方重试提交,并将轮询重试视为安全——幂等状态读取。限制总等待时间并向调用方暴露清晰的超时。

任务 ID 是记录。 将任务 ID 与提交的请求体一起持久化:帧、提示词、分辨率、音频选择和时间戳。当有人两周后问为什么视频看起来不对时,那一行就是全部答案。

输出是临时的。 将返回的视频 URL 视为临时。任务一成功,就下载并存储到你自己的持久存储中,并将存储位置记录到任务上。

成本意识。 视频生成按生成输出计费,因此分辨率、时长和生成次数是重要的杠杆。以低分辨率迭代,有意识地生成最终版本,并记录设置,以便将支出归因到某个用例,而不是在账单上才发现。

可观测性。 在提交时和终端状态时输出日志行,包含任务 ID、延迟和状态。每个模型的简单成功率指标会告诉你提示词更改或选项调整何时开始导致任务失败。

对照文档验证,而不是对照示例。 在每次涉及音频、分辨率或帧输入的发布之前,在模型参考页面上确认字段名和接受的取值,因为这些是最可能漂移的设置。

有了客户端、轮询循环和这些保障措施,kling-3.0-omni/image-to-video 就成为一个可预测的组件,而不是一个你祈祷还能工作的脚本。

立即开始构建持久 AI 产物

获取 API 密钥,通过一个基于任务(task)的 API 调用图像、视频和音频模型。

阅读 API 文档