AI活用

ローカルVLMで画像を分析してComfyUIへ渡す|自動修正ループの作り方

ComfyUIの生成画像をOllamaのVisionモデルで分析し、固定Schemaの評価JSONから限定的な再生成指示に変換する手順を解説。最大回数・改善閾値・人の承認で無限ループを防ぎ、VLMと人の判定のずれを測る記録表も用意します。

この記事の目次
  1. 結論:VLMに「観察」だけさせ、「判断」と「停止」は仕組み側が持つ
  2. VLMを生成後チェックに使う
  3. 画像をVisionモデルに渡す
  4. 評価項目を構造化する
  5. 修正指示に変換する
  6. 無限再生成を防ぐ
  7. VLMの不得意を測る
  8. よくある質問
  9. VLMに「この画像は合格か」と直接聞いてはいけませんか?
  10. どのVisionモデルを使えばよいですか?
  11. 再生成のたびにpromptをLLMに書き直させてはいけませんか?
  12. まとめ

ローカルVLM(Visionモデル)は、ComfyUIの生成画像を「合否を決める審査員」ではなく「決まった項目を観察して報告する係」として使うと安定します。Ollamaの/api/chatに画像と固定JSON Schemaを渡し、返ってきた評価を限られたパラメータの変更だけに変換し、回数上限と人の承認で止まる仕組みにします。

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

結論:VLMに「観察」だけさせ、「判断」と「停止」は仕組み側が持つ

責任範囲

  • VLMを生成後チェックのどこに置くか(観察補助として)を決める
  • Ollama Visionの現行入力形式で画像を渡す最小例を示す
  • 評価項目を固定Schemaで返させ、自由文を排除する
  • 評価結果をプリセットや限定パラメータの変更に変換する
  • 最大回数・改善閾値・人の承認で無限再生成を防ぐ
  • VLMと人の判定のずれを同じ画像で測る記録表を用意する

OllamaとComfyUIの役割分担や、LLMにJSONだけを作らせて実行はこちらのコードが握る設計はOllamaとComfyUIを連携する方法で説明済みです。Schema検証付きでJSONを受け取る実装はOllamaのStructured OutputsでComfyUI設定JSONを作るに分けています。この記事は「生成した画像を見て、次の一手を決める」部分だけを扱い、VLMの一般的なベンチマークと、人が関与しない完全自律のAgentは扱いません。

スポンサーリンク

VLMを生成後チェックに使う

VLMを置く位置は、ComfyUIの出力と人の確認の間です。役割は「被写体は写っているか」「指定した色か」「文字が入っていないか」といった、人が毎回目視していた定型項目を先に拾うことで、人は例外だけを見ればよくなります。

  1. ComfyUIが画像を生成する(/history/{prompt_id}で出力file名を取得)
  2. 画像をOllamaのVisionモデルに渡し、固定Schemaの観察結果を受け取る
  3. 観察結果をコード側のルールで「再生成する/しない/人に回す」に分類する
  4. 再生成する場合は、許可したパラメータだけを変えて再投入する
  5. 上限回数に達したか、ルールで判定できない場合は人の承認待ちに入る

VLMを「絶対評価器」にしない理由は2つあります。1つは、美的な良し悪しの判定が人と一致する保証がないこと。もう1つは、VLMの出力を直接「合格」として扱うと、誤判定がそのまま公開や納品に流れることです。VLMには事実の観察だけを報告させ、合否の線引きはコード側のルールと人が持ちます。

画像をVisionモデルに渡す

Ollamaの公式ドキュメント(2026年8月時点)では、/api/chatのmessageにimages配列を入れ、REST APIではbase64で渡します。PythonとJavaScriptの公式ライブラリでは画像のpathをそのまま渡せます。モデルは公式の一覧でvisionタグが付いたものから選びます。公式ページの例示はgemma4ですが、どのモデルが自分の用途に合うかは後述の記録表で測ってください。

