AI活用

n8n・Ollama・ComfyUIをつなぐ|人の承認を挟む画像生成フロー

n8nをオーケストレーター、Ollamaを解釈、ComfyUIを生成に分け、文章の依頼から画像生成までを自動化する構成を解説。Schema検証・Job IDの保存・Waitノードによる人の承認ゲート・途中からの再開まで、1件を却下から承認まで通す手順付き。

この記事の目次
  1. 結論:3つの役割を混ぜず、承認ゲートを初期版から置く
  2. 3つの役割
  3. n8nで入力を受け取る
  4. Ollamaで構造化する
  5. ComfyUIへJobを投入する
  6. 人の承認
  7. 生成→却下→修正→再生成→承認を1件通す
  8. 途中から再開する
  9. よくある質問
  10. n8nのAI Agentノードにこの全体を任せられませんか?
  11. 承認を省いて完全自動にしてよい条件はありますか?
  12. Ollama Chat Modelノードを使ってはいけませんか?
  13. まとめ

n8n・Ollama・ComfyUIをつなぐときは、n8nを「順番と記録を握る係」、Ollamaを「文章を設定JSONに解釈する係」、ComfyUIを「画像を作る係」に分け、公開や納品の直前にWaitノードで人の承認を必ず挟みます。各ステップの入力と出力をexecution dataに残しておけば、却下された案件を途中から再開できます。

情報確認日:2026年8月22日(日本時間)

結論:3つの役割を混ぜず、承認ゲートを初期版から置く

責任範囲

  • n8n=Orchestrator、Ollama=Interpret、ComfyUI=Generateの役割分担
  • Webhook・Form・手動Triggerから入力の受け口を1つ選ぶ基準
  • Schema検証を通った設定JSONだけを次段へ渡す配線
  • ComfyUIへのJob投入とprompt_idの保存
  • Waitノードによる承認ゲートと、却下→修正→再生成の流れ
  • 各ステップの入出力を保存して途中から再開する設計

n8nのAI Agentノードの設定やメモリ、承認の一般論はn8nでAIエージェントを作る方法に、OllamaとComfyUIの役割分担とJSONを安全に渡す設計はOllamaとComfyUIを連携する方法にあります。n8nの導入手順、OllamaとComfyUIの基礎はこの記事では扱いません。

スポンサーリンク

3つの役割

3つのツールは得意なことが違い、役割を混ぜると障害の原因が追えなくなります。LLMに実行順序を決めさせたり、n8nのCodeノードで画像生成の設定を作文したりしないのが原則です。

役割 担当 やること やらないこと
Orchestrator n8n 順番・分岐・待機・記録・再試行 設定の内容を決める
Interpret Ollama 依頼文を固定Schemaの設定JSONに変換 実行順序の決定・API呼び出し
Generate ComfyUI 設定JSONを受けて画像を生成 依頼文の解釈
  1. Trigger(依頼を受け取る)
  2. HTTP Request → Ollama /api/chatformatにSchema)
  3. Code(Schema検証・許可値チェック)
  4. HTTP Request → ComfyUI /promptprompt_idを保存)
  5. Wait(完了待ち)→ HTTP Request → /history/{prompt_id}/viewで画像取得
  6. Wait(On Webhook Call / On Form Submitted)で人の承認
  7. 承認なら保存先へ、却下なら修正指示を付けて2.または4.へ戻る

n8nで入力を受け取る

入り口は1つに絞ります。複数の入り口を最初から作ると、依頼の形式がそろわず、後段のSchema検証が入り口ごとに分岐します。

Trigger 向く場面 注意
Manual Trigger 最初の検証。自分で依頼文を貼って1件ずつ通す 本番の入り口にはしない
Form Trigger 社内の人が依頼を入力する。用途・対象を選択式にできる 自由文欄は1つに絞る
Webhook 他システムから依頼JSONが飛んでくる 認証と、受け取るフィールドの上限を先に決める

受け取った依頼には、この時点でrequest_idを振ります。以降のすべてのステップの記録をこのIDに紐づけ、却下や再開のときの検索キーにします。

Ollamaで構造化する

