HiAPI
博客

教程

如何通过 HiAPI API 使用 kling-4.0/text-to-video:curl、Python 及一个可运行的请求示例

面向开发者的实操指南:电影感提示词、同步音频、异步轮询与输出下载

2026年11月3日
深炭底色上一条宽大的琥珀色光带斜扫而过,下方配一条细窄的暗棕平行条带,整体用于文章封面。

kling-4.0/text-to-video 的功能与适用场景

kling-4.0/text-to-video 是通过 HiAPI 统一 API 提供的生成式视频模型。你给它一段文本提示词,它返回一段视频片段——运动、镜头运动和场景构图完全由你的描述决定。这是一个文生视频端点:没有源图像需要动画化,因此模型对取景和主体外观拥有完全的自主权。

适用场景:

  • 你需要根据脚本或提示词模板生成短电影感片段——b-roll、预告片、概念板、社交媒体视频。
  • 你希望同步音频随视频一同生成,而不是得到一个需要在后期配乐的无声文件。
  • 你正在构建一条流水线,视频生成只是众多步骤之一(脚本编写、素材准备、发布),因此你需要 HTTP API 而非 Web 界面。

但如果你需要对某个具体真实主体进行逐帧精确控制(图像到视频或编辑模型在这方面表现更好),或者你需要在用户请求中同步获得响应,则本模型不太合适——视频生成需要时间,因此该端点按设计是异步的。

由于一切请求都经由 HiAPI,因此无论使用平台上的哪个模型,凭证、请求/响应格式和任务生命周期都是相同的。这种一致性正是基于它进行构建的实际理由:日后添加或替换模型无需重写认证或轮询层。

HiAPI 上的前提条件与认证

在发出第一个请求之前,请确保你已具备:

  • 一个 HiAPI 账户和一个项目 API 密钥。请将其视为机密——切勿提交到代码仓库,切勿将其放入客户端代码中。
  • 你选择的 HTTP 客户端。以下示例使用 curl 和 Python 3 的 requests。
  • 模型标识符,与目录中完全一致:kling-4.0/text-to-video。模型名称拼写错误是首次调用失败最常见的原因。

凭证在每个请求(包括轮询请求)中作为 HTTP 头发送。HiAPI 上的常规模式是 bearer token:

export HIAPI_API_KEY="your-api-key-here"

然后在请求中:

Authorization: Bearer $HIAPI_API_KEY
Content-Type: application/json

将基础 URL 和密钥保存在环境变量或密钥管理器中,而不是硬编码。对于本地开发,.env 文件加 Python 中的 os.environ 查询就足够了。对于已部署的服务,在运行时注入密钥。

一个值得尽早养成的设置习惯:先构建一个微小的“我是谁”式调用——任何轻量级的认证请求——在调试视频任务之前确认密钥和基础 URL 正确无误。将认证与模型逻辑隔离,日后可节省时间。

一个可运行的 curl 请求

最小调用会创建一个任务并返回任务标识符。结构如下:

curl -X POST "$HIAPI_BASE_URL/video/generations" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kling-4.0/text-to-video",
    "prompt": "A slow aerial push over a rain-soaked Tokyo alley at night, neon signs reflecting in puddles, steam rising from vents, shallow depth of field, cinematic color grading",
    "generate_audio": true
  }'

逐字段解读请求体:

  • model — 模型标识符。必填。
  • prompt — 你的场景描述。你的质量预算大部分都在这里;参见提示词部分。
  • generate_audio — 请求随视频一同生成同步音频。如果只需要视觉轨道,可省略或设为 false。

响应是一个任务对象,而不是完成的视频。预期会有一个状态字段和一个你将用于轮询的 id——例如状态为 queued 或 processing,外加一个任务 id。立即存储该 id;如果你的进程终止,该 id 是你对进行中工作的唯一凭据。

两点实用提示。第一,提示词变长后,将 JSON 保存在文件中并使用 -d @payload.json——多行提示词的 shell 引号处理是常见错误来源。第二,分别捕获响应体和 HTTP 状态码(curl -s -w "\n%{http_code}"),以便区分被拒绝的请求和网络故障。