IMG=$(base64 -i output.png)
curl -X POST http://localhost:11434/api/chat \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemma4",
    "messages": [{
      "role": "user",
      "content": "この画像を観察して、指定した項目だけを報告してください。",
      "images": ["'"$IMG"'"]
    }],
    "stream": false
  }'

ComfyUI側の出力画像は、/history/{prompt_id}の応答に含まれるfilenamesubfoldertype/viewに渡して取得します。Pythonなら次の流れです。

import base64, requests
from ollama import chat

def fetch_output(prompt_id, base="http://127.0.0.1:8188"):
    hist = requests.get(f"{base}/history/{prompt_id}").json()[prompt_id]
    img = next(o for out in hist["outputs"].values() for o in out.get("images", []))
    r = requests.get(f"{base}/view", params={
        "filename": img["filename"], "subfolder": img["subfolder"], "type": img["type"]})
    return r.content

png = fetch_output(prompt_id)
res = chat(model="gemma4", messages=[{
    "role": "user",
    "content": "観察項目だけを報告してください。",
    "images": [png],
}])

画像の解像度を落としてから渡すかどうかは、観察したい項目によります。小さな文字や細部の破綻を見たいなら原寸、構図や色なら縮小で十分です。縮小した場合は「縮小後の画像で観察した」ことをログに残します。

評価項目を構造化する

自由文で「どう思うか」を聞くと、毎回違う観点で違う長さの答えが返り、コードで扱えません。OllamaのformatにJSON Schemaを渡すと、Visionモデルでも同じ形で返せます。公式ドキュメントには「Vision models accept the same format parameter, enabling deterministic descriptions of images.」とあります。

{
  "type": "object",
  "properties": {
    "subject_present": { "type": "boolean" },
    "subject_count": { "type": "integer" },
    "dominant_colors": { "type": "array", "items": { "type": "string" } },
    "composition": { "type": "string", "enum": ["centered", "left", "right", "top", "bottom", "unclear"] },
    "text_detected": { "type": "boolean" },
    "visible_defects": { "type": "array", "items": { "type": "string", "enum": ["extra_limbs", "distorted_face", "cropped_subject", "watermark", "none"] } },
    "confidence": { "type": "number" }
  },
  "required": ["subject_present", "subject_count", "dominant_colors", "composition", "text_detected", "visible_defects", "confidence"]
}

項目を決めるときの原則は「目で見て真偽が決まること」だけに限ることです。subject_presenttext_detectedは人が見ても同じ答えになりますが、「美しいか」「雰囲気が合うか」は人同士でも割れます。割れる項目はSchemaに入れず、人の確認に残します。enumで選択肢を固定しておくと、後段の変換ルールが書きやすくなります。

修正指示に変換する

VLMの観察結果を、そのまま「promptを書き直して」と自由文でLLMに戻すと、何が変わったか追えなくなります。観察結果から変えるのは、事前に許可した少数のパラメータかプリセットだけにします。

観察結果 許可する変更 変えないもの
subject_present: false seedを変えて再生成(promptは固定) prompt・CFG・steps
subject_countが指定と違う 数を表すpromptの一語だけ差し替え(プリセット) その他のprompt・seed
text_detected: true negative promptのプリセット「no text」を追加 positive prompt
visible_defectsdistorted_face 人の確認に回す(自動再生成しない) すべて
confidenceが低い 人の確認に回す すべて
ALLOWED = {"seed", "negative_preset", "count_word"}

def plan_fix(obs, spec):
    if obs["confidence"] < spec["min_confidence"]:
        return {"action": "review"}
    if not obs["subject_present"]:
        return {"action": "regen", "change": {"seed": "next"}}
    if obs["text_detected"]:
        return {"action": "regen", "change": {"negative_preset": "no_text"}}
    if "distorted_face" in obs["visible_defects"]:
        return {"action": "review"}
    return {"action": "accept_candidate"}

変更は一度に1つです。seedとnegative promptを同時に変えると、改善したときにどちらが効いたか分からず、次の画像で同じ手が使えません。

