ComfyUI 工作流怎么打包成 API:教程、排障和 FAQ

介绍如何把 ComfyUI 工作流打包成可调用 API,覆盖工作流导出、参数替换、prompt 提交、结果轮询、图片下载、服务部署和常见报错排查。

ComfyUI 很适合搭建 AI 绘图、换脸、放大、局部重绘和视频生成工作流。但如果只停留在 Web 界面里拖节点,它更像一个创作工具;如果想接入网站、Bot、后台任务或企业流程,就需要把工作流打包成 API。

这篇按“教程 + 排障 + FAQ”整理,目标是把一个能在 ComfyUI 里手动运行的工作流,变成后端可以提交任务、等待结果、下载图片的接口。重点不在写一个复杂平台,而是先跑通最小闭环。

先理解 ComfyUI 的 API 化思路

ComfyUI 的 API 调用本质上不是“把节点变成函数”,而是把整个工作流 JSON 作为 prompt 提交给 ComfyUI 后端。后端按节点图执行,生成结果后,你再通过 history 或文件接口拿输出。

最小链路是:

  1. 在 ComfyUI Web 界面调好工作流。
  2. 导出 API 格式的 workflow JSON。
  3. 在后端代码里替换 prompt、图片、尺寸、seed 等输入。
  4. 调用 /prompt 提交任务。
  5. 根据 prompt_id 查询 /history
  6. 读取输出图片或视频文件。

只要这个链路跑通,就可以继续封装成你自己的 REST API、队列任务或 SaaS 功能。

准备一个稳定工作流

不要一开始就拿几十个自定义节点的复杂工作流做 API 化。先选一个能稳定出图的基础 workflow,例如:

  • 文生图。
  • 图生图。
  • 图片放大。
  • 局部重绘。
  • 固定风格海报。

先在 Web UI 里确认几件事:

  • 模型文件已经下载。
  • 自定义节点没有缺失。
  • 输入图片路径能正常读取。
  • 单次运行能生成结果。
  • 输出节点能保存图片。

如果手动都跑不通,API 调用只会把问题隐藏得更深。

导出 API 格式 workflow

在 ComfyUI 界面里,普通保存的 workflow 和 API 调用需要的 prompt JSON 不完全一样。通常要开启开发者相关选项,然后导出 API 格式。

你要拿到的 JSON 大致长这样:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
{
  "3": {
    "class_type": "KSampler",
    "inputs": {
      "seed": 123456,
      "steps": 20,
      "cfg": 7,
      "sampler_name": "euler",
      "scheduler": "normal"
    }
  },
  "6": {
    "class_type": "CLIPTextEncode",
    "inputs": {
      "text": "a cat sitting on a desk"
    }
  }
}

每个数字 key 是节点 ID。API 化时,你要修改的通常就是这些节点的 inputs

找到需要替换的节点

导出后先不要急着写代码。打开 JSON,标记哪些字段要由用户输入控制。

常见需要替换的字段:

用途 常见节点 常见字段
正向提示词 CLIPTextEncode text
反向提示词 CLIPTextEncode text
随机种子 KSampler seed
步数 KSampler steps
尺寸 EmptyLatentImage widthheight
输入图片 LoadImage image
输出文件 SaveImage filename_prefix

建议在项目里维护一个映射表,例如:

1
2
3
4
5
6
7
{
  "positive_prompt_node": "6",
  "negative_prompt_node": "7",
  "sampler_node": "3",
  "latent_node": "5",
  "save_node": "9"
}

这样后续 workflow 节点调整时,只需要改映射,不用在业务代码里到处找节点 ID。

用 Python 提交 prompt

下面是一个最小示例:读取 workflow JSON,替换提示词,然后提交给本地 ComfyUI。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import json
import urllib.request
import uuid

COMFYUI_URL = "http://127.0.0.1:8188"


def queue_prompt(prompt):
    payload = {
        "prompt": prompt,
        "client_id": str(uuid.uuid4())
    }
    data = json.dumps(payload).encode("utf-8")
    req = urllib.request.Request(
        f"{COMFYUI_URL}/prompt",
        data=data,
        headers={"Content-Type": "application/json"}
    )
    with urllib.request.urlopen(req) as resp:
        return json.loads(resp.read().decode("utf-8"))


