设计实践

Adobe Firefly API:Client ID、令牌与接入凭据自检

Firefly 接入时最常见的问题是凭据配错了:Client ID 写错、产品没加进项目、令牌和 x-api-key 对不上。这篇写一个自检脚本,依次检查环境变量、向 IMS 换令牌、带令牌调用 API,并把每一步的真实报错翻译成具体的修复建议。整个过程不提交生成任务,也就不消耗额度。

BINBHO · 图文教程

Firefly 接入时最常见的问题是凭据配错了:Client ID 写错、产品没加进项目、令牌和 x-api-key 对不上。这篇写一个自检脚本,依次检查环境变量、向 IMS 换令牌、带令牌调用 API,并把每一步的真实报错翻译成具体的修复建议。整个过程不提交生成任务,也就不消耗额度。

先看清成品(下面是在 box 上用错误凭据对真实 Adobe 接口发出的请求)

  • IMS 返回 400 invalid_client。
  • Firefly 返回 401 {"error_code":"401013","message":"Oauth token is not valid"}。
  • 两个返回体都写进了单元测试。

合规提醒:Firefly Services API 面向企业,需要组织开通 Firefly Services,并在 Adobe Developer Console 创建 OAuth Server-to-Server 凭据。CLIENT_SECRET 和访问令牌只能放在服务器端,不要写进前端代码、仓库或截图。提示词和参考图要遵守 Adobe 的生成式 AI 使用准则,不要生成侵犯他人肖像、版权或商标的内容。在中国境内公开发布生成内容,要遵守《生成式人工智能服务管理暂行办法》,并按《人工智能生成合成内容标识办法》加上“AI 生成”标识。Firefly 按生成额度计费,额度取决于你的合同,本文不列价格。

运行说明:box 上没有有效凭据,所以“鉴权通过”这条分支(返回 400 参数错误)没有实际验证过。它依据的假设是:参数不合法的请求会在校验阶段被拦下,不会生成图片。拿到真实凭据后,建议先在测试项目里确认这一点,再用到生产环境。

做完之后你会得到什么?

成果 说明
ff_doctor.py 三步自检,并把报错归类
test_doctor.py 用真实返回体测试分类规则

需要准备哪些环境?

Python 3.9 以上,pip install requests pytest,能访问 adobelogin.com 和 adobe.io。

文件结构

49-adobe-firefly-C-src/
├── ff_doctor.py
└── test_doctor.py

第 1 步:代码

"""Firefly 接入自检:依次检查环境变量、IMS 换令牌、带令牌调用 API,
把每一步的真实报错归类成“该怎么修”。不会提交生成任务(不消耗额度)——
第 3 步向 generate-async 发一个故意不合法的请求体(numVariations=0),
令牌无效时得到 401;令牌有效时应被参数校验以 400 拦下,不会生成图片。
(为什么不用 GET /v3/status/<假ID>:实测它在鉴权之前就返回 openresty 的 404,测不出令牌好坏。)"""
import json, os, sys
import requests

IMS = "https://ims-na1.adobelogin.com/ims/token/v3"
API = "https://firefly-api.adobe.io"
SCOPE = "openid,AdobeID,session,additional_info,read_organizations,firefly_api,ff_apis"
UA = "binbho-tutorial-firefly-doctor/1.0"

RULES = [  # (条件, 类别, 建议)。条件看 HTTP 状态码和返回体
    (lambda s, b: s == 400 and "invalid_client" in b, "凭据错误", "Client ID 不对,或者项目里没有加 Firefly API;到 Adobe Developer Console 核对"),
    (lambda s, b: s == 401 and "invalid_client" in b, "凭据错误", "Client Secret 不对或已轮换"),
    (lambda s, b: s == 400 and "invalid_scope" in b, "scope 错误", "scope 用官方文档给的那一串"),
    (lambda s, b: s == 401 and "401013" in b, "令牌无效", "令牌过期或不属于这个 Client ID:重新换令牌,x-api-key 要和换令牌用的 Client ID 一致"),
    (lambda s, b: s == 403, "无权限", "组织没有开通 Firefly Services,或这个产品配置没有权限"),
    (lambda s, b: s == 400, "参数错误", "看返回的 message(如尺寸、numVariations、seeds 个数)"),
    (lambda s, b: s == 404, "路径或任务不存在", "检查 URL;同步 v3 接口已于 2025-10-03 下线,改用 *-async"),
    (lambda s, b: s == 429, "限流", "按 Retry-After 等待后重试,降低并发"),
    (lambda s, b: s >= 500, "服务端错误", "指数退避重试,持续失败看 Adobe 状态页"),
]


def classify(status, body):
    for cond, cat, tip in RULES:
        if cond(status, body):
            return cat, tip
    return ("正常", "") if status < 400 else ("未知", body[:200])


def show(step, r):
    cat, tip = classify(r.status_code, r.text)
    print(f"[{step}] HTTP {r.status_code} → {cat}" + (f":{tip}" if tip else ""))
    print("      返回:", r.text.strip()[:160])
    return r.status_code < 400


