Seedream 5.5 API 教程:生成、编辑与迁移

使用 Seedream 5.5 API 生成和编辑图像:模型 ID、任务状态、鉴权下载、异步轮询和迁移陷阱。

推理时代阅读约 10 分钟
Seedream 5.5 API 教程:生成、编辑与迁移

Seedream 5.5 迁移的实用之处在于文件工作流,而不只是替换模型名称:生成图像、检查返回的任务、保存图像,然后将保存的文件字节作为编辑参考发送。

AIHubMix 上的 Seedream 5.5目前使用doubao-seedream-5-5和POST /ai/v1/images/generations。本教程将生成陶瓷马克杯产品图,再请求仅修改颜色。

范围:文档已于 2026 年 11 月 12 日核查。代码仅通过本地语法和离线辅助函数检查;未执行生成请求。这些检查不能证明输出质量、编辑保真度、延迟或实际账单。

1. 准备一个简洁明确的请求

使用 Python 3.10+ 和requests。在 Bash 中安装依赖,并在输入密钥时避免将其值写入 shell 历史记录:

python3 -m pip install requests
read -r -s -p 'AIHubMix API key: ' AIHUBMIX_API_KEY
printf '\n'
export AIHUBMIX_API_KEY

对于部署的代码,请通过密钥管理器注入环境变量。不要将密钥放在源文件、请求示例、日志、笔记本或浏览器端代码中。在执行任一 POST 之前,请按异步任务指南的要求启用账户的任务功能。此先决条件适用于原生/ai/v1媒体任务端点,包括同步等待;它与是否选择后台执行是两回事。

阅读当前模型端点架构,并根据path选择对应条目,而不是按数组位置选择。模型专属约束优先于通用参数列表。

设置项 此处使用的值
模型 doubao-seedream-5-5
路由 POST /ai/v1/images/generations
请求体 扁平 JSON
尺寸 2048x2048
图像数量 n = 1
文件格式 png
响应格式 url

架构要求提供model和非空的prompt,将n限制为 1,并列出png和jpeg两种输出格式。它接受image URL、Base64 输入或{url}对象;images接受数组,已公布的参考图上限为 14 张。本文未对该限制进行运行时测试。此示例明确使用一张参考图和默认尺寸,不包含外层input对象或extra扩展字段。

2. 生成图像、检查任务并保存 PNG

统一图像 API 文档定义的任务响应包含id、object: image、model、status、output和error。不要按旧版的data[0].url来读取。

先阅读简要工作流;完整的设置和下载辅助函数见下方折叠的附录。执行这些示例之前,请先将附录代码块加载到同一个 Python 会话中。它会定义settings、api_json和collect,但不会提交图像请求。然后按顺序运行简短代码块。每个 POST 都是单独的付费操作;不要为了修复缺失的响应或下载失败而重新运行。

只提交一次图像请求

generation = {
    **settings,
    "prompt": "Product photograph of one matte blue ceramic mug on a cream tabletop, "
              "soft side lighting, plain warm-gray background, no text or logos.",
}
task = api_json("POST", IMAGES + "/generations", generation)

检查任务,然后保存 PNG

省略async表示同步等待。HTTP 200 的响应仍可能包含status: failed。辅助函数会先保存任务 ID,再检查状态:只有completed才会继续下载;遇到failed、cancelled或未知状态时则会停止。pending和in_progress需要检查已有任务,而不是再次生成。

collect(task, "mug-blue")

此调用使用创建任务时的密钥获取已完成的输出、检查 PNG 字节,并保存为mug-blue.png。它不会自动重试创建请求,也会在转发密钥前拒绝非预期的下载主机或重定向。完整实现见附录;这里描述的是基于文档的行为,并非已验证的生成结果。

模型指南称输出 URL 的有效期约为 30 分钟。请尽快使用相同的 Bearer 请求头下载,并自行保存文件字节。expires_at: null并不意味着永久保留。如果b64_json有值,请直接解码,而不是下载 URL。

3. 编辑本地文件,而不是受保护的下载 URL

在花费费用进行编辑之前,请自行检查保存的图像。已通过鉴权客户端下载的content_url不一定是图像供应商能够获取的公开 URL。请将本地 PNG 重新上传为 Data URI:

reference = Path("mug-blue.png").read_bytes()
if not reference.startswith(b"\x89PNG\r\n\x1a\n"):
    raise SystemExit("The editing reference must be the saved PNG.")
