Skip to content

下载原始 Skill 文件

bash
curl https://ai.ospreyai.cn/docs/raw/skills/comfyui-qwen-image-edit.md -o comfyui-qwen-image-edit.md

对话式接入

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

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

学习:https://ai.ospreyai.cn/docs/raw/skills/comfyui-qwen-image-edit.md,保存为本地的技能 skills

更多接入方式和使用示例详见 API 文档 — AI 助手对话式接入


ComfyUI Qwen-Image-Edit 图片编辑

Overview

通过公网网关 https://ai.ospreyai.cn 使用 ComfyUI Qwen-Image-Edit-2511 模型进行高质量图片编辑。基于 Qwen-VL 视觉语言模型,支持参照编辑(Reference Editing)。

核心特性:

  • Turbo 4 步模式:Lightning LoRA 加速,~2-6 秒完成(RTX 5090)
  • Native 40 步模式:更高质量输出,~15-20 秒完成
  • 自我编辑(单图):同一张图作为输入和参考,描述想要的变化
  • 换装/换物(双图):图1 = 目标人物,图2 = 参照服装/物品
  • 内置 CFGNorm + Switch 节点:Turbo/Native 切换无需手动修改工作流

所有 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=@input.png" -F "type=input" -F "overwrite=true"

# 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":{...}, "extra_data": {}}'

# 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=Qwen_Edit_2511_00001_.png&type=output&subfolder=" \
  -o result.png

Task Routing

场景动作
首次图片编辑→ Route A: Upload & Generate
需要查看任务是否完成→ Route B: Check Status
需要获取或下载 PNG→ Route C: Download
需要调优提示词或切换 Turbo/Native→ 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
  • 提交格式: ComfyUI v1 dict 格式 {"prompt": {...nodes...}, "extra_data": {}}

基本流程

  1. 上传输入图片(+ 可选参考图)到 ComfyUI input 目录
  2. 提交 Qwen-Image-Edit-2511 工作流
  3. 工作流包含 20+ 节点:
    • LoadImage × 2 — 加载输入图和参考图
    • FluxKontextImageScale — 缩放到 VAE 兼容尺寸
    • TextEncodeQwenImageEditPlus × 2 — Qwen-VL 多模态编码(正向/负向)
    • FluxKontextMultiReferenceLatentMethod × 2 — 多参考图 latent 方法
    • UNETLoader — 加载 Qwen-Image-Edit 主模型
    • VAELoader — 加载 VAE 解码器
    • CLIPLoader — 加载 Qwen-VL CLIP
    • VAEEncode — 编码参考图 latent
    • KSampler — 采样去噪(核心节点)
    • VAEDecode — VAE 解码
    • SaveImage — 保存图片

Step 1: 上传图片

bash
# 上传输入图片(必须)
curl -s -H "Authorization: Bearer $API_KEY" -X POST "$GW/api/v1/upload" \
  -F "image=@/path/to/input.png" \
  -F "type=input" \
  -F "overwrite=true"

# 如果换装需要参考图,上传第二张
curl -s -H "Authorization: Bearer $API_KEY" -X POST "$GW/api/v1/upload" \
  -F "image=@/path/to/reference.png" \
  -F "type=input" \
  -F "overwrite=true"

响应:

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

