Difyのループが最大回数まで回るときは、終了条件の値が更新されているかを先に確認します。 子ノードが新しい値を出したことと、その値が次の周回へ渡るループ変数に代入されたことは別です。また、最大回数で処理が止まっても、目的の内容が完成したとは限りません。
この記事は、Difyで反復処理を組み、終了条件が成立しない、出力が一つ前の内容になる、上限到達を成功と扱ってよいか分からない人向けです。架空の社内勉強会の案内文を使い、必要な項目が揃ったかを確認する小さな設計を考えます。
公式情報の確認日:2026年10月30日。Loopの公式資料とVariable Assignerの資料を参照しています。本稿ではDify Cloud上のワークフローや有料モデルを実行していません。掲載するPythonは、設計上の期待を整理する参照モデルをローカルで確認したものです。Dify内部の実行タイミングを再現したコードではありません。
終了条件の前に、毎周どの値が変わるかを確認する
配列を処理するイテレーションと、状態を引き継ぐループを分ける
同じ処理を繰り返す場合でも、毎回の入力が独立しているかどうかで構成が変わります。十件の問い合わせを一件ずつ分類するなら、それぞれを独立して扱うイテレーションが候補になります。一つの案内文を、前回の修正版を受け取って更新するなら、状態を引き継ぐループが候補です。
ここでの目的は、六件、十件といった固定の配列をすべて処理することではありません。一つの案内文について、不足している項目を補い、その結果を次に確認する流れです。前回の修正を捨てて毎回初期文から作り直すと、周回は進んでも内容が積み上がらないことがあります。
まず「今回直した内容を、次も使うか」を紙へ書きます。使うなら、持ち回す値を具体的にします。本文、処理回数、不足項目数、要確認の状態などを別に持つと、どこで進捗が止まっているかを調べやすくなります。本文にdoneという単語が含まれるかだけでは、状態を説明しにくくなります。
| やりたいこと | 候補 | 今回の確認点 |
|---|---|---|
| 複数の資料をそれぞれ変換する | イテレーション | 入力の各要素と出力件数の対応 |
| 一つの案内文を前回の修正から直す | ループ | 更新した本文を次の周回へ渡す |
| 確定した条件で一度だけ振り分ける | IF/ELSE | 反復処理を追加する必要があるか |
配列の独立処理が目的だった場合は、イテレーションの出力件数と失敗の確認も参考になります。ループを使うこと自体が目標ではありません。どの入力がどの結果へつながるかを、より小さい構成で確かめられる方法を選んでください。
初期値と成功の条件を、架空の案内文で固定する
教材の初期文は、日時だけを書いた架空の社内勉強会の案内です。必要な項目は日時、会場、申込締切の三つとします。ここでは、一回の修正で不足する見出しを一つずつ追加するという、実行結果を予想できる規則を使います。モデルがどのような文章を生成するかという実験ではありません。
案内の日時や会場はすべて作り物で、実在する催しの募集情報ではありません。また、見出しが三つあることは、情報が正しいことや文章が読みやすいことまで意味しません。ここでは停止と更新対象の理解を目的に、判定する範囲を見出しの有無へ絞っています。
required = ["日時:", "会場:", "申込締切:"]
initial_text = "架空の社内勉強会\n日時:2026-11-06 13:00"
fixture_lines = {
"会場:": "会場:第2会議室(教材用の架空設定)",
"申込締切:": "申込締切:2026-11-04 17:00(教材用)",
}
def missing_items(text):
return [name for name in required if name not in text]
def revise_fixture(text):
missing = missing_items(text)
if not missing:
return text
return text + "\n" + fixture_lines[missing[0]]
assert missing_items(initial_text) == ["会場:", "申込締切:"]
first_text = revise_fixture(initial_text)
second_text = revise_fixture(first_text)
assert missing_items(first_text) == ["申込締切:"]
assert missing_items(second_text) == []
Difyで試す場合も、いきなり長い原稿と複数の外部ツールを組み合わせず、最初に同様の固定入力と期待値を用意します。初期文で不足二項目、一回目で不足一項目、二回目で不足なし、という期待を先に置けば、どの段階が違うかを追えます。
自由な文章評価へ進むときは、この判定をそのまま品質チェックへ転用しないでください。たとえば「会場:未確認」という文字列があれば、見出しの検査は通ります。それでも公開できる案内とは限りません。項目の存在、内容の根拠、公開の可否を、別の確認として設計する必要があります。
初期文に必要項目がすでに揃っている場合と、入力自体が空の場合も考えます。すでに揃った文を何度も書き直す必要がなければ、ループの外で確認して次へ渡す設計にできます。空の入力から架空の日時や会場を作ってしまいたくない場合は、本文の不足とは別に、入力不足の確認へ回します。
これらは、ループの終了条件だけで解決する話ではありません。開始前に何を受け付けるかを決める話です。初期値が完成しているケースを、Difyが自動的にゼロ回で処理するとは推測せず、必要なら外側の分岐で明示します。開始前の判定と周回中の判定を分けると、実行ログも読み取りやすくなります。
子ノードの出力と、持ち回すループ変数を別の名前にする
ループ内のノードがnext_draftを出力しても、current_draftが自動的にその値へ置き換わると考えないようにします。候補の出力と、次の周回が読む状態は別です。公式のLoop資料でも、変数を定義し、更新した値を引き継ぐ例が示されています。
たとえば、毎回current_draftを入力に修正候補を作り、その候補をnext_draftと呼びます。候補の不足項目を調べてから、current_draftへnext_draftを代入します。代入を忘れると、次の周回も同じ初期文を読み、同じ候補だけが作られることがあります。
回数を表すround_countと、不足数を表すmissing_countも分けます。round_countだけが増えているから改善が進んでいるとは限りません。本文が変わっていない、別の変数に書いている、判定が古い本文を参照している、という可能性が残ります。
| 名前 | 役割 | 確認したい対応 |
|---|---|---|
| current_draft | 次の周回が読む本文 | 代入後の本文に前回の修正が残る |
| next_draft | 今回作った修正候補 | まだ最終状態とは決めない |
| round_count | 教材で数える処理回数 | 一回の修正につき一回進める |
| missing_count | 現在の本文に不足する項目数 | current_draftと同じ版を調べる |
| needs_review | 別途確認へ回す状態 | 項目が揃っても無視しない |
設定画面では、変数の名前が同じように見えても、どのノードやスコープの値かを確認します。会話変数と一回の実行内の値を混同した場合は、会話変数の代入先・実行順・会話の確認も参照できます。本稿のループ変数を、会話をまたいで保存される値と読み替えないでください。
終了条件・最大回数・Exit Loopを別々に試す
公式資料では、ループは終了条件の成立、最大回数への到達、Exit Loopノードの実行という経路で停止すると説明されています。どれも処理を止めますが、止まった理由は同じではありません。最大回数へ達した結果を、条件を満たした結果と一緒に扱わないようにします。
教材では最大回数を三回に決めます。これは教材上の設定で、Difyのプラン上限や既定値を示していません。初期状態で不足二項目なら、規則どおり更新した場合は二回で揃うと予想します。一回だけ許可する設定なら、不足一項目を残して止まると予想します。
要確認の分岐へ入った場合も、意図的に停止させることがあります。たとえば元資料と一致しない日時を検出したら、修正を繰り返して通すより、根拠を確認する経路へ回す設計が考えられます。停止ノードが実行できたことは、案内が完成したという意味ではありません。
終了条件がどのタイミングで評価されるかを、回数だけから推測しないことも大切です。Dify上で試すときは、候補生成、状態への代入、条件確認、Exit Loopへの到達という実際の実行順をトレースで見ます。以下の参照コードのfor文と、Difyの内部実装が同じだとは主張しません。
とくに、停止の直前で何を保存するかを確認します。最後の修正候補を作ってから代入する前に停止する構成では、候補として見えた本文が最終状態へ残っていない可能性があります。最終出力と候補の出力を並べて照合すれば、停止が早い問題と代入忘れを分けて考えられます。
数値・真偽値・空欄の型を、比較する直前に確かめる
回数が数値の3なのか、文字列の3なのかを確認します。同じ表示でも型が違えば、想定する比較や計算にならないことがあります。終了条件へ渡す値は、画面に見える文字だけではなく、ノード出力の型と実際の値を一緒に見てください。
同様に、falseと「false」という文字列、ゼロと空欄も別です。「値が入っているか」を「条件を満たしたか」の代わりに使うと、falseという値が存在しているだけで完成の経路へ進むような設計になりかねません。どの値なら何を意味するかを先に決めます。
教材でmissing_countを使うなら、整数のゼロを不足なしと定義します。未計算や取得失敗の空欄をゼロへ補ってしまうと、確認できていない本文を完成扱いにする可能性があります。計算できなかった状態は、内容不足とは別の要確認経路へ分ける方が、後から説明しやすくなります。
Difyのコードノードへ値を渡す場合は、入力名と戻り値の項目名も照合します。コードノードの変数名・型・戻り値の確認で扱うように、型が数値であることと、正しい項目を受け取っていることは別です。違うカウンターを比較していても、型の検査だけでは見つかりません。
最初の検証では、本文の処理が正しいかと終了条件が正しいかを同時に変えないようにします。固定したmissing_countで条件の分岐だけを確かめ、次に現在の本文からその数値を作る処理をつなげれば、修正が必要な範囲を絞れます。
停止理由と最終出力を、四つのケースで検算する
二回で項目が揃う期待を、参照モデルに書き出す
次のPythonは、Difyへ読み込むワークフローではありません。更新候補を作る、必要なら状態へ戻す、状態の不足数を調べる、という設計の期待を手元で確認するためのコードです。外部API、モデル、メール送信は呼び出しません。
max_roundsはこの参照モデルの反復上限です。write_back=Falseは、本文への書き戻しを忘れた状態を意図的に作るために用意します。manual_stop_atは、要確認の分岐で止まる場合の教材上の指定です。これらをDifyが持つ同名の設定項目と考えないでください。
def run_reference(max_rounds=3, write_back=True, manual_stop_at=None):
current = initial_text
trace = []
if not missing_items(current):
return {"rounds": 0, "text": current, "ready": True,
"reason": "入力時点で項目あり", "trace": trace}
for number in range(1, max_rounds + 1):
candidate = revise_fixture(current)
if write_back:
current = candidate
remaining = len(missing_items(current))
trace.append({"round": number,
"candidate_missing": len(missing_items(candidate)),
"state_missing": remaining})
if manual_stop_at == number:
return {"rounds": number, "text": current, "ready": False,
"reason": "要確認分岐", "trace": trace}
if remaining == 0:
return {"rounds": number, "text": current, "ready": True,
"reason": "項目が揃った", "trace": trace}
return {"rounds": max_rounds, "text": current, "ready": False,
"reason": "回数上限", "trace": trace}
normal = run_reference()
assert normal["rounds"] == 2 and normal["ready"] is True
assert [x["state_missing"] for x in normal["trace"]] == [1, 0]
assert normal["text"] == second_text
正常ケースでは、候補の不足数と状態の不足数が一致して進みます。二回目の終了時に、currentとして保持した本文がsecond_textと一致することも照合します。回数が二であることだけでは、正しい案内が残ったかまでは分からないためです。
Difyで同じ期待を試す場合は、トレースへ表示された値をこの表と比べます。想定と違っても、このPythonの結果へDifyを無理に合わせるのではなく、代入先、分岐の位置、実行順を調べます。参照モデルは確認の出発点であり、利用環境での実行結果の代わりではありません。
最大回数で止まった結果を、完成の出力と分ける
教材の上限を一回にすると、会場は追加できますが申込締切は不足したままです。処理は止まりますが、今回決めた必要項目を満たしません。Difyで最大回数へ達した結果も、内容の受入れ条件をループ外で確かめてから、次の工程へ渡す設計が考えられます。
limited = run_reference(max_rounds=1)
assert limited["rounds"] == 1
assert limited["ready"] is False
assert limited["reason"] == "回数上限"
assert missing_items(limited["text"]) == ["申込締切:"]
単にループの出力が存在するかだけで分岐すると、未完成の本文でも値が入っているため通ってしまいます。本文が空でないこと、項目が揃ったこと、要確認の状態がないことなどを、それぞれの目的に合わせて確認します。終了理由も別の記録として残すと判断しやすくなります。
回数を増やす前には、同じ不足が減っていない理由を調べます。入力の資料に締切が書かれていないのに、反復回数だけを増やしても、根拠を持った締切は作れません。不足している事実を人に確認する方が目的に合う場合もあります。回数上限は、正しい情報を生み出す設定ではありません。
ループの中にLLMや外部ツールを複数置いた場合、周回回数と呼び出し回数も一致しません。一周にモデルを二回呼ぶ経路が三周すれば、その経路だけで六回の呼び出しという計算になります。これは料金の実測ではなく構成上の数え方です。必要な呼び出しを減らせる固定の確認から試しましょう。
候補は変わるのに状態が変わらない場合を見つける
次は、write_backをFalseにして、候補をcurrentへ戻さない状態を作ります。候補には会場が追加されているのに、次の周回は日時だけの初期文を読むため、同じ修正が繰り返されます。回数だけが増え、状態の不足数は二のままです。
stale = run_reference(write_back=False)
assert stale["rounds"] == 3 and stale["ready"] is False
assert stale["reason"] == "回数上限"
assert [x["candidate_missing"] for x in stale["trace"]] == [1, 1, 1]
assert [x["state_missing"] for x in stale["trace"]] == [2, 2, 2]
assert stale["text"] == initial_text
Difyでこれに似た状態なら、子ノードの出力を見ただけで判断せず、代入ノードの入力と代入先を確認します。next_draftを正しく受け取っていても、別の同名変数へ書き込んでいる可能性があります。次の周回の入力が本当に更新後のcurrent_draftか、トレースで照合してください。
カウンターが増えない場合は、本文とは別にround_countの更新を確認します。カウンターの代入が特定の分岐だけにあると、通らない経路では初期値のままになる場合があります。どの経路で一回進める必要があるかは、処理の目的に合わせて明示します。
また、完成の判定が一つ前の候補を参照していないかも見ます。変数名が正しくても、分岐を置く場所によって参照できる値が違う可能性があります。実行順と参照先の二つをセットで確認すると、同じ条件式を何度も書き換えるより原因を見つけやすくなります。
トレースを読むときは、二回目の候補だけを開くのではなく、一回目の代入後の値と二回目の入力を並べます。前回の会場が本文に残っていれば、書き戻しの経路を確認できます。残っていなければ、終了条件より手前で状態の引き継ぎが止まっている可能性があります。小さい入力なら、この二つの文字列を目で照合できます。
ログへ周回番号を付ける際も、画面の表示番号と自作のカウンターを混同しないようにします。初期値をゼロにした自作変数は、どのノードで一を足したかによって値が変わります。「三周したら表示が三になるはず」という推測より、一回目に期待する値を決め、その代入を一つだけ配置して確認します。
要確認の分岐で止めたときは、最後の候補を勝手に採用しない
教材の一回目で要確認の分岐へ進む指定をすると、会場が追加された本文を保持したまま止まります。見出しは一つ増えていますが、readyはFalseです。途中の改善が一つできたことと、次の工程へ渡してよいことを分けています。
reviewed = run_reference(manual_stop_at=1)
assert reviewed["rounds"] == 1
assert reviewed["ready"] is False
assert reviewed["reason"] == "要確認分岐"
assert reviewed["text"] == first_text
assert missing_items(reviewed["text"]) == ["申込締切:"]
| ケース | 回数・不足数 | 扱い |
|---|---|---|
| 正常に書き戻す | 2回・不足0 | 項目の存在を確認。内容の根拠は別途確認 |
| 上限を1回にする | 1回・不足1 | 未完成として確認へ回す |
| 本文へ書き戻さない | 3回・不足2 | 更新対象を調べる |
| 1回目で要確認へ分岐 | 1回・不足1 | 停止理由を残し、自動採用しない |
Exit Loopを使う場合も、止める直前の値をどう扱うかを決めます。最後の候補を確認用に残したいのか、採用済みの前回本文だけを残したいのかで保存先が変わります。候補の内容と採用した内容を同じ変数で上書きし続けると、後でどこまで確認済みか分からなくなることがあります。
完成と要確認を確定した値で振り分けるときは、IF/ELSEの数値・空欄・境界の確認も参考になります。自由文の意図を分類する質問分類器と、確定した状態の分岐を混同しないようにします。停止理由の値が決まっているなら、その値をそのまま検査できます。
ログへ残す項目を決めて、小さい入力から再確認する
検証用の記録には、入力の識別子、初期本文、周回番号、候補の不足数、代入後の不足数、停止理由、最終本文を残します。すべての業務資料を丸ごとログへ保存する必要はありません。まず架空資料や公開してよい少量の入力で、状態の変化を確認しましょう。
本番の資料を扱う場合は、誰がどのログを見られるか、必要な情報だけを残せるかも検討します。ループの動作確認のために、個人情報を含む案内や問い合わせを外部へ送る必要はありません。更新対象の理解だけなら、今回のような固定した教材で確認できます。
終了条件を変えるときは、正常ケースだけでなく、不足が減らないケース、上限到達、要確認停止を再度試します。古い記事の回避策は、利用中の版で同じ問題が起きると確かめてから検討してください。終了条件を取り除くことだけを、現在の一般的な解決法として採用しないようにします。
反復処理の確認から、Difyを使った業務フローの設計まで学習を広げたい人には、バイテックのDifyマスターコースも比較先になります。公式は申請フロー・顧客管理・レポート自動化などを題材として案内しています。ループの添削や不具合調査を個別に依頼できると決めつけず、作りたいフロー、質問できる範囲、支援期間、受講総額とDify・モデルの利用費を無料説明会で確認してください。停止理由を自分で説明できる段階なら、その記録を次の小さなフローへ応用する選択もあります。(公式情報確認日:2026年10月30日、バイテック公式)。
よくある質問
Q. Difyのループは、どの条件で停止しますか?
公式資料では終了条件の成立、最大回数への到達、Exit Loopノードの実行で停止すると説明しています。止まった理由を区別し、内容の完成を別に確認してください。
Q. 子ノードの出力が変われば、次の周回にも反映されますか?
候補の出力とループ変数への代入を分けて確認します。候補だけが変わり、次の周回の入力が古い本文なら、代入先や実行順を調べます。
Q. 最大回数で終わったら成功として扱えますか?
最大回数は反復を止める設定で、必要項目や根拠が揃ったことまで示しません。上限到達後の不足や要確認状態を検査し、完成の経路と分けます。
Q. 最大回数の既定値やプラン上限はこの記事の3回ですか?
三回は本稿の参照教材で選んだ数値です。サービスの既定値やプラン上限として記載していません。利用環境の設定と現行公式案内を確認してください。
Q. Pythonの参照モデルはDifyの実行結果と同じですか?
同じではありません。固定の文字列を更新して設計上の期待を整理したコードです。Dify CloudやLLMは実行しておらず、実際の評価タイミングは利用環境のトレースで確認します。
Q. 項目が揃えば、案内文をそのまま公開してよいですか?
見出しの存在だけでは、日時や会場が正しいことを確認できません。原資料の根拠、未確認の値、文章の意味を別に照合してから、公開の判断をしてください。
反復を止める設定と、完成した内容を受け入れる条件を分けることが中心です。 どの値が変わり、何を理由に止まり、どの本文が外へ渡ったかを説明できる状態で、次の処理へ進めてください。