ComfyUI把所有节点、连线和参数都序列化成一个JSON对象。当你点击保存时,界面上有两种常见选项:一个是普通的保存工作流,另一个是导出API格式。很多人以为只是文件后缀不同,其实两者在字段结构和使用场景上有明显区别。搞清楚这一点,对接下来的自动化流程会少走很多弯路。

在ComfyUI的界面里,每个节点都是一个对象,保存后的JSON大致由三个部分组成:节点列表、连接列表以及节点所需的额外信息。比如一个最简的文生图工作流,会包含Checkpoint加载器、CLIP文本编码器、采样器、VAE解码器和保存图像节点。JSON中的nodes字段通常是以节点ID为键的对象,每个对象包含type、pos、size、flags、order、mode、inputs、outputs、properties以及widgets_values等字段。
一、ComfyUI工作流JSON的基本结构
其中type是节点类型标识,pos和size决定节点在画布上的位置和大小,widgets_values则是节点上各个控件当前的参数值。例如CLIP文本编码器节点的widgets_values里就保存了正向提示词和负向提示词。而inputs和outputs数组记录了该节点与其他节点之间的连接关系,包括连接到的节点ID、插槽索引和连接类型。理解了这些字段,再去看API格式JSON就会轻松很多。
普通保存的JSON还附带了一些ComfyUI界面特有的信息,比如节点的折叠状态、分组颜色、注释框等。这些信息对界面还原很有用,但在自动化调用时反而是冗余负担,甚至可能因为版本差异导致加载失败。下面是一个简化后的普通格式JSON片段,可以看到节点坐标和widgets_values的原始位置。
{
"nodes": {
"1": {
"type": "CheckpointLoaderSimple",
"pos": [10, 10],
"size": [300, 100],
"widgets_values": ["v1-5-pruned-emaonly.safetensors"]
},
"2": {
"type": "CLIPTextEncode",
"pos": [350, 10],
"widgets_values": ["a cat", "low quality"]
}
},
"links": [[1, 1, 2, 0, "MODEL"]]
}
这个JSON结构里,nodes对象的键1和2就是节点ID,widgets_values数组按照控件在节点面板中的顺序存放具体数值。links数组中的每一项是一个连接描述,第一项是连接ID,第二项是源节点ID,第三项是源节点输出插槽索引,第四项是目标节点ID,第五项是目标节点输入插槽索引,最后一项是连接的数据类型。这种记录方式非常直观,但也依赖界面布局信息。
二、普通保存与API格式保存的核心差异
打开ComfyUI,在界面右侧或菜单里可以看到保存和导出API格式两个选项。普通保存生成的JSON用于在图形界面中重新加载,它会保留节点坐标、连线路径、分组等信息,确保下次打开时画布布局与原来一致。但这种格式常常会因为ComfyUI版本更新、自定义节点缺失或界面设置不同而出现兼容性问题。
API格式则更贴近执行引擎的实际输入。它同样是一个JSON对象,但通常不会包含pos、size、links等UI相关字段,而是把每个节点的输入参数直接展开。具体来说,API格式会把节点的widgets_values里的参数转换成以输入名称命名的字段,例如把CLIP文本编码器的两个文本输入变成text和negative_prompt字段。如果某个输入来自其他节点的输出,则会用数组形式表示,第一项是源节点ID,第二项是输出插槽索引。
这样做的好处是API格式更稳定、更轻量,适合通过HTTP请求提交。在ComfyUI的API文档中,提交到/api/prompt端点的prompt参数就是这种API格式。你可以先把工作流在界面上调通,再导出API格式保存,之后就可以用任何支持HTTP的客户端重复调用。以下是一个简化后的API格式JSON示例。
{
"3": {
"class_type": "CLIPTextEncode",
"inputs": {
"text": "a cat on the grass",
"clip": ["1", 1]
}
},
"2": {
"class_type": "CheckpointLoaderSimple",
"inputs": {
"ckpt_name": "v1-5-pruned-emaonly.safetensors"
}
}
}
对比普通格式可以发现,节点ID仍然作为对象键存在,但每个节点内部改用了class_type来表示节点类型,参数则直接放在inputs对象里。这里的clip字段用数组["1", 1]表示它接收到来自节点1的第1个输出,数据类型为CLIP。这种格式省略了所有画布坐标和连线样式,因此文件体积更小,也更容易被程序化处理。
三、如何通过API提交保存的JSON工作流
ComfyUI启动后默认监听在8188端口,你可以通过POST请求把API格式的JSON提交到/api/prompt。请求体是一个JSON对象,其中prompt字段对应工作流数据,client_id字段用于标识客户端,便于后续查询进度。返回结果中会包含一个prompt_id,通过这个ID可以轮询历史记录接口获取生成结果。
使用Python的requests库可以很方便地完成这一过程。下面的示例展示了如何读取本地保存的API格式JSON文件并提交。注意提交前要确保所有节点类型在你的ComfyUI环境中都存在,尤其是第三方自定义节点。如果某些节点缺失,服务端会返回节点类型无效的错误。
import json
import requests
with open("workflow_api.json", "r", encoding="utf-8") as f:
workflow = json.load(f)
payload = {
"prompt": workflow,
"client_id": "my-client-001"
}
resp = requests.post("http://127.0.0.1:8188/api/prompt", json=payload)
print(resp.json())
如果想同步等待图片生成完成,可以使用WebSocket监听进度,或者轮询/api/history/加prompt_id的接口。历史记录里会返回输出图片的文件名、子文件夹和类型。这些图片默认保存在ComfyUI的output目录下。对于更复杂的自动化任务,还可以结合队列管理和自定义节点来实现批量处理。
四、参数保存中的常见问题与最佳实践
保存工作流时最容易踩的坑是节点ID冲突。普通保存的JSON中节点ID是画布内的唯一标识,但在API格式中,这些ID会直接作为对象键。如果你手动合并多个工作流或者复制节点,可能会产生重复ID。虽然ComfyUI界面通常会自动更新ID,但在编辑JSON文件时就需要格外小心,最好在合并后重新分配不冲突的ID。
另一个常见问题是控件值缺失。有些自定义节点在API格式中可能不会完整暴露所有参数,特别是那些依赖于前端状态或文件选择器的参数。导出API格式后,务必检查每个节点的inputs字段是否包含了你需要的所有输入。如果发现缺失,可以手动补上,或者回到界面中调整节点后再重新导出。对于文件路径类参数,注意使用相对于ComfyUI根目录的路径,而不是绝对路径,这样工作流在不同机器间迁移时更稳定。
版本管理方面,建议把普通格式JSON和API格式JSON分开存放。普通格式用于团队协作和界面分享,API格式用于自动化部署。可以使用Git管理这些文件,并在提交时附上ComfyUI版本和自定义节点列表,方便追溯问题。最后,定期测试保存的工作流能否在干净环境中加载,是避免临近交付时才发现兼容性问题的有效手段。