---
name: comfyui-flux2-edit
description: 通过公网网关使用 ComfyUI FLUX.2-klein-9b-kv 模型进行图生图/局部编辑。覆盖图片上传、工作流提交、队列/任务状态查询、PNG 图片下载的完整流程，支持换装、换背景、局部修改。
version: "1.0"
category: image-generation
triggers:
  - 图生图
  - 换装
  - 换背景
  - 局部编辑
  - flux2 编辑
  - flux2 edit
  - flux klein
  - 图片编辑
  - 服装迁移
  - 局部修改
  - comfyui flux
---

::: info 下载原始 Skill 文件
```bash
curl https://ai.ospreyai.cn/docs/raw/skills/comfyui-flux2-edit.md -o comfyui-flux2-edit.md
```
:::

## 对话式接入

本 Skill 文件可被 AI 助手（Claude Code、Cursor、ChatGPT 等）学习，通过自然语言对话完成图片编辑。

在 AI 对话中发送以下指令即可：

```
学习：https://ai.ospreyai.cn/docs/raw/skills/comfyui-flux2-edit.md，保存为本地的技能 skills
```

> 更多接入方式和使用示例详见 [API 文档 — AI 助手对话式接入](/api#ai-助手对话式接入)。

---

# ComfyUI FLUX.2-klein 图生图编辑

## Overview

通过公网网关 `https://ai.ospreyai.cn` 使用 ComfyUI **FLUX.2-klein-9b-kv-fp8** 模型进行图片编辑，基于 **Reference Conditioning** 技术实现服装/形象迁移和背景替换。

核心特性：
- **4 步极速生成**：使用 FluxKVCache + Flux2Scheduler 加速，< 1 秒完成
- **双图参考编辑**：图1 提供主体人物/姿势，图2 提供服装/风格参考
- **单图自我编辑**：图1=图2 时可实现局部微调（去外套、改细节）
- **Qwen3-8B CLIP**：文本编码更自然，提示词理解更精准

所有 API 均需 Bearer Token 鉴权（`Authorization: Bearer sk-xxx`）。

## Quick Start

```bash
export GW="https://ai.ospreyai.cn"
export API_KEY="sk-your-api-key"

# 1. 上传参考图片
curl -s -H "Authorization: Bearer $API_KEY" -X POST "$GW/api/v1/upload" \
  -F "image=@person.png" -F "type=input"

curl -s -H "Authorization: Bearer $API_KEY" -X POST "$GW/api/v1/upload" \
  -F "image=@outfit.png" -F "type=input"

# 2. 提交编辑工作流（完整 JSON 见下方 Route A）
curl -s -H "Authorization: Bearer $API_KEY" -X POST "$GW/api/v1/ai/image/generate" \
  -H "Content-Type: application/json" \
  -d '{"prompt":{...}}'

# 3. 查询任务状态
curl -s -H "Authorization: Bearer $API_KEY" "$GW/api/v1/ai/tasks/{prompt_id}"

# 4. 下载生成的图片
curl -s -H "Authorization: Bearer $API_KEY" \
  "$GW/api/v1/ai/image/view/?filename=Result_00001_.png&type=output&subfolder=" \
  -o result.png
```

## Task Routing

| 场景 | 动作 |
|------|------|
| 首次图片编辑 | → Route A: Upload & Generate |
| 需要查看任务是否完成 | → Route B: Check Status |
| 需要获取或下载 PNG | → Route C: Download |
| 需要调优提示词或编辑效果 | → Route D: Tune Parameters |
| 需要排查服务、节点、模型或文件问题 | → Route E: Troubleshoot |

## Route A: Upload & Generate

### 服务信息

- 网关地址: `https://ai.ospreyai.cn`
- 上传接口: `POST /api/v1/upload`
- 提交接口: `POST /api/v1/ai/image/generate`
- 鉴权方式: `Authorization: Bearer sk-xxx`

### 基本流程

1. 上传参考图片到 ComfyUI 的 input 目录
2. 提交 FLUX.2-klein 图生图编辑工作流
3. 工作流包含 15 个节点：
   - `LoadImage` × 2 — 加载图1（主体）和图2（服装参考）
   - `ImageScaleToTotalPixels` — 缩放到 1MP 以内
   - `GetImageSize` — 获取图片尺寸
   - `CLIPTextEncode` — 提示词编码
   - `ConditioningZeroOut` — 负向条件置零
   - `EmptyFlux2LatentImage` — 生成空 latent
   - `Flux2Scheduler` — 控制步数（4 步）
   - `RandomNoise` — 随机噪声
   - `UNETLoader` — 加载 FLUX.2-klein 主模型
   - `VAELoader` — 加载 VAE 解码器
   - `SamplerCustomAdvanced` — 高级采样器
   - `VAEDecode` — VAE 解码 latent → 像素图
   - `SaveImage` — 保存图片为 PNG
   - `ReferenceLatent` × 4 + `VAEEncode` × 2 — Reference Conditioning 子图

### Step 1: 上传图片

```bash
# 上传主体图片（图1）
curl -s -H "Authorization: Bearer $API_KEY" -X POST "$GW/api/v1/upload" \
  -F "image=@/path/to/person.png" \
  -F "type=input"

# 上传服装参考图片（图2）
curl -s -H "Authorization: Bearer $API_KEY" -X POST "$GW/api/v1/upload" \
  -F "image=@/path/to/outfit.png" \
  -F "type=input"
```

**响应：**

```json
{"name": "person.png", "subfolder": "", "type": "input"}
```

> 记住返回的 `name` 值，工作流中 `LoadImage` 节点需要使用。`type=input` 确保上传到 ComfyUI input 目录。

最大文件大小: 50MB。推荐使用 PNG/JPG 格式。

### Step 2: 提交编辑工作流

```bash
curl -s -H "Authorization: Bearer $API_KEY" -X POST "$GW/api/v1/ai/image/generate" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": {
      "122": {"class_type": "KSamplerSelect", "inputs": {"sampler_name": "euler"}},
      "126": {"class_type": "UNETLoader", "inputs": {"unet_name": "flux-2-klein-9b-kv-fp8.safetensors", "weight_dtype": "default"}},
      "133": {"class_type": "CLIPLoader", "inputs": {"clip_name": "qwen_3_8b_fp8mixed.safetensors", "type": "flux2", "device": "default"}},
      "138": {"class_type": "CFGGuider", "inputs": {"model": ["139", 0], "positive": ["132_121", 0], "negative": ["132_119", 0], "cfg": 1.0}},
      "127": {"class_type": "VAELoader", "inputs": {"vae_name": "flux2-vae.safetensors"}},
      "135": {"class_type": "CLIPTextEncode", "inputs": {"clip": ["133", 0], "text": "Have the man in Figure 1 put on the clothes from Figure 2, keep same posture"}},
      "139": {"class_type": "FluxKVCache", "inputs": {"model": ["126", 0]}},
      "123": {"class_type": "SamplerCustomAdvanced", "inputs": {"noise": ["125", 0], "guider": ["138", 0], "sampler": ["122", 0], "sigmas": ["137", 0], "latent_image": ["129", 0]}},
      "125": {"class_type": "RandomNoise", "inputs": {"noise_seed": 720512742793301}},
      "124": {"class_type": "VAEDecode", "inputs": {"samples": ["123", 0], "vae": ["127", 0]}},
      "94":  {"class_type": "SaveImage", "inputs": {"images": ["124", 0], "filename_prefix": "Result"}},
      "685": {"class_type": "ConditioningZeroOut", "inputs": {"conditioning": ["135", 0]}},
      "137": {"class_type": "Flux2Scheduler", "inputs": {"width": ["128", 0], "height": ["128", 1], "steps": 4}},
      "129": {"class_type": "EmptyFlux2LatentImage", "inputs": {"width": ["128", 0], "height": ["128", 1], "batch_size": 1}},
      "128": {"class_type": "GetImageSize", "inputs": {"image": ["130", 0]}},
      "130": {"class_type": "ImageScaleToTotalPixels", "inputs": {"image": ["76", 0], "upscale_method": "lanczos", "megapixels": 1.0, "resolution_steps": 1}},
      "131": {"class_type": "ImageScaleToTotalPixels", "inputs": {"image": ["81", 0], "upscale_method": "lanczos", "megapixels": 1.0, "resolution_steps": 1}},
      "76": {"class_type": "LoadImage", "inputs": {"image": "person.png"}},
      "81": {"class_type": "LoadImage", "inputs": {"image": "outfit.png"}},
      "134_116": {"class_type": "ReferenceLatent", "inputs": {"conditioning": ["685", 0], "latent": ["134_117", 0]}},
      "134_117": {"class_type": "VAEEncode", "inputs": {"pixels": ["130", 0], "vae": ["127", 0]}},
      "134_118": {"class_type": "ReferenceLatent", "inputs": {"conditioning": ["135", 0], "latent": ["134_117", 0]}},
      "132_119": {"class_type": "ReferenceLatent", "inputs": {"conditioning": ["134_116", 0], "latent": ["132_120", 0]}},
      "132_120": {"class_type": "VAEEncode", "inputs": {"pixels": ["131", 0], "vae": ["127", 0]}},
      "132_121": {"class_type": "ReferenceLatent", "inputs": {"conditioning": ["134_118", 0], "latent": ["132_120", 0]}}
    }
  }'
```

**响应：**

```json
{"prompt_id": "f5a1b2c3-...", "number": 150, "node_errors": {}}
```

> `node_errors` 为空对象 `{}` 表示工作流校验通过。如有错误会在此列出。

**使用前需修改：**

| 节点 | 字段 | 修改为 |
|------|------|--------|
| 76 | image | 图1文件名（主体人物，已上传） |
| 81 | image | 图2文件名（服装参考，已上传） |
| 135 | text | 你的英文提示词 |
| 94 | filename_prefix | 输出文件名前缀 |
| 125 | noise_seed | 任意整数（不同值生成不同结果） |

### 节点说明

| 节点 ID | class_type | 功能 |
|---------|-----------|------|
| 76 | LoadImage | 加载图1（主体人物/目标图片） |
| 81 | LoadImage | 加载图2（服装/风格参考图片） |
| 126 | UNETLoader | 加载 FLUX.2-klein-9b-kv-fp8 主模型 |
| 133 | CLIPLoader | 加载 Qwen3-8B CLIP 文本编码器 |
| 127 | VAELoader | 加载 Flux2 VAE 解码器 |
| 130 | ImageScaleToTotalPixels | 图1缩放到 1MP（加速推理） |
| 131 | ImageScaleToTotalPixels | 图2缩放到 1MP（加速推理） |
| 128 | GetImageSize | 获取缩放后的图片尺寸 |
| 135 | CLIPTextEncode | 编辑提示词编码 |
| 685 | ConditioningZeroOut | 负向条件置零 |
| 139 | FluxKVCache | KV Cache 加速（核心加速节点） |
| 137 | Flux2Scheduler | 调度器，控制采样步数（4） |
| 129 | EmptyFlux2LatentImage | 生成空 latent |
| 125 | RandomNoise | 随机噪声种子 |
| 122 | KSamplerSelect | 选择采样器（euler） |
| 138 | CFGGuider | CFG 引导，连接正负向条件 |
| 123 | SamplerCustomAdvanced | 高级采样器（核心生成节点） |
| 124 | VAEDecode | VAE 解码 latent → 像素图 |
| 94 | SaveImage | 保存图片为 PNG |
| 134_116~134_118 | ReferenceLatent + VAEEncode | 图1 Reference Conditioning 子图 |
| 132_119~132_121 | ReferenceLatent + VAEEncode | 图2 Reference Conditioning 子图 |

### 工作流连接图

```
LoadImage(76) ──→ ImageScale(130) ──→ GetImageSize(128) ──→ EmptyFlux2Latent(129)
                    │                    │
                    └→ VAEEncode(134_117) ──→ RefLatent(134_116/134_118)
                                                    ↓
CLIPLoader(133) ──→ CLIPTextEncode(135) ──→ ConditioningZeroOut(685) ──→ RefLatent(134_116)
                    │                                                          ↓
                    └→ RefLatent(134_118) ──→ RefLatent(132_121) ──────────→ CFGGuider(138)
                                                  ↑                               ↓
LoadImage(81) ──→ ImageScale(131) ──→ VAEEncode(132_120) ──→ RefLatent(132_119)  │
                                                                                   ↓
UNETLoader(126) ──→ FluxKVCache(139) ──→ CFGGuider(138) ──→ SamplerCustomAdv(123)
                                                                   ↓
Flux2Scheduler(137) ──→ SamplerCustomAdv(123)                     │
RandomNoise(125) ──→ SamplerCustomAdv(123)                        │
                                                                   ↓
                                                            VAEDecode(124) ──→ SaveImage(94)
                                                            VAELoader(127) ──→ VAEDecode(124)
```

### 关键参数

| 节点 | 字段 | 说明 | 示例值 |
|------|------|------|--------|
| 76 | image | 图1文件名（主体人物） | `"person.png"` |
| 81 | image | 图2文件名（服装参考） | `"outfit.png"` |
| 135 | text | 编辑提示词（英文） | `"Have the man wear the clothes from Figure 2"` |
| 137 | steps | 采样步数 | 4（固定，Flux2Scheduler 内置） |
| 130/131 | megapixels | 缩放上限（MP） | 1.0 |
| 125 | noise_seed | 随机种子 | 720512742793301 |
| 94 | filename_prefix | 输出文件名前缀 | `"Result"` |

### 输出图片参数

- **分辨率**: 与输入图片一致（缩放至 ≤1MP）
- **格式**: PNG
- **输出路径**: `subfolder=""`（空字符串）, `type=output`

## Route B: Check Status

### 查询队列

```bash
curl -s -H "Authorization: Bearer $API_KEY" "$GW/api/v1/ai/queue"
```

**响应：**

```json
{
  "queue_running": [[150, "f5a1b2c3-...", {...}]],
  "queue_pending": []
}
```

- `queue_running` 不为空 → 任务正在执行
- `queue_pending` 不为空 → 任务排队等待

### 查询任务状态

```bash
curl -s -H "Authorization: Bearer $API_KEY" "$GW/api/v1/ai/tasks/{prompt_id}"
```

**进行中：**

```json
{
  "f5a1b2c3-...": {
    "status": {"status_str": "success", "completed": false},
    "outputs": {}
  }
}
```

**已完成：**

```json
{
  "f5a1b2c3-...": {
    "status": {"status_str": "success", "completed": true},
    "outputs": {
      "94": {
        "images": [
          {"filename": "Result_00001_.png", "subfolder": "", "type": "output"}
        ]
      }
    }
  }
}
```

> FLUX.2-klein 4 步生成极快（< 1 秒），通常提交后立即完成。

## Route C: Download

下载前先从任务状态中获取输出文件信息（`filename`, `subfolder`, `type`），然后通过查看接口下载。

```bash
# 下载 PNG 图片（注意 subfolder 为空字符串）
curl -s -H "Authorization: Bearer $API_KEY" \
  "$GW/api/v1/ai/image/view/?filename=Result_00001_.png&type=output&subfolder=" \
  -o result.png
```

**关键点：**

- 图片输出的 `subfolder` 为空字符串
- 图片输出的 `type` 为 `output`
- 下载的是 PNG 格式，可直接查看

### Python 调用示例

```python
import requests
import time
import os

GW = os.getenv("GW", "https://ai.ospreyai.cn")
API_KEY = os.getenv("API_KEY", "")
headers = {"Authorization": f"Bearer {API_KEY}"}

# 1. 上传图片
def upload(path):
    with open(path, "rb") as f:
        resp = requests.post(f"{GW}/api/v1/upload", headers=headers,
                             files={"image": f}, data={"type": "input"})
        return resp.json()["name"]

img1 = upload("person.png")
img2 = upload("outfit.png")
print(f"Uploaded: {img1}, {img2}")

# 2. 提交编辑工作流（prompt 同上方 curl 示例，替换 76/81 的 image 和 135 的 text）
prompt = {
    "prompt": {
        # ... 完整工作流 JSON
    }
}
resp = requests.post(f"{GW}/api/v1/ai/image/generate", headers=headers, json=prompt)
prompt_id = resp.json()["prompt_id"]
print(f"Task submitted: {prompt_id}")

# 3. 轮询任务状态
while True:
    resp = requests.get(f"{GW}/api/v1/ai/tasks/{prompt_id}", headers=headers)
    data = resp.json()
    task = data.get(prompt_id, {})
    if task.get("status", {}).get("completed"):
        print("Task completed!")
        break
    time.sleep(2)

# 4. 下载图片
outputs = task.get("outputs", {}).get("94", {})
image_info = outputs.get("images", [{}])[0]
resp = requests.get(f"{GW}/api/v1/ai/image/view/", headers=headers,
                    params={"filename": image_info["filename"],
                            "subfolder": image_info.get("subfolder", ""),
                            "type": image_info.get("type", "output")})
with open("result.png", "wb") as f:
    f.write(resp.content)
print(f"Downloaded: result.png ({len(resp.content)} bytes)")
```

## Route D: Tune Parameters

| 参数 | 节点 | 建议 |
|------|------|------|
| 提示词 | 135 text | 英文效果更稳定；用 Figure 1/Figure 2 指代两张图片 |
| 缩放分辨率 | 130/131 megapixels | 默认 1.0MP；增大可提高画质但显存占用更高 |
| 随机种子 | 125 noise_seed | 不同值生成不同结果；相同种子可复现 |
| 采样步数 | 137 steps | 默认 4 步，由 Flux2Scheduler 内置；不建议修改 |
| CFG 强度 | 138 cfg | 默认 1.0，Flux 系列推荐保持为 1.0 |
| 输出前缀 | 94 filename_prefix | 自定义输出文件名前缀 |

### 提示词技巧

Reference Conditioning 用 Figure 1/Figure 2 指代两张图：

**换装：**

```text
Have the man in Figure 1 put on the clothes from Figure 2, keep same posture
```

**改背景：**

```text
Change the background of Figure 1 to African savannah at sunset, keep person unchanged
```

**局部修改（单图自我参考，图1=图2）：**

```text
Remove the outer jacket from the person in Figure 1, keep inner clothing and background unchanged
```

**添加元素：**

```text
Add a red scarf to the person in Figure 1, keep everything else identical
```

**风格迁移：**

```text
Apply the artistic style from Figure 2 to the image in Figure 1
```

## Route E: Troubleshoot

| 问题 | 排查方法 |
|------|---------|
| 上传图片失败 | 检查文件大小（≤50MB）、格式（推荐 PNG/JPG）、Bearer Token 是否有效 |
| 工作流提交报 `value_not_in_list` | 模型文件名不正确，检查 UNet/CLIP/VAE 名称是否与服务器一致 |
| 工作流提交报 `return_type_mismatch` | 节点间连接类型不匹配，检查节点输出→输入的链接是否正确 |
| `node_errors` 非空 | 图片文件未上传成功，检查 LoadImage 的 image 值是否与 upload 返回值一致 |
| 生成结果与原图完全一样 | 提示词没被应用，检查 135 text 是否有效英文描述 |
| 生成结果异常/乱码 | 检查 `type=input` 是否在 upload 时正确设置 |
| 401 鉴权失败 | 检查 Bearer Token 是否有效：`curl -H "Authorization: Bearer sk-xxx" $GW/api/v1/ai/queue` |
| 429 请求被限流 | AI 接口 10次/分/IP，稍后重试 |
| 503 后端服务不可用 | 检查内网 ComfyUI 服务是否运行 |
| 图片质量不理想 | 使用英文提示词；确保参考图清晰度足够；更换 noise_seed 重试 |

### 模型文件清单

| 类型 | 文件 | 用途 |
|------|------|------|
| UNet | `flux-2-klein-9b-kv-fp8.safetensors` | 主去噪模型 |
| CLIP | `qwen_3_8b_fp8mixed.safetensors` | Qwen3-8B 文本编码器 |
| VAE | `flux2-vae.safetensors` | VAE 解码器 |

### 技术细节

- **FluxKVCache**：KV Cache 缓存加速，避免重复计算
- **Flux2Scheduler**：内置 4 步调度，无需手动设置 steps
- **Reference Conditioning**：通过 4 个 ReferenceLatent 节点 + 2 个 VAEEncode 子图实现双图参考
- **SamplerCustomAdvanced**：相比 KSampler 提供更精细的采样控制
- **CFG 1.0**：Flux 系列模型的推荐 CFG 值

### 内网直连 vs 公网网关

| | 内网直连 | 公网网关 |
|---|---|---|
| 地址 | `192.168.1.236:8188` | `ai.ospreyai.cn` |
| 鉴权 | 无 | `Authorization: Bearer sk-xxx` |
| 上传路径 | `/upload/image` | `/api/v1/upload` |
| 提交路径 | `/prompt` | `/api/v1/ai/image/generate` |
| 任务查询 | `/history/{id}` | `/api/v1/ai/tasks/{id}` |
| 队列查询 | `/queue` | `/api/v1/ai/queue` |
| 下载路径 | `/view?filename=...` | `/api/v1/ai/image/view/?filename=...&type=output&subfolder=` |
| 限流 | 无 | 10次/分/IP |

## Verification Checklist

- [ ] 参考图片上传成功，返回 `name` 值
- [ ] 工作流提交成功，`node_errors` 为空
- [ ] 任务在 `/api/v1/ai/queue` 中执行完成（通常 < 1 秒）
- [ ] 任务状态 `completed: true`，outputs 包含 PNG 文件信息
- [ ] 成功下载 PNG 文件（注意 `subfolder` 为空字符串）
- [ ] 图片可正常查看且编辑效果符合预期