with open("workflow_api.json", "r", encoding="utf-8") as f:
    workflow = json.load(f)

workflow["6"]["inputs"]["text"] = "a clean product photo of a white sneaker"
workflow["7"]["inputs"]["text"] = "blurry, low quality, text watermark"
workflow["3"]["inputs"]["seed"] = 123456789

result = queue_prompt(workflow)
print(result)

成功后通常会返回 prompt_id。后续就靠它查询结果。

查询任务结果

提交任务后,ComfyUI 不会马上把图片直接返回给你。需要查询 history:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import json
import time
import urllib.request


def get_history(prompt_id):
    with urllib.request.urlopen(f"{COMFYUI_URL}/history/{prompt_id}") as resp:
        return json.loads(resp.read().decode("utf-8"))


prompt_id = result["prompt_id"]

for _ in range(60):
    history = get_history(prompt_id)
    if prompt_id in history:
        print(json.dumps(history[prompt_id], indent=2, ensure_ascii=False))
        break
    time.sleep(1)
else:
    raise TimeoutError("ComfyUI task timed out")

history 里会包含输出节点和文件信息。你需要从里面找到 filenamesubfoldertype 等字段,再调用 /view 下载。

下载输出图片

一个常见下载方式是:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from urllib.parse import urlencode


def download_image(filename, subfolder="", folder_type="output"):
    params = urlencode({
        "filename": filename,
        "subfolder": subfolder,
        "type": folder_type
    })
    url = f"{COMFYUI_URL}/view?{params}"
    with urllib.request.urlopen(url) as resp:
        return resp.read()

拿到 bytes 后可以写入本地文件、上传对象存储,或者返回给你的业务 API。

封装成自己的 REST API

实际项目里,不建议让前端直接调用 ComfyUI。更稳的结构是:

1
2
3
4
5
6
7
8
9
用户前端
你的业务 API
任务队列
ComfyUI API
对象存储 / 本地输出目录

业务 API 负责:

  • 校验用户参数。
  • 控制尺寸、步数、模型和风格。
  • 限制并发和频率。
  • 管理任务状态。
  • 返回最终图片 URL。

ComfyUI 只负责执行生成,不直接暴露给公网。

常见报错:连接不上 ComfyUI

典型表现:

1
2
connection refused
failed to connect to 127.0.0.1:8188

排查顺序:

  1. ComfyUI 是否正在运行。
  2. 端口是否是 8188
  3. 后端代码和 ComfyUI 是否在同一台机器。
  4. Docker、WSL 或远程服务器里 localhost 是否指向正确。
  5. 防火墙是否拦截端口。

如果后端跑在容器里,127.0.0.1 指的是容器本身,不是宿主机。需要改成宿主机地址或使用 Docker 网络服务名。

常见报错:prompt 格式不对

典型表现:

1
2
invalid prompt
prompt outputs failed validation

常见原因:

  • 使用了普通 workflow JSON,而不是 API 格式 JSON。
  • 节点 ID 写错。
  • 删除了某个必需输入。
  • 输入类型不匹配,例如把数字写成字符串。
  • 自定义节点缺失,导致 class_type 无法识别。

排查方法:

  • 先用原始导出的 JSON 提交,不做任何替换。
  • 原始 JSON 能跑,再逐个替换字段。
  • 对照 Web UI 中的节点 ID 和字段。
  • 看 ComfyUI 控制台的详细报错。

不要一次替换十几个字段。每次只改一个变量,最容易定位。

常见报错:找不到模型或自定义节点

典型表现:

1
2
3
CheckpointLoaderSimple: ckpt_name not found
Cannot import custom node
class_type not found

原因通常是 API 服务器环境和你导出 workflow 的环境不一致。

检查这些目录:

  • models/checkpoints
  • models/loras
  • models/vae
  • models/controlnet
  • custom_nodes

如果在开发机能跑,部署机不能跑,优先检查模型文件名、自定义节点版本和依赖包。

常见报错:输入图片读取失败

图生图、换脸、局部重绘经常遇到图片路径问题。

注意几点:

  • LoadImage 通常读取 ComfyUI 输入目录里的文件。
  • 直接传本机任意路径不一定可用。
  • API 调用前可能需要先上传图片到 ComfyUI。
  • Windows 和 Linux 路径格式不同。

