FLUX.2 没有单独的编辑端点。编辑用的就是你做文生图时那个 POST,只是多加了几个 input_image 字段。这个设计决定了一件事:客户端该怎么搭,从第一行代码就定下来了。

下面每个端点、字段名和限额都来自 Black Forest Labs 官方文档。官方没写过的地方,本文直接说没有,不留空话。

FLUX.2 编辑端点的工作方式

三步任务流程

一次编辑是一个任务,不是一次就把图片装回来的响应。你把请求 POST 到 https://api.bfl.ai/v1/flux-2-pro-preview,这是官方编辑示例页用的那个端点;如果同一个提示词需要在多次运行里得到同样结果,就换成固定快照 flux-2-pro。返回里带着 idpolling_url,用同一个密钥请求头去 GET 这个轮询地址,等 status 变成 Ready,图片才会以 result.sample 的形式出现。

result.sample 指向的签名链接十分钟后失效,所以下载必须和轮询放在同一次运行里完成。

鉴权只有一个 x-key 请求头,不是 Bearer token。密钥怎么建,API 接入教程里写过。

一段可直接运行的多参考请求

关键字段名

prompt 是请求体里唯一的必填字段。input_image 装第一张参考图,input_image_2 装第二张,编号一直排到 input_image_8。FLUX.2 [pro] 的 API 参考文档正好列了这八个字段;[klein] 4B 的参考文档只到 input_image_4,并把这个模型描述为「like Pro but max 4 images」,也就是和 Pro 一样但最多四张。按四张参考图去组装请求体,两个档位都能通过校验。

widthheight 是整数,官方给出的最小值是 64。seed、取值 0 到 5 的 safety_tolerance、可为 jpegpngwebpoutput_formatwebhook_urlwebhook_secret 都是可选项,user 用来传一个 1 到 256 字符的终端用户标识。

有一处空白值得提前安排:官方请求体里没有 negative_prompt 字段。凡是你不想要的东西,都只能写进对目标画面的正面描述里。

# 需要 curl 和 jq。BFL_API_KEY 从 dashboard.bfl.ai 获取。
API_BASE="https://api.bfl.ai/v1"
MODEL="flux-2-pro-preview"

response=$(curl -s -X POST "${API_BASE}/${MODEL}" \
  -H "x-key: ${BFL_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Keep the room from image 1, place the chair from image 2 on the rug from image 3, keep the chair proportions and the rug pattern, warm afternoon light.",
    "input_image": "https://example.com/room.jpg",
    "input_image_2": "https://example.com/chair.jpg",
    "input_image_3": "https://example.com/rug.jpg",
    "width": 1024,
    "height": 1024,
    "output_format": "png"
  }')

polling_url=$(echo "${response}" | jq -r .polling_url)

while true; do
  sleep 0.5
  result=$(curl -s "${polling_url}" \
    -H "accept: application/json" \
    -H "x-key: ${BFL_API_KEY}")

  status=$(echo "${result}" | jq -r .status)
  echo "Status: ${status}"

  if [ "${status}" = "Ready" ]; then
    curl -sL -o edit.png "$(echo "${result}" | jq -r .result.sample)"
    break
  elif [ "${status}" = "Error" ] || [ "${status}" = "Failed" ]; then
    echo "Generation failed: ${result}"
    break
  fi
done

在提示词里指代每张图

多参考图指南用一句话把规则说完了:把每张图承担的角色讲清楚,模型才知道该从哪儿取什么。它给出的推荐写法相当直白,比如「Change image 1 to match the style of image 2」。

也正因为如此,字段顺序本身就是提示词的一部分。一句「用第 5 张图里的木料」,只有在你确实把木料放在第五位的时候才成立。

指南还列了一串该避开的说法:Make it betterImprove the lightingMake it more professionalFix the image。这些句子的毛病是同一个——让模型去猜你想要的目标状态。

从左到右的编辑流水线:一张输入图方块、三个控制滑块和两张输出图方块,用箭头连接。
本站自绘示意图,非官方示例:一张输入图、三个控制项与两张输出图。 AI Tool Blog
Create a house for the chickens from image 1 using materials from images 2, 3, 4, and 5. Use the wood from image 5 for the base, the materials from images 2 and 4 for the walls and floor, and the material from image 3 for a small pillow nest. Place the chickens from image 1 in their new home, sitting on the pillow nest. Next to them, include the eggs from image 6. Apply the style of image 1 to the entire new scene.

