ComfyUI 节点插件开发指南:从零搭建并分发你的第一个自定义节点

ComfyUI 节点插件开发指南:从零搭建并分发你的第一个自定义节点

【免费下载链接】ComfyUI The most powerful and modular diffusion model GUI, api and backend with a graph/nodes interface. 【免费下载链接】ComfyUI 项目地址: https://gitcode.com/GitHub_Trending/co/ComfyUI

ComfyUI 是一款高度模块化的扩散模型 GUI,核心是图/节点式的工作流接口。而 ComfyUI 节点插件开发,就是让你不改一行核心代码,只写一个 Python 文件,就能把任何自定义功能变成工作流里可拖拽、可连线、可保存分享的节点。读完这篇,你可以独立写出第一个节点,并让它像官方节点一样跑进真实工作流。

先想清楚你要解决什么场景

写插件之前,先看看自己卡在哪个环节,场景决定了节点的形态:

  • 批量图像调优:官方节点给的是"一个参数一种效果",但你的产线要"先锐化再调对比度再合并"。痛点是流程被锁死在固定节点里 → 插件化解法:写一个多模式增强节点,把整条调优逻辑收进一个节点,工作流一眼看完。
  • 接入外部 AI 服务:想用某个云端 API(图像生成、视频生成、3D)却找不到现成节点。痛点是接口分散在各家文档里 → 插件化解法:一个节点封装"配置 → 请求 → 容错"全流程,工作流里只露出一根输入线和一根输出线。
  • 自定义工作流逻辑:两个节点之间总要做点胶水逻辑,靠手动连线很繁琐。痛点是重复操作无法沉淀 → 插件化解法:把胶水逻辑固化成节点,整张图保存后别人直接复用。

30 秒看懂扩展机制

ComfyUI插件开发中的输入参数配置界面

ComfyUI 的新式节点 API 集中在 comfy_api/latest 里,骨架只有三件事:

H3 节点的生命周期

  1. 注册:ComfyUI 启动时扫描 custom_nodes 目录,找到文件里的 comfy_entrypoint() 函数并调用它,拿到你提供的节点类清单。
  2. 声明输入输出define_schema 返回一张"节点身份证"——叫什么名、归哪个分类、有哪些输入控件、输出什么类型。前端就是靠它自动画出输入控件。
  3. 执行:工作流被触发时调用 execute,输入参数按声明顺序作为实参传入。
  4. 返回结果:把结果包进 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 也通了。

ComfyUI插件节点输出示例图像

让扩展"真正好用"的几个关键机制

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 节点都走同一条路:

  1. 配置:把密钥、端点做成输入或走统一代理,不硬编码。
  2. 请求async 发出请求,上传大文件时用工具函数处理编码。
  3. 容错:超时、限流、格式错误都有明确的异常分支,别让用户看到裸 traceback。
  4. 回调:用 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文本转语音
视频 APIcomfy_api_nodes/nodes_kling.py可灵视频生成

H3 让别人找到并使用你的扩展

分发一个节点其实只有四件事:README 里放"安装命令 + 一张工作流截图 + 节点菜单入口的截图",用户 10 秒内能决定要不要用;版本号走语义化(破坏输入输出兼容就升大版本,加参数升小版本);display_namecategory 起得像人话,这是节点在菜单里最容易被搜到的方式;最后到社区渠道发一个"带示例工作流 JSON"的介绍,比纯文字说明有说服力得多。

动手清单

  1. 克隆仓库:git clone https://gitcode.com/GitHub_Trending/co/ComfyUI,确认 custom_nodes/ 目录存在。
  2. 照模板建文件:把 custom_nodes/example_node.py.example 复制成 custom_nodes/my_first_node.py,改类名、node_idexecute 里的逻辑。
  3. 重启 ComfyUI,在节点菜单搜你的名字,连线、执行,确认输出正确。
  4. comfy_extras/ 里的官方节点找灵感——每个文件都是生产级的写法示范。
  5. 把节点打包发布:补上 README、语义化版本号,发到社区渠道并附上示例工作流。

跑通第一个节点的那一刻,你会明白这套扩展机制有多顺手——而你的工作流,从此也能长出别人没有的节点。

【免费下载链接】ComfyUI The most powerful and modular diffusion model GUI, api and backend with a graph/nodes interface. 【免费下载链接】ComfyUI 项目地址: https://gitcode.com/GitHub_Trending/co/ComfyUI

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值