mcpField
B
bytedance-seedream

bytedance-seedream

github.com/calesthio创意与媒体219106 次安装15 周前更新
代码仓库

关于 bytedance-seedream

供通过 ByteDance 一方 Ark 图像生成 API 使用 Seedream 的应用参考的技能,指导模型与区域选择、请求构造、参考图编辑、成组输出、流式传输及响应校验。

主要功能

  1. 01按区域和能力选择模型
  2. 02根据文本提示生成图像
  3. 03编辑并融合参考图
  4. 04支持成组输出和流式传输
  5. 05校验请求与 API 响应

适用场景

  1. 01制作一组相关营销图像
  2. 02依据多张参考图编辑图像
  3. 03流式获取并校验生成结果

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/calesthio/generative-media-skills --skill bytedance-seedream

安装后在 Claude Code 中输入/bytedance-seedream确认已加载。

附带文件EVAL.md

详细介绍

ByteDance Seedream

当应用通过一方 Ark 图像生成 API 直接调用 Seedream 时,请使用此技能。它涵盖文生图、参考图引导生成、自然语言编辑、多图融合、分组输出和流式分组输出。不涵盖 Seedance 视频生成、第三方网关、消费级应用、模型训练或 UI 自动化。

以下所有时效性信息均已于 2026-07-09 对照一方文档核实。正式上线前,请重新检查链接中的模型目录、可用性、定价和退役页面。

证据标签

  • 已记录 表示 ByteDance Seed、Volcengine 或 BytePlus 的当前页面说明了该行为。
  • 提供方声明 表示一方产品或研究页面提出了定性性能声明;在应用的语料上测试之前,应将其视为营销内容。
  • 运营建议 表示根据已记录的契约推断出的生产实践,并非提供方保证。
  • 未知 表示经审阅的一方页面未明确说明该行为。请进行功能检测或咨询支持,而不要凭空编造契约。

先选择商业服务平台

模型 ID 和主机不可互换。

部署方式API 基础 URL截至 2026-07-09 记录的当前图像模型 ID重要限制
Volcengine Ark,中国https://ark.cn-beijing.volces.com/api/v3doubao-seedream-5-0-pro-260628、doubao-seedream-5-0-260128(也记录为别名 doubao-seedream-5-0-lite-260128)、doubao-seedream-4-5-251128、doubao-seedream-4-0-250828仅限中国主机和 doubao-... ID
BytePlus ModelArk,亚太地区https://ark.ap-southeast.bytepluses.com/api/v3seedream-5-0-260128(也记录为别名 seedream-5-0-lite-260128)、seedream-4-5-251128、seedream-4-0-250828仅限全球主机和非 doubao ID
BytePlus ModelArk,欧洲https://ark.eu-west.bytepluses.com/api/v3文档说明 Seedream 5.0 Lite 在欧盟可用;部署任何其他版本前,请确认实时目录可用性页面禁止在欧盟/欧盟市场使用受限模型,包括 Seedream 4.0

三者均使用:

POST {API_BASE}/images/generations
Authorization: Bearer {ARK_API_KEY}
Content-Type: application/json

不要将中国令牌发送到 BytePlus 主机,也不要将 BytePlus 令牌发送到中国主机。请使用精确的主机允许列表,绝不要记录密钥,并在运行时从密钥管理器注入密钥。

已记录的区域注意事项: BytePlus 表示,亚太地区请求通常优先由亚太地区处理,但也可能由欧盟处理;有需要时,欧盟请求也可能由亚太地区处理。因此,选择端点并不能保证数据驻留。如果数据驻留是要求,请获取合同层面的明确答复。

根据契约选择模型,而非只看名称

能力China 5.0 Pro5.0 Lite4.54.0
文本生成和参考图/编辑输入支持支持支持支持
参考图数量上限10141410(保守值);通用 API 标注为 14
分组输出(sequential_image_generation)不支持支持支持支持
SSE 流式传输(stream)不支持支持支持支持
web_search 工具不支持支持不支持不支持
输出格式控制PNG/JPEGPNG/JPEG仅 JPEG仅 JPEG
分辨率标注1K/2K2K/3K/4K2K/4K1K/2K/4K
提示词优化standard;不支持 faststandard;不支持 faststandard;不支持 faststandard 或 fast

5.0 Pro 这一行适用于上文所述的中国版模型;BytePlus 当前的全球图像 API 未记录全球版 5.0 Pro ID。请勿自行编造。