自我编辑模式只需一张图(图2 = 图1),换装模式需要两张不同的图。

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

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": {
      "41": {"inputs": {"image": "input.png"}, "class_type": "LoadImage"},
      "83": {"inputs": {"image": "input.png"}, "class_type": "LoadImage"},
      "170:160": {"inputs": {"image": ["41", 0]}, "class_type": "FluxKontextImageScale"},
      "170:161": {"inputs": {"unet_name": "qwen_image_edit_2511_bf16.safetensors", "weight_dtype": "default"}, "class_type": "UNETLoader"},
      "170:162": {"inputs": {"clip_name": "qwen_2.5_vl_7b_fp8_scaled.safetensors", "type": "qwen_image", "device": "default"}, "class_type": "CLIPLoader"},
      "170:146": {"inputs": {"vae_name": "qwen_image_vae.safetensors"}, "class_type": "VAELoader"},
      "170:145": {"inputs": {"shift": 3.1, "model": ["170:161", 0]}, "class_type": "ModelSamplingAuraFlow"},
      "170:152": {"inputs": {"strength": 1.0, "model": ["170:145", 0]}, "class_type": "CFGNorm"},
      "170:156": {"inputs": {"pixels": ["170:160", 0], "vae": ["170:146", 0]}, "class_type": "VAEEncode"},
      "170:168": {"inputs": {"value": false}, "class_type": "PrimitiveBoolean"},
      "170:165": {"inputs": {"value": 4}, "class_type": "PrimitiveInt"},
      "170:166": {"inputs": {"value": 40}, "class_type": "PrimitiveInt"},
      "170:154": {"inputs": {"value": 1.0}, "class_type": "PrimitiveFloat"},
      "170:155": {"inputs": {"value": 4.0}, "class_type": "PrimitiveFloat"},
      "170:149": {"inputs": {"prompt": "", "clip": ["170:162", 0], "vae": ["170:146", 0], "image1": ["170:160", 0], "image2": ["83", 0]}, "class_type": "TextEncodeQwenImageEditPlus"},
      "170:147": {"inputs": {"reference_latents_method": "index_timestep_zero", "conditioning": ["170:149", 0]}, "class_type": "FluxKontextMultiReferenceLatentMethod"},
      "170:151": {"inputs": {"prompt": "Remove the jacket, keep the T-shirt underneath", "clip": ["170:162", 0], "vae": ["170:146", 0], "image1": ["170:160", 0], "image2": ["83", 0]}, "class_type": "TextEncodeQwenImageEditPlus"},
      "170:148": {"inputs": {"reference_latents_method": "index_timestep_zero", "conditioning": ["170:151", 0]}, "class_type": "FluxKontextMultiReferenceLatentMethod"},
      "170:153": {"inputs": {"lora_name": "Qwen-Image-Edit-2511-Lightning-4steps-V1.0-bf16.safetensors", "strength_model": 1.0, "model": ["170:152", 0]}, "class_type": "LoraLoaderModelOnly"},
      "170:163": {"inputs": {"switch": ["170:168", 0], "on_false": ["170:152", 0], "on_true": ["170:153", 0]}, "class_type": "ComfySwitchNode"},
      "170:164": {"inputs": {"switch": ["170:168", 0], "on_false": ["170:154", 0], "on_true": ["170:155", 0]}, "class_type": "ComfySwitchNode"},
      "170:167": {"inputs": {"switch": ["170:168", 0], "on_false": ["170:166", 0], "on_true": ["170:165", 0]}, "class_type": "ComfySwitchNode"},
      "170:169": {"inputs": {"seed": 42, "steps": ["170:167", 0], "cfg": ["170:164", 0], "sampler_name": "euler", "scheduler": "simple", "denoise": 1.0, "model": ["170:163", 0], "positive": ["170:148", 0], "negative": ["170:147", 0], "latent_image": ["170:156", 0]}, "class_type": "KSampler"},
      "170:158": {"inputs": {"samples": ["170:169", 0], "vae": ["170:146", 0]}, "class_type": "VAEDecode"},
      "9": {"inputs": {"filename_prefix": "Qwen_Edit_2511", "images": ["170:158", 0]}, "class_type": "SaveImage"}
    },
    "extra_data": {}
  }'

响应:

json
{"prompt_id": "a1b2c3d4-...", "number": 200, "node_errors": {}}

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

使用前需修改:

节点字段修改为
41image输入图片文件名(已上传)
83image参考图片文件名(自我编辑时同 41;换装时用参考图)
170:151prompt你的英文编辑提示词
170:168valuetrue = Turbo 4步 / false = Native 40步
170:169seed任意整数(不同值生成不同结果)

节点说明