def main():
    cid = os.environ.get("FIREFLY_SERVICES_CLIENT_ID")
    sec = os.environ.get("FIREFLY_SERVICES_CLIENT_SECRET")
    print("[1] 环境变量:", "CLIENT_ID ✓" if cid else "CLIENT_ID ✗", "CLIENT_SECRET ✓" if sec else "CLIENT_SECRET ✗")
    if not (cid and sec):
        sys.exit("缺少环境变量")
    s = requests.Session(); s.headers["User-Agent"] = UA
    r = s.post(IMS, data={"grant_type": "client_credentials", "client_id": cid, "client_secret": sec, "scope": SCOPE}, timeout=30)
    token = r.json().get("access_token") if show("2 IMS 换令牌", r) else os.environ.get("FIREFLY_TEST_TOKEN", "invalid")
    r = s.post(f"{API}/v3/images/generate-async", timeout=30, json={"prompt": "doctor check", "numVariations": 0},
               headers={"x-api-key": cid, "Authorization": f"Bearer {token}", "Content-Type": "application/json"})
    show("3 API 鉴权", r)
    if r.status_code == 400:
        print("      ✓ 鉴权通过(400 说明请求已经过了鉴权、停在参数校验)")


if __name__ == "__main__":
    main()

第 2 步:测试

from ff_doctor import classify

# 以下返回体是本文在 box 上用错误凭据真实请求得到的
REAL_IMS = '{"error":"invalid_client","error_description":"invalid client_id parameter"}'
REAL_API = '{"error_code":"401013","message":"Oauth token is not valid"}'


def test_real_bodies():
    assert classify(400, REAL_IMS)[0] == "凭据错误"
    assert classify(401, REAL_API)[0] == "令牌无效"


def test_others():
    assert classify(400, '{"message":"Size must be one of ..."}')[0] == "参数错误"
    assert classify(429, "")[0] == "限流"
    assert classify(503, "")[0] == "服务端错误"
    assert classify(200, "{}")[0] == "正常"
$ python -m pytest -q
2 passed in 0.08s

第 3 步:真实运行

$ FIREFLY_SERVICES_CLIENT_ID=not-a-real-client FIREFLY_SERVICES_CLIENT_SECRET=not-a-real-secret python ff_doctor.py
[1] 环境变量: CLIENT_ID ✓ CLIENT_SECRET ✓
[2 IMS 换令牌] HTTP 400 → 凭据错误:Client ID 不对,或者项目里没有加 Firefly API;到 Adobe Developer Console 核对
      返回: {"error":"invalid_client","error_description":"invalid client_id parameter"}
[3 API 鉴权] HTTP 401 → 令牌无效:令牌过期或不属于这个 Client ID:重新换令牌,x-api-key 要和换令牌用的 Client ID 一致
      返回: {"error_code":"401013","message":"Oauth token is not valid"}

$ env -u FIREFLY_SERVICES_CLIENT_ID python ff_doctor.py
[1] 环境变量: CLIENT_ID ✗ CLIENT_SECRET ✓
缺少环境变量

检查点:每一步都打印了 HTTP 状态码、类别和原始返回体。凭据填对之后,第 2 步应显示“正常”,第 3 步应显示 400 和“✓ 鉴权通过”。

调试示例

第 3 步最初返回的是 404 网页,而不是 401。 第一版的第 3 步用 GET /v3/status/urn:ff:jobs:doctor-check 来测鉴权,看起来最安全,因为什么都不会生成。但实际跑出来是 404 Not Found,返回的是 openresty 的 HTML 页面。也就是说,网关在检查令牌之前就因为任务不存在而直接返回了,这个请求根本测不出令牌是好是坏。

后来改成向 generate-async 发送 numVariations=0 的请求,这次真实拿到了 401 401013,说明请求确实经过了鉴权这一步。

常见问题

现象 原因 处理
invalid_client Client ID 或 Secret 错误,或者项目里没有加 Firefly API 到 Developer Console 核对,必要时重新生成 Secret
invalid_scope scope 自己拼写有误 复制官方文档给出的 scope
401013 令牌过期,或 x-api-key 和令牌不匹配 重新换令牌,两边的 ID 保持一致
403 组织没有开通 Firefly Services 联系 Adobe 销售或管理员

模板规则

  • 自检不能提交真实的生成任务。
  • 分类规则要用真实的返回体来测试。
  • 报错时打印原始返回体,但不要打印 Secret 和令牌。

项目资源

用途 地址
Firefly API 快速入门(取令牌、generate-async) https://developer.adobe.com/firefly-services/docs/firefly-api/guides/
异步 API 使用说明(statusUrl 轮询) https://developer.adobe.com/firefly-services/docs/firefly-api/guides/how-tos/using-async-apis
Seeds 说明 https://developer.adobe.com/firefly-services/docs/firefly-api/guides/concepts/seeds/
Adobe Developer Console https://developer.adobe.com/console
Adobe IMS 令牌端点(POST) https://ims-na1.adobelogin.com/ims/token/v3

把这个脚本放进部署流程的健康检查里,换凭据或换环境时跑一下,就能马上确认配置有没有问题。