**一方资料中的 4.0 冲突:**当前通用图像 API 描述了 2–14 张参考输入,而 Seedream 4.0 专属卡片对多图生成单图和多图生成图组工作流标注了 2–10 张。4.0 生产环境的默认验证上限设为 10。只有在经授权的实时试运行确认了确切的端点契约后,才使用 11–14 张;并记录响应和检索日期。

需要分组输出、SSE 或可选实时网页搜索时,使用 5.0 Lite。需要其文档中描述的高端单图契约,且不需要分组或流式输出时,使用 China 5.0 Pro。只有出于经验证的兼容性、质量或成本原因才保留 4.5 或 4.0,并维护迁移测试。

Seedream 5.0 Lite 的一方发布页面介绍了多模态推理、世界知识和可选实时搜索。这些属于供应商声明;请使用有代表性的提示词评估事实准确性和视觉质量。

请求契约

核心字段
  • model 为必填项,可以是文档中列出的模型 ID,也可以是已配置的端点 ID。
  • prompt 为必填项。指南建议不超过约 300 个汉字或 600 个英文单词;这是建议,并非文档中规定的服务器硬性限制。
  • image 为可选项,接受单个 URL/数据 URL 或数组。数据 URL 必须为 data:image/<lowercase-format>;base64,<payload>。
  • 输入格式:JPEG、PNG、WebP、BMP、TIFF、GIF、HEIC 或 HEIF。每个维度必须大于 14 px,宽高比必须在 [1/16, 16] 范围内,文件大小不得超过 30 MB,图像总面积不得超过 36 MP。
  • size 接受模型支持的分辨率标注或明确的 WIDTHxHEIGHT 字符串。发送前请根据所选模型进行验证。
  • response_format 为 url 或 b64_json;默认值为 url。返回的 URL 会在 24 小时后过期。
  • watermark 默认为 true,会添加可见的中文“AI 生成”标记。设为 false 可移除该可见标记;但这并不免除披露、溯源或法律义务。
  • 仅当前的 5.0 模型支持 output_format 设置为 png 或 jpeg;默认值为 JPEG。请勿向 4.5/4.0 发送此参数并期待格式转换。

当前规范的 4.0–5.0 API 页面未记录这些模型支持 seed 或 guidance_scale。BytePlus 仅为旧版 3.0 模型记录了这些控制项,并明确标注当前系列不支持 guidance。除非你所控制的确切端点契约明确记录了这些参数,否则请勿发送。输出相似不等于输出确定。

尺寸限制
模型默认值显式像素面积宽高比
China 5.0 Pro1024x1024921,600 至 4,624,220 像素[1/16, 16]
5.0 Lite2048x20483,686,400 至 16,777,216 像素[1/16, 16]
4.52048x20483,686,400 至 16,777,216 像素[1/16, 16]
4.02048x2048921,600 至 16,777,216 像素[1/16, 16]

建议在提示词中使用有文档说明的标签,并搭配宽高比或用途描述,或者选择有文档说明的推荐尺寸。常见的文档示例包括:5.0 Lite 2K 方形 2048x2048、横向 2848x1600、纵向 1600x2848;5.0 Lite 4K 方形 4096x4096;以及 Pro 1K 方形 1024x1024 或 2K 方形 2048x2048。不要假设不同模型中相同标签对应的像素尺寸也相同。

自然语言编辑与参考图

当前统一 API 没有单独的蒙版/局部重绘接口。提供一个或多个 image 参考图,并在 prompt 中描述要进行的编辑:

  1. 按顺序标识参考图:“在图像 1 中……使用图像 2 的光照效果。”
  2. 指明目标操作:添加、移除、替换、重新着色、重新布光、改变风格或合成。
  3. 明确说明不变项:“保持姿势、面部比例、相机、背景和所有文字不变。”
  4. 对于难以识别的局部区域,请在源图上用箭头、方框或涂鸦标注,并在提示词中指明该标记。提示词指南对此技巧有说明。
  5. 进行融合时,为每张参考图指定单一用途,不要让模型自行推断用途。

参考图 URL 必须可供服务获取。建议使用有效期较短且权限范围最小的预签名 URL。数据 URL 无需公开托管,但仍会将图像传给提供方,而且可能导致请求体过大。

分组输出

适用于 5.0 Lite、4.5 或 4.0:

{
  "sequential_image_generation": "auto",
  "sequential_image_generation_options": {"max_images": 4}
}

