尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

ERNIE-Image Turbo接入ComfyUI:自定义节点开发与工作流搭建实战

ERNIE-Image Turbo接入ComfyUI:自定义节点开发与工作流搭建实战 很多团队现在会遇到一种割裂负责大模型应用的人把 ERNIE-Image Turbo 当作云端 API 来调负责出图的人又在本地使用 ComfyUI 搭建 Stable Diffusion 工作流两边各有一套工具链。ERNIE-Image Turbo 的优势在于中文提示词理解、海报文案渲染和整体排版直接用于电商海报、营销图和社媒配图时很顺手ComfyUI 的优势则在于节点化编排、参数可视化和生成结果统一管理。如果能把两者打通运营和设计同学就不用反复在控制台与本地工具之间切换。题目里提到的int4_convrotint8_convrot并不是 ComfyUI 的内置节点名而是一种模型量化部署格式的描述。它涉及 int4、int8 量化位宽以及与卷积旋转结构相关的算子优化。真正落地时ComfyUI 并不直接加载这种格式的权重而是通过一个自定义 API 节点调用部署好的服务。这篇博客会围绕这条主线讲清楚 ERNIE-Image Turbo 接入 ComfyUI 的完整流程环境准备、自定义节点开发、工作流搭建、运行验证、常见问题排查以及从个人实验走向团队使用时需要补上的工程细节。1. 先搞清楚 ERNIE-Image Turbo 与 ComfyUI 之间是什么关系1.1 ERNIE-Image Turbo 是模型ComfyUI 是工作流引擎ERNIE-Image Turbo 是百度智能云千帆大模型平台提供的文生图模型使用场景包括海报生成、电商主图、营销 banner、插画配图等。它比较突出的能力是中文语义理解可以对提示词中的主体、风格、构图、文案位置做出相对一致的还原。实际使用中它通常以 HTTP API 的方式暴露客户端传入 prompt、尺寸、生成数量等参数服务端返回图片的 base64 数据或 URL。ComfyUI 则是一个节点化的本地图像生成工具用户把不同节点连成工作流每个节点负责一个明确步骤比如加载模型、编码文本、采样、保存图片。ComfyUI 本身不会自动内置每个云端模型它只提供工作流框架和自定义节点机制。要接入 ERNIE-Image Turbo本质上要做的事情是在 ComfyUI 中新增一个节点这个节点负责调用千帆 API并把返回的图片转换成 ComfyUI 能识别的IMAGE张量。这里需要先理解一个关键点标题里的int4_convrotint8_convrot不能直接被拖进 ComfyUI 的“加载模型”节点。常见的模型加载节点针对的是本地权重文件而量化部署格式是否能在本地加载取决于推理框架是否支持相关量化算子。对大多数只需要在 ComfyUI 里出图的同学来说默认接入方式应该是通过 API 调用而不是本地加载权重。1.2 两种接入路径的选型对比如果确实拿到了int4_convrotint8_convrot格式的本地权重并且团队想完全在内部网络部署也可以走第二条路把量化模型打包成一个 HTTP 推理服务然后在 ComfyUI 里用自定义节点调用这个本地服务。两种方式的差异如下对比项路径 A直接调用千帆云 API路径 B本地部署量化模型使用成本按调用量计费无 GPU 资源投入需要 GPU 和显存维护推理服务数据集传输图片和提示词会发送到云服务数据保留在内部网络部署复杂度低只需要 API Key 和网络高需要适配推理框架和量化算子模型升级平台侧升级接入方无感知需要自己拉取新模型并重新验证适用场景快速集成、业务试点、中小并发私有化交付、数据合规要求高如果输入材料没有明确给出本地权重来源推荐优先走路径 A。路径 B 只有在已经具备模型文件和部署环境时才值得考虑而且必须提前确认一件事推理框架是否支持int4_convrotint8_convrot这种量化格式。不要假设 int4 量化就一定能被所有框架直接加载。2. 环境准备安装 ComfyUI 并规划自定义节点目录2.1 官方源码方式安装 ComfyUI国内很多人会使用一键整合包方便确实方便但自定义节点开发时整合包自带的 Python 环境、PyTorch 版本和依赖管理不一定完全可控。这里以官方源码方式安装更能看清环境细节。git clone https://github.com/comfyanonymous/ComfyUI.git cd ComfyUI python -m venv venv source venv/bin/activate pip install -r requirements.txtWindows 环境下激活虚拟环境使用venv\Scripts\activate。安装完成后先启动一次 ComfyUI确认基础环境没有问题python main.py看到类似Starting server和To see the GUI go to: http://127.0.0.1:8188的输出说明 ComfyUI 已经启动。这时不要急着写节点先保持这个环境可用。2.2 依赖版本速查自定义节点本质上是一个 Python 包ComfyUI 在启动时会扫描custom_nodes目录并加载每个子目录中通过NODE_CLASS_MAPPINGS导出的节点。依赖建议如下软件或依赖推荐版本说明Python3.10 或 3.11兼顾 PyTorch 和多数自定义节点兼容性PyTorch2.1.0 以上具体版本要匹配显卡驱动和 CUDAComfyUI当前最新稳定版不同版本对自定义节点 API 有细微差异requests2.31.0 以上用于调用千帆 HTTP 接口Pillow10.0.0 以上处理图片编码和解码numpy1.24.0 以上图片数据转换如果原始材料没有给出明确版本落地前要先确认依赖版本。尤其是 PyTorch不要盲目安装最新版应该根据本机显卡驱动选择对应 CUDA 版本。一个常见坑是在虚拟环境中重复安装多个 PyTorch 版本导致自定义节点里的torch和 ComfyUI 主程序的torch不是同一个最终出现类型不匹配。2.3 自定义节点项目结构在ComfyUI/custom_nodes下新建一个目录命名为comfyui-ernie-image-turbo。目录结构如下custom_nodes/ └── comfyui-ernie-image-turbo/ ├── __init__.py ├── nodes.py └── requirements.txt__init__.py负责导出节点类nodes.py写节点逻辑requirements.txt声明额外依赖。ComfyUI 启动时会根据requirements.txt尝试自动安装依赖也可以手动进入该目录执行pip install -r requirements.txt。注意ComfyUI 对自定义节点的错误处理不会太友好。节点代码如果在 import 阶段抛异常整个节点不会出现在菜单里但主程序可能只会在启动日志中留下一段 traceback。因此每次修改 Python 文件后建议重启 ComfyUI并仔细看启动日志。3. 实现一个可复用的 ERNIE-Image Turbo API 节点3.1 先准备百度智能云侧配置在写节点代码之前先到百度智能云千帆控制台完成三件事创建应用拿到API Key和Secret Key。确认当前账号已经开通文生图服务并确认模型部署点名称。记录 API 调用地址不同阶段可能使用不同的 endpoint以控制台文档为准。先用 curl 做一次最小验证确保网络、密钥和参数都正确。下面是常见千帆 API 地址的示例实际地址以你拿到的文档为准curl --location https://qianfan.baidubce.com/v2/text2image \ --header Authorization: Bearer {access_token} \ --header Content-Type: application/json \ --data { model: ernie-image-turbo, prompt: 一张夏日音乐节海报主体是无线耳机背景是蓝紫色渐变画面包含中文文案夏日音乐节, size: 1024x1024, num_images: 1 }这里的{access_token}需要先用 API Key 和 Secret Key 换取。curl 这一步的意义是把问题隔离在网络层和权限层。如果 curl 都失败后续所有 ComfyUI 节点代码都会失败先不要继续写代码而是先排查密钥、网络和模型名。3.2 获取并缓存 access_token百度智能云使用 OAuth 2.0 的 client credentials 模式换取access_token。这个 token 有有效期且换取本身有频率限制所以不能每次调用文生图接口都重新请求应该缓存到内存中接近过期时再刷新。import time import requests _token_cache { token: None, expires_at: 0, } def get_access_token(api_key: str, secret_key: str) - str: now time.time() # 提前 60 秒刷新避免 token 正好在请求边界过期 if _token_cache[token] and _token_cache[expires_at] - now 60: return _token_cache[token] url https://aip.baidubce.com/oauth/2.0/token params { grant_type: client_credentials, client_id: api_key, client_secret: secret_key, } resp requests.post(url, paramsparams, timeout10) resp.raise_for_status() data resp.json() if access_token not in data: raise RuntimeError(f获取 access_token 失败: {data}) _token_cache[token] data[access_token] _token_cache[expires_at] now int(data.get(expires_in, 2592000)) return _token_cache[token]expires_in的默认值不一定要写死实际以接口返回为准。这里给了一个保守的示例实际项目中还可以把 token 持久化到本地文件避免进程重启后立刻重新换取。3.3 自定义节点主代码核心节点类可以放在nodes.py里。节点输入包括提示词、图片尺寸、生成数量、seed以及可选的控制参数。需要注意不同版本的文生图接口暴露的参数并不完全一致节点里传了而服务端不支持的参数会导致 400 错误。下面的示例把sampling_steps和cfg_scale作为可选参数仅在非空时加入请求体。import base64 import io import os import numpy as np import requests import torch from PIL import Image from .auth import get_access_token class ERNIEImageTurboNode: classmethod def INPUT_TYPES(cls): return { required: { prompt: (STRING, { multiline: True, default: 一张产品海报主体是运动水杯背景是干净明亮的室内场景画面包含中文文案轻量出行 }), size: ( [1024x1024, 1536x1024, 1024x1536], {default: 1024x1024} ), num_images: (INT, { default: 1, min: 1, max: 4 }), seed: (INT, { default: 0, min: 0, max: 2147483647 }), }, optional: { sampling_steps: (INT, { default: 20, min: 1, max: 50 }), cfg_scale: (FLOAT, { default: 5.0, min: 0.0, max: 30.0 }), } } RETURN_TYPES (IMAGE, STRING, INT) RETURN_NAMES (image, request_id, status_code) FUNCTION generate CATEGORY Baidu/ERNIE def generate(self, prompt, size, num_images, seed, sampling_steps20, cfg_scale5.0): api_key os.environ.get(BAIDU_API_KEY, ) secret_key os.environ.get(BAIDU_SECRET_KEY, ) model_name os.environ.get(ERNIE_IMAGE_MODEL, ernie-image-turbo) if not api_key or not secret_key: raise ValueError(请先设置 BAIDU_API_KEY 和 BAIDU_SECRET_KEY 环境变量) token get_access_token(api_key, secret_key) payload { model: model_name, prompt: prompt, size: size, num_images: num_images, seed: seed, } if sampling_steps is not None: payload[sampling_steps] sampling_steps if cfg_scale is not None: payload[cfg_scale] cfg_scale headers { Authorization: fBearer {token}, Content-Type: application/json, } resp requests.post( https://qianfan.baidubce.com/v2/text2image, headersheaders, jsonpayload, timeout120, ) if resp.status_code ! 200: raise RuntimeError( f图片生成失败: {resp.status_code} {resp.text} ) data resp.json() image_tensor self._extract_image(data) request_id data.get(id, ) return (image_tensor, request_id, resp.status_code) def _extract_image(self, data): if data not in data or not data[data]: raise RuntimeError(f响应中没有图片数据: {data}) item data[data][0] if b64_image in item: raw base64.b64decode(item[b64_image]) img Image.open(io.BytesIO(raw)) elif url in item: img_resp requests.get(item[url], timeout30) img_resp.raise_for_status() img Image.open(io.BytesIO(img_resp.content)) else: raise RuntimeError(f无法从响应中提取图片: {item}) img img.convert(RGB) tensor torch.from_numpy(np.array(img).astype(np.float32) / 255.0) return tensor.unsqueeze(0) NODE_CLASS_MAPPINGS { ERNIEImageTurboNode: ERNIEImageTurboNode, } NODE_DISPLAY_NAME_MAPPINGS { ERNIEImageTurboNode: 百度 ERNIE-Image Turbo 文生图海报, }这里有几个关键点返回类型IMAGE对应 ComfyUI 内部通用的图像张量格式是B, H, W, C取值在 0 到 1 之间float32。_extract_image先处理 base64再处理 URL实际返回格式以接口文档为准。os.environ读取密钥避免把密钥硬编码到工作流 JSON 里。raise RuntimeError会把错误信息直接显示在 ComfyUI 的节点执行结果中方便排查。如果接口不支持sampling_steps或cfg_scale不要直接删代码而是可以加一个环境变量开关例如ERNIE_IMAGE_ENABLE_EXTRA_PARAMS1时才写入这两个参数这样能兼容不同版本的模型服务。3.4 注册节点并确认加载状态__init__.py负责导出节点内容如下from .nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS __all__ [NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS]requirements.txt声明额外依赖requests2.31.0 numpy1.24.0 Pillow10.0.0 torch2.1.0需要注意上面nodes.py里写了from .auth import get_access_token因此目录里还需要一个auth.py内容就是 3.2 节那段 token 缓存代码。实际项目中也可以把这段代码直接放进nodes.py这是结构选择问题不影响运行。重启 ComfyUI 后在启动日志中能看到类似这样的加载记录Import times for custom nodes: 0.1 seconds: comfyui-ernie-image-turbo如果在右键菜单中找不到节点优先检查启动日志里的 traceback。常见原因包括依赖未安装、Python 语法错误、环境变量未设置导致 import 阶段崩溃。4. 在 ComfyUI 里搭建文生图海报工作流4.1 最小工作流一个节点加一个保存节点节点加载成功之后工作流搭建很简单。在 ComfyUI 的编辑区右键选择Add Node在Baidu/ERNIE分类下找到“百度 ERNIE-Image Turbo 文生图海报”节点。把它拖到画布上然后从输出端口image拉一条线连接到 ComfyUI 自带的Save Image节点。此时点击Queue PromptComfyUI 会执行节点中的generate方法。执行成功后图片会出现在ComfyUI/output目录中预览区也会显示图片。这个最小工作流适合验证整个链路是否通畅。如果不想先手动创建节点也可以把节点定义导出成工作流 JSON团队内部共享。不过要注意工作流 JSON 中不要包含密钥密钥必须继续依赖环境变量。4.2 参数传入与 seed 语义远程 API 节点和本地 Stable Diffusion 节点的参数语义不完全一样。下表整理了这个示例节点的常见参数参数示例值作用注意事项prompt一张夏日音乐节海报...描述海报主题、主体、风格、文案中文提示词效果通常更稳size1024x1024输出图片宽高不同模型支持的尺寸集合不同num_images1一次生成图片数量数量越大单次调用耗时和费用越高seed42随机种子影响生成结果服务端是否支持需以接口文档为准sampling_steps20采样步数不是所有模型服务都暴露该参数cfg_scale5.0提示词引导强度数值过大可能出现画面过饱和一个容易踩的坑是在 ComfyUI 里传入了sampling_steps但服务端模型版本不支持该参数结果请求直接返回 400。所以示例代码里把这些参数设计成可选加入并在节点说明中标注如果遇到参数校验失败优先检查是不是多传了服务端不认识的字段。4.3 批量生成海报的编排方式如果运营需要一次生成多个尺寸或多个提示词方案可以通过 ComfyUI 的Primitive节点分别输入 prompt 和 size形成多条执行分支。但这里要有意识地区分两种批量ComfyUI 的批量执行同一次工作流执行中处理多个输入。远程 API 每次请求会生成num_images张图片ComfyUI 会把图片张量当成一个 batch。多次点击 Queue Prompt每次独立调用一次 API。适合需要不同提示词的场景。远程 API 是按调用次数计费的批量生成前先算清楚成本不要像本地 Stable Diffusion 一样无限制地刷图。注意ComfyUI 主程序与自定义节点运行在同一个 Python 进程中。节点里的timeout120会阻塞整个执行线程因此在远程 API 响应较慢时ComfyUI 会显示等待状态这是正常现象不要误以为程序卡死。5. 运行验证如何判断节点调用成功5.1 验证链路拆分接入过程中最忌讳直接看最终图片是否生成而是应该把链路拆成三段验证。第一段是 API 本身。使用 curl 或者一段独立 Python 脚本确认access_token能拿到确认文生图接口能返回 JSON。此阶段出现问题错误大概率在密钥、模型名和网络。第二段是自定义节点。在generate方法里加入短暂的关键信息输出比如发送的payload和服务端返回的data[id]。确认请求体符合预期。第三段是 ComfyUI 集成。确认IMAGE张量能被Save Image节点正确保存预览区能看到图片输出目录里有文件。# 在 generate 方法中临时加入 print([ERNIE] request_id:, request_id) print([ERNIE] image_size:, image_tensor.shape)正常输出类似[ERNIE] request_id: as-bc123456 [ERNIE] image_size: torch.Size([1, 1024, 1024, 3])torch.Size中的四个数字分别对应 batch、高度、宽度、通道数。如果最后一位不是 3说明图片模式转换出了问题。5.2 检查输出文件ComfyUI 的Save Image节点默认把图片保存到ComfyUI/output目录文件名包含节点生成的时间戳和随机后缀。打开图片后建议按以下顺序检查图片是否完整有没有半张图或大片噪点。图片尺寸是否与请求的size一致。中文文案是否正确有没有乱码或错字。图片格式是否正常能否用常见图片查看器打开。如果图片能保存但尺寸不对大概率是服务端自动裁剪或缩放需要确认size参数是否被正确传递。如果图片无法预览问题通常出在张量转换而不是 API。5.3 记录 request_id 便于回溯文生图 API 返回的request_id是定位问题的重要依据。在节点里把request_id通过输出端口暴露出来可以连接到Primitive节点显示也可以保存到日志中。遇到图片内容异常或计费争议时这个 ID 是向平台反馈的关键信息。6. 常见问题排查从现象倒推根因6.1 认证、参数、限流三类高频问题下面汇总接入过程中最常见的几类现象和排查路径。问题现象常见原因检查方式处理建议401 invalid access tokenaccess_token 获取失败或已过期检查 API Key、Secret Key单独打印 token重新换取 token检查系统时间是否准确400 参数校验失败传入了服务端不支持的参数抓取实际请求 JSON注释掉采样步数、cfg 等可选参数再试429 请求频率超限短时间内并发调用过多统计调用频率和并发数增加睡眠间隔、使用令牌桶限流请求超时接口响应慢或网络波动查看 ComfyUI 日志中的 timeout 位置适当增大 timeout增加重试机制图片为空白或纯色提示词被服务端过滤或模型输出异常查看返回内容和 request_id调整提示词向平台反馈该 request_id401出现时不要只检查 token 本身还要看本地和服务器时间是否一致。OAuth 的 token 校验对时间误差比较敏感服务器时间偏差过大会导致 token 被判定为无效。6.2 图像转换与 ComfyUI 显示问题ComfyUI 自定义节点最常见的本地错误集中在张量转换上。TypeError: expected Tensor as element 1 in argument 0, but got PIL.Image这个错误说明节点返回了 PIL Image而不是 torch.Tensor。ComfyUI 的IMAGE类型要求张量必须用torch.from_numpy转换。ValueError: image has wrong mode这个错误通常出现在convert(RGB)之前或之后。远程 API 返回的图片可能是 RGBA、L、CMYK 等模式统一转换成RGB可以规避大部分兼容性问题。RuntimeError: The size of tensor a (3) must match the size of tensor b (4)当 ComfyUI 把多张图片拼成 batch 时如果某张图片是RGBA某些是RGB维度不一致会导致拼接失败。因此_extract_image里必须保证每张图都执行convert(RGB)。6.3 量化模型本地部署的常见坑如果团队选择把int4_convrotint8_convrot模型本地部署使用场景会从“ComfyUI 调 API”变成“ComfyUI 调本地推理服务”。此时 ComfyUI 侧代码几乎不用改只需要把请求地址换成本地服务地址但部署侧的坑更多。首先不要看到 int4 就认为一定低显存。量化确实能降低显存占用但推理框架对convrot这类算子的支持程度决定实际性能。如果框架没有针对性优化有些算子会回退到慢速实现出现“显存降了但速度也降了”的情况。其次本地推理服务要考虑模型并发。ComfyUI 的 Queue 会逐个执行节点如果团队多人共用同一个 ComfyUI多个请求会同时打到本地推理服务。建议在服务侧增加队列和超时控制不要让模型进程被一次性请求压垮。最后本地部署仍然需要请求参数协议与云端一致。最好把推理服务包装成和千帆兼容的 HTTP 接口这样 ComfyUI 节点不需要为本地和云端各写一套。7. 从个人桌面走向团队生产的最佳实践7.1 密钥与配置外置示例代码中通过环境变量读取BAIDU_API_KEY和BAIDU_SECRET_KEY这是为了不把密钥写进工作流 JSON。团队使用时有更严格的要求开发环境使用本地.env文件加入.gitignore。生产环境使用配置中心、密钥管理服务或容器环境变量注入。不要把密钥放在公开的 ComfyUI 工作流分享文件中。ComfyUI 的节点配置会随着工作流 JSON 一起保存如果直接在节点输入框里填 API Key那么分享工作流就等于泄露密钥。7.2 成本控制与缓存远程文生图按调用量计费生产环境必须做成本控制。建议从三个层面入手请求级控制限制num_images上限避免一次请求生成过多图片。队列级控制在 ComfyUI 外层增加请求队列限制并发数。结果级缓存对相同 prompt、size、seed 的请求做结果缓存重复生成直接返回本地图片路径。缓存可以使用简单的文件缓存以参数组合的哈希值作为文件名把生成结果保存到固定目录。下次执行节点时先检查缓存是否存在如果命中就直接返回。7.3 日志、监控和重试自定义节点不能只靠print。接入团队生产环境时至少记录三类信息请求参数包括模型名、prompt 摘要、size、num_images。请求结果request_id、status_code、耗时。异常信息完整 traceback 和响应体。日志结构建议使用 JSON 格式方便接入日志平台。重试策略也要谨慎对于 4xx 错误不要盲目重试对于 5xx、超时和网络抖动可以指数退避重试两次。无限重试会让限流问题雪上加霜。# 伪代码示例故障时的请求重试 for attempt in range(3): try: resp requests.post(url, jsonpayload, timeout120) if resp.status_code 429: time.sleep(2 ** attempt) continue resp.raise_for_status() return parse_success(resp.json()) except requests.exceptions.Timeout: time.sleep(2 ** attempt)注意ComfyUI 节点是同步执行的重试会阻塞工作流队列。对超时和重试逻辑要设置总的上限避免单个节点卡住整个队列。7.4 扩展方向接入 ERNIE-Image Turbo 之后可以围绕 ComfyUI 做更有价值的工作流扩展在提示词前增加大模型节点自动把用户输入改写成适合海报生成的中文提示词。使用 ComfyUI 的Show Text节点展示 prompt方便运营确认每次请求的输入。结合Image Scale、Image Crop等本地节点对海报做二次裁剪和美化。把多个 ERNIE 节点放在同一工作流中对比不同尺寸、不同提示词的出图效果。如果本地有量化模型服务可以把节点中的 API 地址抽象成配置项一键切换云端和本地。扩展时遵循一个原则让 ComfyUI 节点保持“只负责请求和图片转换”不要把提示词工程、结果存储、权限校验全部塞进节点里。节点职责单一后续维护才容易。8. 新手最值得先做的三个练习如果读完上面内容后想自己动手建议按顺序完成三个练习难度逐步增加。第一个练习把示例代码跑通。不要急着改功能先用最小工作流生成一张海报确认 curl、节点、保存图片这条链路是通的。第二个练习把节点改为通用百度文生图节点。当前节点写死了ernie-image-turbo模型名可以改成从环境变量或下拉列表读取。练习目标是理解 ComfyUI 的节点参数如何从 Python 代码映射到 UI。第三个练习给节点增加num_images数组输出。当前示例只取了data[data][0]第一张图实际 API 可能返回多张图。请尝试把多张图片全部解析并转换成B, H, W, C的 batch 张量返回给 ComfyUI。这样可以在一次请求中生成多张海报并直接进入Save Image批量保存。这三个练习覆盖了 API 调用、参数映射、图片张量转换和批量处理是 ComfyUI 自定义节点开发最核心的能力。如果今天只做一件事先把 curl 调用跑通再写节点最后在工作流里接上Save Image。远程模型接入本地工作流最大的风险不在 ComfyUI而在 API 参数、认证和网络边界。把这三层逐段验证清楚后续无论是接入 ERNIE-Image Turbo还是迁移到本地int4_convrotint8_convrot量化服务整个工作流都不会推翻重来。
返回列表