更稳的做法是把用户图片上传到 ComfyUI 的 input 目录或使用上传接口,再把返回的文件名写入 LoadImage 节点。

常见报错:任务一直排队

如果 /prompt 返回了 prompt_id,但 history 一直没有结果,可能是:

  • GPU 正在执行其他任务。
  • 某个节点卡住。
  • 模型加载非常慢。
  • 显存不足导致反复切换。
  • ComfyUI 后端已经报错但业务端没读取到。

建议做:

  • 查看 ComfyUI 控制台日志。
  • 给业务侧轮询加超时。
  • 限制并发任务数量。
  • 对大图、视频、高清修复设置更长但明确的超时。
  • 失败后返回可读错误,而不是让用户一直等。

常见报错:显存不足

典型表现:

1
2
CUDA out of memory
Allocation failed

处理方法:

  • 降低分辨率。
  • 减少 batch size。
  • 减少 steps。
  • 换更小模型。
  • 关闭不必要的 ControlNet、LoRA 或高清修复。
  • 对不同工作流设置不同队列。

API 化以后更要控制用户输入。不要让用户随便传 4096 x 4096、100 steps、多个 LoRA 叠加,否则服务很容易被打满。

常见问题:结果文件找不到

如果 history 里有文件名,但下载时报 404,常见原因是:

  • filenamesubfoldertype 参数传错。
  • 输出节点不是 SaveImage
  • 文件保存到临时目录。
  • 任务失败,history 里没有真正输出。
  • 多实例部署时,请求到了另一台 ComfyUI。

排查时先打印 history 的 outputs,不要凭记忆拼 URL。

上线前要做的保护

把 ComfyUI 包成 API 后,不要裸奔上线。至少要考虑:

  • 鉴权。
  • 频率限制。
  • 并发限制。
  • 输入尺寸限制。
  • prompt 长度限制。
  • 禁止用户选择任意模型路径。
  • 输出文件清理。
  • 失败重试和超时。
  • 任务状态持久化。

ComfyUI 很适合做生成引擎,但业务层要自己补上权限、队列、审计和资源控制。

FAQ

ComfyUI 自带 API 吗?

有。常用的是 /prompt/history/{prompt_id}/view 这类接口。实际项目通常会在外面再包一层业务 API,避免直接暴露 ComfyUI。

为什么导出的 workflow 不能直接调用?

可能导出的是普通 workflow,不是 API 格式;也可能缺模型、自定义节点,或者某些输入路径只在 Web UI 当前环境里有效。先用原始 API JSON 测试,再逐步替换参数。

前端可以直接调用 ComfyUI 吗?

不建议。前端直接调用会暴露服务地址,也不好做鉴权、限流和参数控制。更稳的是前端调用你的后端,后端再调用 ComfyUI。

多用户并发怎么处理?

不要让所有请求直接打到 ComfyUI。建议加任务队列,限制并发数,按任务状态轮询。高成本工作流可以单独队列。

输出图片应该存在哪里?

开发阶段可以存在 ComfyUI output 目录。上线后更建议上传到对象存储或业务文件服务,再返回稳定 URL,同时定期清理本地临时文件。

节点 ID 变了怎么办?

如果重新编辑 workflow,节点 ID 可能变化。建议维护一份参数映射,并在每次更新 workflow 后跑一次最小 API 测试。

可以把多个 workflow 做成一个 API 吗?

可以,但不要让用户直接传任意 workflow JSON。更安全的做法是后端维护白名单模板,用户只能选择模板和有限参数。

视频工作流和图片工作流有什么区别?

视频工作流通常耗时更长、显存压力更大、输出文件更大,必须加更严格的队列、超时和文件管理。先把图片链路跑通,再扩展视频。

小结

把 ComfyUI 工作流打包成 API,本质是把稳定的节点图变成可替换参数的 prompt JSON,再通过 /prompt 提交任务,通过 /history 查询结果,通过 /view 获取文件。真正的难点不在发请求,而在工作流稳定性、参数边界、队列并发、文件管理和错误处理。

建议先从一个最简单的文生图 workflow 开始,跑通导出、提交、轮询、下载四步。等最小闭环稳定后,再加入上传图片、风格模板、用户队列和生产级保护。

记录并分享
使用 Hugo 构建
主题 StackJimmy 设计