max_images 的取值范围为 1–15,默认值为 15。输入参考图数量与生成图像数量之和不得超过 15。auto 允许模型自行决定是否创建图像以及创建多少张;它不保证达到请求的最大数量。请在提示词中写明确切的集合,例如:“准确创建四张故事板帧;帧 1……帧 2……”,然后验证实际数量。不要向 China 5.0 Pro 发送这些字段。

可选的网络搜索

目前仅 5.0 Lite 有文档说明支持:

{"tools": [{"type": "web_search"}]}

是否使用网络搜索由模型决定;usage.tool_usage.web_search 会报告搜索使用情况。搜索会增加延迟,并带来外部内容风险。应将搜索到的事实和视觉细节视为不可信内容,检查出处和权利信息,不要承诺一定会进行搜索。

谨慎解读响应

对于非流式请求,响应顶层通常包含 model、created、data、usage,也可能包含 error。每个 data 成员可能是成功生成的图像(包含 url 或 b64_json、size,以及受支持时的 output_format),也可能是单项错误。因此,即使响应状态码为 200,也可能存在部分失败。

请验证:

  1. 顶层状态和错误。
  2. 每个 data 项目。
  3. 实际成功数量是否符合所请求的交付内容。
  4. 声明的尺寸/格式是否与解码后的字节一致。
  5. usage.generated_images、输出 token 数量和工具使用情况,以便监控成本并进行观测。

文档指出,只对成功生成的图像计费。不要因客户端超时就推断费用为零:由于没有文档说明幂等键,超时的请求可能已在服务端完成。

当 stream: true 时,请解析服务器发送的事件,直到最终完成事件:

  • image_generation.partial_succeeded:包含 image_index(从零开始的索引)以及 url 或 b64_json 和 size。
  • image_generation.partial_failed:包含 image_index 和错误信息。
  • image_generation.completed:最终用量/工具摘要。
  • 顶层 error:请求失败。

流式指南指出,内容过滤器导致的失败之后仍可能继续生成后续图像,而内部 500 错误会停止后续生成。不要将第一个部分事件视为任务已完成。

生产工作流

1. 建立策略边界

调用 API 前,记录以下信息:

  • 部署环境、账户、端点区域、准确的模型/端点 ID 和上线日期;
  • 允许的输入分类,以及是否允许个人数据、人脸、商标或机密材料;
  • 每项任务的参考图数量、输出数量、尺寸、字节数、延迟、重试次数和费用上限;
  • 可见水印/披露政策,以及持久的溯源政策;
  • 公开、商业或高影响用途的审核人员和审批标准。
2. 规范化并验证输入
  • 解码本地输入并检查其实际类型;不要只相信文件名或客户端 MIME 类型。
  • 上传前拒绝格式错误、超大、解压炸弹、压缩比极高和不受支持的输入。
  • 除非有意保留,否则移除不必要的 EXIF/位置信息。
  • 为每个来源计算哈希、保存其许可证/同意记录,并分配稳定的参考编号。
  • 从提示词到审计记录,保持参考图顺序稳定。
3. 构建可测试的提示词

使用自然语句,而不是堆砌关键词:

[deliverable/use]. [subject and action]. [environment].
[camera/composition]. [lighting/color/style].
[exact text in double quotes]. [reference roles]. [edit invariants].
[numbered outputs if a set].

对于排版,将必需文案放在双引号中,指定位置和层级,然后运行 OCR 并进行人工校对。生成结果不保证排版准确。

4. 提交任务并记录任务流水

创建客户端任务 ID,并持久化请求哈希、已删减敏感信息的参数、模型、主机、开始时间、尝试次数和状态。图像 API 是同步调用或请求范围内的 SSE;当前文档未说明异步图像任务、轮询 API、Webhook 或幂等键。不要套用 Seedance 视频任务语义。

5. 有针对性地重试

对于网络故障、429 限流和暂时性 500 错误,使用带上限的指数退避、随机抖动,并在存在 Retry-After 时遵循该值。限制总尝试次数和并发数。以下情况不要重试:

  • 身份验证、账户、余额、服务未开放或模型访问失败;
  • 参数无效/缺失或图像有问题;
  • 内容安全、版权或隐私拒绝;
  • 如果无法接受重复计费/输出,不要在超时情况不明时自动重试。