将请求转换为 Python

同样的请求在 Python 中的写法,结构便于直接引入真实脚本:

import os
import requests

BASE_URL = os.environ["HIAPI_BASE_URL"].rstrip("/")
API_KEY = os.environ["HIAPI_API_KEY"]

HEADERS = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
}

def create_video_job(prompt: str, generate_audio: bool = True) -> dict:
    payload = {
        "model": "kling-4.0/text-to-video",
        "prompt": prompt,
        "generate_audio": generate_audio,
    }
    resp = requests.post(
        f"{BASE_URL}/video/generations",
        headers=HEADERS,
        json=payload,
        timeout=30,
    )
    resp.raise_for_status()
    return resp.json()

if __name__ == "__main__":
    job = create_video_job(
        "A slow aerial push over a rain-soaked Tokyo alley at night, "
        "neon signs reflecting in puddles, steam rising from vents, "
        "shallow depth of field, cinematic color grading"
    )
    print(job)

为何这样写:

  • requests.post(..., json=payload) 一并处理序列化和内容类型,避免手动 json.dumps。
  • raise_for_status() 将 4xx/5xx 转为可捕获的异常,而不是将错误响应体静默地解析为任务。
  • timeout 是必需的。没有它,卡住的连接会无限期阻塞 worker。
  • 配置位于从环境读取的模块级常量中,因此该函数可测试且可复用。

此函数仅创建任务。它立即返回任务句柄——这正是你在 Web 处理器或队列 worker 中想要的。后续章节将介绍如何等待结果。

编写经得起考验的电影感提示词

文生视频模型奖励结构化。一个指明主体、镜头、运动和情绪的提示词,始终优于只有一句话的想法,因为每个元素都消除了模型原本会随机决定的歧义。

一个可用的模板:

[主体与场景],[机位与运动],
[画面内运动],[光照],[氛围或调色],[风格参考]

应用示例:

黎明时分,一个身穿黄色雨衣的孤独骑行者穿过一座石桥,低机位镜头以车轮高度平行跟拍,雾气在水面上飘荡,柔和的蓝调时刻光线搭配温暖的灯笼点缀,平静而略带忧郁,宽银幕变形镜头质感。

各部分的作用:

  • 主体与场景 — 具体名词胜于抽象概念。“一个身穿黄色雨衣的孤独骑行者”给了模型可渲染的东西;“自由”则没有。
  • 镜头 — 机位(低机位、航拍、平视)和运动(跟拍、缓慢推近、静止)是对观感制作价值最有力的杠杆。
  • 画面内运动 — 雾气飘荡、布料摆动、水面涟漪。视频需要运动;请明确说出。
  • 光照与氛围 — 时间、色温、情感基调。保持彼此协调一致。
  • 风格 — 镜头或格式语言(变形镜头、浅景深、电影感调色)比指定导演更能可靠地塑造质感。

提示词保持几句话的密集描述,而不是一段相互竞争的想法。如果一个镜头需要两个截然不同的动作,生成两个片段。并且迭代:一次只改变一个变量——先镜头,再光照——这样你能了解模型对什么有反应,而不是靠猜。

处理异步任务与轮询

视频生成不是瞬时的,所以模式是先创建,再轮询。你已经有了创建部分;以下是循环:

import time

def wait_for_job(job_id: str, timeout: int = 600) -> dict:
    deadline = time.monotonic() + timeout
    delay = 2.0
    while time.monotonic() < deadline:
        resp = requests.get(
            f"{BASE_URL}/video/generations/{job_id}",
            headers=HEADERS,
            timeout=30,
        )
        resp.raise_for_status()
        job = resp.json()
        status = job.get("status")

        if status == "succeeded":
            return job
        if status in ("failed", "cancelled"):
            raise RuntimeError(f"Job {job_id} ended with status {status}: {job}")

        time.sleep(delay)
        delay = min(delay * 1.5, 15.0)  # back off, cap at 15s

    raise TimeoutError(f"Job {job_id} did not finish in {timeout}s")

在生产环境中重要的设计决策:

使用退避轮询,而不是紧循环

