Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Workflow ノード設計哲学:コンポーザビリティ、Human-in-the-Loop、確定的ノードと確率的ノードの共存

はじめに

Dify の Workflow は、単なる「LLM を呼び出すフローチャート」ではない。その設計哲学の核心は、AI が自律的に処理できる領域と、人間が判断すべき領域を明示的に分離し、一つのフローの中で共存させることにある。

本稿では、Dify Workflow のノード設計を支える4つの設計原則 – コンポーザビリティ、Human-in-the-Loop(HITL)、確定的ノードと確率的ノードの使い分け、エラーハンドリングパターン – を詳細に解説する。


1. Workflow の基本アーキテクチャ

1.1 ノードとエッジの構成

Dify Workflow は、有向非巡回グラフ(DAG)として表現される:

graph LR
    START((Start)) --> A[LLM Node]
    A --> B{IF/ELSE}
    B -->|条件A| C[Knowledge Base]
    B -->|条件B| D[HTTP Request]
    C --> E[LLM Node 2]
    D --> E
    E --> F[Human Review]
    F -->|承認| G[HTTP Request<br/>外部API送信]
    F -->|差し戻し| A
    G --> END((End))

1.2 ノードの分類体系

Dify の Workflow ノードは、その性質によって以下のように分類できる:

カテゴリノードタイプ出力の性質実行時間
確率的ノードLLM, Question Classifier非決定的(同じ入力でも異なる出力)中〜長
確定的ノードIF/ELSE, Code, Template, Variable決定的(同じ入力なら同じ出力)
外部連携ノードHTTP Request, Tool外部依存可変
データノードKnowledge Retrieval, Variable Aggregator検索結果依存
人間介在ノードHuman Input (待機型)人間の判断依存不定
制御ノードStart, End, Iteration, Parameter Extractorフロー制御

2. コンポーザビリティ:ノードの組み合わせ可能性

2.1 設計原則

Dify Workflow のコンポーザビリティは、以下の原則に基づいている:

  1. 各ノードは独立した入出力インターフェースを持つ: ノード間の依存は変数の受け渡しのみ
  2. ノードの型に関わらず同一の接続プロトコル: LLM ノードも HTTP ノードも同じ方法で接続可能
  3. ネストと参照の分離: 変数の参照は明示的で、暗黙のグローバル状態を持たない
graph TB
    subgraph "コンポーザビリティの原則"
        direction LR
        subgraph "ノード A"
            A_IN[入力変数] --> A_PROC[処理ロジック]
            A_PROC --> A_OUT[出力変数]
        end
        
        subgraph "ノード B"
            B_IN[入力変数] --> B_PROC[処理ロジック]
            B_PROC --> B_OUT[出力変数]
        end
        
        A_OUT -->|"変数参照"| B_IN
    end

2.2 変数スコーピングモデル

Workflow 内の変数は、以下のスコープルールに従う:

スコープ説明アクセス方法
ノードローカルノード内部の処理で使用ノード内のみ
ノード出力ノードの処理結果下流ノードから {{node_id.output}} で参照
Workflow 入力Start ノードで定義される入力パラメータ全ノードから {{start.input_name}} で参照
環境変数Workflow レベルの定数全ノードから参照可能
会話変数Chat 型 Workflow で会話をまたいで保持全ノードから参照・更新可能

この設計により、ノードは「自分の入力と参照先のノード出力」だけを知っていればよく、Workflow 全体の状態を把握する必要がない。これがコンポーザビリティの基盤である。

2.3 Iteration ノード:繰り返し処理のコンポーザビリティ

Iteration ノードは、配列データに対してサブフローを繰り返し実行する。これにより、単一ノードでは表現できない複雑な処理を、コンポーザブルな形で実現できる:

graph TB
    START((Start)) --> SPLIT[Code Node<br/>文書を章ごとに分割]
    SPLIT --> ITER[Iteration Node]
    
    subgraph ITER[Iteration: 各章に対して]
        I_LLM[LLM Node<br/>要約生成]
        I_LLM --> I_QA[LLM Node<br/>QA 生成]
    end
    
    ITER --> AGG[Variable Aggregator<br/>結果統合]
    AGG --> END((End))

3. Human-in-the-Loop(HITL):人間介在の設計哲学

3.1 なぜ Human-in-the-Loop が必要なのか

AI の自律性が高まるほど、人間の制御が重要になる。これは矛盾ではなく、リスク管理の基本原則である:

