Overclocking Time's 「ベンチャー非正規雇用」の限界エンジニアゆるふわ技術ブログ!
  • Home
  • Blog
JA EN
☰ 目次
    2026年3月24日
    #生成AI#技術解説#ComfyUI

    ComfyUIサーバーで既存ワークフローをCLIから呼び出してパラメータ可変で使う方法

    ComfyUIサーバーで既存ワークフローをCLIから呼び出してパラメータ可変で使う方法

    ComfyUIはGUIで触っているだけでもとても強力ですが、実運用で特に頼もしいのは CLI・サーバー・ヘッドレス環境 での活用です。

    たとえば、こんな場面です。

    • GPUサーバー上でバッチ生成したい
    • WebアプリやBotの裏側からComfyUIを呼び出したい
    • 同じワークフローをプロンプトだけ変えて何百回も回したい
    • プロンプト / seed / steps / cfg / width / height などのパラメータを外部から差し替えたい

    そこで本記事では、自作ツールを題材に、普段GUIで作った既存ワークフローをそのまま取り込み、CLIからプロンプトや各種パラメータを動的に上書きして再利用する ところまで解説します。

    解説に使用するツールはGithubで公開しています。 利用、改変OKです。

    GitHub - suzuai-tech/comufyui_cli_workflow_builder
    Contribute to suzuai-tech/comufyui_cli_workflow_builder development by creating an account on GitHub.
    GitHub

    ComfyUIは「ノードUI」でもあり「生成サーバー」でもある

    032256.png ComfyUIはブラウザUIの印象が強いのですが、実体はPython製の生成サーバーです。

    ComfyUIを起動すると、通常は 8188 番ポートでHTTP APIとWebSocketが利用できるようになります。つまり、ブラウザから操作する代わりに、外部のPythonスクリプトやCLIツールからワークフローを渡して生成を実行する ことができます。

    この構成を取ると、ComfyUIで次のようなことができます。

    • ローカルPCではなくGPUサーバー上で生成
    • 生成指示だけを別プロセス・別マシンから送信
    • 進捗をWebSocketで監視

    ヘッドレスでの通信はどう書くの?

    ヘッドレス運用といっても、やること自体はそこまで難しくありません。 基本は HTTPでワークフローをキューに積み、WebSocketで進捗を受け取る だけです。

    たとえば、最小構成のイメージは次のようになります。

    import json
    import uuid
    import asyncio
    import requests
    import websockets
    
    COMFYUI_API_URL = "http://127.0.0.1:8188"
    CLIENT_ID = str(uuid.uuid4())
    
    workflow = {
        "3": {
            "class_type": "KSampler",
            "inputs": {
                "seed": 123456,
                "steps": 20,
                "cfg": 7.0,
                "sampler_name": "euler",
                "scheduler": "normal",
                "model": ["4", 0],
                "positive": ["6", 0],
                "negative": ["7", 0],
                "latent_image": ["5", 0],
            },
        }
    }
    
    async def main():
        payload = {"prompt": workflow, "client_id": CLIENT_ID}
        response = requests.post(f"{COMFYUI_API_URL}/prompt", json=payload, timeout=30)
        response.raise_for_status()
        prompt_id = response.json()["prompt_id"]
    
        ws_url = f"ws://127.0.0.1:8188/ws?clientId={CLIENT_ID}"
        async with websockets.connect(ws_url) as websocket:
            async for message in websocket:
                event = json.loads(message)
    
                if event.get("type") == "progress":
                    data = event.get("data", {})
                    print(f"progress: {data.get('value')} / {data.get('max')}")
    
                if event.get("type") == "executing":
                    data = event.get("data", {})
                    if data.get("prompt_id") == prompt_id and data.get("node") is None:
                        print("generation finished")
                        break
    
    asyncio.run(main())
    

    このサンプルでは、まず /prompt にワークフローをPOSTして生成キューへ積み、そのあと ws に接続して進捗イベントを受け取っています。

    実運用では、ここにさらに次の処理を足していく形になります。

    • UI形式ワークフローのAPI形式への変換
    • プロンプトや seed / steps の動的な上書き
    • 完成ファイルのダウンロード
    • エラーハンドリングやタイムアウト管理

    API運用で大事なのは「既存ワークフローの再利用」

    ComfyUIをAPIで使うだけなら、技術的にはそこまで難しくありません。 しかし、普段ComfyUIのGUIで作っているワークフローを、実運用に載せようとすると、次のような壁にぶつかりがちです。

    • seed や steps 、 プロンプトを実行ごとに変えたいがワークフローを投げるだけだと固定化される
    • 既存ワークフローが肥大化してパラメータが増大し、量産・自動化の資産として使い回しづらい
    • GUIで保存したJSONをそのままAPIに投げても動かない

    実際の利用においては 既存ワークフローを一度取り込めば、その後はCLIからプロンプトや主要パラメータを差し替えながら使い回せる ことが必要です。

    言い換えると、GUIでの試行錯誤を、そのままサーバー運用や自動生成の資産として活かせます。

    ツールの全体像

    GitHub - suzuai-tech/comufyui_cli_workflow_builder
    Contribute to suzuai-tech/comufyui_cli_workflow_builder development by creating an account on GitHub.
    GitHub

    本ツールの流れはとてもシンプルです。

    Step 1. ワークフローを登録する

    まず、ComfyUIで作った既存のワークフローJSONを取り込みます。

    • UI形式ならAPI形式へ変換
    • ワークフローごとのメタ情報を保存
    • プロンプトノード候補も検出

    Step 2. 生成時に値を注入する

    生成時には、保存済みワークフローに対して次を上書きします。

    • positive prompt
    • negative prompt
    • seed
    • steps
    • cfg
    • 必要に応じて node_id:field 形式の直接指定

    Step 3. ComfyUI APIへ投入する

    準備済みのワークフローをComfyUIに送り、WebSocketで進捗を受け取りながら、完成した画像や動画を保存します。

    CLI実行例

    リポジトリにはZimageTurboの生成サンプルもありますので参考にしてみてください。

    1. 既存のComfyUIワークフローを取り込む

    python comfyui_cli.py convert \
      --input "C:/path/to/workflow.json" \
      --workflow-id my_workflow \
      --name "My Workflow"
    

    このコマンドで、既存のワークフローを comfyui_workflows/my_workflow/ に保存します。

    ポイントは、入力がUI形式でもたいていはそのまま受け付けられる ことです。 毎回API形式で保存し直す前提にしなくてよいので、GUIで作ったワークフローをそのまま運用へ持ち込みやすくなります。

    とはいえまだノードによっては動かなかったり、調整の余地があるので上手くいかない場合はAPI形式のJSONを読み込ませてください

    2. プロンプトとパラメータをCLIから差し替えて生成

    python comfyui_cli.py generate \
      --workflow-id my_workflow \
      --prompt "a futuristic city at night, neon lights, cinematic, masterpiece" \
      --negative "low quality, blurry, text, watermark" \
    

    このとき行っていることは、単なる固定JSONの送信ではありません。

    • 保存済みワークフローを読み込む
    • プロンプトノードを自動検出して差し替える

    ということをしています。 つまり、ComfyUIのGUIで作ったワークフローを、CLIから再利用している わけです。

    3. 特定ノードの値をピンポイントで上書き

    python comfyui_cli.py generate \
      --workflow-id my_workflow \
      --prompt "product photo, white background, studio lighting" \
      --param seed=123456789 \
      --param steps=28 \
      --param cfg=7.0
      --param 5:width=1024 \
      --param 5:height=1024
    

    node_id:field=value で直接paramを指定できます。

    プロンプトノードを自動で見つける

    CLI化で困りやすいのは、どのノードにプロンプトを入れればよいかが毎回違うことです。

    本ツールでは core/comfyui_generator.py の PromptInjector.find_prompt_nodes() で、KSamplerから逆引きする形でプロンプトノードを見つけています。

    @staticmethod
    def find_prompt_nodes(workflow):
        result = {"positive": None, "negative": None}
    
        for node_id, node_data in workflow.items():
            if node_data.get("class_type") not in KSAMPLER_CLASS_TYPES:
                continue
    
            inputs = node_data.get("inputs", {})
    
            pos_ref = inputs.get("positive")
            if isinstance(pos_ref, list) and pos_ref:
                ref_id = str(pos_ref[0])
                if workflow[ref_id].get("class_type") in CLIP_TEXT_ENCODE_TYPES:
                    result["positive"] = ref_id
    
            neg_ref = inputs.get("negative")
            if isinstance(neg_ref, list) and neg_ref:
                ref_id = str(neg_ref[0])
                if workflow[ref_id].get("class_type") in CLIP_TEXT_ENCODE_TYPES:
                    result["negative"] = ref_id
    
            if result["positive"] or result["negative"]:
                break
    
        return result
    

    この方式の利点はとても分かりやすいです。

    • ノードIDを人間が覚えなくてよい
    • ワークフローごとに違う構造へある程度追従できる
    • CLI利用者が --prompt だけ指定すれば済む

    つまり、ワークフロー内部構造の知識をCLIの利用者に強く求めない 設計になっています。

    UI形式ワークフローをAPI形式へ変換してみる

    一般的に技術記事や画像メタデータに含まれるJSONデータはGUI形式で共有されるので、なるべきそのまま使えるようにGUI由来のJSONをAPI実行可能な形へ変換すること を試してみました。

    コアは core/comfyui_generator.py の WorkflowLoader.convert_ui_to_api() です。

    @staticmethod
    def convert_ui_to_api(ui_data, object_info=None):
        nodes = ui_data.get("nodes", [])
        links_list = ui_data.get("links", [])
    
        link_map = {}
        for link in links_list:
            link_map[link[0]] = [str(link[1]), link[2]]
    
        api_workflow = {}
    
        for node in nodes:
            node_id = str(node["id"])
            class_type = node.get("type", "")
    
            if class_type in {"Note", "MarkdownNote", "Reroute", "PrimitiveNode"}:
                continue
    
            api_inputs = {}
            for inp in node.get("inputs", []):
                name = inp.get("name", "")
                link_id = inp.get("link")
                if link_id is not None and link_id in link_map:
                    api_inputs[name] = list(link_map[link_id])
    
            api_workflow[node_id] = {
                "class_type": class_type,
                "inputs": api_inputs,
            }
    

    1. linksを辿ってノード間接続を再構築する

    ComfyUIのUI形式JSONでは、どの出力がどの入力に繋がっているかが links 配列で表現されています。 API形式では、これを ノードIDベースの inputs 辞書 に組み直す必要があります。

    2. 表示専用ノードを落とす

    Note や Reroute のような見た目補助ノードは、API実行には不要です。 これらをそのまま持ち込むと失敗の原因になりやすいため、変換段階で除外しています。

    3. widget値も入力へ写像する

    実装では widgets_values と object_info を使って、リンクでは渡されない数値や文字列入力も inputs に落とし込んでいます。

    これにより、GUI用の保存JSONを、CLIやサーバーから実行できるJSONへ変換 できます。

    seed や steps`を動的に注入する

    seed や steps を柔軟に変えたり必要に応じて固定できたりすると嬉しいですよね。

    その処理は PromptInjector.inject() にまとまっています。

    @staticmethod
    def inject(workflow, positive_prompt=None, negative_prompt=None, meta=None, extra_params=None):
        wf = copy.deepcopy(workflow)
    
        detected = PromptInjector.find_prompt_nodes(wf)
        pos_node_id = detected["positive"]
        neg_node_id = detected["negative"]
    
        if positive_prompt is not None and pos_node_id and pos_node_id in wf:
            wf[pos_node_id]["inputs"]["text"] = positive_prompt
    
        if negative_prompt is not None and neg_node_id and neg_node_id in wf:
            wf[neg_node_id]["inputs"]["text"] = negative_prompt
    
        if extra_params:
            for key, value in extra_params.items():
                if ":" in key:
                    node_id, field_name = key.split(":", 1)
                    if node_id in wf:
                        wf[node_id]["inputs"][field_name] = value
                    continue
    
                for node_id, node_data in wf.items():
                    if node_data.get("class_type") in KSAMPLER_CLASS_TYPES:
                        if key in node_data.get("inputs", {}):
                            wf[node_id]["inputs"][key] = value
                            break
    
        return wf
    

    ここで対応している上書き方法は、主に3種類あります。

    1. プロンプトの自動差し替え

    • positive prompt
    • negative prompt

    2. 汎用パラメータ名での上書き

    たとえば steps=30 や cfg=7.5 を指定すると、KSampler系ノードから該当フィールドを探して注入してくれます。

    3. node_id:field形式の直接指定

    ワークフローごとに個別の入力を触りたい場合は、たとえば 5:width=1024 のように直接指定できます。

    この3段構えにしているおかげで、

    • よく使う操作は簡単に
    • 変則的なワークフローにも対応可能に

    というバランスにしています。

    まとめ

    ComfyUIをCLI・サーバー・ヘッドレスで使うこと自体は、APIを叩けば比較的簡単に実現できます。 でも、実運用で本当に価値が出る必要なのはその先です。

    既存ワークフローをそのまま取り込み、CLIからプロンプトや主要パラメータを差し替えて再利用できること。 ここが整うと、ComfyUIは単なるGUIツールではなく、運用しやすい生成基盤になってくれます。

    今回のツールで押さえているポイントは次の通りです。

    1. ComfyUIをサーバーとして使う前提で設計している
    2. 既存ワークフローを取り込み、必要に応じてUI形式からAPI形式へ変換できる
    3. プロンプトノードを自動検出し、CLIから自然に上書きできる
    4. seed / steps / cfg などを動的に注入できる
    5. 進捗監視とファイル保存まで一連で扱える

    ComfyUIを資産を生かして自動化したいなら、重要なのは「APIで呼べること」そのものではなく、GUIで作ったワークフローを運用で使い回せること ですね。

    airi

    airi

    都内 ❘ 27♀ ❘ 非正規 ♡インターネットエンジェル♡ 趣味はダンス(お休み中)とゲーム ベンチャーを転々としています

    関連記事

    • 【Bookworm】ラズパイのSSHが1日で繋がらなくなる?Raspberry Pi Connectで見つけた真犯人とnmcliによる恒久対策
      【Bookworm】ラズパイのSSHが1日で繋がらなくなる?Raspberry Pi Connectで見つけた真犯人とnmcliによる恒久対策
    • 漫画のセリフとキャプションを自動配置するツールをYOLOモデルを使って作ってみた
      漫画のセリフとキャプションを自動配置するツールをYOLOモデルを使って作ってみた
    • ローカル完結!Qwen3.5を用いた画像データセット用キャプション自動生成ツールの作り方
      ローカル完結!Qwen3.5を用いた画像データセット用キャプション自動生成ツールの作り方
    • Ollama APIと履歴 / VRAM管理を自前実装!公式ライブラリの依存を断ち切る!
      Ollama APIと履歴 / VRAM管理を自前実装!公式ライブラリの依存を断ち切る!
    • yolo26を使ってデータセット作成を効率化する
      yolo26を使ってデータセット作成を効率化する

    最新記事

    • 【Bookworm】ラズパイのSSHが1日で繋がらなくなる?Raspberry Pi Connectで見つけた真犯人とnmcliによる恒久対策
      【Bookworm】ラズパイのSSHが1日で繋がらなくなる?Raspberry Pi Connectで見つけた真犯人とnmcliによる恒久対策
    • 漫画のセリフとキャプションを自動配置するツールをYOLOモデルを使って作ってみた
      漫画のセリフとキャプションを自動配置するツールをYOLOモデルを使って作ってみた
    • ローカル完結!Qwen3.5を用いた画像データセット用キャプション自動生成ツールの作り方
      ローカル完結!Qwen3.5を用いた画像データセット用キャプション自動生成ツールの作り方
    • Ollama APIと履歴 / VRAM管理を自前実装!公式ライブラリの依存を断ち切る!
      Ollama APIと履歴 / VRAM管理を自前実装!公式ライブラリの依存を断ち切る!
    • yolo26を使ってデータセット作成を効率化する
      yolo26を使ってデータセット作成を効率化する
    Overclocking Time's

    「ベンチャー非正規雇用」の限界エンジニアゆるふわ技術ブログ!

    Navigation

    Home Blog Privacy Policy

    Connect

    About Connect

    © 2026 Overclocking Time's. All rights reserved.