从短间隔开始并逐渐增大——上面的指数退避在固定上限处封顶。持续快速轮询会消耗你的速率限制且毫无收益,因为生成时间不会因为你更早询问而缩短。

始终设置截止时间

每个轮询循环都需要最大墙钟时间和超限时明确的错误。否则卡住的任务会无声地永久占用一个 worker。

将终态视为最终结果

区分进行中状态(queued、processing)与终态(succeeded、failed、cancelled)。只有成功才返回输出;其他状态应抛出异常或转入重试路径,而不是继续循环。

将轮询与请求处理器解耦

在 Web 服务器中,不要让 HTTP 响应阻塞等待视频。创建任务,将 id 返回给客户端,并从后台 worker 或队列消费者进行轮询。持久化任务 id 及其最后已知状态,以便重启后不会丢失进行中工作的跟踪。

下载与验证输出

当任务达到 succeeded 时,响应会携带对生成媒体的引用——通常是指向已完成文件的 URL。及时下载,并将该 URL 视为临时性的:获取、存储并记录你自己的持久位置,而不是将提供商 URL 直接传给最终用户。

import pathlib

def download_output(job: dict, dest: pathlib.Path) -> pathlib.Path:
    url = job["output"]["video_url"]
    with requests.get(url, stream=True, timeout=120) as r:
        r.raise_for_status()
        dest.parent.mkdir(parents=True, exist_ok=True)
        with open(dest, "wb") as f:
            for chunk in r.iter_content(chunk_size=1 << 20):
                f.write(chunk)
    return dest

在将文件交给任何人之前值得做的验证步骤:

  • 检查传输已完成。 分块流式传输,比较写入大小与 Content-Length(如存在),读取不足时大声失败。
  • 确认音轨。 如果你请求了 generate_audio,文件应有音频流。用 ffprobe 快速探测是最便宜的检查:
ffprobe -v error -show_entries stream=codec_type -of csv=p=0 output.mp4

你应该同时看到 video 和 audio 行。如果音频缺失,重新检查该标志是否在创建调用中设置——事后无法添加。

  • 对时长和分辨率做基本校验,对照你的预期,以便在发布前捕获截断或规格错误的渲染。
  • 用确定性键存储(任务 id 加哈希),先写入临时路径,成功后重命名,并将任务 id 与资产一同保留以便追踪。

如果下载在传输中途失败,使用退避重试;如果 URL 已过期,你需要重新获取任务记录以获得新的引用。

将其封装为可复用的客户端

上述各部分自然构成一个类。将它们整合在一起,你就得到了可跨项目导入的东西,而无需复制:

class KlingTextToVideo:
    def __init__(self, base_url: str, api_key: str):
        self.base_url = base_url.rstrip("/")
        self.headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json",
        }

    def generate(self, prompt: str, generate_audio: bool = True,
                 timeout: int = 600) -> dict:
        job = create_video_job(prompt, generate_audio)  # create half
        return wait_for_job(job["id"], timeout=timeout)  # poll half

随着客户端成熟,有几件事需要添加:

  • 以任务 id 为键的结构化日志——状态转换、轮询次数、经过时间和失败。当渲染出现异常时,任务 id 是你在日志间的关联键。
  • 仅对瞬态错误(网络、5xx)进行有界重试。切勿盲目重试被拒绝的请求;格式错误的请求体会以相同方式每次失败,只会浪费配额。
  • 在你的层实现幂等性。 如果你的服务可能提交相同的提示词两次,在调用创建之前去重,而不是为两个任务付费。
  • 将提示词模板作为数据而非代码。 将电影感模板放在配置文件中,这样提示词迭代无需部署。
  • 按输出秒数核算成本。 计费基于生成的输出用量,因此跟踪每个任务生成的时长,可将支出归因到功能或用户。

客户端就绪后,流水线很短:从模板构建提示词,调用 generate,下载并探测输出,然后将存储的文件交给你的应用。如果日后在 HiAPI 上添加其他模型,相同的认证头、任务生命周期和下载模式同样适用——这正是只构建一次抽象层的主要理由。

立即开始构建持久 AI 产物

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

阅读 API 文档