graph TB
    subgraph "AI 自律性と人間制御のマトリクス"
        direction TB
        A["低リスク x 高自動化<br/>完全自動処理<br/>例: FAQ 自動回答"]
        B["高リスク x 高自動化<br/>AI ドラフト + 人間承認<br/>例: 契約書レビュー"]
        C["低リスク x 低自動化<br/>手動 + AI アシスト<br/>例: 創造的文書作成"]
        D["高リスク x 低自動化<br/>人間主導 + AI 補助<br/>例: 法務判断"]
    end

3.2 HITL の3つのモード

Dify の Workflow における Human-in-the-Loop は、公開資料に基づくと以下の3つのモードで運用される:

Approval(承認)モード

AI が生成した結果を人間が確認し、承認または却下する:

graph LR
    A[LLM Node<br/>回答ドラフト生成] --> B[Human Input<br/>承認/却下]
    B -->|承認| C[HTTP Request<br/>顧客に送信]
    B -->|却下| D[LLM Node<br/>再生成]
    D --> B

適用シナリオ:

  • 顧客向けメール自動生成後の送信前確認
  • 契約書条項の AI レビュー結果の法務確認
  • 稟議書ドラフトの上長承認

Correction(修正)モード

AI の生成結果を人間が直接編集できる:

graph LR
    A[LLM Node<br/>レポート生成] --> B[Human Input<br/>内容修正可能]
    B --> C[Template Node<br/>フォーマット適用]
    C --> D[HTTP Request<br/>レポート保存]

適用シナリオ:

  • AI 生成レポートの数値・表現の修正
  • 翻訳結果のニュアンス調整
  • 技術文書の専門用語の修正

Escalation(エスカレーション)モード

AI の確信度が低い場合、自動的に人間にエスカレートする:

graph TB
    A[LLM Node<br/>問い合わせ分類] --> B{IF/ELSE<br/>確信度チェック}
    B -->|確信度 > 0.8| C[自動回答]
    B -->|確信度 <= 0.8| D[Human Input<br/>担当者対応]
    D --> E[回答送信]
    C --> E

適用シナリオ:

  • カスタマーサポートの自動応答 / エスカレーション判定
  • OCR 認識結果の信頼度に応じた人間確認
  • クレーム対応の自動 / 手動振り分け

3.3 HITL の設計上の考慮事項

考慮事項説明推奨対応
タイムアウト人間の応答が得られない場合の処理デフォルト動作の設定(エスカレーション or 保留)
並行処理複数の承認待ちが同時に発生する場合承認キューの管理・優先度設定
監査証跡誰がいつ何を承認/修正したか操作ログの永続化
権限管理承認権限を持つユーザーの制御Workspace のロール設定と連携
SLA承認待ち時間の管理アラート通知の設定

4. 確定的ノードと確率的ノードの共存

4.1 なぜ区別が重要なのか

Workflow を設計する上で最も重要な認識は、ノードによって出力の予測可能性が根本的に異なるということだ:

ノードタイプ出力の性質テスト方法エラー対応
確定的 (Code, IF/ELSE)同じ入力で同じ出力ユニットテスト可能バグとして修正
確率的 (LLM)同じ入力で異なる出力の可能性統計的評価が必要プロンプト / パラメータ調整

この区別を意識しないと、以下の問題が発生する:

  • LLM ノードの出力を後続処理が正確にパースできない(形式の不安定性)
  • テスト時は動作したが本番で異なる出力が発生(再現性の欠如)
  • エラーの原因が LLM の出力なのかロジックなのか切り分けできない

4.2 確定的ノードの役割

確定的ノードは、Workflow に予測可能性と制御可能性を提供する:

# Code ノードの例:LLM 出力の正規化
def main(llm_output: str) -> dict:
    """
    LLM の確率的な出力を、確定的な構造に変換する。
    これにより、後続ノードは安定したデータ形式に依存できる。
    """
    import json
    import re
    
    # JSON ブロックの抽出(LLM が余分なテキストを付加する場合への対応)
    json_match = re.search(r'\{[\s\S]*\}', llm_output)
    if json_match:
        try:
            data = json.loads(json_match.group())
            return {
                "status": "success",
                "category": data.get("category", "unknown"),
                "confidence": float(data.get("confidence", 0)),
                "summary": data.get("summary", "")
            }
        except (json.JSONDecodeError, ValueError):
            pass
    
    return {
        "status": "parse_error",
        "category": "unknown",
        "confidence": 0.0,
        "summary": llm_output[:200]
    }