这段提示词来自官方编辑示例页。每张参考图都有一个编号、一种材质和一个去处,这就是生产环境里的编辑提示词该有的样子。

会轮询并保存结果的 Python 客户端

放进服务里时,提交和轮询应该待在同一个函数中,这样十分钟的窗口不会被错过。

import os
import time

import requests

API_BASE = "https://api.bfl.ai/v1"
MODEL = "flux-2-pro-preview"
HEADERS = {
    "accept": "application/json",
    "x-key": os.environ["BFL_API_KEY"],
    "Content-Type": "application/json",
}


def edit(prompt: str, references: list[str], width: int = 1024, height: int = 1024) -> bytes:
    payload = {"prompt": prompt, "width": width, "height": height}
    # input_image, input_image_2, ... input_image_8
    for index, url in enumerate(references[:8], start=1):
        key = "input_image" if index == 1 else f"input_image_{index}"
        payload[key] = url

    response = requests.post(f"{API_BASE}/{MODEL}", headers=HEADERS, json=payload, timeout=30)
    response.raise_for_status()
    polling_url = response.json()["polling_url"]

    for _ in range(120):
        time.sleep(0.5)
        result = requests.get(polling_url, headers=HEADERS, timeout=30).json()
        status = result["status"]
        if status == "Ready":
            sample = requests.get(result["result"]["sample"], timeout=60)
            return sample.content
        if status in ("Error", "Failed"):
            raise RuntimeError(result)
    raise TimeoutError("no result after 120 polls")


with open("edit.png", "wb") as handle:
    handle.write(
        edit(
            "Keep the room from image 1, place the chair from image 2 on the rug from image 3, warm afternoon light.",
            [
                "https://example.com/room.jpg",
                "https://example.com/chair.jpg",
                "https://example.com/rug.jpg",
            ],
        )
    )

两处细节决定了它的可靠程度。参考图列表在建字段之前就被切到八张,列表过长时是降级而不是校验失败;下载放在轮询循环内部,签名链接还在有效期内。

API 文档写明的限额

参考图数量不是一个固定数字。总览页给出的上限是:[klein] 四张,[max]、[pro] 和 [flex] 走 API 八张、在 playground 十张,[dev] 是建议最多六张。到了 [pro] 这一档,数量还会随输出尺寸变化,因为 9 MP 的预算是输入加输出一起算的:

[pro] 的输出尺寸9 MP 预算允许的参考图
1 MP最多 8 张
2 MP最多 7 张
3 MP最多 6 张

提示词增强也分档位。[pro] 和 [max] 默认会改写你的提示词,并提供了 disable_pup 来关掉;[klein] 完全不带提示词增强,所以总览页才反复提醒这一档要写得更细。编辑计费写在价格页上,FLUX.2 API 价格一文里整理过。

轮询状态在 Get Result 页面上有完整枚举:Task not foundPendingReasoningGeneratingRequest ModeratedContent ModeratedReadyError。审核态不是你的请求体写错了,原样重发也过不去。

如果你操心的是提示词而不是管道,FLUX.2 提示词指南讲了结构和十六进制配色;当你开始拿别的模型来比同一次编辑,可以看与 Nano Banana Pro 的对比

官方页面没有交代的部分

本文查阅的文档页里,没有成功率、没有中位延迟,也没有按国家或地区拆分的可用性说明。免费 API 额度同样不存在:playground 不需要注册也不需要绑卡,但它是演示,不是额度。

文件处理也是空白。API 参考把每个字段称为输入图的路径,官方示例传的是公网 URL,但文件体积上限、可接受的 MIME 类型和参考图的保存策略都没有公布。

稳妥的做法很无聊但有效:把模型 ID 写进配置,把密钥放进密钥管理服务,并在创建任务的那一次运行里把图片取回来。

常见问题

FLUX.2 有专门的图像编辑端点吗? 没有。编辑和生成共用 /v1/flux-2-pro-preview/v1/flux-2-pro,只是在请求体里加了 input_image 系列字段。

一次请求能带几张参考图? [klein] 四张,[max]、[pro]、[flex] 走 API 八张、playground 十张,[dev] 六张。在 [pro] 上,9 MP 的输入加输出预算会随着输出变大而压低这个上限。

能传负向提示词吗? 官方请求体里没有 negative_prompt 字段。想排除什么,就改成对目标状态的正面描述。

一定要轮询吗? 是。提交的响应只返回 polling_url,只有状态变成 Ready 之后才有 result.sample

结果链接有效期多久? 十分钟。下载要在同一次运行里完成。