edit = {
    **settings,
    "prompt": "Change only the mug color from blue to forest green. Preserve the mug shape, "
              "camera angle, tabletop, background, lighting, and shadows.",
    "image": "data:image/png;base64," + base64.b64encode(reference).decode("ascii"),
}
task = api_json("POST", IMAGES + "/generations", edit)
collect(task, "mug-green")

此请求要求保留原图内容,但不能证明确实保留了。请先检查结果,再进行下一轮付费操作。包含内嵌编码在内的整个 JSON 请求必须小于或等于 32 MiB;此外还受模型专属媒体限制约束。也可以使用真正公开且图像供应商可访问的 URL 作为输入。

4. 可选:异步等待与中断恢复

第 1 节中的账户先决条件与执行模式是两回事。403 async_not_enabled表示需要在控制台启用任务功能。只有后台执行是可选的。

如果你更愿意在后台生成,请构建相同的generation字典,然后用下面两行替换同步版本的task = api_json(...)行以及单独的collect(...)调用。提交前选择一种模式;不要同时运行两个版本:

task = api_json("POST", IMAGES + "/generations", {**generation, "async": True})
collect(task, "mug-blue", poll=True)

辅助函数会大约每 15 秒查询一次GET /ai/v1/images/{id},直到任务进入终止状态。这是文档建议的轮询间隔,并非服务器强制执行的间隔。Webhook 需要设置async: true;接收端不在本教程范围内。

如果收集流程中断,需要时可在新会话中仅重新加载附录里的通用设置/辅助函数代码块,而不要重新运行创建代码块。如果已保存 ID,可在不发送任何 POST 的情况下恢复:

task_id = Path("mug-blue.task-id").read_text(encoding="utf-8").strip()
task = api_json("GET", IMAGES + "/" + quote(task_id, safe=""))
collect(task, "mug-blue", poll=True)

恢复编辑任务时使用相应的mug-green文件名前缀。如果创建响应在保存 ID 之前就已丢失,请改为检查近期任务:

recent = api_json("GET", IMAGES + "?limit=20&order=desc")
for row in recent.get("data", []):
    print(row.get("id"), row.get("model"), row.get("status"), row.get("created_at"))

请根据模型和创建时间进行匹配;不要盲目选择最新任务。如有需要,将next_after作为下一个请求的after游标。如果无法确定匹配项,请联系支持团队,而不是再次提交请求。列表是一个快照;轮询时请查询任务详情。某行中的output_error表示无法读取输出,并不代表生成没有产出或费用发生了变化。

继续使用创建任务时的密钥。特别是,统一任务列表、详情和内容访问按 token 隔离:同一账户上的其他密钥无法读取这些任务。本地中断并不能证明任务失败或未产生费用。

5. 迁移错误与当前错误

旧版 Doubao 适配器文档列出了/v1/models/doubao/<id>/predictions下的旧 ID,并使用input封装对象。其模型清单未列出 5.5。请使用此处可查到的 5.5 路由和扁平请求体;本文未测试旧版生成请求是否会失败。请准确复制当前带连字符的 ID,不要假设带点号的写法是其别名。

不要沿用顶层的quality、background、aspect_ratio或seed:这些字段不在该模型的当前架构中。ByteDance 的发布公告介绍了原生透明 RGBA 输出,但网关架构既未提供background,也未提供layer_decomposition。仅选择 PNG 并不能保证透明输出。

这些是文档中记录的统一图像错误,并非实测的 5.5 响应。请将 HTTP 状态与任务状态分开检查,并根据code分支处理,而不是匹配完整错误消息。

位置 代码 后续操作
HTTP 400 schema_violation 更正架构字段
HTTP 400 capability_not_supported 移除不支持的选项
HTTP 504 sync_timeout 尝试恢复;联系支持团队
HTTP 502 task_status_unavailable 不要重新提交
HTTP 422 output_blocked 修改内容
HTTP 403 async_not_enabled 启用任务功能
任务失败 result_delivery_failed 调查现有结果

对于 HTTP 502 task_status_unavailable,指南说明:

无法确认模型供应商的任务状态。不要再次提交。请向支持团队提供任务 ID 或请求 ID。

对于任务级别的result_delivery_failed,指南说明:

无法交付生成结果。不要自动重新提交。请向支持团队提供任务 ID。