4.3 確率的ノードと確定的ノードの協調パターン

graph TB
    subgraph "推奨パターン: 確率 then 確定 then 分岐"
        A[LLM Node<br/>確率的: テキスト生成] --> B[Code Node<br/>確定的: 出力パース]
        B --> C{IF/ELSE<br/>確定的: 条件分岐}
        C -->|正常| D[次の処理]
        C -->|パースエラー| E[エラー処理]
    end
    
    subgraph "アンチパターン: 確率の連鎖"
        X[LLM Node 1] --> Y[LLM Node 2<br/>前段の出力形式に依存]
        Y --> Z[LLM Node 3<br/>さらに不安定な入力]
    end
    
    style A fill:#f96,stroke:#333,color:#fff
    style B fill:#69b,stroke:#333,color:#fff
    style C fill:#69b,stroke:#333,color:#fff
    style X fill:#f96,stroke:#333,color:#fff
    style Y fill:#f96,stroke:#333,color:#fff
    style Z fill:#f96,stroke:#333,color:#fff

設計原則: LLM(確率的)ノードの直後には、必ず Code(確定的)ノードを配置して出力を正規化する。これにより、確率的な出力の不安定性が Workflow 全体に伝播することを防ぐ。


5. エラーハンドリングパターン

5.1 Workflow におけるエラーの種類

エラー種別発生源対応パターン
モデルエラーLLM ノードレート制限、タイムアウト、不正な出力リトライ / フォールバック
外部 API エラーHTTP Request接続タイムアウト、5xx エラーリトライ / 代替エンドポイント
データエラーKnowledge Retrieval検索結果0件、不正なデータ形式デフォルト値 / エスカレーション
ロジックエラーCode / IF/ELSE型不一致、NULL 参照バグ修正(設計時に対応)
人間介在タイムアウトHuman Input承認者が応答しないエスカレーション / 自動承認

5.2 リトライパターン

graph TB
    A[LLM Node] -->|成功| B[次のノード]
    A -->|エラー| C{リトライ回数<br/>< 上限?}
    C -->|Yes| D[待機<br/>指数バックオフ]
    D --> A
    C -->|No| E{フォールバック<br/>モデルあり?}
    E -->|Yes| F[代替 LLM Node<br/>別モデルで実行]
    F --> B
    E -->|No| G[エラー終了<br/>/ エスカレーション]

5.3 フォールバックパターン

Provider 抽象層と組み合わせることで、モデルレベルのフォールバックが可能:

graph LR
    subgraph "フォールバックチェーン"
        P1[GPT-4o<br/>Primary] -->|エラー| P2[Claude Sonnet<br/>Secondary]
        P2 -->|エラー| P3[GPT-4o-mini<br/>Tertiary]
        P3 -->|エラー| ERR[エラー処理]
    end

5.4 グレースフルデグラデーション

完全な失敗ではなく、品質を段階的に低下させながらもサービスを継続するパターン:

段階状態対応
正常全ノード正常動作最高品質の回答
軽度劣化Rerank 不可Vector Search のみで回答(精度やや低下)
中度劣化Knowledge Base 検索不可LLM の内部知識のみで回答(注意書き付き)
重度劣化Primary LLM 不可フォールバックモデルで回答
サービス停止全モデル不可人間にエスカレーション

6. Workflow 設計のベストプラクティス

6.1 ノード設計の原則

原則説明具体例
単一責任1ノード = 1つの責任「分類 + 要約」を1つの LLM に詰め込まない
確率的出力の即時正規化LLM 直後に Code ノードを配置JSON パース・バリデーション
フェイルセーフすべてのパスにエラー処理を配置IF/ELSE でエラー分岐を設計
可観測性中間結果をログ出力重要なノードの出力を変数として保持
冪等性同じ入力で再実行しても副作用がない外部 API 呼び出しの冪等性を確保

6.2 実践的な Workflow テンプレート

顧客問い合わせ自動応答