無限再生成を防ぐ

「合格するまで回す」ループは、VLMの判定が揺れるだけで止まらなくなります。止める条件を3つ、先に置きます。

  1. 最大回数。1依頼あたりの再生成回数に上限を置く。上限に達したら、最後の候補ではなく全候補を人に見せる
  2. 改善閾値。前回と同じ観察結果が2回続いたら、同じ変更を繰り返さずに止める。「変えたのに観察が変わらない」は、その変更が効いていない証拠
  3. 人の承認。自動で「採用」まで進めない。VLMがaccept_candidateを返した画像も、公開や納品の前に人が確認する
MAX_ROUNDS = 3
history = []
for round_no in range(MAX_ROUNDS):
    obs = observe(image)              # VLM 観察
    history.append(obs)
    plan = plan_fix(obs, spec)
    if plan["action"] != "regen":
        break
    if len(history) >= 2 and history[-1] == history[-2]:
        plan = {"action": "review"}   # 改善なし → 人へ
        break
    image = regenerate(plan["change"])
# ここで必ず人の承認待ちに入る。自動で公開しない

上限回数の「適切な値」は用途次第です。1枚あたりの生成時間と、人が見られる候補枚数から逆算して決め、最初は小さく置きます。

VLMの不得意を測る

自分の用途でVLMを信用してよい項目と、信用してはいけない項目は、実際に測らないと分かりません。同じ10枚を、VLMと人が同じSchemaで評価し、項目ごとの一致率を出します。

手順
1. 生成済み画像を10枚選ぶ(良い例・悪い例を混ぜる)
2. 人が先にSchemaの各項目を記入する(VLMの結果を見ない)
3. 同じ画像をVLMに渡し、同じSchemaで観察させる
4. 項目ごとに一致/不一致を数える
5. 不一致の画像は、どちらが正しいかを人が再確認する
項目 一致(/10) VLMが誤り 人が誤り 自動判定に使うか
subject_present
subject_count
dominant_colors
composition
text_detected
visible_defects

不一致が出やすい項目の傾向は、機構から予想できます。画像の中の文字は「文字がある」ことは分かっても、綴りが正しいかの判定はモデルと解像度に左右されます。手指の本数や細かな破綻は、縮小画像では見落とします。「雰囲気が合うか」のような美的評価は、そもそも人同士でも一致しません。どの項目を自動判定に使うかは、この表で一致率が高かったものだけに限り、不一致が多い項目は人の確認に戻します。結果はモデル名とOllamaのversion、画像の解像度とセットで記録してください。モデルを替えたら測り直します。

よくある質問

VLMに「この画像は合格か」と直接聞いてはいけませんか?

聞くこと自体はできますが、その答えをそのまま採用判定に使わないでください。合否の基準はモデルの内部にあって確認できず、画像ごとに揺れます。観察項目に分解し、合否の線引きはコード側のルールと人が持つ形にします。

どのVisionモデルを使えばよいですか?

公式の一覧でvisionタグが付いたモデルから、VRAMに載るものを選び、上の記録表で自分の項目の一致率を測って決めます。モデル間の精度は用途と画像で変わるため、この記事では特定モデルを推奨しません。

再生成のたびにpromptをLLMに書き直させてはいけませんか?

初期の段階では避けてください。promptの自由な書き換えは、変わった箇所が追えず、改善の再現ができません。許可したパラメータとプリセットだけを変える方式で安定してから、範囲を広げます。

まとめ

ローカルVLMは、ComfyUIの生成画像を固定Schemaで観察し、許可した少数のパラメータ変更に変換する「観察係」として使うと安定します。Ollamaの/api/chatimagesformatを渡せば、同じ形の観察結果が得られます。最大回数・改善閾値・人の承認の3つで必ず止まる仕組みにし、どの項目を自動判定に使うかは同じ10枚で人と比べた一致率で決めてください。この流れをn8nで他の処理とつなぐ方法は別記事で扱います。

スポンサーリンク