Ark 文档说明了账户/模型/端点特定的 IPM、RPM、TPM、突发流量、配额和过载错误。BytePlus 当前的 Seedream 教程列出当前全球模型每分钟 500 张图像,但账户和端点限制可能更低。测量实际响应头/错误,并将准入控制配置在已验证的限值之下。

6. 安全获取产物

对于 url 响应,请立即下载,因为链接有效期为 24 小时。产物 URL 是需要单独处理的不可信获取目标:

  • 要求使用 HTTPS,拒绝凭据/片段,并拒绝意外重定向;
  • 绝不要在获取产物时附带 Ark Authorization 请求头;
  • 在缓冲数据前限制流式传输的字节数;
  • 将图像内容类型加入允许列表,验证文件签名字节,解码尺寸并重新扫描;
  • 写入临时文件,按需执行 fsync,然后原子性地移至正式位置;
  • 为最终字节计算哈希,并保存尺寸、MIME、模型、提示词哈希、来源哈希、审核状态和提供方用量。

对于 b64_json,使用严格的 base64 解码,并执行相同的解码后字节数、文件签名、尺寸和扫描检查。

7. 发布前进行评估

使用固定回归集,涵盖文生图、单图编辑、多参考图身份一致性、排版、违禁内容、极端宽高比、群组人数、部分失败、限流、超时和下载文件损坏等情况。评估提示词遵循度、编辑局部性、参考图一致性、视觉缺陷、OCR 准确度、安全性、延迟和成本。尽可能以盲测方式比较候选模型和现有模型。

示例:严格的非流式生成

这个完整的 Python 标准库示例不会由此技能执行。它使用 BytePlus AP 和 5.0 Lite,请求返回 base64 以避免资源 URL 过期,验证 API 基地址,限制解码后的大小,检查 PNG/JPEG 文件特征,并写入持久化元数据。设置 ARK_API_KEY;也可以设置 ARK_API_BASE 和 SEEDREAM_MODEL,并确保二者是兼容且有文档记录的组合。

import base64
import binascii
import hashlib
import json
import os
import pathlib
import tempfile
import time
import urllib.error
import urllib.request