graph TB
    START((Start<br/>問い合わせ受信)) --> CLASS[LLM Node<br/>問い合わせ分類]
    CLASS --> PARSE[Code Node<br/>分類結果パース]
    PARSE --> ROUTE{IF/ELSE<br/>カテゴリ判定}
    
    ROUTE -->|FAQ| KB[Knowledge Retrieval<br/>FAQ検索]
    ROUTE -->|技術問題| TECH[Knowledge Retrieval<br/>技術文書検索]
    ROUTE -->|クレーム| HUMAN[Human Input<br/>担当者対応]
    
    KB --> ANS[LLM Node<br/>回答生成]
    TECH --> ANS
    
    ANS --> CONF{IF/ELSE<br/>確信度チェック}
    CONF -->|高| SEND[HTTP Request<br/>回答送信]
    CONF -->|低| REVIEW[Human Input<br/>回答確認]
    REVIEW -->|承認| SEND
    REVIEW -->|修正| ANS
    
    HUMAN --> SEND
    SEND --> END((End))

6.3 アンチパターン

アンチパターン問題改善案
LLM チェーン確率的ノードの連鎖で出力が不安定化間に Code ノードを挟んで正規化
モノリシック LLM1つの LLM に複数の責任を持たせるタスクを分割して別ノードに
エラー無視エラーパスを設計しないすべての分岐にエラー処理を配置
過剰自動化リスクの高い処理も自動実行HITL ノードでチェックポイントを設置
ハードコーディングプロンプトに固定値を埋め込む変数・環境変数を活用

7. 日本企業における Workflow 設計の考慮事項

7.1 稟議プロセスとの統合

日本企業特有の稟議制度は、HITL ノードと自然に対応する:

graph TB
    A[AI ドラフト生成] --> B[Human Input<br/>起案者確認]
    B --> C[Human Input<br/>課長承認]
    C --> D{IF/ELSE<br/>金額判定}
    D -->|100万円以上| E[Human Input<br/>部長承認]
    D -->|100万円未満| F[処理実行]
    E --> F

7.2 監査証跡の要件

日本の企業コンプライアンスでは、AI が生成した内容と人間が承認した記録の両方を保持する必要がある:

記録項目保持すべきデータ保存期間の目安
AI 生成ログプロンプト、モデル名、生成結果、タイムスタンプ5-10年
人間判断ログ承認者 ID、判断内容、タイムスタンプ5-10年
外部 API 呼び出しリクエスト/レスポンス、エンドポイント3-5年
エラーログエラー種別、発生時刻、影響範囲1-3年

7.3 段階的導入戦略

フェーズ自動化レベルHITL 配置リスク管理
Phase 1AI ドラフト + 全件人間確認全ノードに HITL最小リスク・信頼構築
Phase 2高確信度は自動 + 低確信度は人間確認条件付き HITL効率と安全のバランス
Phase 3大部分自動 + 例外のみ人間対応エスカレーション型 HITL高効率・監視体制が前提

8. Workflow ノード設計の将来展望

8.1 Agent ノードとの融合

Dify は Workflow ノードと Agent 機能の融合を進めており、以下のような進化が見込まれる:

  • Agent as Node: Workflow の一ノードとして Agent を埋め込み、自律的なタスク実行を可能にする
  • 動的ルーティング: LLM が Workflow の分岐先を動的に決定する(従来の IF/ELSE を超えた柔軟性)
  • マルチ Agent 協調: 複数の Agent が Workflow 内で協調して問題を解決

8.2 より高度なエラーハンドリング

  • 自動リカバリ: エラー発生時に LLM がエラー原因を分析し、自動的に修復を試みる
  • 学習型フォールバック: 過去のエラーパターンから最適なフォールバック戦略を学習
  • 予防的チェック: ノード実行前に入力データの妥当性を AI が事前検証

まとめ

Dify Workflow のノード設計哲学は、以下の4つの柱に集約される:

  1. コンポーザビリティ: 各ノードが独立した入出力インターフェースを持ち、自由に組み合わせ可能。変数スコーピングにより、ノード間の依存関係が明示的かつ最小限に保たれる。

  2. Human-in-the-Loop: AI の自律性と人間の制御を共存させる。Approval、Correction、Escalation の3モードにより、リスクレベルに応じた人間介在を設計できる。

  3. 確定的ノードと確率的ノードの使い分け: LLM(確率的)の出力を Code(確定的)ノードで即座に正規化し、Workflow 全体の予測可能性を確保する。

  4. エラーハンドリング: リトライ、フォールバック、グレースフルデグラデーション、エスカレーションの多層的なエラー対応により、本番環境での堅牢性を実現する。

成熟した Workflow とは、自動化比率が高いものではなく、「どこを自動化し、どこに人間を配置し、どこにエラー処理を置くか」の境界設計が明確なものである。Dify のノード設計哲学は、その境界設計を GUI 上で視覚的に行えるフレームワークを提供している。


参考資料