ComfyUI 节点插件开发指南:从零搭建并分发你的第一个自定义节点
ComfyUI 是一款高度模块化的扩散模型 GUI,核心是图/节点式的工作流接口。而 ComfyUI 节点插件开发,就是让你不改一行核心代码,只写一个 Python 文件,就能把任何自定义功能变成工作流里可拖拽、可连线、可保存分享的节点。读完这篇,你可以独立写出第一个节点,并让它像官方节点一样跑进真实工作流。
先想清楚你要解决什么场景
写插件之前,先看看自己卡在哪个环节,场景决定了节点的形态:
- 批量图像调优:官方节点给的是"一个参数一种效果",但你的产线要"先锐化再调对比度再合并"。痛点是流程被锁死在固定节点里 → 插件化解法:写一个多模式增强节点,把整条调优逻辑收进一个节点,工作流一眼看完。
- 接入外部 AI 服务:想用某个云端 API(图像生成、视频生成、3D)却找不到现成节点。痛点是接口分散在各家文档里 → 插件化解法:一个节点封装"配置 → 请求 → 容错"全流程,工作流里只露出一根输入线和一根输出线。
- 自定义工作流逻辑:两个节点之间总要做点胶水逻辑,靠手动连线很繁琐。痛点是重复操作无法沉淀 → 插件化解法:把胶水逻辑固化成节点,整张图保存后别人直接复用。
30 秒看懂扩展机制
ComfyUI 的新式节点 API 集中在 comfy_api/latest 里,骨架只有三件事:
H3 节点的生命周期
- 注册:ComfyUI 启动时扫描
custom_nodes目录,找到文件里的comfy_entrypoint()函数并调用它,拿到你提供的节点类清单。 - 声明输入输出:
define_schema返回一张"节点身份证"——叫什么名、归哪个分类、有哪些输入控件、输出什么类型。前端就是靠它自动画出输入控件。 - 执行:工作流被触发时调用
execute,输入参数按声明顺序作为实参传入。 - 返回结果:把结果包进
NodeOutput返回,下游节点自动接上。
H3 最小骨架代码
下面这个 14 行骨架是一个"图像反色"节点,也是 custom_nodes/example_node.py.example 的精髓:
from comfy_api.latest import ComfyExtension, io
class Invert(io.ComfyNode):
@classmethod
def define_schema(cls):
return io.Schema(
node_id="Invert",
display_name="Invert Image",
category="example",
inputs=[io.Image.Input("image")],
outputs=[io.Image.Output()],
)
@classmethod
def execute(cls, image):
return io.NodeOutput(1.0 - image)
每个概念配一句白话:node_id 是节点的唯一标识(旧工作流靠它找回你的节点);category 决定节点在菜单的哪个分类下;io.Image.Input("image") 声明"我要一张图,参数名就叫 image",execute(cls, image) 里的实参名字必须和它一致。
从零搭建你的第一个扩展
H3 从模板文件起步
在 ComfyUI 根目录执行 git clone https://gitcode.com/GitHub_Trending/co/ComfyUI 拿到仓库(或更新你本地已有的仓库)。然后在 custom_nodes/ 目录下新建 my_first_node.py——这个目录就是插件的"插座",重启 ComfyUI 后会自动加载里面所有能识别的 Python 文件。
H3 写一个完整的示例节点
下面是一个中等复杂度的"图像锐化"节点:带滑杆参数、带取值范围校验,并附上把节点注册进 ComfyUI 所需的入口代码:
from typing_extensions import override
from comfy_api.latest import ComfyExtension, io
class Sharpen(io.ComfyNode):
@classmethod
def define_schema(cls):
return io.Schema(
node_id="MySharpen",
display_name="Sharpen",
category="image/adjust",
inputs=[
io.Image.Input("image"),
io.Float.Input(
"strength", default=1.5, min=0.5, max=3.0,
step=0.1, display_mode=io.NumberDisplay.slider,
tooltip="Sharpening strength",
),
],
outputs=[io.Image.Output(display_name="image")],
)
@classmethod
def execute(cls, image, strength):
blurred = image - image.mean(dim=(-2, -1), keepdim=True)
return io.NodeOutput(image + strength * blurred)
class MyExtension(ComfyExtension):
@override
async def get_node_list(self):
return [Sharpen]
async def comfy_entrypoint() -> MyExtension:
return MyExtension()
几个容易踩的点:min/max/step 会直接约束前端滑杆的行程;tooltip 会显示成控件的悬浮提示;execute 的参数顺序必须和 inputs 声明顺序完全一致。
H3 在界面里验证它真的生效了
保存文件后重启 ComfyUI(或在管理界面点刷新自定义节点)。然后在前端节点菜单里按 Ctrl+K 搜索 "Sharpen"——只要能从分类 image/adjust 下拉出来,说明注册成功。把它拖进画布,左侧会同时出现 image 输入线和 strength 滑杆。接着跑一遍看看效果:随便接一张图进来,点队列执行,输出图会变锐,说明 execute 也通了。
让扩展"真正好用"的几个关键机制
H3 懒加载输入:省掉没必要的计算
这个机制解决"上游参数还没被用到就先算了一遍"的浪费——延迟求值,即系统先判断到底需不需要真的去算这个参数,不需要就跳过。
class PrintToScreen(io.ComfyNode):
# 声明时标记 lazy=True
inputs = [io.String.Input("text", lazy=True),
io.Combo.Input("mode", options=["enable", "disable"])]
@classmethod
def check_lazy_status(cls, text, mode):
return ["text"] if mode == "enable" else []
不处理会怎样:每次运行工作流,text 都会无条件被计算,哪怕这个开关根本没开,白白消耗上游算力。
H3 输入指纹:精确控制重跑时机
这个机制解决"输入其实没变,节点却被反复重新执行"的重复劳动——指纹值变了才重跑。
@classmethod
def fingerprint_inputs(cls, image, strength):
# 图像哈希 + 强度,两者都没变就沿用缓存结果
return f"{image.sum().item()}_{strength}"
不处理会怎样:节点只能靠"上游输出变了"来触发重跑,你自己内部的执行逻辑(比如依赖外部状态)变化时它永远不重跑,结果停在旧值上。
H3 异步执行:别把界面卡死
这个机制解决"网络请求阻塞整个事件循环"的卡死问题——execute 可以声明成 async。
@classmethod
async def execute(cls, prompt):
image = await call_external_api(prompt) # 等待期间不阻塞其他任务
comfy_api.ExecutionSync.set_progress(0.5, 1.0)
return io.NodeOutput(image)
不处理会怎样:一次两分钟的云端生成会把整台 ComfyUI 的事件循环挂住,其他队列任务全部排队干等,界面看起来像死机。
对接外部生态与分发
H3 接入外部 API 的四步模式
comfy_api_nodes/ 目录是集成的样板间,所有 API 节点都走同一条路:
- 配置:把密钥、端点做成输入或走统一代理,不硬编码。
- 请求:
async发出请求,上传大文件时用工具函数处理编码。 - 容错:超时、限流、格式错误都有明确的异常分支,别让用户看到裸 traceback。
- 回调:用
set_progress回报进度,长任务里用户看得见你在干嘛。
仓库里已覆盖的主流服务:
| 服务类型 | 对应模块路径 | 适用场景 |
|---|---|---|
| 多模态生成 | comfy_api_nodes/nodes_gemini.py | 图像生成、图文理解 |
| 3D 模型 | comfy_api_nodes/nodes_tripo.py | 文本/图片转 3D |
| 视频生成 | comfy_api_nodes/nodes_luma.py | 文生视频、图生视频 |
| 语音合成 | comfy_api_nodes/nodes_elevenlabs.py | 文本转语音 |
| 视频 API | comfy_api_nodes/nodes_kling.py | 可灵视频生成 |
H3 让别人找到并使用你的扩展
分发一个节点其实只有四件事:README 里放"安装命令 + 一张工作流截图 + 节点菜单入口的截图",用户 10 秒内能决定要不要用;版本号走语义化(破坏输入输出兼容就升大版本,加参数升小版本);display_name 和 category 起得像人话,这是节点在菜单里最容易被搜到的方式;最后到社区渠道发一个"带示例工作流 JSON"的介绍,比纯文字说明有说服力得多。
动手清单
- 克隆仓库:
git clone https://gitcode.com/GitHub_Trending/co/ComfyUI,确认custom_nodes/目录存在。 - 照模板建文件:把 custom_nodes/example_node.py.example 复制成
custom_nodes/my_first_node.py,改类名、node_id和execute里的逻辑。 - 重启 ComfyUI,在节点菜单搜你的名字,连线、执行,确认输出正确。
- 翻 comfy_extras/ 里的官方节点找灵感——每个文件都是生产级的写法示范。
- 把节点打包发布:补上 README、语义化版本号,发到社区渠道并附上示例工作流。
跑通第一个节点的那一刻,你会明白这套扩展机制有多顺手——而你的工作流,从此也能长出别人没有的节点。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考