ALLOWED = {
    "https://ark.cn-beijing.volces.com/api/v3": {
        "doubao-seedream-5-0-pro-260628", "doubao-seedream-5-0-260128",
        "doubao-seedream-5-0-lite-260128", "doubao-seedream-4-5-251128",
        "doubao-seedream-4-0-250828",
    },
    "https://ark.ap-southeast.bytepluses.com/api/v3": {
        "seedream-5-0-260128", "seedream-5-0-lite-260128",
        "seedream-4-5-251128", "seedream-4-0-250828",
    },
    "https://ark.eu-west.bytepluses.com/api/v3": {
        "seedream-5-0-260128", "seedream-5-0-lite-260128",
    },
}
MAX_IMAGE_BYTES = 25 * 1024 * 1024
MAX_JSON_BYTES = ((MAX_IMAGE_BYTES + 2) // 3) * 4 + 1024 * 1024


def atomic_write(path, data):
    path.parent.mkdir(parents=True, exist_ok=True)
    handle, temporary = tempfile.mkstemp(dir=path.parent, prefix=".seedream-")
    try:
        with os.fdopen(handle, "wb") as output:
            output.write(data)
            output.flush()
            os.fsync(output.fileno())
        os.replace(temporary, path)
    except Exception:
        try:
            os.unlink(temporary)
        except FileNotFoundError:
            pass
        raise

api_base = os.getenv(
    "ARK_API_BASE", "https://ark.ap-southeast.bytepluses.com/api/v3"
).rstrip("/")
api_key = os.environ.get("ARK_API_KEY", "")
model = os.getenv("SEEDREAM_MODEL", "seedream-5-0-260128")
configured_endpoint = os.getenv("SEEDREAM_ALLOWED_ENDPOINT_ID")
configured_endpoint_base = os.getenv("SEEDREAM_ALLOWED_ENDPOINT_API_BASE")
configured_endpoint_model = os.getenv("SEEDREAM_ALLOWED_ENDPOINT_MODEL")
allowed_models = ALLOWED.get(api_base, set())
bound_endpoint = (
    model == configured_endpoint
    and api_base == configured_endpoint_base
    and configured_endpoint_model in allowed_models
)
if api_base not in ALLOWED or (model not in allowed_models and not bound_endpoint):
    raise SystemExit("Refusing an undocumented host/model namespace pairing")

payload = {
    "model": model,
    "prompt": (
        "Square editorial product photograph for a coffee brand. A matte black "
        "ceramic cup on pale limestone, soft window light from camera left, subtle "
        "steam, generous negative space, realistic materials. No logos or text."
    ),
    "size": "2K",
    "response_format": "b64_json",
    "output_format": "png",
    "watermark": True,
}
body = json.dumps(payload, separators=(",", ":")).encode("utf-8")
request = urllib.request.Request(
    api_base + "/images/generations",
    data=body,
    method="POST",
    headers={
        "Authorization": "Bearer " + api_key,
        "Content-Type": "application/json",
        "Accept": "application/json",
    },
)
print({
    "model": payload["model"], "size": payload["size"],
    "request_sha256": hashlib.sha256(body).hexdigest(),
    "watermark": payload["watermark"],
})
if os.environ.get("SEND_SEEDREAM_REQUEST") != "1":
    raise SystemExit("Dry run only; set SEND_SEEDREAM_REQUEST=1 after approving the paid call")
if not api_key:
    raise SystemExit("Set ARK_API_KEY before the paid call")
try:
    with urllib.request.urlopen(request, timeout=180) as response:
        raw = response.read(MAX_JSON_BYTES + 1)
except urllib.error.HTTPError as exc:
    # Do not print request headers or secrets.
    detail = exc.read(64 * 1024).decode("utf-8", "replace")
    raise SystemExit(f"Ark HTTP {exc.code}: {detail}") from exc

if len(raw) > MAX_JSON_BYTES:
    raise SystemExit("JSON response exceeded configured base64 envelope cap")
try:
    document = json.loads(raw)
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
    raise SystemExit("Response was not valid bounded JSON") from exc
if document.get("error"):
    raise SystemExit(f"Ark error: {document['error']}")
successful = [item for item in document.get("data", []) if item.get("b64_json")]
failed = [item.get("error") for item in document.get("data", []) if item.get("error")]
if len(successful) != 1 or failed:
    raise SystemExit(f"Expected one image; successes={len(successful)} failures={failed}")

encoded = successful[0]["b64_json"]
if len(encoded) > ((MAX_IMAGE_BYTES + 2) // 3) * 4 + 8:
    raise SystemExit("Encoded image exceeds the configured cap")
try:
    image = base64.b64decode(encoded, validate=True)
except (binascii.Error, ValueError) as exc:
    raise SystemExit("Image was not strict base64") from exc
if len(image) > MAX_IMAGE_BYTES:
    raise SystemExit("Decoded image exceeds the configured cap")
if image.startswith(b"\x89PNG\r\n\x1a\n"):
    suffix = ".png"
elif image.startswith(b"\xff\xd8\xff"):
    suffix = ".jpg"
else:
    raise SystemExit("Unexpected image signature")

out = pathlib.Path("seedream-output" + suffix)
atomic_write(out, image)
metadata = {
    "created_unix": int(time.time()),
    "model": document.get("model", model),
    "request_sha256": hashlib.sha256(body).hexdigest(),
    "artifact_sha256": hashlib.sha256(image).hexdigest(),
    "declared_size": successful[0].get("size"),
    "usage": document.get("usage"),
    "watermark_requested": payload["watermark"],
}
atomic_write(
    out.with_suffix(out.suffix + ".json"),
    json.dumps(metadata, indent=2, ensure_ascii=False).encode("utf-8"),
)
print(out.resolve())

生产代码还应使用维护良好的图像库解码像素,限制宽度、高度和面积,扫描内容,使用原子写入,脱敏日志,并集成上述重试策略。

示例:多个参考图、分组输出和安全获取 URL

这个完整示例不会由此技能执行。它需要两个 BytePlus 可获取的 HTTPS 参考图 URL,以及一个由管理员维护、以逗号分隔的 SEEDREAM_ASSET_HOSTS 允许列表,该列表应根据服务提供商当前的资源域名生成。由于分组模式为 auto,模型返回的图像可能少于三张;程序会记录每项失败,并安全下载成功结果的 URL,且不会转发 API 令牌。

import hashlib
import ipaddress
import json
import os
import pathlib
import socket
import tempfile
import urllib.error
import urllib.parse
import urllib.request

API_BASE = "https://ark.ap-southeast.bytepluses.com/api/v3"
API_KEY = os.environ.get("ARK_API_KEY", "")
REF_1 = os.environ.get("SEEDREAM_REFERENCE_1", "https://example.invalid/reference-1")
REF_2 = os.environ.get("SEEDREAM_REFERENCE_2", "https://example.invalid/reference-2")
MAX_DOWNLOAD = 25 * 1024 * 1024
MAX_API_JSON = 8 * 1024 * 1024
ASSET_HOSTS = {
    host.strip().lower()
    for host in os.environ.get("SEEDREAM_ASSET_HOSTS", "").split(",")
    if host.strip()
}
if not ASSET_HOSTS:
    raise SystemExit("Set SEEDREAM_ASSET_HOSTS to approved provider artifact hostnames")


class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        return None


def validate_artifact_url(url):
    parsed = urllib.parse.urlsplit(url)
    if (
        parsed.scheme != "https" or parsed.hostname not in ASSET_HOSTS
        or parsed.username or parsed.password or parsed.fragment
    ):
        raise ValueError("Artifact URL is outside the approved HTTPS allowlist")
    addresses = {
        info[4][0] for info in socket.getaddrinfo(parsed.hostname, parsed.port or 443)
    }
    if not addresses or any(not ipaddress.ip_address(value).is_global for value in addresses):
        raise ValueError("Artifact host did not resolve exclusively to public addresses")


def atomic_write(path, data):
    path.parent.mkdir(parents=True, exist_ok=True)
    handle, temporary = tempfile.mkstemp(dir=path.parent, prefix=".seedream-meta-")
    try:
        with os.fdopen(handle, "wb") as output:
            output.write(data)
            output.flush()
            os.fsync(output.fileno())
        os.replace(temporary, path)
    except Exception:
        try:
            os.unlink(temporary)
        except FileNotFoundError:
            pass
        raise

for ref in (REF_1, REF_2):
    parsed = urllib.parse.urlsplit(ref)
    if parsed.scheme != "https" or parsed.username or parsed.password:
        raise SystemExit("References must be credential-free HTTPS URLs")

payload = {
    "model": "seedream-5-0-260128",
    "prompt": (
        "Create exactly three coordinated campaign images. Preserve the product "
        "shape and label from image 1. Use only the blue-and-amber palette and soft "
        "paper texture from image 2. Frame 1: square hero. Frame 2: vertical detail. "
        "Frame 3: wide environmental scene. Keep all label spelling unchanged."
    ),
    "image": [REF_1, REF_2],
    "size": "2K",
    "sequential_image_generation": "auto",
    "sequential_image_generation_options": {"max_images": 3},
    "response_format": "url",
    "output_format": "jpeg",
    "watermark": True,
}
request = urllib.request.Request(
    API_BASE + "/images/generations",
    data=json.dumps(payload).encode("utf-8"),
    method="POST",
    headers={
        "Authorization": "Bearer " + API_KEY,
        "Content-Type": "application/json",
        "Accept": "application/json",
    },
)
print({
    "model": payload["model"], "references": len(payload["image"]),
    "max_images": payload["sequential_image_generation_options"]["max_images"],
    "request_sha256": hashlib.sha256(json.dumps(payload, separators=(",", ":")).encode()).hexdigest(),
})
if os.environ.get("SEND_SEEDREAM_REQUEST") != "1":
    raise SystemExit("Dry run only; set SEND_SEEDREAM_REQUEST=1 after approving the paid call")
if not API_KEY or "example.invalid" in {urllib.parse.urlsplit(REF_1).hostname, urllib.parse.urlsplit(REF_2).hostname}:
    raise SystemExit("Set ARK_API_KEY and both SEEDREAM_REFERENCE environment variables")
try:
    with urllib.request.urlopen(request, timeout=240) as response:
        response_bytes = response.read(MAX_API_JSON + 1)
except urllib.error.HTTPError as exc:
    raise SystemExit(f"Ark HTTP {exc.code}") from exc
if len(response_bytes) > MAX_API_JSON:
    raise SystemExit("Grouped API response exceeded configured JSON cap")
try:
    document = json.loads(response_bytes)
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
    raise SystemExit("Grouped API response was not valid bounded JSON") from exc

def safe_download(url: str, destination: pathlib.Path) -> dict:
    opener = urllib.request.build_opener(NoRedirect())
    current = url
    response = None
    for _ in range(6):
        validate_artifact_url(current)
        # Deliberately no Authorization header here or on any redirect hop.
        req = urllib.request.Request(current, headers={"Accept": "image/png,image/jpeg"})
        try:
            response = opener.open(req, timeout=90)
            break
        except urllib.error.HTTPError as exc:
            if exc.code not in {301, 302, 303, 307, 308}:
                raise
            location = exc.headers.get("Location")
            exc.close()
            if not location:
                raise ValueError("Artifact redirect omitted Location")
            current = urllib.parse.urljoin(current, location)
    else:
        raise ValueError("Too many artifact redirects")

    hasher = hashlib.sha256()
    total = 0
    prefix = b""
    destination.parent.mkdir(parents=True, exist_ok=True)
    with response:
        content_type = response.headers.get_content_type()
        if content_type not in {"image/png", "image/jpeg"}:
            raise ValueError(f"Unexpected Content-Type: {content_type}")
        declared = response.headers.get("Content-Length")
        if declared and int(declared) > MAX_DOWNLOAD:
            raise ValueError("Artifact exceeds byte cap")
        fd, temporary = tempfile.mkstemp(dir=destination.parent, prefix=".seedream-")
        try:
            with os.fdopen(fd, "wb") as output:
                while True:
                    chunk = response.read(64 * 1024)
                    if not chunk:
                        break
                    total += len(chunk)
                    if total > MAX_DOWNLOAD:
                        raise ValueError("Artifact exceeded byte cap while streaming")
                    if len(prefix) < 12:
                        prefix += chunk[: 12 - len(prefix)]
                    hasher.update(chunk)
                    output.write(chunk)
                output.flush()
                os.fsync(output.fileno())
            is_png = prefix.startswith(b"\x89PNG\r\n\x1a\n")
            is_jpeg = prefix.startswith(b"\xff\xd8\xff")
            if not (is_png or is_jpeg):
                raise ValueError("Unexpected artifact signature")
            os.replace(temporary, destination)
        except Exception:
            try:
                os.unlink(temporary)
            except FileNotFoundError:
                pass
            raise
    return {"bytes": total, "sha256": hasher.hexdigest(), "content_type": content_type}

if document.get("error"):
    raise SystemExit(f"Ark error: {document['error']}")
manifest = {"model": document.get("model"), "usage": document.get("usage"), "items": []}
success_index = 0
for index, item in enumerate(document.get("data", [])):
    if item.get("error"):
        manifest["items"].append({"index": index, "error": item["error"]})
        continue
    if not item.get("url"):
        manifest["items"].append({"index": index, "error": "missing url"})
        continue
    success_index += 1
    path = pathlib.Path("seedream-set") / f"image-{success_index:02d}.jpg"
    audit = safe_download(item["url"], path)
    manifest["items"].append({"index": index, "path": str(path), **audit})

atomic_write(
    pathlib.Path("seedream-set/manifest.json"),
    json.dumps(manifest, indent=2, ensure_ascii=False).encode("utf-8"),
)
if success_index == 0:
    raise SystemExit("No successful images")
print(f"Saved {success_index} image(s); review count, labels, and visual consistency")

示例:请求范围的 SSE

这个 curl 示例不会由此技能执行。它演示的是线路格式,而非生产环境中的解析方式。生产环境中应使用真正的 SSE 解析器,保留事件顺序,处理部分失败,并等待 image_generation.completed。

: "${ARK_API_KEY:?set ARK_API_KEY}"
: "${SEND_SEEDREAM_REQUEST:?set SEND_SEEDREAM_REQUEST=1 after approving the paid call}"
[ "${SEND_SEEDREAM_REQUEST}" = "1" ] || { echo 'Refusing paid call without SEND_SEEDREAM_REQUEST=1' >&2; exit 2; }
curl --fail-with-body --no-buffer \
  --connect-timeout 10 --max-time 300 \
  'https://ark.ap-southeast.bytepluses.com/api/v3/images/generations' \
  -H "Authorization: Bearer ${ARK_API_KEY}" \
  -H 'Content-Type: application/json' \
  -H 'Accept: text/event-stream' \
  --data-binary @- <<'JSON'
{
  "model": "seedream-5-0-260128",
  "prompt": "Create a three-frame horizontal storyboard of a paper airplane crossing a quiet library: establishing shot, close tracking shot, final landing. Consistent airplane and lighting in every frame.",
  "size": "2K",
  "sequential_image_generation": "auto",
  "sequential_image_generation_options": {"max_images": 3},
  "response_format": "url",
  "stream": true,
  "watermark": true
}
JSON

错误映射和可观测性

类别常见的有文档记录的错误操作
400 请求错误参数无效或缺失、图像/base64/URL 无效、输入/输出安全、版权或隐私问题不要盲目重试;修正输入或停止
401/403身份验证、访问被拒绝、账户欠费、服务未开通、条款/访问问题轮换/修复凭证或账户状态;不要将其作为暂时性错误重试
404未找到模型/端点、模型访问已禁用检查服务入口、区域、模型 ID、端点、生命周期和访问权限
429模型/账户 IPM、RPM、TPM、端点限制、突发流量、配额、过载排队、降低并发、遵循重试时机、使用带随机抖动的退避策略
500内部服务错误如果可以接受重复风险,则进行有限次数的重试;否则手动核对

记录尝试次数、状态/错误代码、限流类别、队列等待时间、服务提供商延迟、下载延迟、成功/失败图像数量、字节数、分辨率、模型、区域端点、联网搜索使用情况、预估和开票费用、审核结果以及审核人员的决定等指标。绝不要记录原始机密;尽量减少遥测中的原始提示词和源 URL。

成本和生命周期控制

价格波动较大。中国区定价页面在 2026-07-09 显示:4.0 每张成功生成的图像 ¥0.20,4.5 为 ¥0.25,5.0 Lite 为 ¥0.22,5.0 Pro 在不超过 2.36 MP 时为 ¥0.30,高于 2.36 MP 时为 ¥0.60。查阅的 BytePlus 官方页面显示,4.0 每张图像 US$0.03,4.5 每张图像 US$0.04;尚未确认当前 5.0 Lite 的全球公开价格。请重新核对账户控制台和官方价格页面,因为区域、合同、税费、套餐和促销活动都可能导致价格不同。

提交前,根据最大成功分组数和适用的分辨率档位估算费用上限。核对服务提供商返回的 usage 和账单,不要只依赖估算。文档说明,审核失败的输出不计费,而部分失败分组中的成功结果仍会计费。

固定使用带日期的模型 ID,订阅模型退役通知,并维护一套黄金迁移测试集。当前目录和 API 页面是判断可用性的权威来源;不要根据产品名称推断生命周期。别名行为可能会变化。请将任何旧版 3.0 seed 工作流视为迁移项目,不要将旧版参数沿用到 4.0/5.0。

隐私、权利与安全门槛

BytePlus 当前的 Model Service 条款规定,未经单独同意,不会使用输入和输出训练基础模型,且 BytePlus 不主张拥有输出的所有权。条款还指出,输出可能并非独一无二,BytePlus、关联方及第三方持有的权利仍归其所有;客户仍需对输入、输出、适用法律及权利清理负责。这并不保证输出具备版权资格、独占性、不侵权、准确或安全。

BytePlus 的数据处理页面说明,处理活动可能发生在马来西亚、印度尼西亚和/或欧盟/欧洲经济区,并指出触发安全过滤器的内容可能会在马来西亚保留 180 天。该保留期限与 24 小时的生成图像 URL 有所不同。请根据实际合同确认当前的 DPA、子处理方所在地、加密选项、删除机制和支持承诺。

生成和发布前:

  • 获取源图像、商标、受版权保护的角色、私人数据、面孔、肖像及涉及生物特征敏感用途的许可/同意;
  • 禁止性剥削、虐待未成年人、未经同意的私密影像、欺骗性冒充、欺诈、非法监视/身份识别、仇恨/极端主义、鼓励自残及其他不允许的内容;
  • 不得绕过安全系统,也不得移除/遮挡必要的凭证、水印、元数据或披露信息;
  • 在法律、平台政策或具体情境要求时,为 AI 生成的材料添加标注,即使技术上使用了 watermark: false 也不例外;
  • 对事实声明、排版文字、肖像、医疗/法律/财务影响、选举/公共事务及重大影响用途,必须进行人工审核;
  • 保存来源记录;在审核过的图像 API 合同中,未发现普遍适用的 C2PA 保证。

在适用的 Model Service 条款禁止的情况下,不得使用该模型或其输出训练或改进其他模型。请咨询法律顾问,获取针对具体司法管辖区和合同的建议。

第一方来源

核查日期:2026-07-09:

已知缺口

经审阅的第一方图像 API 页面未明确说明幂等键、异步图像任务/轮询/Webhook 契约、当前系列模型的确定性种子、通用的数据驻留保证、通用的 C2PA 输出,或公开的 BytePlus 5.0 Lite 当前价格。请勿声称具备这些能力。若有需要,请重新查阅文档或获取提供商的书面答复。

评论

支持 Markdown