教程
recraft-v4-styles/text-to-image API 教程:curl、Python 与异步轮询
在 HiAPI 上使用风格参考图像生成的完整请求演练
recraft-v4-styles/text-to-image 能为你做什么
recraft-v4-styles/text-to-image 是一个风格参考图像生成端点。你向它提供一个描述想要看到的内容的文本提示词,以及一张或多张描述外观风格的参考图像。模型将两者融合:提示词承载内容与构图意图,参考图承载视觉风格。
在选择端点时,这一区别很重要。当外观可以完全用文字表达时——比如「一个咖啡杯的扁平矢量插画,双色,粗轮廓」——直接使用提示词生成图像就是正确的工具。然而在实际产品工作中,大多数场景涉及一种难以描述、却易于展示的品牌外观。一张现成的关键视觉图、一张此前已批准的渲染图,或一个设计系统样本,远比一段形容词段落更精确地定义了风格规格。这正是该端点填补的空白。
从开发者的角度来看,可以将其视为一个双输入变换:提示词 + 风格参考 -> 图像。集成它的一切都源于这个形状。你不仅仅是在序列化一个字符串,而是在组装一个包含多个部分的小型负载,其中参考图是一等输入。你的请求构建代码应当以此方式对待它们,而你的校验层应当在任务离开你的服务之前,拒绝那些只有提示词而没有可用风格参考的任务。
该端点是异步的。提交请求会返回一个任务,然后你轮询该任务,最终图像以 URL 形式到达,而非内联字节。这是针对图像工作负载的刻意设计:生成耗时足够长,保持 HTTP 连接打开既浪费又脆弱。一旦你接受了「提交-轮询-下载」的生命周期,集成的其余部分就是普通的 API 管道工作。
一次可用请求的剖析
在编写任何代码之前,先理清字段分组。一个 recraft-v4-styles/text-to-image 请求包含三个逻辑部分。
提示词
提示词是你的内容指令。围绕画面中应包含的内容来编写——主体、构图、取景、物体数量、背景处理。避免将提示词预算花在参考图已经传达的风格形容词上;提示词与参考图之间的信号冲突往往会产生模糊的结果。保持提示词简短、具体且可测试。
风格参考输入
这是该端点区别于普通文本生成图像调用的部分。风格参考是与提示词一同提供的图像,用于定义视觉语言。在组装它们时,应将它们视为一组精心策划的集合,而非一堆杂乱的图片:
- 一致性胜过数量。 几张共享同一连贯外观的参考图,比许多风格各异的参考图更能教会模型。
- 分辨率和取景很重要。 参考图应能代表你想要的输出,而不是缩略图或严重裁剪的碎片。
- 它们必须是可访问的。 如果你的参考图以 URL 形式传递,这些 URL 在请求时必须可以被公开获取。一个六十秒后过期的签名链接会导致慢速任务失败。
将你的参考图集保存在配置中,而不是散落在代码各处,这样你可以将其与品牌资产一同进行版本管理。
输出选项
其余字段描述你想要返回的产物——这些是影响返回图像而非其内容的旋钮。确切的参数名称和可接受值列在该端点的参考页面上;请在那里阅读,而不是猜测,并将它们集中到代码中的一个位置,这样规格变更只需触及一个文件。
一个有用的习惯:在你选择的语言中将 JSON 体构建为字典/对象,在首次运行时以调试级别记录一次,并在将糟糕结果归咎于模型之前,先肉眼检查其结构。
使用 curl 调用
从 curl 开始。它移除了你和线格式之间的所有库抽象层,因此如果请求有误,你会立即发现。
提交是一个 POST 请求,带有 JSON 体和携带你 HiAPI API 密钥的认证头:
curl -sS -X POST "https://api.hiapi.ai/v1/recraft-v4-styles/text-to-image" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "a ceramic coffee cup on a linen tablecloth, soft daylight",
"style_references": [
"https://your-cdn.example.com/style/ref-01.png"
]
}'
关于你看到的内容,有几点说明:
- 将密钥保存在环境变量(
$HIAPI_API_KEY)中,绝不要内联在 shell 历史记录或已提交的脚本中。 -d负载是纯 JSON。Shell 中的引号规则是常见的麻烦来源——如果你遇到解析错误,将请求体回显到一个文件中,并使用-d @body.json代替。- 此处显示的字段名遵循上一节的请求剖析;在发布之前,请在该端点参考页面上确认确切的名称和可接受的输出选项。
响应不是图像。它是一个任务确认:一个标识你刚排队的生成的标识符,以及一个告诉你它尚未完成的状态字段。首次运行时仔细阅读响应体,并记下保存任务 ID 的字段名——该字符串是你后续所有操作的句柄。保存它。如果你的首次响应包含错误对象,请停下来修复请求;轮询一个你从未收到的 ID 是最常见的死胡同。
用 Python 实现同样的请求
将 curl 调用移植到 Python 一次,你就拥有了一个可复用的原语。下面的示例使用 requests 并完全镜像请求体。
import os
import requests
API_KEY = os.environ["HIAPI_API_KEY"]
BASE_URL = "https://api.hiapi.ai/v1"
HEADERS = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
STYLE_REFERENCES = [
"https://your-cdn.example.com/style/ref-01.png",
]
def submit_generation(prompt: str) -> dict:
body = {
"prompt": prompt,
"style_references": STYLE_REFERENCES,
}
resp = requests.post(
f"{BASE_URL}/recraft-v4-styles/text-to-image",
headers=HEADERS,
json=body,
timeout=30,
)
resp.raise_for_status()
return resp.json()
if __name__ == "__main__":
job = submit_generation(
"a ceramic coffee cup on a linen tablecloth, soft daylight"
)
print(job)
在调整此代码时,有两个设计选择值得保留:
- 始终设置超时。 提交应快速返回;如果它挂起,你想要一个异常,而不是一个卡住的进程。
- 返回解析后的 JSON,而非响应对象。 下游代码应依赖于任务负载,而非
requests的内部实现。这使得日后替换传输层变得轻而易举。
如上所述,在模块或配置级别定义参考图集。当品牌外观演变时,你只需更新一个列表,而无需在调用点中寻找。并将请求体构建保留在单个函数中,这样端点所需的任何字段都只需在一个地方添加。
轮询异步结果
生成是异步的,因此你提交的任务在完成之前会经历多个状态。你的客户端需要一个等待循环,用于检查状态、在检查之间退避,并最终放弃。
import time
def wait_for_result(job_id: str, timeout_s: int = 300, interval_s: float = 3.0) -> dict:
deadline = time.monotonic() + timeout_s
delay = interval_s
while time.monotonic() < deadline:
resp = requests.get(
f"{BASE_URL}/recraft-v4-styles/text-to-image/{job_id}",
headers=HEADERS,
timeout=30,
)
resp.raise_for_status()
data = resp.json()
status = data.get("status")
if status in ("succeeded", "failed"):
return data
time.sleep(delay)
delay = min(delay * 1.5, 15.0)
raise TimeoutError(f"job {job_id} did not finish in {timeout_s}s")
在此循环中,重要的是:
- 将未知状态视为待处理。 与显式的终端状态进行比较,而不是与进行中状态的允许列表进行比较;如果平台添加了中间状态,你的循环会继续等待而不是崩溃。
- 退避。 固定每秒轮询一次持续五分钟,就是五分钟的浪费请求。从小间隔开始,倍增并设上限。
- 限制等待时间。 每个循环都需要一个截止时间,并且截止时间应以调用者可以处理的异常形式出现。
- 根据失败分支。 终端失败状态携带诊断信息。记录完整的响应体——这就是「它坏了」和「参考 URL 返回了 403」之间的区别。
如果你要处理大量图像,不要在 Web 请求处理程序中休眠。提交、持久化任务 ID,并让后台工作进程或计划任务驱动轮询。端点参考页面记录了精确的状态值和轮询路径;每当你升级客户端时,请检查它。
下载并存储输出
当任务到达终端成功状态时,响应包含生成图像的 URL。该 URL 是 API 与你的存储层之间的交接点。
将其作为字节获取,并写入你需要的位置:
def download_image(url: str, dest_path: str) -> None:
resp = requests.get(url, timeout=60)
resp.raise_for_status()
with open(dest_path, "wb") as f:
f.write(resp.content)
对于本地实验之外的任何场景,不要只将产物留在返回的 URL 上。将生成的图像视为临时指针,并将其复制到你控制的存储中——对象存储桶、你的 CDN 源站或媒体管道。原因有三:
- 生命周期。 生成的输出 URL 不保证无限期持久。在重试期间从过时的 URL 重新下载是一种潜在的静默失败。
- 稳定的键。 你的应用程序需要一个可以放入数据库行的确定性标识符,而不是一个每次重新生成都会变化的 URL。
- 溯源。 将提示词、风格参考集版本和任务 ID 与图像一起存储,可以让你日后重现或审计任何资产。将这些元数据保存在伴随记录或数据库列中。
上传到对象存储时,流式传输响应体,而不是将大图像完全缓冲在内存中,并从响应头设置内容类型。如果你通过自己的 CDN 将图像返回给客户端,此步骤也是应用缓存头的地方。
首次运行故障排除
大多数首次尝试会以几种可预测的方式失败。请按顺序检查这些。
请求体被拒绝
400 类错误通常意味着 JSON 格式错误或缺少必需字段。先运行 curl 版本并检查你发送的确切请求体。常见问题:shell 引号破坏了 JSON、将 API 期望为数组的字段发送为字符串,或输出选项的值超出可接受范围。端点参考页面是字段名和值的权威来源——将你的请求体与它进行字面比对。
你有提示词但没有可用的风格参考
该端点围绕风格参考构建。如果你的请求包含提示词,但参考图为空、无法访问,或在获取时返回认证错误,任务要么会被拒绝,要么会产生忽略你预期外观的输出。使用平台会使用的相同无凭据客户端获取每个参考 URL 进行验证。需要会话 cookie 或签名查询字符串的参考图视为已损坏。
你从未获得任务 ID
提交响应是确认,而不是图像。如果你编写的代码期望首次响应中包含图像字节,它将在每次运行时失败。解析响应,提取任务 ID,然后才开始轮询。在最初几次运行时记录完整的提交响应,以便你能看到其形状。
你轮询过于频繁
一分钟内数百次状态检查不会让任务更快完成,反而更可能触发速率限制。使用递增间隔并设上限,每个任务使用单个工作进程。如果你要分派大量任务,请添加小的并发限制,以免提交突发变成轮询突发。
任务完成但下载失败
获取输出 URL 时出现 403 或 404 通常意味着 URL 已过期,或任务在你误读的状态检查之后失败。确认终端状态为成功,然后重新阅读响应以获取实际的输出字段。一旦产物可用就立即存储,而不是推迟下载。
按照管道运行的顺序——提交、轮询、下载——逐一解决这些问题,首次运行失败几乎总能化解为显而易见的修复。端点参考及其请求模式是确认任何字段级疑问的最快方式。