OllamaにはHTTP Requestノードから/api/chatを直接呼びます。n8nには「Ollama Chat Model」ノードもありますが、ここではformatにJSON Schemaを確実に渡し、応答をそのまま次のCodeノードで検証したいため、HTTP Requestを使います。

{
  "model": "<使用モデル>",
  "stream": false,
  "messages": [
    { "role": "system", "content": "依頼文を画像生成の設定に変換します。Schema以外のフィールドを作らず、不明な項目はunspecifiedにします。" },
    { "role": "user", "content": "{{ $json.request_text }}" }
  ],
  "format": {
    "type": "object",
    "properties": {
      "subject": { "type": "string" },
      "style_preset": { "type": "string", "enum": ["photo", "illustration", "flat", "unspecified"] },
      "aspect": { "type": "string", "enum": ["1:1", "4:5", "16:9", "unspecified"] },
      "negative_preset": { "type": "string", "enum": ["default", "no_text", "no_people", "unspecified"] }
    },
    "required": ["subject", "style_preset", "aspect", "negative_preset"]
  }
}

応答のJSONは、formatを付けていても必ず検証します。Codeノードで「必須フィールドが揃っているか」「enumの値が許可リストにあるか」「subjectに禁止語が含まれないか」を確認し、通らなければ次へ進めず、そのrequest_idを人の確認に回します。

// Code ノード(Run Once for Each Item)
const allowed = {
  style_preset: ["photo", "illustration", "flat"],
  aspect: ["1:1", "4:5", "16:9"],
  negative_preset: ["default", "no_text", "no_people"],
};
const cfg = JSON.parse($json.message.content);
const errors = [];
for (const [key, values] of Object.entries(allowed)) {
  if (!values.includes(cfg[key])) errors.push(`${key}=${cfg[key]}`);
}
if (!cfg.subject || cfg.subject.length > 200) errors.push("subject");
return [{ json: { request_id: $('Trigger').item.json.request_id, cfg, valid: errors.length === 0, errors } }];

unspecifiedは検証で落とす側に入れています。LLMが決められなかった項目を既定値で埋めて進めると、後で「なぜこの設定か」が追えなくなるためです。

ComfyUIへJobを投入する

検証を通った設定を、あらかじめ用意したworkflow JSON(API形式)の該当ノードに差し込み、/promptにPOSTします。差し込む場所はノードIDとフィールド名で固定し、LLMの出力をworkflow全体に触れさせません。

// Code ノード:テンプレートに値を差し込む
const tpl = JSON.parse(JSON.stringify($('Template').item.json.workflow));
const cfg = $json.cfg;
const size = { "1:1": [1024, 1024], "4:5": [832, 1040], "16:9": [1344, 768] }[cfg.aspect];
tpl["6"].inputs.text = `${PRESET_POSITIVE[cfg.style_preset]}, ${cfg.subject}`;
tpl["7"].inputs.text = PRESET_NEGATIVE[cfg.negative_preset];
tpl["5"].inputs.width = size[0];
tpl["5"].inputs.height = size[1];
return [{ json: { request_id: $json.request_id, body: { prompt: tpl, client_id: $json.request_id } } }];

/promptの応答にはprompt_idが返ります。これをrequest_idとセットで保存します。n8nのexecution dataに残るだけでなく、Set ノードでrequest_idprompt_id・投入時刻・設定JSONをひとまとまりにして外部(DBやSheet)にも書いておくと、n8nのexecutionが消えた後も追えます。

完了の検知は、WaitノードでAfter Time Intervalを置いてから/history/{prompt_id}を取得し、outputsが空なら再度待つ、というループで十分です。並列投入やキャンセル、再試行の設計はComfyUI APIのQueueを制御するに分けています。画像は/historyで得たfilenamesubfoldertype/viewに渡し、HTTP RequestのResponse FormatをFileにして受け取ります。

人の承認

初期版では、生成した画像を自動で公開・納品しません。WaitノードのOn Webhook CallかOn Form Submittedで承認を待ちます。n8nの公式ドキュメントによると、Waitノードは再開用URLを$execution.resumeUrlで参照でき、これを通知(メール・チャット)に埋め込めば、承認者がリンクを開くだけでフローが再開します。

