基本原理
我们将通过 API,从“外部”执行 ComfyUI 的 工作流。
要做的事情很简单,只需将 API 格式的 JSON(= prompt)POST 到 http://127.0.0.1:8188/prompt 即可。
这样 ComfyUI 服务器就会接收到“执行该 工作流”的指令,并将其放入队列执行,最后返回 prompt_id(执行 ID)。
稍微有点绕的是,这里出现的“prompt”并不是文本提示词,而是指 整个工作流(执行图表)。
文本只是其中的一部分,例如 CLIPTextEncode 的 inputs.text 等就属于这部分。
这次,我们试着用 Python 来执行。
预先启动 ComfyUI
API 并不是“让你无需启动 ComfyUI 的机制”。
Python 是向正在运行的 ComfyUI 服务器发送指令。
- 请预先启动 ComfyUI
- 确保浏览器能打开
http://127.0.0.1:8188
准备 API 用的工作流
这次使用的工作流(SD1.5 text2image)
首先,我们使用最简单的 Stable Diffusion 1.5 的 text2image。
SD1.5_text2image_vae-ft-mse-840000.json
{
"id": "8b9f7796-0873-4025-be3c-0f997f67f866",
"revision": 0,
"last_node_id": 10,
"last_link_id": 10,
"nodes": [
{
"id": 8,
"type": "VAEDecode",
"pos": [
1209,
188
],
"size": [
210,
46
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": 7
},
{
"name": "vae",
"type": "VAE",
"link": 10
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"slot_index": 0,
"links": [
9
]
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.33",
"Node name for S&R": "VAEDecode"
},
"widgets_values": []
},
{
"id": 3,
"type": "KSampler",
"pos": [
863,
186
],
"size": [
315,
262
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": 1
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 4
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 6
},
{
"name": "latent_image",
"type": "LATENT",
"link": 2
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"slot_index": 0,
"links": [
7
]
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.33",
"Node name for S&R": "KSampler"
},
"widgets_values": [
1234,
"fixed",
20,
8,
"euler",
"normal",
1
]
},
{
"id": 9,
"type": "SaveImage",
"pos": [
1451,
189
],
"size": [
354.2876035004722,
433.23967321788405
],
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 9
}
],
"outputs": [],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.33"
},
"widgets_values": [
"ComfyUI"
]
},
{
"id": 6,
"type": "CLIPTextEncode",
"pos": [
415,
186
],
"size": [
411.95503173828126,
151.0030493164063
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 3
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"slot_index": 0,
"links": [
4
]
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.33",
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"beautiful scenery nature glass bottle landscape, , purple galaxy bottle,"
]
},
{
"id": 7,
"type": "CLIPTextEncode",
"pos": [
416.1970166015625,
392.37848510742185
],
"size": [
410.75801513671877,
158.82607910156253
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 5
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"slot_index": 0,
"links": [
6
]
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.33",
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"text, watermark"
]
},
{
"id": 5,
"type": "EmptyLatentImage",
"pos": [
582.1350317382813,
606.5799999999999
],
"size": [
244.81999999999994,
106
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"slot_index": 0,
"links": [
2
]
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.33",
"Node name for S&R": "EmptyLatentImage"
},
"widgets_values": [
512,
512,
1
]
},
{
"id": 10,
"type": "VAELoader",
"pos": [
896.9256198347109,
69.4815990090115
],
"size": [
281.0743801652891,
58
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "VAE",
"type": "VAE",
"links": [
10
]
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.76",
"Node name for S&R": "VAELoader"
},
"widgets_values": [
"vae-ft-mse-840000-ema-pruned.safetensors"
],
"color": "#322",
"bgcolor": "#533"
},
{
"id": 4,
"type": "CheckpointLoaderSimple",
"pos": [
38.10000000000001,
363.8900000000004
],
"size": [
315,
98
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"slot_index": 0,
"links": [
1
]
},
{
"name": "CLIP",
"type": "CLIP",
"slot_index": 1,
"links": [
3,
5
]
},
{
"name": "VAE",
"type": "VAE",
"slot_index": 2,
"links": []
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.33",
"Node name for S&R": "CheckpointLoaderSimple"
},
"widgets_values": [
"v1-5-pruned-emaonly-fp16.safetensors"
]
}
],
"links": [
[
1,
4,
0,
3,
0,
"MODEL"
],
[
2,
5,
0,
3,
3,
"LATENT"
],
[
3,
4,
1,
6,
0,
"CLIP"
],
[
4,
6,
0,
3,
1,
"CONDITIONING"
],
[
5,
4,
1,
7,
0,
"CLIP"
],
[
6,
7,
0,
3,
2,
"CONDITIONING"
],
[
7,
3,
0,
8,
0,
"LATENT"
],
[
9,
8,
0,
9,
0,
"IMAGE"
],
[
10,
10,
0,
8,
1,
"VAE"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 0.9090909090909091,
"offset": [
61.89999999999999,
30.518400990988496
]
},
"frontendVersion": "1.34.6",
"VHS_latentpreview": false,
"VHS_latentpreviewrate": 0,
"VHS_MetadataImage": true,
"VHS_KeepIntermediate": true
},
"version": 0.4
}
下载模型
📂ComfyUI/
└── 📂models/
├── 📂checkpoints/
│ └── v1-5-pruned-emaonly-fp16.safetensors
└── 📂vae/
└── vae-ft-mse-840000-ema-pruned.safetensors
获取 API 用的工作流
-
- 在 ComfyUI 的节点 UI 中,打开上述 工作流
-
- 从菜单中选择
File → Export (API)
-
- 以易懂的名称保存(例:SD1.5_text2image_API.json)
示例
SD1.5_text2image_API.json
{
"3": {
"inputs": {
"seed": 1234,
"steps": 20,
"cfg": 8,
"sampler_name": "euler",
"scheduler": "normal",
"denoise": 1,
"model": [
"4",
0
],
"positive": [
"6",
0
],
"negative": [
"7",
0
],
"latent_image": [
"5",
0
]
},
"class_type": "KSampler",
"_meta": {
"title": "KSampler"
}
},
"4": {
"inputs": {
"ckpt_name": "v1-5-pruned-emaonly-fp16.safetensors"
},
"class_type": "CheckpointLoaderSimple",
"_meta": {
"title": "Load Checkpoint"
}
},
"5": {
"inputs": {
"width": 512,
"height": 512,
"batch_size": 1
},
"class_type": "EmptyLatentImage",
"_meta": {
"title": "Empty Latent Image"
}
},
"6": {
"inputs": {
"text": "beautiful scenery nature glass bottle landscape, , purple galaxy bottle,",
"clip": [
"4",
1
]
},
"class_type": "CLIPTextEncode",
"_meta": {
"title": "CLIP Text Encode (Prompt)"
}
},
"7": {
"inputs": {
"text": "text, watermark",
"clip": [
"4",
1
]
},
"class_type": "CLIPTextEncode",
"_meta": {
"title": "CLIP Text Encode (Prompt)"
}
},
"8": {
"inputs": {
"samples": [
"3",
0
],
"vae": [
"10",
0
]
},
"class_type": "VAEDecode",
"_meta": {
"title": "VAE Decode"
}
},
"9": {
"inputs": {
"filename_prefix": "ComfyUI",
"images": [
"8",
0
]
},
"class_type": "SaveImage",
"_meta": {
"title": "Save Image"
}
},
"10": {
"inputs": {
"vae_name": "vae-ft-mse-840000-ema-pruned.safetensors"
},
"class_type": "VAELoader",
"_meta": {
"title": "Load VAE"
}
}
}
与普通工作流 JSON 的区别
普通的 JSON 包含了“便于在 UI 中编辑和分享的信息”。
API 的 工作流 则是去除了这些多余信息,变成了方便服务器接收并执行的形式。
首先让它跑起来
Python 环境准备
创建虚拟环境,并安装用于 HTTP 通信的 requests。
python -m venv venv
venv\Scripts\activate
pip install requests
run_min.py
这是读取 SD1.5_text2image_API.json 并投递给 /prompt 的最小代码。
这与在节点 UI 中按下 ▷Run 基本相同。
import json
import requests
BASE = "http://127.0.0.1:8188"
prompt = json.load(open("SD1.5_text2image_API.json", encoding="utf-8"))
res = requests.post(f"{BASE}/prompt", json={"prompt": prompt}).json()
print(res)
文件放置
your_project/
├── run_min.py
└── SD1.5_text2image_API.json
执行
在终端执行 run_min.py。
cd path\to\your_project
venv\Scripts\activate
python run_min.py
让我们确认一下是否执行成功。
- Python 侧返回
{"prompt_id": "...", ...} (这就是执行 ID。)
- ComfyUI 侧的终端显示执行日志
ComfyUI/output/ 中增加了图像文件
如果到这里都能成功,说明已经可以通过 API 执行了。
从 CLI 更改提示词
即使直接编辑 API 用的 JSON,当然也可以更改参数。
但是,如果想在不弄脏原文件的情况下连续执行,用 Python 进行替换 会更容易处理。
要重写哪里(以本次的 JSON 为例)
这次我们只更改“提示词”。
- Positive prompt:节点
6 的 inputs.text
注:节点 ID 不是固定的
本页面的示例使用了 6,但这 仅仅是因为在这个分发的 JSON 中是这样。
如果是用自己的 工作流,最稳妥的方法是打开 JSON 寻找。
e.g. SD1.5_text2image_API.json
"6": {
"inputs": {
"text": "beautiful scenery nature glass bottle landscape, , purple galaxy bottle,",
"clip": [
"4",
1
]
},
"class_type": "CLIPTextEncode",
"_meta": {
"title": "CLIP Text Encode (Prompt)"
}
},
run.py(输入提示词)
import json
import requests
BASE = "http://127.0.0.1:8188"
prompt = json.load(open("SD1.5_text2image_API.json", encoding="utf-8"))
pos = input("positive prompt: ").strip()
if pos:
prompt["6"]["inputs"]["text"] = pos
res = requests.post(f"{BASE}/prompt", json={"prompt": prompt}).json()
print(res)
print("done (check ComfyUI/output)")
执行
与刚才一样执行。
cd path\to\your_project
venv\Scripts\activate
python run.py
这次中途会要求输入参数。
positive prompt: 输入喜欢的提示词
如果该提示词生成的图像保存在了 ComfyUI/output/ 中,那就 OK 了。
掌握了感觉后,剩下的就只是制作了
这次只使用了“执行”的 API,但 API 还有很多其他功能。
- 实时接收进度(WebSocket)
- 队列控制(停止、中断等)
- 图像上传(传递给 i2i 的输入)
- 获取节点的输入规格(自动化 工作流 编辑的立足点)
在这个页面,只要掌握了“能从外部执行 工作流”的感觉就足够了。
剩下的不管是 vibe coding 还是什么,请试着实际制作你想要的东西吧!