sync_timeout并不能证明任务从未执行。请保留error.tid;只有实际返回了任务 ID 时才使用该 ID,并联系支持团队处理结果不确定的情况。对于可恢复的下载失败,请重试获取结果,而不是重新生成。410 artifact_expired则有所不同:该产物已无法获取。请先检查已保存的文件;如果仍需要替代文件,请明确授权重新付费生成。对于429 too_many_downloads,请检查已保存的副本或联系支持团队;仅仅等待并不能保证恢复访问。

指南明确规定,output_blocked不收取生成费用,包括该错误出现在失败任务中的情况。不要将此例外扩大到所有失败或受到审核的请求;output_policy_violation仍按现有审核计费规则处理。

6. 预算要考虑完整工作流,而不只是单次输出

新浪科技的发布报道称,单次 2K 方形图像编辑的费用约为 CNY 0.28,其中包含图像输入和输出,文本 token 免费。这是媒体报道,并非经核实的官方价目表或 AIHubMix 报价。

举例来说,对现有图像进行两轮付费编辑,且每轮都符合上述取整后的场景,费用约为2 × CNY 0.28 = CNY 0.56。这不包括最初的文生图生成、额外参考图、额外候选结果或重试。这不是实测账单。

对于这个先生成再编辑的示例,可按总费用 = 初次生成费用 + 参考图编辑费用计算。根据已查阅的页面,无法确定完整的官方 5.5 价目,因此本教程不提供固定总价或精确的新旧版本节省比例。

提交之前:

  • 确认当前 ID、路由、扁平请求体和模型架构。
  • 将密钥保存在环境变量中;保留任务 ID 以便恢复。
  • 将每个 POST 视为单独的付费操作,绝不要把下载重试当作生成重试。
  • 编辑前,先检查任务状态和已保存的图像。
  • 使用正确的 MIME 类型重新上传本地文件。
  • 考虑再次生成之前,先找回状态不确定的任务。

先从一张图和一次有明确目的的编辑开始;查看文件后,再决定是否增加编辑轮次。

附录:通用设置和下载辅助函数

阅读工作流程时可以跳过本附录。运行简短示例前,展开此代码块,并在同一个 Python 会话中加载一次。它会配置会话并定义函数,但不会生成图像。若要在新会话中恢复中断的任务,只需重新加载此代码块,而不是创建或编辑代码块。

展开完整辅助代码 — 在运行简短示例前加载一次;不会发起图像请求
import base64
import os
import time
from pathlib import Path
from urllib.parse import quote, urlsplit

import requests

BASE = "https://aihubmix.com"
IMAGES = "/ai/v1/images"
settings = {
    "model": "doubao-seedream-5-5",
    "size": "2048x2048",
    "n": 1,
    "output_format": "png",
    "response_format": "url",
}
client = requests.Session()
client.headers["Authorization"] = "Bearer " + os.environ["AIHUBMIX_API_KEY"]


def api_json(method, path, body=None):
    try:
        response = client.request(
            method, BASE + path, json=body,
            timeout=(15, 600), allow_redirects=False,
        )
    except requests.RequestException:
        raise SystemExit(
            "Connection interrupted. Recover the existing task before another POST."
        ) from None
    try:
        data = response.json()
    except ValueError:
        raise SystemExit(
            f"HTTP {response.status_code}: non-JSON response; recover before another POST."
        ) from None
    if not isinstance(data, dict):
        raise SystemExit("Unexpected response; recover before another POST.")
    if not 200 <= response.status_code < 300:
        error = data.get("error") or {}
        raise SystemExit(
            f"HTTP {response.status_code}: code={error.get('code')}, "
            f"tid={error.get('tid')}. No automatic generation retry."
        )
    return data