节点 IDclass_type功能
41LoadImage加载输入图片(目标编辑图)
83LoadImage加载参考图片(换装模式用服装参考)
170:160FluxKontextImageScale将图片缩放到 VAE 兼容尺寸
170:161UNETLoader加载 Qwen-Image-Edit-2511 主模型
170:162CLIPLoader加载 Qwen-VL 2.5 7B CLIP 文本编码器
170:146VAELoader加载 Qwen Image VAE
170:145ModelSamplingAuraFlow采样偏移调整(shift=3.1)
170:152CFGNormCFG 归一化,稳定训练
170:156VAEEncode编码缩放后的输入图为 latent
170:151TextEncodeQwenImageEditPlus正向提示词多模态编码(prompt + image1 + image2)
170:149TextEncodeQwenImageEditPlus负向提示词多模态编码(空 prompt = 保持原图)
170:148FluxKontextMultiReferenceLatentMethod正向多参考 latent 方法
170:147FluxKontextMultiReferenceLatentMethod负向多参考 latent 方法
170:153LoraLoaderModelOnly加载 Lightning 4步 LoRA 加速器
170:168PrimitiveBooleanTurbo/Native 模式开关
170:163ComfySwitchNode模型切换(LoRA / 原生)
170:164ComfySwitchNodeCFG 切换(1.0 / 4.0)
170:167ComfySwitchNodeSteps 切换(4 / 40)
170:169KSampler采样去噪核心节点
170:158VAEDecodeVAE 解码 latent → 像素图
9SaveImage保存图片为 PNG

工作流连接图

LoadImage(41) ──→ FluxKontextImageScale(170:160) ──→ VAEEncode(170:156) ──→ KSampler(170:169)

LoadImage(83) ──→ TextEncodeQwenImageEditPlus(170:151) ──→ FluxKontextMultiRef(170:148) ──┤
CLIPLoader(170:162) ──→┤                                                                   │
VAELoader(170:146)  ──→┤                                                                   │

LoadImage(83) ──→ TextEncodeQwenImageEditPlus(170:149) ──→ FluxKontextMultiRef(170:147) ───┤

UNETLoader(170:161) → ModelSamplingAuraFlow(170:145) → CFGNorm(170:152) → Switch(170:163) ─┤
                                                           ↓                                │
                                          LoraLoader(170:153) ──────────────────────────────┘

                                                                                   VAEDecode(170:158)

                                                                                     SaveImage(9)

关键参数

节点字段说明示例值
41image输入图片文件名"input.png"
83image参考图片文件名"input.png"(自我编辑)或 "ref.png"(换装)
170:151prompt编辑提示词(英文)"Remove the jacket, keep the T-shirt"
170:168valueTurbo 开关false = Native 40步, true = Turbo 4步
170:169seed随机种子42
9filename_prefix输出文件名前缀"Qwen_Edit_2511"

输出图片参数

  • 分辨率: 与输入图片一致
  • 格式: PNG
  • 输出路径: subfolder=""(空字符串), type=output

Route B: Check Status

查询队列

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

响应:

json
{
  "queue_running": [[200, "a1b2c3d4-...", {...}]],
  "queue_pending": []
}
  • queue_running 不为空 → 任务正在执行
  • queue_pending 不为空 → 任务排队等待

查询任务状态

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

进行中:

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

已完成:

json
{
  "a1b2c3d4-...": {
    "status": {"status_str": "success", "completed": true},
    "outputs": {
      "9": {
        "images": [
          {"filename": "Qwen_Edit_2511_00001_.png", "subfolder": "", "type": "output"}
        ]
      }
    }
  }
}

Turbo 模式 ~2-6 秒,Native 模式 ~15-20 秒。首次运行需加载 Qwen-VL 模型(约 30 秒),后续快。

Route C: Download

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

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

关键点:

  • 图片输出的 subfolder 为空字符串
  • 图片输出的 typeoutput
  • 下载的是 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": (os.path.basename(path), f.read(), "image/png")},
                             data={"overwrite": "true", "type": "input"})
        return os.path.basename(path)

input_name = upload("input.png")
ref_name = upload("ref.png")  # 自我编辑时用 input.png
print(f"Uploaded: {input_name}, {ref_name}")

# 2. 提交编辑工作流
prompt_text = "Remove the jacket, keep the T-shirt underneath"
nodes = {
    "41": {"inputs": {"image": input_name}, "class_type": "LoadImage"},
    "83": {"inputs": {"image": ref_name}, "class_type": "LoadImage"},
    # ... 完整工作流节点,替换 170:151 的 prompt 和 41/83 的 image
}

resp = requests.post(f"{GW}/api/v1/ai/image/generate",
                     json={"prompt": nodes, "extra_data": {}},
                     headers={**headers, "Content-Type": "application/json"})
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("9", {})
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

