关于 adobe-firefly-image
面向静态图像制作流程的 Adobe Firefly Services 集成技能,涵盖 API 与模型选择、身份验证、请求校验及质量检查,适用于生成和编辑图像。
主要功能
- 01API 与模型选择
- 02OAuth Server-to-Server 指南
- 03图像生成与编辑流程
- 04请求和响应校验
- 05制作与溯源质量检查
适用场景
- 01为静态图像任务选择端点
- 02准备付费图像生成批次
- 03编辑或扩展提供的图像
- 04扩大制作前检查素材
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ calesthio/ generative-media-skills --skill adobe-firefly-image安装后在 Claude Code 中输入/adobe-firefly-image确认已加载。
附带文件EVAL.md
详细介绍
Adobe Firefly Image
将其视为静态图像制作与集成技能。除非用户明确要求组合系统,否则不要涉及视频、音频,以及无关的 Photoshop、Lightroom、Express 或 Creative Production 操作。
证据规范
请始终如一地使用以下标签:
- 已记录表示当前 Adobe API 参考、变更日志、产品条款或安全材料明确说明了该说法。
- 推断表示结论可根据已记录的字段或行为得出,但 Adobe 并未直接说明。
- 生产经验法则表示在目标账户和工作流中进行测试的实用建议。
以下易变的 API 信息已于 2026-07-09 核实。正式发布前,请立即重新查阅 API 参考、变更日志和使用说明。Adobe 的一些说明页面中的示例与端点参考相互冲突。如有冲突,请优先采用 API 参考中的操作架构,以及之后明确发布的变更日志条目;不要混用不兼容版本中的字段。
从生产约定开始
生成任何内容之前:
- 确认组织有权使用 Firefly Services,且项目已配置 OAuth Server-to-Server 凭据。
- 记录交付内容、宽高比或尺寸、对所提供素材可接受的修改范围、权利/同意状态、区域设置、无障碍需求和审核负责人。
- 从下方矩阵中选择操作。不要仅因为某个模型是最新版本就选择它。
- 在付费批次开始前,明确端点、模型标头、变体数量和计费单位。
- 先运行一个有代表性的样例,立即下载,并在扩大规模前完成视觉与来源信息质量检查。
当前 API 与模型对应关系
文档记录,已于 2026-07-09 验证:
| 需求 | 端点 | 模型选择 | 关键限制 |
|---|---|---|---|
| 生成新的 Image 5 图像 | POST /v4/images/generate-async | 请求头 x-model-version: image5;请求体 modelId: firefly_image | 每次请求生成一个变体;最多一张参考图像 |
| 使用自然语言编辑 Image 5 图像 | 同一 v4 端点 | 相同的请求头和请求体 | 非空的 referenceBlobs 数组会将操作切换为指令式编辑 |
| 生成 Image 3/4 图像并使用显式控件 | POST /v3/images/generate-async | image3、image4_standard 或 image4_ultra | 最多四个变体;样式/结构控件位于此处 |
| 使用自定义模型生成图像 | 同一 v3 端点 | image3_custom 或 image4_custom,以及 customModelId | 自定义模型必须已配置,并与应用共享 |
| 生成相似图像 | POST /v3/images/generate-similar-async | image3、image4_standard 或 image4_ultra | 必须提供源图像 |
| 替换蒙版区域 | POST /v3/images/fill-async | 操作架构,而非 Image 5 指令式编辑架构 | 必须提供源图像和蒙版 |
| 扩展画布/扩图 | POST /v3/images/expand-async | 操作架构 | 必须提供源图像;蒙版与放置位置之间存在互斥限制 |
| 在对象周围生成提示词指定的背景 | POST /v3/images/generate-object-composite-async | 操作架构 | 没有背景时使用 |
| 在现有背景中精确保留对象 | POST /v3/images/precise-composite | 精确合成流程 | 必须提供现有背景和放置蒙版 |
| 将对象融入现有背景 | POST /v3/images/adaptive-composite | 自适应合成流程 | 可能会重新生成/调整对象,使其更加逼真 |
| 注重保真度的放大 | POST /v3/images/upscale | x-model-version: precise_upsampler_v1 | 放大倍数为 2、3、4 或 6;异步执行 |
| 上传输入图像或蒙版 | POST /v2/storage/image | 无 | 返回的上传 ID 有效期为七天 |
Image 5 是最新记录的图像模型,支持 1 MP、2.4 MP 和 4 MP 分辨率。当前 API 架构中仍包含 v3 模型值;不要仅因 Image 5 已推出就称这些模型已移除。Adobe 于 2025-10-03 移除了已弃用的 v3 同步接口,并于 2025-09-10 移除了 Generate Images v2、Expand v1 和 Fill v1。请仅基于当前异步路由进行开发。请参阅更新日志。
有意处理文档冲突
**文档冲突,已于 2026-07-09 验证:**Image 5 API 参考文档为 /v4/images/generate-async 定义了 aspectRatio、resolutionLevel、modelId、modelSpecificPayload 和 referenceBlobs。与此同时,Image 5 迁移页面将其中一些字段描述为已移除,并给出了一个类似 v3 自定义模型架构的请求载荷。在将该迁移表作为可执行架构使用前,请先对照 API 参考文档解决这一冲突。
同样,放大指南的正文仍提及 /v1/images/upsample-async,而当前 API 参考文档和 2026-04-24 更新日志则指出应使用 POST /v3/images/upscale 和 precise_upsampler_v1。请使用后者。creative_upsampler_v1 会返回 422 错误。这是对来源质量的判断,并不意味着 Adobe 所有正文页面都已过时。
从服务端进行身份验证
文档说明: Firefly 请求需要 Adobe IMS 持有者令牌,以及作为 x-api-key 的 Client ID。请使用 OAuth Server-to-Server。切勿在浏览器代码、日志、提示词或生成的产物中暴露客户端密钥或访问令牌。Adobe 文档指出,访问令牌有效期为 24 小时;请主动刷新令牌,并且只有在获取新令牌后,才在收到 401 后重试一次。请参阅身份验证和凭据设置。
令牌请求:
POST https://ims-na1.adobelogin.com/ims/token/v3
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=...&client_secret=...&scope=openid,AdobeID,session,additional_info,read_organizations,firefly_api,ff_apis
之后,每次 Firefly API 调用都要包含:
Authorization: Bearer <access-token>
x-api-key: <client-id>
Content-Type: application/json
Accept: application/json
请使用 Developer Console 中实际配置的作用域和产品配置文件;不要凭猜测扩大作用域。令牌交换成功后出现 403,通常意味着权限、产品配置文件、组织、自定义模型共享、策略或区域访问问题,而非 OAuth 格式错误。
了解请求和响应约定
图像 5:v4 生成和指令编辑
文档说明的 v4 字段:
prompt:必需,长度为 1–1500 个字符。aspectRatio:1:1、4:3、3:4、16:9、9:16或auto。resolutionLevel:1MP、2.4MP(默认值)或4MP。modelId:firefly_image。numVariations:最多为 1。seeds:最多包含一个整数;如果两者都出现,其数量必须与numVariations相同。modelSpecificPayload.localeCode:Image 5 使用的区域设置控制项。modelSpecificPayload.prompt_reasoner:speed(默认值)或quality;Adobe 表示,quality会填充altText,而speed返回的该字段为空。referenceBlobs:最多一个。为空表示文生图;非空且usage: general表示指令编辑。如果提供了该字段,请省略aspectRatio,或将其设为auto。
不要向 v4 发送仅适用于 v3 的 style、structure、contentClass、visualIntensity 或 customModelId 字段。v4 操作架构未说明负面提示词字段或任意输出格式选择器。请验证实际响应资源的 Content-Type,不要假设它是 JPEG 或 PNG。
v4 提交会返回 HTTP 200,其中包含 links.result.href、links.cancel.href 和进度值。该操作仍是异步的。请使用相同的授权标头轮询返回的结果链接,直到进入终止状态,然后尽快下载图像 URL。
图像 3/4:v3 生成
文档说明的 v3 字段:
prompt:必需,长度为 1–1024 个字符。negativePrompt:最多 1024 个字符;Image 3 或 Image 4 自定义模型不支持此字段。contentClass:photo或art。numVariations:1–4。如果提供了种子,种子数量必须与变化数量相同。promptBiasingLocaleCode:类似 BCP-47 的语言区域值,例如en-US。style.presets:文档说明的 snake_case 预设;请谨慎组合。style.strength:1–100,默认值为 50;可与风格参考一起使用。structure.strength:1–100,默认值为 50;控制与结构参考在轮廓/深度上的相似程度。visualIntensity:2–10;image4_custom不支持此字段。upsamplerType:仅适用于image4_custom;可设为default或low_creativity。
支持的生成尺寸:
| 模型 | 文档明确列出的尺寸 |
|---|---|
| Image 4 | 2048×2048、2304×1792、1792×2304、2688×1536、1440×2560 |
| Image 3 | 2048×2048、1024×1024、2304×1792、1792×2304、2688×1536、2688×1512、1344×768、1344×756、1152×896、896×1152 |
不要四舍五入到方便的等效宽高比尺寸。服务会验证确切的尺寸组合。
v3 提交会返回 HTTP 202,其中包含 jobId、statusUrl 和 cancelUrl。请轮询 statusUrl。运行中的响应包含 status: running;成功响应包含 status: succeeded 和 result.outputs[],其中每项都包含一个种子和图像 URL。请将 failed、cancelled、HTTP 终止错误和本地超时视为终止结果;为支持请求,请留存响应正文和请求 ID。
上传、蒙版和源 URL
上传 API 接受二进制图像数据。当前 API 参考列出的格式包括 PNG、JPEG、WebP、TIFF 和 JXL,最大大小为 15 MB;较早的上传指南只列出 JPEG、PNG 和 WebP。**生产经验法则:**蒙版或透明图像使用 PNG;普通输入使用 JPEG 或 PNG,除非已在目标账户中测试过更广泛的格式。
上传响应的格式为 {"images":[{"id":"..."}]}。API 契约说明 ID 的有效期为七天。Adobe 的 Firefly Services 安全说明则另外指出,上传的参考内容会缓存 24 小时;请勿将缓存保留时间等同于 ID 有效期。
对于 v3 源 URL,操作参考通常将 amazonaws.com、windows.net、dropboxusercontent.com 和 storage.googleapis.com 列入允许列表。Image 5 使用说明还列出了 frontdoor.prod.azure.cxp.adobe.com。具体要求因操作而异。请使用有效期较短的 HTTPS 预签名 URL,并限制对象权限,同时确认相应操作的确切允许列表。切勿传入任意公共 URL 并假定 Firefly 会抓取它。
蒙版是生产资产,而非注释。在付费调用前,请检查尺寸、Alpha/灰度语义、反转、羽化和对齐情况。expand-async 禁止在同时应用蒙版时为源使用 placement。其输出最大可达 3999×3999。对于局部蒙版替换,请使用 Fill;对于更大的画布,请使用 Expand;如果只需一个参考图并希望通过自然语言优化整张图像,请使用 Image 5 instruct edit。
根据需要保持不变的内容选择参考图和合成方式
风格和结构
使用 v3 style.imageReference 迁移视觉语言,例如调色板、媒介、光线或氛围。使用 structure.imageReference 引导轮廓和深度。强度范围为 1–100;默认值为 50。这些控制项可以同时使用,但强度都较高时可能会降低对提示词的遵循度。
**生产经验法则:**每次只调整一个控制项,例如依次测试 30、60、90,同时使用相同提示词和种子,然后根据书面评估标准进行选择。种子有助于复现,但无法保证在模型、端点或服务更新后仍能复现。Adobe 的种子指南指出,相同设置会生成相同图像;但仍应记录模型标头、端点、负载和源哈希,因为生命周期变化可能导致跨版本假设失效。
只能使用 风格预设目录中有文档记录的预设值。不要将 UI 标签自行编造为 API 枚举值。以下是一些有用且有文档记录的示例:product_photo、studio_light、golden_hour、flat_design、watercolor、cinematic、bw 和 shallow_depth_of_field;这并非完整目录。
合成方式选择
- 如果对象已存在,但需要根据提示词生成背景,请使用 Generate Object Composite。请求必须提供可用于合成的输入:更大的目标画布、透明度或蒙版。
- 如果必须精确保留所提供对象的像素或包装细节,请使用 Precise Composite。它使用背景图像 + 填充区域蒙版 + 对象图像。
blend的范围为 0–1;0 表示完全协调,1 表示保留对象的原始外观。最多可生成三种变体。 - 如果在现有背景中实现逼真效果比精确保留对象像素更重要,请使用 Adaptive Composite。
harmonization和shadowIntensity的范围为 0–1;preserveBackground会在修改对象区域的同时保留原始背景像素。最多可生成三种变体。
这三种方式都是异步的。不要承诺“precise”就无需进行像素差异和标签可读性审查。对于受监管的包装或产品声明,除非可以接受并审查改动,否则不要使用 Adaptive Composite。
放大
使用当前的 POST /v3/images/upscale,并设置 x-model-version: precise_upsampler_v1、输入图像源、1 到 4 个种子,以及 2、3、4 或 6 的 upscaleFactor。轮询返回的 links.result.href。API 参考文档将输出尺寸描述为输入尺寸乘以放大倍数。请验证服务限制和实际结果;一份文字指南称最大尺寸为 6k,而端点架构主要说明放大倍数,因此处理较大输入前应先检查限制,不要假定每种放大倍数都受支持。
先指导图像,再撰写文案
生产经验法则: 将提示词写成精简的美术指导简报,而不是堆砌形容词。请说明:
- 主体及动作;
- 环境与时代;
- 构图、视角、镜头语言或版式;
- 光照与色彩关系;
- 材质与表面特征;
- 仅在必要时,逐字引用必须显示的文本;
- 通过 API 文档规定的负面提示词字段排除不需要的内容(如有)。
未经授权,不要模仿在世艺术家的风格,也不要使用第三方标志、人脸、受版权保护的角色或参考素材。进行本地化时,设置 API 的区域设置字段,并请母语审校人员检查文化细节;区域设置偏差不等于翻译,也不代表已获得法律许可。
对于准确的排版文字、标志、包装文案、二维码和合规标记,请先生成干净的视觉底图,再叠加已获批准的矢量或栅格图稿。将模型生成的文字视为草稿内容。若用于产品,请要求保留负空间,并采用适合后续叠加图稿的镜头几何。
示例 1 — 完整的 Image 5 生成客户端
示例,并非强制公式。 这个 Python 3.11+ 标准库程序会获取令牌、提交一个 Image 5 任务,通过有界指数退避轮询返回的结果 URL,下载第一张图像,并记录响应元数据。请在安全的服务器环境中设置 FIREFLY_SERVICES_CLIENT_ID 和 FIREFLY_SERVICES_CLIENT_SECRET。
import json
import os
import random
import time
import urllib.error
import urllib.parse
import urllib.request
from datetime import datetime, timezone
from email.utils import parsedate_to_datetime
from pathlib import Path
CLIENT_ID = os.environ["FIREFLY_SERVICES_CLIENT_ID"]
CLIENT_SECRET = os.environ["FIREFLY_SERVICES_CLIENT_SECRET"]
TOKEN_URL = "https://ims-na1.adobelogin.com/ims/token/v3"
GENERATE_URL = "https://firefly-api.adobe.io/v4/images/generate-async"
SCOPES = "openid,AdobeID,session,additional_info,read_organizations,firefly_api,ff_apis"
MIN_API_INTERVAL = 15.0 # Default 4-RPM org entitlement; use a distributed limiter at scale.
_last_api_request = 0.0
def wait_for_org_quota():
global _last_api_request
delay = MIN_API_INTERVAL - (time.monotonic() - _last_api_request)
if delay > 0:
time.sleep(delay)
_last_api_request = time.monotonic()
def retry_after_seconds(value, fallback):
if not value:
return fallback
try:
return max(0.0, float(value))
except ValueError:
when = parsedate_to_datetime(value)
if when.tzinfo is None:
when = when.replace(tzinfo=timezone.utc)
return max(0.0, (when - datetime.now(timezone.utc)).total_seconds())
def request_json(url, *, method="GET", headers=None, body=None, attempts=5):
data = None if body is None else json.dumps(body).encode("utf-8")
for attempt in range(attempts):
req = urllib.request.Request(url, data=data, method=method, headers=headers or {})
wait_for_org_quota()
try:
with urllib.request.urlopen(req, timeout=60) as response:
return response.status, dict(response.headers), json.load(response)
except urllib.error.HTTPError as exc:
detail = exc.read().decode("utf-8", "replace")
retryable = exc.code == 429 or 500 <= exc.code < 600
if not retryable or attempt == attempts - 1:
raise RuntimeError(f"HTTP {exc.code} from {url}: {detail}") from exc
fallback = min(30, 2 ** attempt + random.random())
time.sleep(max(MIN_API_INTERVAL, retry_after_seconds(exc.headers.get("Retry-After"), fallback)))
except (urllib.error.URLError, TimeoutError) as exc:
if attempt == attempts - 1:
raise RuntimeError(f"Network failure from {url}: {exc}") from exc
time.sleep(max(MIN_API_INTERVAL, min(30, 2 ** attempt + random.random())))
def get_token():
form = urllib.parse.urlencode({
"grant_type": "client_credentials",
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"scope": SCOPES,
}).encode("utf-8")
req = urllib.request.Request(
TOKEN_URL,
data=form,
method="POST",
headers={"Content-Type": "application/x-www-form-urlencoded"},
)
with urllib.request.urlopen(req, timeout=30) as response:
return json.load(response)["access_token"]
def find_first_image_url(value):
if isinstance(value, dict):
image = value.get("image")
if isinstance(image, dict) and isinstance(image.get("url"), str):
return image["url"]
for child in value.values():
found = find_first_image_url(child)
if found:
return found
elif isinstance(value, list):
for child in value:
found = find_first_image_url(child)
if found:
return found
return None
def main():
token = get_token()
headers = {
"Authorization": f"Bearer {token}",
"x-api-key": CLIENT_ID,
"Content-Type": "application/json",
"Accept": "application/json",
"x-model-version": "image5",
}
payload = {
"prompt": (
"Editorial product photograph of a matte cobalt reusable bottle on pale limestone, "
"three-quarter view, soft north-window light, restrained blue and warm-gray palette, "
"clean negative space at upper left, realistic condensation, no logo or lettering"
),
"aspectRatio": "4:3",
"resolutionLevel": "4MP",
"modelId": "firefly_image",
"numVariations": 1,
"seeds": [42345],
"referenceBlobs": [],
"modelSpecificPayload": {"localeCode": "en-US", "prompt_reasoner": "quality"},
}
status, response_headers, accepted = request_json(
GENERATE_URL, method="POST", headers=headers, body=payload
)
result_url = accepted["links"]["result"]["href"]
cancel_url = accepted["links"]["cancel"]["href"]
deadline = time.monotonic() + 300
final = accepted
result_headers = {}
while time.monotonic() < deadline:
_, result_headers, final = request_json(result_url, headers=headers)
state = str(final.get("status", "")).lower()
if state in {"succeeded", "failed", "cancelled", "canceled"}:
break
if find_first_image_url(final):
break
else:
cancel_result = None
try:
cancel_status, _, cancel_body = request_json(cancel_url, method="PUT", headers=headers)
cancel_result = {"status": cancel_status, "body": cancel_body}
except Exception as exc:
cancel_result = {"error": str(exc)}
Path("firefly-response.json").write_text(
json.dumps({
"request": payload,
"accept_status": status,
"accept_headers": response_headers,
"accepted": accepted,
"cancel_attempt": cancel_result,
}, indent=2),
encoding="utf-8",
)
raise TimeoutError("Firefly job did not finish within 300 seconds")
if str(final.get("status", "")).lower() in {"failed", "cancelled", "canceled"}:
raise RuntimeError(f"Firefly job ended unsuccessfully: {json.dumps(final)}")
image_url = find_first_image_url(final)
if not image_url:
raise RuntimeError(f"No image URL in terminal response: {json.dumps(final)}")
with urllib.request.urlopen(image_url, timeout=120) as image_response:
content_type = image_response.headers.get_content_type()
suffix = {"image/png": ".png", "image/jpeg": ".jpg", "image/webp": ".webp"}.get(
content_type, ".bin"
)
Path("firefly-output" + suffix).write_bytes(image_response.read())
Path("firefly-response.json").write_text(
json.dumps({
"request": payload,
"accept_status": status,
"accept_headers": response_headers,
"accepted": accepted,
"result_headers": result_headers,
"result": final,
}, indent=2),
encoding="utf-8",
)
if __name__ == "__main__":
main()
预期结果:生成一个 4:3 的 Image 5 资源,以实际媒体类型对应的扩展名下载到本地,并附带一个审计 JSON。可能的失败原因:缺少使用权限(403)、误将 v3 架构中的字段发送到 v4(422)、组织级限流(429)、令牌过期(401),或输出 URL 在下载前过期。
有意义的变体:将 prompt_reasoner 切换为 speed 以测试延迟;如果账户费率卡更有利,可使用 resolutionLevel: 1MP 进行低成本预检;或者提供一个获准的 referenceBlobs 项目,移除或自动设置宽高比,并将提示词写成编辑指令。
示例 2 — 上传并进行蒙版填充
示例,并非强制公式。 这个 Python 3 程序会上传一个 PNG 源图和蒙版,提交 4 个带种子的 v3 填充变体,然后轮询返回的状态 URL。它假设源图和蒙版尺寸相同,且已通过目视确认蒙版语义无误。
import json
import os
import random
import time
import urllib.error
import urllib.request
from datetime import datetime, timezone
from email.utils import parsedate_to_datetime
from pathlib import Path
CLIENT_ID = os.environ["FIREFLY_SERVICES_CLIENT_ID"]
TOKEN = os.environ["FIREFLY_SERVICES_ACCESS_TOKEN"]
BASE = "https://firefly-api.adobe.io"
AUTH = {"Authorization": f"Bearer {TOKEN}", "x-api-key": CLIENT_ID}
MIN_API_INTERVAL = 15.0 # Default 4-RPM org entitlement; replace with shared storage at scale.
_last_api_request = 0.0
def wait_for_org_quota():
global _last_api_request
delay = MIN_API_INTERVAL - (time.monotonic() - _last_api_request)
if delay > 0:
time.sleep(delay)
_last_api_request = time.monotonic()
def retry_after_seconds(value, fallback):
if not value:
return fallback
try:
return max(0.0, float(value))
except ValueError:
when = parsedate_to_datetime(value)
if when.tzinfo is None:
when = when.replace(tzinfo=timezone.utc)
return max(0.0, (when - datetime.now(timezone.utc)).total_seconds())
def open_bytes(req, *, attempts=5):
for attempt in range(attempts):
wait_for_org_quota()
try:
with urllib.request.urlopen(req, timeout=60) as response:
return response.status, dict(response.headers), response.read()
except urllib.error.HTTPError as exc:
detail = exc.read().decode("utf-8", "replace")
retryable = exc.code == 429 or 500 <= exc.code < 600
if not retryable or attempt == attempts - 1:
raise RuntimeError(f"HTTP {exc.code}: {detail}") from exc
fallback = min(30, 2 ** attempt + random.random())
time.sleep(max(MIN_API_INTERVAL, retry_after_seconds(exc.headers.get("Retry-After"), fallback)))
except (urllib.error.URLError, TimeoutError) as exc:
if attempt == attempts - 1:
raise RuntimeError(f"Network failure: {exc}") from exc
time.sleep(max(MIN_API_INTERVAL, min(30, 2 ** attempt + random.random())))
raise AssertionError("unreachable")
def upload_png(path):
req = urllib.request.Request(
f"{BASE}/v2/storage/image",
data=Path(path).read_bytes(),
method="POST",
headers={**AUTH, "Content-Type": "image/png", "Accept": "application/json"},
)
_, _, raw = open_bytes(req)
return json.loads(raw)["images"][0]["id"]
def json_request(url, *, method="GET", payload=None):
data = None if payload is None else json.dumps(payload).encode("utf-8")
req = urllib.request.Request(
url,
data=data,
method=method,
headers={**AUTH, "Content-Type": "application/json", "Accept": "application/json"},
)
_, _, raw = open_bytes(req)
return json.loads(raw)
source_id = upload_png("source.png")
mask_id = upload_png("mask.png")
job = json_request(
f"{BASE}/v3/images/fill-async",
method="POST",
payload={
"image": {"source": {"uploadId": source_id}},
"mask": {"source": {"uploadId": mask_id}, "invert": False},
"prompt": (
"replace only the masked vase with a handmade cobalt ceramic vase, "
"preserve table and shadows"
),
"negativePrompt": "lettering, logo, extra objects",
"promptBiasingLocaleCode": "en-US",
"numVariations": 4,
"seeds": [101, 202, 303, 404],
"size": {"width": 2048, "height": 2048},
},
)
Path("fill-accepted.json").write_text(json.dumps(job, indent=2), encoding="utf-8")
deadline = time.monotonic() + 300
while time.monotonic() < deadline:
result = json_request(job["statusUrl"])
state = result.get("status", "")
if state == "succeeded":
Path("fill-result.json").write_text(json.dumps(result, indent=2), encoding="utf-8")
break
if state in {"failed", "cancelled", "canceled"}:
raise RuntimeError(json.dumps(result))
else:
cancel_result = None
try:
cancel_result = json_request(job["cancelUrl"], method="PUT")
except Exception as exc:
cancel_result = {"error": str(exc)}
Path("fill-accepted.json").write_text(
json.dumps({"accepted": job, "cancel_attempt": cancel_result}, indent=2),
encoding="utf-8",
)
raise TimeoutError("Fill job did not finish within 300 seconds")
outputs = result.get("result", {}).get("outputs", [])
if len(outputs) != 4:
raise RuntimeError(f"Expected four outputs, received {len(outputs)}")
suffixes = {"image/png": ".png", "image/jpeg": ".jpg", "image/webp": ".webp"}
for index, output in enumerate(outputs, start=1):
with urllib.request.urlopen(output["image"]["url"], timeout=120) as response:
content_type = response.headers.get_content_type()
suffix = suffixes.get(content_type)
if suffix is None:
raise RuntimeError(f"Unexpected output Content-Type: {content_type}")
Path(f"fill-output-{index}{suffix}").write_bytes(response.read())
预期结果:fill-result.json 中包含 4 个 URL 及其种子,另有 4 个下载到本地的图像文件。可能的失败原因:源图与蒙版尺寸不匹配、蒙版意图颠倒、不支持的尺寸、种子数量不匹配、返回的媒体类型异常,或提示词要求填充端点更改蒙版未覆盖的像素。
安全地操作异步任务
- 轮询前先持久化提交响应。
- 遵循返回的
statusUrl或links.result.href;不要根据任务 ID 重构主机名。 - 将上传、提交和轮询请求统一纳入组织级限流器。在默认每分钟 4 次请求的额度下,所有工作进程发出的 Firefly API 请求之间平均至少间隔 15 秒;使用自适应轮询,遵守
Retry-After,只有在组织的合同额度允许时才加快轮询。较早的一秒间隔示例不能取代当前使用说明。 - 设置本地截止时间并提供取消功能。当前任务的取消方式是向返回的取消链接发送
PUT请求。 - 对 429 和暂时性 5xx 错误,使用带随机抖动的指数退避重试。对未修改的 400/403/413/415/422 错误不要重试。
- 将 401 视为需要刷新令牌,然后仅重试一次。重复出现 401 表示配置错误。
- 立即将输出下载到受控存储中;预签名 URL 是交付机制,不是持久化资产管理方案。
- 存储端点、模型标头、移除机密信息后的载荷、种子、上传 ID、源文件哈希、任务 ID、Adobe 请求/关联标头、输出哈希、媒体类型、尺寸和审核状态。
当前参考文档中常见响应的含义:
| 状态 | 操作 |
|---|---|
| 400 | 修正格式错误的请求;验证 JSON 和路由 |
| 401 | 刷新/验证 Bearer 令牌 |
| 403 | 检查授权、产品配置文件、组织、策略、自定义模型访问权限和区域 |
| 404/410 | 将任务或临时资源视为不可用/已过期;不要循环重试 |
| 408/499 | 请求/客户端中断;重新提交前确认任务是否已被接受 |
| 409 | 任务状态冲突,通常是终止后取消;获取状态 |
| 413 | 减少上传/请求大小 |
| 415 | 修正 Content-Type 或图像编码 |
| 422 | 修正字段、枚举值、大小、蒙版、种子数量、模型标头或不兼容的架构 |
| 429 | 遵守 Retry-After,执行退避,并实施组织级准入控制 |
| 451 | 停止并上报法律/可用性限制;不要规避限制 |
| 500/503 | 设置重试上限;若问题持续,查看 Adobe 状态/联系支持 |
切勿自动修改被拒绝的提示词以绕过安全过滤器。请说明失败类别,并请求用户提供符合要求的修订内容。
容量、访问权限和成本
已记录,已于 2026-07-09 验证:Firefly API 的默认限制为每个组织每分钟 4 次请求,每天 9,000 次请求。如果账户经理提高每分钟请求数,但未更改每日请求数,每日限制就会变得重要。更高的限制需联系账户经理。请在各工作进程之间构建共享限流器;除非合同另有说明,轮询也会消耗请求额度。请参阅使用说明。
Firefly Services 是面向企业提供的服务。引用的开发者架构中没有通用的公开单张图像价格。Adobe 按 API 工作量以 Operations 计量;一个 API 操作或工作流可能消耗多个 Operation,适用费率可在 Admin Console/合同中查询。不要编造美元估算。在报价批量任务前,请查询组织的授权额度、费率卡、包含的 Operations、超额费用条款、模型/功能可用性和区域限制。请参阅 Shared Credit 产品说明。
权利、安全、隐私和来源信息
输入和输出
确保拥有提示词、参考图像、蒙版、人物/肖像、品牌、自定义模型训练数据及预期分发方式的相关权利。Adobe 当前的 2026v2 企业 API 条款规定,输入和输出属于客户内容,并提醒输出可能并非独一无二。这些条款还限制使用 Developer Tools 或其衍生内容/数据/输出来创建、训练、测试或改进 AI 系统,并限制客户软件仅供内部使用,除非销售订单明确允许其他用途。这并不保证内容可获得版权保护、具有独占性、不构成侵权、适合预期用途,或可用于面向外部的集成。请与法律顾问一起审阅当前的 Adobe Creative API 产品条款页面、2026v2 PSLT 和客户的销售订单。
Adobe 表示,Firefly 基础模型使用授权内容(如 Adobe Stock)和公有领域内容进行训练,并称不会使用客户/用户内容训练这些模型。Firefly Services 安全说明书指出,企业输入/输出不会纳入基础模型训练,但用户自行控制的反馈/改进计划或服务本身即为 AI 训练的功能属于有限例外。应将其表述为 Adobe 记录在案的政策,而非经过独立审计的保证。请参阅 Adobe 的方法 和 Firefly Services 安全事实说明书。
赔偿
切勿不加限定地声称“Firefly 输出享有赔偿保障”。Adobe 合同中的 Firefly 输出知识产权赔偿仅在销售订单/符合条件的优惠中明确包含该赔偿时适用;适用范围仅限列明的符合条件的 Firefly 功能和界面,并须在定义的导出事件发生后,且受管辖协议中的排除条款、义务和责任上限约束。当前条款排除的情形包括因修改或组合 Firefly 输出而产生的索赔,这与合成、重新编码或其他后期处理直接相关。产品说明将 Firefly API 列为一种界面,但前提是符合条件的客户使用 Operations 并下载输出;并非所有功能都必然适用于每种界面。请核实当前的 Firefly 产品说明、销售订单、2026v2 PSLT、符合条件的功能、导出证据及下游转换。法律解释问题请咨询法律顾问。
安全
遵循当前的 Adobe 生成式 AI 用户指南。该指南禁止非法或有害用途、露骨色情内容、性化未成年人、仇恨/贬损内容、美化血腥暴力、宣扬自残或暴力极端主义、有害欺骗、侵犯隐私、不合规的受监管活动、欺骗性冒充以及侵犯第三方权利。Adobe 表示,提示词、输入和输出可能会经过自动和人工审核,以防止滥用并进行过滤。
在适用情况下,添加应用级别的同意、年龄、权利、隐私、冒充和受监管用途把关机制。对于公开声明、新闻类图像、真实人物描绘、儿童、健康/金融/法律场景、选举及高影响决策,应保留人工审核。API 成功响应并不代表已通过安全或权利审核。
隐私与保留期限
Adobe 安全事实表中记录的企业服务默认设置: Firefly 生成式 API 的提示词文本、配置设置和假名化用户 ID 会保留 90 天;通过 API 上传的参考内容会缓存 24 小时;生成的 Firefly API 图像会存储 24 小时,并通过预签名 URL 提供访问;Content Credentials 的加密哈希和清单信息可能会保留在 Content Credentials 云端。请确认当前协议和区域部署情况,因为产品界面和合同可能会影响数据处理方式。
尽量减少个人或机密数据,使用短时有效的源 URL,避免在提示词中嵌入机密,将结果下载到客户受管控的存储空间,并按照政策删除本地暂存副本。公开的 v4 请求架构不提供由客户端控制的 storeInputs 开关;不要凭空编造这样的开关。
Content Credentials
Adobe 表示,对于像 Text to Image 这类像素完全由 Firefly 生成的素材,会自动附加 Content Credentials。凭证会标明 Adobe 为签发方,并添加可检测篡改的生成来源信息;但无法证明内容真实、所有权归属、已获同意或安全。请保留交付的原始文件及其清单。不要把剥离来源信息作为常规优化。如果流水线对素材进行重新编码、合成或导出,请测试凭证是否保留;如果组织的工具支持,请创建新的签名来源记录。请参阅 Adobe 的 Content Credentials 概述和主要的 C2PA 规范。
质量保障关卡
任何适用检查未通过时,都应拒绝或修改素材:
- 提示词意图:主体、动作、数量、本地化要求和排除项均符合要求。
- 构图:裁剪、安全区域、留白、地平线、透视和目标纵横比均正确。
- 解剖与几何:手部、面部、倒影、阴影、重复结构和物体接触关系均协调合理。
- 产品/品牌:轮廓、比例、颜色、材质、标签、徽标和声明均与获批参考一致;需要保留原样时,执行像素差异比较。
- 编辑隔离:比较未更改区域;以 100% 和 200% 缩放检查蒙版接缝、光晕、纹理重复和光照过渡。
- 文本:每个字符都清晰且正确;否则在后期替换。
- 技术:能够成功解码;媒体类型、像素尺寸、色彩配置文件、Alpha 通道、文件大小和校验和均符合交付要求。
- 无障碍:检查生成的
altText;不要仅仅因为prompt_reasoner: quality填充了它就予以信任。 - 安全/权利:已完成人工审查,评估相关风险;已记录参考素材和肖像使用许可。
- 来源信息:已保留原始文件和 Content Credentials,或存在有记录的转换链。
- 可复现性:已存储端点、模型标头、请求、种子、源文件哈希、作业 ID 和审核决定,且不含机密。
对于批量生成,应在生成前设定数值验收阈值,例如标签零改动、蒙版外区域的改动不超过经测量的容差、尺寸完全一致,以及人工拒绝率上限。应针对不同提示词、本地化设置、种子和参考类型进行抽样;不要仅凭一张出色的主视觉图就批准整条流水线。
已核查的来源
所有时效性信息均于 2026-07-09 根据第一方来源核查: