OpenMAIC 接入 ComfyUI 本地生图:工作流编排、节点适配与生产部署指南
【免费下载链接】OpenMAICOpen Multi-Agent Interactive Classroom — Get an immersive, multi-agent learning experience in just one click项目地址: https://gitcode.com/GitHub_Trending/op/OpenMAIC
OpenMAIC(Open Multi-Agent Interactive Classroom)将 ComfyUI 作为"无 API Key"的本地图像生成 Provider,通过内置适配器把课堂 Agent 产出的图片描述注入到 ComfyUI 工作流中完成出图。本文以仓库根目录下的 comfyui-setup-instructions.md 为骨架,结合 comfyui-image-adapter.ts、comfyui-workflows.ts 与 public/comfyui-workflow.json 等源码,完整讲解工作流文件的存放与命名规则、必需/推荐节点的标题约定、API 格式导出、OpenMAIC 设置页配置,以及生产环境下的 SSRF 安全边界与性能调优。读完本文,你将能够把一个普通 ComfyUI 工作流改造成 OpenMAIC 可自动驱动的生图工作流,并安全地将其部署到同机或跨机环境。
一、工作流文件存放位置与命名约定
OpenMAIC 通过 HTTP API 与 ComfyUI 通信,其"模型"概念对应的是你存放在 Next.jspublic/目录下的工作流 JSON 文件。目录结构如下:
your-project/ public/ comfyui-workflow.json → 在下拉框中显示为 "Workflow" comfyui-anime-style.json → 显示为 "Anime Style" comfyui-line-art.json → 显示为 "Line Art" comfyui-portrait.json → 显示为 "Portrait"命名约定
- 文件名必须以
comfyui-开头,或包含workflow; - 用连字符(hyphen)分隔单词,这些单词会直接成为 UI 下拉框中的显示名;
comfyui-前缀会被自动剥离;- 示例:
comfyui-anime-style.json在下拉框中显示为"Anime Style"。
这套规则在源码中有严格对应:lib/media/comfyui-workflows.ts中的isComfyuiWorkflowFilename()规定文件必须以.json结尾,且小写化后以comfyui开头或包含workflow;filenameToDisplayName()则负责把文件名转换为显示名——先去掉.json后缀和comfyui-/comfyui_前缀,再把连字符/下划线替换为空格并按单词首字母大写(Title Case)。
工作流发现逻辑统一收敛在 lib/media/comfyui-workflows.ts 的listComfyuiWorkflows():它读取public/目录下满足命名规则且是普通文件的 JSON 文件,按显示名排序返回。这个函数同时被两个消费方引用:
- API 路由 app/api/comfyui-workflows/route.ts:
GET /api/comfyui-workflows返回{ workflows: [{ id, name }] },供设置页的 Workflows 下拉列表使用; - ComfyUI 图像适配器:校验客户端提交的工作流 id 是否为真实存在的文件。
这样设计保证了"UI 展示的列表"与"适配器实际接受的 id"永远一致,不会出现下拉框里能选、提交后却报找不到文件的漂移问题。
二、工作流中的必需与推荐节点(按标题匹配)
适配器**按节点的标题(JSON 中的_meta.title字段)**定位节点,而不是按节点类型或 ID。在 ComfyUI 中设置节点标题的方法:右键节点 →Title→ 输入名称。
必需节点
| 节点标题 | 推荐类型 | 用途 |
|---|---|---|
Input Prompt | PrimitiveStringMultiline | OpenMAIC 生成的图片描述会被注入到此节点的value输入 |
推荐节点(存在即自动打补丁)
| 节点标题 | 推荐类型 | 用途 |
|---|---|---|
Width | PrimitiveInt | 输出宽度(像素),按请求的宽高比设置 |
Height | PrimitiveInt | 输出高度(像素),按请求的宽高比设置 |
KSampler | KSampler | 每次生成都会随机化 seed,保证输出多样化 |
Enable prompt enhancement? | PrimitiveBoolean | 设为false跳过 LLM Prompt 增强(推荐,更快) |
源码层面的匹配逻辑在 comfyui-image-adapter.ts 的findNodeIdByTitle():遍历工作流对象的所有节点,找出_meta.title与目标标题不区分大小写相等的第一个节点并返回其 id。随后patchWorkflow()按照"优先使用显式节点、否则回退到遗留节点"的策略逐项打补丁:
- Prompt 注入:优先
Input Prompt节点,写入inputs.value = options.prompt; - 尺寸注入:同时找到
Width与Height节点时,分别写入inputs.value;否则回退到Empty Flux 2 Latent节点的inputs.width / inputs.height; - Seed 随机化:找到
KSampler节点后,把inputs.seed替换为一个Math.floor(Math.random() * 1e15)生成的随机整数。
回退行为(Fallback)
- 如果
Width和Height节点均不存在,适配器自动回退到直接修补Empty Flux 2 Latent节点的width、height输入——因此没有独立尺寸节点的旧工作流也能正常工作; Width和Height必须同时存在才走显式节点方案:如果只找到其中一个,适配器会回退到 latent 节点方案并打印一条 warning 日志(源码在patchWorkflow()中对widthNodeId || heightNodeId分支有明确提示);- Prompt 节点回退:如果找不到
Input Prompt,适配器回退到名为String (Multiline - Prompt)的节点——仓库自带的示例工作流 public/comfyui-workflow.json 中,"88:94"节点正是PrimitiveStringMultiline类型、标题为"String (Multiline - Prompt)",无需任何重命名即可直接使用; - 兜底报错:若两种标题都找不到,适配器会抛出明确的错误,提示"add a node titled 'Input Prompt'"(见
patchWorkflow()第 314-320 行)。
尺寸还有一个重要细节:resolveDimensions()会把请求的宽高比换算成像素后,同时按 Provider 的maxResolution边界收缩。lib/media/image-providers.ts中comfyui-image的maxResolution为{ width: 1920, height: 1920 },因此竖屏比例(如 9:16)不会因为"宽度钉死 1920"而溢出到 3413 高度,从而避免 OOM 或生成失败。
三、节点连线方式
Input Prompt
把Input Prompt节点的输出接到提示词进入管线的地方——典型是CLIPTextEncode节点的text输入,如果使用了 Prompt 模板,也可以接到StringReplace节点。
Width 与 Height
把每个节点的输出接到你的Empty Flux 2 Latent(或等效的空 Latent)节点对应的width和height输入。
示例连线:
[Input Prompt] ──→ CLIPTextEncode (text) [Width] ──→ EmptyLatentImage (width) [Height] ──→ EmptyLatentImage (height)仓库示例工作流 public/comfyui-workflow.json 展示了完整的参考接法:"88:94"(String (Multiline - Prompt))→"88:97"(Switch,由"88:96"的Enable prompt enhancement?布尔节点控制走原始 Prompt 还是 LLM 增强路径)→"88:67"(CLIPTextEncode)→"88:70"(KSampler);尺寸则由"88:71"(Empty Flux 2 Latent,类型EmptyFlux2LatentImage)提供latent_image输入。这个文件同时印证了文档中所有节点标题约定都是可运行的真实配置。
四、如何导出 API 格式的工作流
工作流 JSON必须是 ComfyUI 的 API 格式(不是默认的保存格式),否则适配器无法识别节点结构。
- 在 ComfyUI 中进入Settings,开启Dev Mode Options;
- 工具栏会出现一个新的Save (API Format)按钮;
- 点击Save (API Format)导出正确的 JSON;
- 把文件放到 Next.js 的
public/目录中。
⚠️ 普通的Save按钮导出的是另一种格式(包含 UI 布局、位置信息等),无法工作。
为什么必须是 API 格式?从适配器读取方式可以反推:loadWorkflow()加载 JSON 后直接把它当作{ 节点id: { inputs, class_type, _meta } }的扁平映射来遍历并按标题定位节点(Object.entries(workflow)),这正是 ComfyUI/prompt接口期望的"prompt graph"格式。普通保存格式中节点不是这种扁平结构,findNodeIdByTitle()无法工作,nodeInputs()也会返回undefined并触发"malformed"错误。
五、在 OpenMAIC 中配置
- 进入Settings → Image Generation;
- 在 Provider 列表中选择ComfyUI Image;
- 设置Base URL为你的 ComfyUI 地址(默认
http://localhost:8188); - 在Workflows列表中选择要使用的工作流;
- 点击Test Connection验证 ComfyUI 是否可达。
无需 API Key:lib/media/image-providers.ts中comfyui-image的requiresApiKey: false,默认baseUrl为http://localhost:8188。值得注意的是它的models: []是刻意为之——真实可选的工作流是运行时通过GET /api/comfyui-workflows(即public/目录下的文件)动态发现的,并不存在静态模型列表。
Test Connection的底层实现是testComfyuiImageConnectivity():向${baseUrl}/system_stats发起一次 GET 探测(10 秒超时、不跟随重定向),返回 HTTP 200 即判定连通。测试用例 tests/media/auth-probe-adapters.test.ts 覆盖了包括 ComfyUI 在内的各 Provider 探测行为,验证请求 URL、redirect 策略与错误消息。
默认工作流选择
如果没有显式选择工作流——例如走自主课堂媒材生成(classroom-media)路径,或在设置页尚未点击任何工作流时——适配器会自动回退到public/中发现按显示名排序的第一个工作流文件(listComfyuiWorkflows()按name.localeCompare排序后取known[0])。它不依赖任何硬编码文件名,所以你不需要准备一个叫comfyui-workflow.json的文件,任何一个comfyui-*.json都会被用作默认;如果public/中一个工作流文件都没有,生成会直接失败,并抛出明确错误提示"Add at least one comfyui-*.json workflow"。
同时,工作流 id 是客户端可控参数(来自x-image-model请求头),适配器做了两层防护:先用isComfyuiWorkflowFilename()校验必须是裸文件名(不含路径分隔符与..),再与listComfyuiWorkflowFilenames()返回的实时目录清单比对,最后还会验证解析后的文件路径仍落在public/目录内——三重防线防止路径穿越(详见loadWorkflow()服务端分支注释)。
六、部署拓扑与 SSRF 安全边界(生产环境必读)
默认 Base URLhttp://localhost:8188假设OpenMAIC 与 ComfyUI 运行在同一台主机(典型的本地 / 自托管部署)。
当 OpenMAIC 以NODE_ENV=production运行时,由客户端提供的Base URL(x-base-url)若指向localhost、127.0.0.1或私有/内网 IP 段,会被 SSRF 防护validateUrlForSSRF以 HTTP 403 拒绝。这是有意为之,与其它本地 Provider 的行为一致——防止浏览器客户端把服务端请求导向内部服务。
SSRF 防护实现在 lib/server/ssrf-guard.ts:validateUrlForSSRF()会拦截localhost、.local域名、0.0.0.0、::1以及isPrivateIP()判定的各类私网地址(IPv4 的 10/8、172.16/12、192.168/16、127/8、169.254/16 等,还包括 IPv4-mapped IPv6、6to4、Teredo、ISATAP 等 IPv6 隧道中内嵌私网 IPv4 的情况),对非 IP 主机名还会做 DNS 解析后再校验解析结果。自托管场景可通过环境变量ALLOW_LOCAL_NETWORKS=true跳过私网检查。
实际影响:
- 同机 / 自托管:开箱即用。服务端解析的默认值不受客户端 URL 的 SSRF 检查约束,因此 OpenMAIC 与 ComfyUI 同机时默认的
localhost:8188完全可用; - 生产环境中 ComfyUI 在另一台机器:应让 OpenMAIC 通过可路由、非私网的地址访问 ComfyUI(或在公共主机名后接反向代理终结)。生产环境下浏览器发来的
localhost/私网 URL 会被拒绝; - 本地开发(
NODE_ENV≠production):跳过 SSRF 检查,localhost正常工作。
七、生成流程与性能调优
一次完整生成的调用链
generateWithComfyuiImage()的完整流程(源码注释与日志分段清晰可循):
- 加载工作流:优先使用调用方传入的已解析
workflowJson;否则服务端从磁盘读取(浏览器端从window.location.originfetch,实际生成总在服务端 API 路由执行);未指定时取public/下第一个发现的工作流; - 打补丁:
patchWorkflow()注入 Prompt、Width/Height(或 latent 尺寸)、随机 seed; - 入队:
POST {baseUrl}/prompt,body 为{ prompt: workflow, client_id },若 ComfyUI 返回node_errors立即抛出带细节的错误; - 轮询:每 1500ms 请求一次
/history/{prompt_id},直到status.completed;单次请求超时 30s,整体硬超时 5 分钟(GENERATION_TIMEOUT_MS = 300_000);若status_str === "error"会从执行消息中提取execution_error的真实原因快速失败,而不是傻等超时; - 取图:从 history 中提取第一个输出节点的图片,请求
/view?filename=...&subfolder=...&type=...,服务端用Buffer转 base64 返回。
性能建议
- 关闭 Prompt 增强:如果工作流里带 LLM 增强器,把它的开关节点设为
false。增强每张图可能额外花费 3-5 分钟,而 OpenMAIC 生成的 Prompt 本身已经足够描述性。示例工作流中对应"88:96"节点(PrimitiveBoolean,标题Enable prompt enhancement?),它控制"88:97"Switch 节点在原始 Prompt("88:94")与 LLM 增强路径("88:95"TextGenerate)之间切换; - 适配器会自动随机化
KSampler的 seed(每次生成一个 15 位随机整数),无需手工干预即可保证输出多样性; - 输出尺寸由 OpenMAIC 请求的宽高比换算而来,并受 lib/media/image-providers.ts 中
maxResolution限制(ComfyUI 默认1920×1920),竖屏比例会被等比收缩进边界框内。
八、常见故障排查要点
结合源码中的错误分支,以下问题都有明确的对症:
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
| 下拉框没有工作流可选 | public/下没有符合命名规则的文件 | 放入comfyui-*.json或含workflow的.json文件并重启 |
| "missing a prompt input node" | 工作流缺少Input Prompt或String (Multiline - Prompt)标题节点 | 右键节点 → Title 重命名,或改用 API 格式重新导出 |
| "prompt node is malformed" | 工作流不是 API 格式 | 用Save (API Format)重新导出 |
node_errors报错 | 工作流节点连线断裂 | 在 ComfyUI 中检查节点连接后重新导出 |
| 超时(5 分钟) | 生成本身过慢或队列阻塞 | 关闭 Prompt 增强、检查 ComfyUI 队列 |
| "finished but returned no images" | 工作流缺少SaveImage节点 | 在管线末端加入 SaveImage 节点 |
| 生产环境 403 | 浏览器提交了localhost/私网 Base URL | 将 ComfyUI 暴露为可路由的非私网地址,或设置ALLOW_LOCAL_NETWORKS=true(自托管场景) |
小结
OpenMAIC 对 ComfyUI 的接入遵循"按标题找节点、运行时打补丁、无 Key 直连本地"的设计:你只需把工作流以 API 格式放入public/、按约定命名、给关键节点设置好标题,就能让课堂 Agent 自动完成"描述 → 出图 → 回填课件"的闭环。通过 lib/media/adapters/comfyui-image-adapter.ts、lib/media/comfyui-workflows.ts、lib/server/ssrf-guard.ts 等源码,你可以进一步追踪每个补丁点与安全边界的具体实现,也可以参考 public/comfyui-workflow.json 作为可直接运行的模板。
【免费下载链接】OpenMAICOpen Multi-Agent Interactive Classroom — Get an immersive, multi-agent learning experience in just one click项目地址: https://gitcode.com/GitHub_Trending/op/OpenMAIC
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考