参数节点建议
编辑提示词170:151 prompt英文效果更稳定;用 image 1/image 2 指代两张图片
负向提示词170:149 prompt保持空字符串 "" = 尽量保持原图不变
Turbo/Native170:168 valuefalse = Native 40步(质量更好); true = Turbo 4步(速度更快)
随机种子170:169 seed不同值生成不同结果;相同种子可复现
采样偏移170:145 shift默认 3.1;增大更锐利,减小更平滑
输出前缀9 filename_prefix自定义输出文件名前缀

提示词策略

自我编辑(单图,图2 = 图1):

text
Remove the jacket, keep the T-shirt underneath
text
Change the background to a sunny beach, keep the person unchanged
text
Replace the red wine glass with a bouquet of flowers
text
Add a red scarf around the neck, keep everything else identical
text
Turn the daytime scene into a night scene with stars

换装(双图):

text
Have the person in image 1 wear the clothes from image 2
text
Apply the makeup style from image 2 to the face in image 1
text
Replace the outfit of the person in image 1 with the outfit in image 2, keep the same pose

Turbo vs Native

模式170:168步数CFG速度质量适用场景
Turbo (Lightning LoRA)true41.0~2-6s快速预览、简单编辑
Nativefalse404.0~15-20s更好精细编辑、最终输出

Route E: Troubleshoot

问题排查方法
上传图片失败检查文件大小(≤50MB)、格式(推荐 PNG)、Bearer Token 是否有效
500 on generateAPI 格式错误,确认用 v1 dict 格式 {"prompt": {...}, "extra_data": {}},不是数组格式
Bad linked input节点连接格式问题,确认用 ["node_id", slot_index] 格式,如 ["41", 0]
504 timeout首次运行需加载 Qwen-VL 模型(约 30s),后续快;如持续超时检查 GPU 状态
工作流提交报 value_not_in_list模型文件名不正确,检查 UNet/CLIP/VAE/LoRA 名称是否与服务器一致
node_errors 非空图片文件未上传成功或节点连接有误
生成结果与原图完全一样提示词可能未被正确编码;检查 170:151 prompt 和 image1/image2 连接
生成结果质量差尝试切换到 Native 模式(170:168 = false);使用更详细的英文提示词
下载图片返回空检查 subfolder 是否为空字符串
401 鉴权失败检查 Bearer Token 是否有效:curl -H "Authorization: Bearer sk-xxx" $GW/api/v1/ai/queue
429 请求被限流AI 接口 10次/分/IP,稍后重试
503 后端服务不可用检查内网 ComfyUI 服务是否运行

模型文件清单

类型文件用途
UNetqwen_image_edit_2511_bf16.safetensorsQwen-Image-Edit 主模型
CLIPqwen_2.5_vl_7b_fp8_scaled.safetensorsQwen-VL 多模态文本编码器
VAEqwen_image_vae.safetensorsQwen Image VAE 解码器
LoRAQwen-Image-Edit-2511-Lightning-4steps-V1.0-bf16.safetensorsTurbo 4步加速器

技术细节

  • Qwen-VL 多模态编码TextEncodeQwenImageEditPlus 同时接收 prompt + image1 + image2,实现图文联合理解
  • FluxKontextMultiReferenceLatentMethod:用于多参考图的 latent 方法,index_timestep_zero 策略在 timestep=0 时注入参考信息
  • CFGNorm:CFG 归一化层,稳定 Flux 架构训练
  • ComfySwitchNode:通过 PrimitiveBoolean 控制 Turbo/Native 三路切换(模型、CFG、步数)
  • 子图前缀 170::避免与顶层节点 ID(41, 83, 9)冲突
  • v1 dict 格式:ComfyUI v0.20.1 使用 {"prompt": {...}, "extra_data": {}} 而非数组格式

内网直连 vs 公网网关

内网直连公网网关
地址192.168.1.236:8188ai.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
API 格式数组 [...]v1 dict {"prompt": {...}, "extra_data": {}}

Verification Checklist

  • [ ] 输入图片上传成功,返回 name
  • [ ] 工作流提交成功,node_errors 为空
  • [ ] 任务在 /api/v1/ai/queue 中执行完成
  • [ ] 任务状态 completed: true,outputs 包含 PNG 文件信息
  • [ ] 成功下载 PNG 文件(注意 subfolder 为空字符串)
  • [ ] 图片可正常查看且编辑效果符合预期

AI API Gateway Documentation