承認フォームの項目(On Form Submitted)
- 判定: 承認 / 却下
- 却下理由(選択式): 被写体が違う / 構図 / 色 / 文字が入った / その他
- 修正指示(自由文・任意)
- 表示: request_id, prompt_id, 画像, 設定JSON

却下理由を選択式にしているのは、次の分岐をコードで決めるためです。「被写体が違う」ならOllamaの解釈からやり直し、「構図」や「色」ならプリセットを変えてComfyUIの投入からやり直す、と戻る場所を理由ごとに固定します。自由文の修正指示は、LLMに渡すのではなく人が読む欄として残します。

Waitノードの時間制限はn8nサーバーの時刻で動き、期限を過ぎると次へ進みます。承認待ちに制限を付ける場合、制限切れの分岐は「自動承認」ではなく「未承認として停止」にしてください。

生成後に定型項目を自動で観察し、人が見る候補を減らす方法はローカルVLMで画像を分析してComfyUIへ渡すに分けています。VLMの観察を挟んでも、最終の承認は人が行う前提は変わりません。

生成→却下→修正→再生成→承認を1件通す

動作確認は、承認で終わる1件ではなく、一度却下してから承認に至る1件で行います。戻りの配線と記録はこの経路でしか確かめられません。

段階 確認すること 記録(request_id固定)
1. 依頼投入 request_idが振られたか
2. Ollama解釈 Schema検証を通ったか、落ちた項目は何か
3. ComfyUI投入 prompt_idが保存されたか
4. 画像取得 /historyのoutputsから取れたか
5. 却下 理由に応じた戻り先へ分岐したか
6. 再生成 前回と変えた項目は1つだけか、新しいprompt_idが記録されたか
7. 承認 承認者・時刻・採用prompt_idが残ったか

途中から再開する

長いフローは途中で止まります。Ollamaが応答しない、ComfyUIがVRAM不足で落ちる、承認者が数日開かない、のどれも起きます。最初からやり直すのではなく、止まった段階から再開できるように、各ステップの入力と出力をrequest_id単位で保存します。

  1. 各ステップの出力を外部に書く。解釈結果の設定JSON、prompt_id、画像のfile名を、ステップ完了のたびに保存する
  2. 再開の入り口を分ける。「設定JSONがある」なら投入から、「prompt_idがある」なら画像取得から、と再開位置を記録で判定する
  3. 同じ入力で同じ結果になるようにする。ComfyUI投入はseedを固定して記録に含め、再投入しても同じ画像が得られる状態にする
  4. 重複投入を防ぐ。再開時にprompt_idが既にあれば/promptを呼ばず、/historyの確認から始める
  5. 失敗を記録に残す。HTTP Requestのエラー出力を捨てず、request_id・段階・エラー本文を保存してから停止する

「同じ入力で同じ結果」を保つのは、再開のたびに違う画像が出て承認者が混乱するのを防ぐためです。seedの固定は、この冪等性を画像生成で成り立たせる最小の条件です。

よくある質問

n8nのAI Agentノードにこの全体を任せられませんか?

初期版では勧めません。Agentに順序を決めさせると、却下後にどこへ戻ったかが実行ごとに変わり、記録と再開が成り立ちません。順序はn8nの配線で固定し、LLMは解釈だけに使います。

承認を省いて完全自動にしてよい条件はありますか?

この記事の範囲では扱いません。まず承認付きで運用し、却下理由の記録がたまってから、どの種類の依頼なら自動化できるかを判断します。

Ollama Chat Modelノードを使ってはいけませんか?

使えます。ここでHTTP Requestを選んだのは、formatのSchema指定と応答の検証を自分の手で確実に行うためです。ノードの設定項目は版で変わるため、使う場合は公式ドキュメントで現行の項目を確認してください。

まとめ

n8nが順番と記録を、Ollamaが依頼文の解釈を、ComfyUIが生成を担当し、役割を混ぜないことが全体の安定につながります。Schema検証を通った設定だけをテンプレートに差し込んで/promptに投入し、request_idprompt_idをセットで保存します。承認はWaitノードで必ず挟み、却下理由ごとに戻り先を固定し、各ステップの出力を外部に残して途中から再開できる形にしてください。最初の動作確認は、一度却下してから承認に至る1件で行います。

スポンサーリンク