def collect(task, stem, poll=False):
    task_id = task.get("id")
    if not isinstance(task_id, str) or not task_id:
        raise SystemExit("No task ID returned. Recover from the task list; do not resubmit.")
    Path(stem + ".task-id").write_text(task_id + "\n", encoding="utf-8")
    while True:
        status = task.get("status")
        print("Task:", task_id, "status:", status)
        if status == "completed":
            break
        if status not in {"pending", "in_progress"}:
            error = task.get("error") or {}
            raise SystemExit(
                f"Stopped: status={status}, code={error.get('code')}; task={task_id}."
            )
        if not poll:
            raise SystemExit("Task still active. Query the saved ID; do not recreate it.")
        time.sleep(15)
        task = api_json("GET", IMAGES + "/" + quote(task_id, safe=""))

    output = task.get("output") or []
    if not output:
        raise SystemExit("Completed without readable output; investigate the saved task.")
    item = output[0]
    if item.get("b64_json"):
        image_bytes = base64.b64decode(item["b64_json"], validate=True)
    else:
        url = item.get("content_url")
        if not isinstance(url, str):
            raise SystemExit("Missing content_url; investigate the saved task.")
        parsed = urlsplit(url)
        if (parsed.scheme, parsed.netloc) != ("https", "aihubmix.com"):
            raise SystemExit("Unexpected download origin; key was not forwarded.")
        try:
            response = client.get(url, timeout=(15, 120), allow_redirects=False)
        except requests.RequestException:
            raise SystemExit("Download interrupted; recover this task, not a new generation.") from None
        if not 200 <= response.status_code < 300:
            try:
                detail = response.json()
            except ValueError:
                detail = {}
            error = detail.get("error", {}) if isinstance(detail, dict) else {}
            code = error.get("code") if isinstance(error, dict) else None
            if response.status_code == 410 or code == "artifact_expired":
                raise SystemExit("Artifact expired; retrieval cannot recover it. Check saved files first.")
            if code == "too_many_downloads":
                raise SystemExit("Download allowance exhausted; check saved files or contact support.")
            raise SystemExit(
                f"Download HTTP {response.status_code}, code={code}; "
                "inspect the saved task. Retry retrieval only if recoverable."
            )
        image_bytes = response.content
    if not image_bytes.startswith(b"\x89PNG\r\n\x1a\n"):
        raise SystemExit("Expected PNG bytes; stopped before saving or reusing the result.")
    destination = Path(stem + ".png")
    destination.write_bytes(image_bytes)
    print("Saved:", destination)
    return destination

600 秒的读取超时是客户端的设置,并非服务延迟承诺。PNG 检查适用于本教程明确指定的输出格式;如果选择 JPEG,请相应调整。

常见问题

Seedream 5.5 应使用哪个模型 ID 和端点?
使用 doubao-seedream-5-5,并通过 POST /ai/v1/images/generations 发送扁平 JSON。不要假定其他拼写或旧版适配器与之等效。

本教程是否包含经过测试的生成结果?
没有。代码基于文档编写,只进行了本地语法检查和离线辅助函数检查。没有执行推理请求、图像质量测量、延迟测量或计费测试。

必须使用异步模式吗?
不必;默认是同步等待。后台执行使用布尔值 async: true。即使采用同步等待,也必须先在账户层级启用原生媒体任务端点所需的任务功能。

HTTP 200 是否意味着图像已成功生成?
不是。请检查任务状态及其错误信息。即使 HTTP 请求成功,任务仍可能失败;只有任务完成后才能下载。

结果下载 URL 能否作为下一次编辑的参考图?
不要想当然。使用创建任务时的密钥下载图像,然后将本地图像编码为 Data URI,或提供真正公开的图像 URL。

超时或响应丢失后应该怎么做?
不要自动重复生成。使用创建任务时的密钥检查已保存的任务 ID,或从图像列表中找回该任务。保留请求 ID;如果结果仍不确定,请联系支持团队。

选择 PNG 是否就能保证输出透明背景,或产生 CNY 0.28 的账单?
两者都不能保证。该架构未提供经过验证的透明度控制。CNY 0.28 是媒体报道中的近似 2K 编辑示例,并非 AIHubMix 报价,也不是文生图加编辑的固定价格。

继续阅读:Seedream 5.5 系列

来源

  1. Seedream 5.5 发布公告 — ByteDance Seed;供应商对功能的说明。
  2. 当前 Seedream 5.5 端点架构 — AIHubMix;请求字段和特定于模型的限制。
  3. 统一图像生成 API — AIHubMix;任务、下载和当前错误说明。
  4. Seedream 5.5 模型指南 — AIHubMix;模型 ID 和短时有效的身份验证下载链接;特定路由的错误说明请参阅统一 API 文档。
  5. Seedream 5.5 发布报道 — 新浪科技;编辑成本的近似示例,并非官方价格表。

一个网关接入多家 AI 服务商, 为生产环境而生。

访问推理时代官网