Z-Image-ComfyUI本地部署后,如何远程调用API?
当你在本地或云服务器上成功启动 Z-Image-ComfyUI 镜像,看到熟悉的 ComfyUI 界面在浏览器中流畅运行时,一个更实际的问题自然浮现:能不能不点鼠标、不进网页,直接用代码批量生成图片?
答案非常明确:可以,而且非常自然。
Z-Image-ComfyUI 并非一个仅面向视觉操作的“演示工具”,它本质是一个开箱即用的、可编程的图像生成服务。阿里开源的 Z-Image 系列模型(尤其是 Z-Image-Turbo)与 ComfyUI 的节点式架构深度协同,让远程 API 调用不再是需要额外插件或二次开发的“附加功能”,而是其原生能力的一部分。
本文将完全基于你已部署好的镜像环境,手把手带你完成从“能访问网页”到“能写脚本调用”的关键跃迁——不依赖任何第三方插件,不修改核心代码,只用标准 HTTP 请求和一份导出的工作流 JSON,实现稳定、可复用、可集成的远程调用。
1. 理解 ComfyUI 的 API 设计逻辑:它本来就是个服务
1.1 不是“加了插件才支持”,而是“不加插件就支持”
很多开发者第一次接触 ComfyUI 时,会下意识把它和 Stable Diffusion WebUI 类比,误以为 API 功能需要安装ComfyUI Manager或Custom-Nodes才能启用。这是一个常见误解。
实际上,ComfyUI 的后端服务(main.py启动的server模块)自诞生起就内置了一套轻量但完备的 REST 接口。这些接口由 ComfyUI 核心框架直接提供,无需任何扩展即可使用。你每次在界面上点击“Queue Prompt”,浏览器背后发送的正是POST /prompt请求。
这意味着:只要 ComfyUI 进程在运行,API 就已就绪。
1.2 默认端口与网络可达性是前提
Z-Image-ComfyUI 镜像默认监听8188端口。但在远程调用前,必须确认两点:
服务是否真正监听在 0.0.0.0:8188(而非 127.0.0.1:8188)?
检查启动日志中是否有类似Starting server on 0.0.0.0:8188的输出。若只有127.0.0.1,需在启动命令中显式指定--listen 0.0.0.0(镜像通常已预设,但建议验证)。网络策略是否放行该端口?
若部署在云服务器(如阿里云 ECS),需检查安全组规则,确保入方向 TCP 8188 端口对你的调用方 IP(或0.0.0.0/0测试时)开放;若在本地 Docker 运行,确认-p 8188:8188映射正确。
快速验证方式:在部署机器本地执行
curl -X GET http://localhost:8188/object_info
若返回大量 JSON 数据(含"CLIPTextEncode"、"KSampler"等节点定义),说明 API 已正常工作。
1.3 关键 API 接口一览(无需认证,开箱即用)
| 接口路径 | 方法 | 用途 | 是否必需 |
|---|---|---|---|
GET /object_info | 获取所有可用节点及其参数结构 | 查看 Z-Image 相关节点(如ZImageTurboLoader)的输入字段名 | 建议首次调用前查看 |
GET /models/checkpoints | 列出已加载的模型文件 | 确认Z-Image-Turbo.safetensors等模型是否被识别 | 避免因路径错误导致任务失败 |
POST /prompt | 提交完整工作流 JSON | 核心接口,触发图像生成 | |
GET /history/{prompt_id} | 查询某次任务的执行结果与输出文件名 | 获取生成图片的存储路径 | |
GET /queue | 查看当前排队/运行中的任务 | 监控服务负载,避免过载 | 生产环境推荐 |
这些接口全部返回标准 JSON,无 Cookie 或 Session 依赖,天然适合程序化调用。
2. 准备可调用的工作流:从界面到 JSON 的关键一步
2.1 为什么不能“手写 JSON”?节点 ID 是最大陷阱
初学者常试图手动编写 ComfyUI 工作流 JSON,但很快会陷入困境:节点 ID(如"6"、"7")是界面自动生成的随机字符串,且连接关系("inputs": {"model": ["3", 0]})高度依赖拓扑顺序。稍有不慎,JSON 就会因 ID 错误或引用失效而被后端拒绝。
正确做法:永远从界面导出,而非手写。
2.2 三步导出一个 Z-Image-Turbo 可用工作流
在 ComfyUI 界面中加载并配置好 Z-Image-Turbo 工作流
- 使用镜像预置的
Z-Image-Turbo加载节点(通常名为ZImageTurboLoader或类似); - 连接至
KSampler(注意:Z-Image-Turbo 通常要求sampler_name为"dpmpp_2m_sde_gpu",scheduler为"karras"); - 设置
CLIPTextEncode节点输入正向提示词(text字段)和反向提示词(text字段); - 连接
SaveImage节点,确保输出路径有效(默认为ComfyUI/output/)。
- 使用镜像预置的
点击右上角菜单 → “Save (json)”
- 保存为
zimage_turbo_basic.json(或其他易识别名称); - 文件将下载到浏览器默认目录,随后上传至你的调用脚本所在机器。
- 保存为
用文本编辑器打开 JSON,定位关键可变字段
以典型结构为例:"6": { "class_type": "ZImageTurboLoader", "inputs": { "ckpt_name": "Z-Image-Turbo.safetensors" } }, "7": { "class_type": "CLIPTextEncode", "inputs": { "text": "一位穿汉服的少女站在竹林中,水墨风格", "clip": ["6", 1] } }, "8": { "class_type": "CLIPTextEncode", "inputs": { "text": "模糊,畸变,低质量", "clip": ["6", 2] } }你需要关注的字段是:
7.inputs.text(正向提示词)8.inputs.text(反向提示词)12.inputs.width/12.inputs.height(分辨率,若存在EmptyLatentImage节点)13.inputs.seed(随机种子,若需固定结果)
提示:在 ComfyUI 中双击节点可快速查看其输入字段名,避免 JSON 中拼写错误。
3. Python 远程调用实战:简洁、健壮、生产就绪
3.1 基础版:单次调用,同步等待结果
以下脚本适用于测试环境或低频调用场景,逻辑清晰,无外部依赖:
import requests import json import time import sys # 配置项(根据你的部署环境修改) BASE_URL = "http://192.168.1.100:8188" # 替换为你的服务器IP或域名 WORKFLOW_PATH = "./zimage_turbo_basic.json" def load_workflow(): with open(WORKFLOW_PATH, "r", encoding="utf-8") as f: return json.load(f) def submit_prompt(workflow_json): """提交工作流,返回 prompt_id""" response = requests.post( f"{BASE_URL}/prompt", json={"prompt": workflow_json}, timeout=10 ) if response.status_code != 200: raise Exception(f"提交失败: {response.status_code} {response.text}") return response.json()["prompt_id"] def get_history(prompt_id, max_wait=120): """轮询历史记录,直到生成完成或超时""" start_time = time.time() while time.time() - start_time < max_wait: try: resp = requests.get(f"{BASE_URL}/history/{prompt_id}", timeout=5) if resp.status_code == 200 and resp.json(): history = resp.json() if prompt_id in history: return history[prompt_id] except requests.RequestException: pass time.sleep(1) raise TimeoutError(f"等待超时 ({max_wait}s),prompt_id: {prompt_id}") def get_image_url(history_data): """从 history 数据中提取第一张输出图片的 URL""" for node in history_data.get("outputs", {}).values(): if "images" in node: img_info = node["images"][0] filename = img_info["filename"] subfolder = img_info.get("subfolder", "") type_ = img_info["type"] # 构造 view URL if subfolder: url = f"{BASE_URL}/view?filename={filename}&subfolder={subfolder}&type={type_}" else: url = f"{BASE_URL}/view?filename={filename}&type={type_}" return url raise ValueError("未在 history 中找到输出图片") # 主流程 if __name__ == "__main__": if len(sys.argv) < 2: print("用法: python api_call.py '你的中文提示词'") sys.exit(1) prompt_text = sys.argv[1] # 1. 加载工作流 workflow = load_workflow() # 2. 注入提示词(假设正向提示词节点ID为"7",反向为"8") workflow["7"]["inputs"]["text"] = prompt_text workflow["8"]["inputs"]["text"] = "低质量,模糊,畸变,文字错误" # 3. 提交任务 prompt_id = submit_prompt(workflow) print(f" 任务已提交,ID: {prompt_id}") # 4. 等待完成并获取图片URL try: history = get_history(prompt_id) image_url = get_image_url(history) print(f" 生成完成!图片地址: {image_url}") except (TimeoutError, ValueError) as e: print(f"❌ 生成失败: {e}")使用方式:python api_call.py "一只橘猫坐在窗台上,阳光洒落,写实摄影"
3.2 进阶版:支持并发与错误重试(生产推荐)
对于需要高吞吐的业务,建议封装为类,并加入重试、并发控制和日志:
import requests import json import time from concurrent.futures import ThreadPoolExecutor, as_completed from typing import List, Dict, Optional class ZImageAPIClient: def __init__(self, base_url: str, timeout: int = 30): self.base_url = base_url.rstrip("/") self.timeout = timeout def _request(self, method: str, path: str, **kwargs): url = f"{self.base_url}{path}" try: resp = requests.request(method, url, timeout=self.timeout, **kwargs) resp.raise_for_status() return resp except requests.RequestException as e: raise ConnectionError(f"API 请求失败 {method} {url}: {e}") def submit(self, workflow: Dict, prompt_id: Optional[str] = None) -> str: data = {"prompt": workflow} if prompt_id: data["client_id"] = prompt_id resp = self._request("POST", "/prompt", json=data) return resp.json()["prompt_id"] def wait_for_result(self, prompt_id: str, max_wait: int = 180) -> Dict: start = time.time() while time.time() - start < max_wait: try: resp = self._request("GET", f"/history/{prompt_id}") hist = resp.json() if prompt_id in hist and hist[prompt_id].get("status", {}).get("completed"): return hist[prompt_id] except: pass time.sleep(1) raise TimeoutError(f"等待超时 {max_wait}s") def get_image_url(self, history: Dict) -> str: for node in history.get("outputs", {}).values(): if "images" in node: img = node["images"][0] params = f"filename={img['filename']}&type={img['type']}" if img.get("subfolder"): params += f"&subfolder={img['subfolder']}" return f"{self.base_url}/view?{params}" raise ValueError("未找到输出图片") # 示例:批量生成 def batch_generate(client: ZImageAPIClient, workflow: Dict, prompts: List[str]): results = {} with ThreadPoolExecutor(max_workers=2) as executor: # 单卡建议 max_workers ≤ 2 future_to_prompt = { executor.submit(generate_single, client, workflow, p): p for p in prompts } for future in as_completed(future_to_prompt): prompt = future_to_prompt[future] try: url = future.result() results[prompt] = {"status": "success", "url": url} print(f" '{prompt[:20]}...' → {url}") except Exception as e: results[prompt] = {"status": "error", "message": str(e)} print(f"❌ '{prompt[:20]}...' → {e}") return results def generate_single(client: ZImageAPIClient, workflow: Dict, prompt_text: str) -> str: # 克隆工作流,注入提示词 wf_copy = json.loads(json.dumps(workflow)) wf_copy["7"]["inputs"]["text"] = prompt_text wf_copy["8"]["inputs"]["text"] = "低质量,模糊,畸变" pid = client.submit(wf_copy) history = client.wait_for_result(pid) return client.get_image_url(history) # 使用 if __name__ == "__main__": client = ZImageAPIClient("http://your-server-ip:8188") with open("zimage_turbo_basic.json") as f: wf = json.load(f) prompts = [ "敦煌飞天壁画风格,飘带飞扬,金色背景", "未来城市夜景,悬浮汽车,霓虹灯牌,赛博朋克", "手绘水彩风格,一束野花插在玻璃瓶中" ] results = batch_generate(client, wf, prompts)4. 常见问题排查指南:让调用不再“黑盒”
4.1 404 Not Found?检查路径与大小写
/prompt是小写,不是/Prompt或/PROMPT;GET /history/{id}中的{id}必须与POST /prompt返回的prompt_id完全一致(区分大小写);- 确保
BASE_URL末尾没有斜杠,否则可能变成http://x:8188//prompt。
4.2 500 Internal Error?大概率是工作流 JSON 错误
- 最常见原因:节点 ID 引用错误(如
"inputs": {"model": ["999", 0]},但 ID999不存在); - 次常见:参数类型错误(如
width输入了字符串"1024"而非数字1024); - 解决方法:先在 ComfyUI 界面中加载该 JSON,点击“Queue Prompt”,观察界面报错信息,再修正 JSON。
4.3 图片 URL 返回 404?路径权限或挂载问题
- ComfyUI 默认将图片存于
ComfyUI/output/目录; - 若镜像使用 Docker,确认该目录已正确挂载为卷(
-v /host/output:/root/ComfyUI/output); GET /view接口读取的是容器内路径,若挂载异常,文件虽生成但无法通过 HTTP 访问。
4.4 生成结果为空白或黑图?检查模型与采样器匹配
- Z-Image-Turbo必须搭配特定采样器(官方推荐
dpmpp_2m_sde_gpu+karrasscheduler); - 若工作流中误用了
euler或ddim,会导致采样崩溃或输出无效; - 在
KSampler节点中确认sampler_name和scheduler字段值正确。
5. 安全与生产化建议:从能用到好用
5.1 必做:添加基础访问控制
即使内网部署,也应防止意外暴露。最简方案是 Nginx 反向代理 + Basic Auth:
location / { proxy_pass http://127.0.0.1:8188; auth_basic "Z-Image API"; auth_basic_user_file /etc/nginx/.htpasswd; }生成密码文件:htpasswd -c /etc/nginx/.htpasswd youruser
调用时添加头:Authorization: Basic base64encode("user:pass")
5.2 推荐:使用请求体签名防篡改
对敏感业务(如电商图生成),可在请求体中加入时间戳+HMAC签名,服务端校验时效性与完整性,避免提示词被恶意篡改。
5.3 必须监控:记录关键指标
- 每次调用的
prompt_id、提示词长度、耗时、返回状态; - GPU 显存占用(可通过
nvidia-smi --query-compute-apps=pid,used_memory --format=csv,noheader,nounits定期采集); - 任务队列长度(
GET /queue返回的queue_running+queue_pending)。
6. 总结:远程调用不是终点,而是工程化的起点
Z-Image-ComfyUI 的远程 API 能力,其价值远不止于“用代码代替点击”。它标志着你已跨过实验阶段,正式进入可集成、可编排、可运维的工程实践轨道。
- 你拥有了确定性:同一份工作流 JSON,在任何环境都能复现相同结果;
- 你获得了可组合性:Z-Image 的提示词可由上游 NLP 模型动态生成,输出图片可自动送入下游审核系统;
- 你掌握了可控性:资源消耗、并发上限、超时策略、错误重试,全部由你定义;
- 你铺平了规模化路径:从单卡部署,到多实例负载均衡,再到混合云弹性伸缩,底层 API 接口保持完全一致。
现在,你手中已握有通往企业级 AIGC 服务的钥匙。下一步,是把它嵌入你的内容流水线、电商中台或设计协作平台——而这一切,都始于你刚刚成功调通的那一次POST /prompt。
--- > **获取更多AI镜像** > > 想探索更多AI镜像和应用场景?访问 [CSDN星图镜像广场](https://ai.csdn.net/?utm_source=mirror_blog_end),提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。