Difyのプロンプトを一文変えたら、通常の質問への回答は良くなった。しかし、情報不足の質問にも断定するようになった。このような退行を防ぐには、文面の履歴だけでなく、何を評価し、誰が公開し、どこまで戻せるかを決める必要があります。
本稿は、Difyで問い合わせ回答などを構築するエンジニア向けの設計案です。2026年10月7日に確認したDify Cloudの公式仕様と、ローカルで検証した小さな判定コードを分けて扱います。冒頭の状況は説明用の仮想例であり、Dify環境での実運用・LLM精度・費用削減の実測を示すものではありません。
プロンプトの履歴だけでは再現できない
同じプロンプトでも、モデル、検索対象、取得件数、ツールの返却形式が変われば結果が変わります。「変更前に戻した」と判断する単位を、プロンプトから実行条件の組へ広げます。
まず、製品が持つ機能と、追加で設計する責任を分けます。
| 公式仕様として確認したこと | 運用で追加すること |
|---|---|
| Chatflow/WorkflowではCurrent Draftと公開版が分かれる | 編集権限と公開責任、評価対象の固定 |
| DSLはプロンプト・モデル設定・ノード設定を含む | 検索データの版、外部ツール仕様、評価ケースの記録 |
| DSLはナレッジ本体や利用ログを含まない | それらを別の管理対象として復旧手順に含める |
Dify Cloudの版管理では、過去版の復元は現在の下書きを置き換えます。本番へ戻すには、その下書きを再公開する手順が必要です。また、過去版の復元・特定版のDSL出力・APIでの版指定は有料プランの機能で、Sandboxは最新公開版を実行します。セルフホスト版へそのまま当てはめず、利用環境で確認します。Version Control
DSL出力時にはSecret型環境変数を含めるか選ぶ場合があります。DSLだから安全とは判断せず、Secretを除外し、HTTPヘッダーや固定文字列への埋め込みも点検します。Manage Apps
同じアプリの下書きか、実験用アプリか
| 選択肢 | 利点 | 制約・採用条件 |
|---|---|---|
| 同じアプリの下書きで実験 | 構成の差が小さく、管理対象が増えない | 誤公開と共同編集の競合を管理できる場合 |
| アプリを複製して実験 | 実験の編集・公開先を分けられる | 接続先や設定が本番からずれるため差分照合が必要 |
| プロンプトを外部管理 | 複数製品で承認・評価を共通化できる | 同期や復旧経路が増える。共通化の実需要がある場合 |
本稿の設計案では、外部書込みを伴う処理は複製した実験用アプリで評価し、DSLと評価記録をGitで管理します。Difyは複製やDSL入出力を提供しますが、ナレッジや外部接続まで完全に隔離されるとは考えません。実験側は読取り専用接続かスタブを使います。Manage Apps
公開単位を「評価済みの組」に固定する
以下はDify標準機能の説明ではなく、追加するリリース手順です。
保存するのは、DSL、モデル識別子と生成設定、ナレッジの版と検索設定、ツールの入出力契約、評価ケース、判定ポリシーです。秘密値は保存せず、権限を持つ保管先への参照だけを持たせます。モデルの版を固定できない提供形態では、その制約と評価日時も記録します。
例えば、これらを固定したファイル名・順序でハッシュ化します。次のコマンドは、用意済みのリリース用ディレクトリで実行する例です。
shasum -a 256 app.yml model.json knowledge.json tools.json cases.json policy.json > SHA256SUMS &&
shasum -a 256 SHA256SUMS
評価結果へこの識別値を付け、評価後に一つでも変更されたら承認を取り直します。ハッシュは内容の一致を確認するもので、品質や承認の証明にはなりません。評価結果の作成者・保存先・改変権限も管理します。
本番アプリへの取込みで環境固有の参照が変わる場合は、許可した置換を記録して照合し、最終構成で再評価します。実験アプリの合格を、別の構成の公開許可へ流用しません。
未完了の評価を合格にしない判定コード
評価ケースは、正常回答、根拠不足、矛盾、指示の上書き要求、ツール障害などで構成します。各ケースは、事前に決めた基準に従ってpass/block/reviewへ分類します。形式違反や禁止操作はblock、自動判定しきれない意味上の判断はreviewとします。
次のPythonコードは、評価の網羅性と判定結果を集約する部分だけです。回答の品質を採点する処理やDify API呼出しは含みません。
import re
def release_gate(expected_sha, expected_ids, report):
if (not isinstance(expected_sha, str)
or re.fullmatch(r"[0-9a-f]{64}", expected_sha) is None
or not isinstance(expected_ids, set) or not expected_ids
or any(not isinstance(i, str) or not i for i in expected_ids)):
raise ValueError("invalid release configuration")
if not isinstance(report, dict):
return "HOLD_INVALID"
if report.get("release_sha") != expected_sha:
return "HOLD_RELEASE_CHANGED"
if report.get("completed") is not True:
return "HOLD_INCOMPLETE"
cases = report.get("cases")
if not isinstance(cases, list) or any(not isinstance(c, dict) for c in cases):
return "HOLD_INVALID"
ids = [c.get("id") for c in cases]
if (any(not isinstance(i, str) or not i for i in ids)
or len(ids) != len(set(ids)) or set(ids) != expected_ids):
return "HOLD_COVERAGE"
verdicts = [c.get("verdict") for c in cases]
if any(v not in ("pass", "block", "review") for v in verdicts):
return "HOLD_INVALID"
if "block" in verdicts:
return "BLOCK_REGRESSION"
if "review" in verdicts:
return "HUMAN_REVIEW"
return "READY_FOR_APPROVAL"
執筆時に、記事のコードを抽出し、合成データ25ケースで判定を確認しました。LLM呼出しは行っていません。実行出力の抜粋です。
all_pass -> READY_FOR_APPROVAL
release_changed -> HOLD_RELEASE_CHANGED
interrupted -> HOLD_INCOMPLETE
empty_case_list -> HOLD_COVERAGE
duplicate_case -> HOLD_COVERAGE
unknown_verdict -> HOLD_INVALID
human_review -> HUMAN_REVIEW
block_over_review -> BLOCK_REGRESSION
quality_gate_tests: pass=25 fail=0 (synthetic; no Dify/LLM calls)
ハッシュ生成例も架空ファイルで、同じ入力の再現性、変更の検出、欠損時の停止を確認しました。これらはDifyとの結合動作や復旧手順の検証を代替しません。
優先順位は、設定不正→対象一致→完了→全件網羅→判定値の検証→退行→人の判断→公開承認待ちです。重大な退行が一件でもあれば、平均点が良くても公開候補から外します。人によるレビュー結果もケース記録へ反映して再集約します。
評価ランナーは、中断・タイムアウト時にcompleted: trueを保存しません。成功済みケースを保持しつつ未処理を識別し、同じリリース・ケースIDの結果を照合してから再開します。外部書込みの結果不明は、モデル呼出しの再試行と同列に扱いません。
ノード単体の成功をリリース評価にしない
Difyの単一ノード実行やVariable Inspectorは、上流を再実行せず入力条件を変える実験に役立ちます。しかし、そのキャッシュには古い検索結果が残る可能性があります。プロンプトの比較が終わったら、新しい入力から全体を実行して回帰を確認します。Single Node
| 失敗パターン | 品質管理・復旧の設計 |
|---|---|
| 少数の成功例だけを選ぶ | 同じ固定ケースで旧版・候補版を比較。評価対象の追加も記録 |
| モデルと検索設定も同時変更 | まず変更軸を絞る。組合せ変更は別の候補として評価 |
| テスト用の接続で本番へ書く | スタブ・読取り専用権限・送信前のHITLを使う |
| プロンプトだけ戻して復旧扱い | 検索データ、ツール、接続権限の変更も照合 |
| 評価途中で停止したのに合格扱い | 未完了・欠損・重複を保留にする |
LLM判定器を使う場合も、判定器の設定と基準を固定します。「LLMが合格と言った」を唯一の公開根拠にせず、構造検証、禁止操作、根拠の確認、人のレビューを組み合わせます。誤答コストが高いケースと、文章表現の好みを同じ平均点へ押し込みません。
ロールバックと監視も先に試す
公開前に、現在の正常版と戻す手順を保存します。過去版の復元機能を使えない環境なら、DSLの再取込みなど別の復旧経路を実環境で確認します。インポートが成功しても、接続先・モデル・ナレッジまで正常とは限りません。設定照合とスモークテストを経て復旧を判断します。
下書きへ復元しただけでは本番は戻りません。また、旧版を再公開しても、実行中の処理や既に完了した外部書込みが取り消されるとは限りません。新規受付を止める条件、実行中処理の扱い、結果照合・補償が必要な操作を決めます。Version Control
監視では、リリース識別子、run ID、判定理由、承認記録を結びます。エラー率に加え、根拠不足、Human Review比率、応答時間、トークン消費を版別に比較します。ログは必要最小限の参照やマスク済み情報とし、入力全文を別の分析ログへ無条件に複製しません。
Difyの本番Logsと開発時のRun Historyは分けて見ます。Chatflow/WorkflowのPreview・Test Runは本番Logsの対象外です。実験結果が本番ログへ自動集約される前提を置きません。Logs/Run History
次に測るものと改善すること
- DSLに秘密値がなく、評価対象の識別値と全ケース結果が一致している
- 根拠不足・矛盾・障害で、安全な保留や引継ぎになる
- 複数回の評価で、合格率だけでなく結果のばらつきを確認する
- 応答時間・トークン消費の上限を業務要件から決める
- 戻す手順を試し、外部副作用と実行中処理の扱いを確認する
この設計による費用対効果は未実測です。評価に使った費用・確認工数と、退行対応や再調査の工数を別々に計測します。先に、問題発生時に「どの変更が原因か」「何を戻すか」を説明できる状態を目指します。
参考リンク
- Dify Cloud:Version Control
- Dify Cloud:Manage Apps
- Dify Cloud:Single Node
- Dify Cloud:Run History
- Dify Cloud:Logs
同様の仕組みの設計・構築・運用については相談可能。
この記事を書いた人✏️@YushiYamamoto
ITPRODX.com代表 / AIアーキテクト
Next.js / TypeScript / n8nを活用した自律型アーキテクチャ設計を専門としています。
日々の自動化の検証結果や、ビジネス側の視点(ROI等)に関するより深い考察は、以下の公式サイトおよびnoteで発信しています。
