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 介接器文件列出較舊的 ID,使用 /v1/models/doubao/<id>/predictions 路由與 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 發表報導 — Sina Technology;編輯成本的估算情境,並非官方費率。

一個閘道接入多家 AI 服務商, 為正式環境而生。

前往推理時代官網