Nexeed Lab

Difyのワークフロー機能で複雑なAI処理を自動化する実践ガイド

Difyのワークフロー機能で複雑なAI処理を自動化する実践ガイド

「LLMを組み合わせた複雑な処理を自動化したいけれど、コードを書くのはハードルが高い」——そんな悩みを抱えている開発者やビジネス担当者は多いのではないでしょうか。

Difyはノーコード・ローコードでAIアプリを構築できるプラットフォームとして注目されていますが、特に強力なのが「ワークフロー(Workflow)」機能です。単純なチャットボット作成にとどまらず、複数のLLM呼び出しやデータ変換、条件分岐、外部API連携などを視覚的なキャンバス上で組み合わせることで、エンタープライズレベルの自動化パイプラインを構築できます。

この記事では、Difyのワークフロー機能の基本的な考え方から、実践的なユースケースの構築手順まで、具体的なコード例や設定例を交えながら徹底解説します。すでにDifyを使い始めている方も、これから導入を検討している方も、ワークフロー機能の真の実力を引き出すヒントが得られるはずです。


Difyのワークフロー機能とは何か

Difyには大きく分けて「チャットフロー(Chatflow)」と「ワークフロー(Workflow)」の2種類のアプリ形式があります。

  • チャットフロー: ユーザーとの対話型のやり取りを前提とした形式。チャットボットやカスタマーサポートに向いています。
  • ワークフロー: バッチ処理や非同期処理、複数ステップを持つ自動化パイプラインに向いた形式。入力を受け取り、複数の処理ステップを経て最終的な出力を返します。

ワークフローの最大の特徴は、ノードベースのビジュアルプログラミングです。処理の各ステップをノードとして配置し、それらをエッジ(矢印)で繋いでいくことで、複雑なロジックを視覚的に設計できます。

主要なノードの種類

Difyのワークフローで使用できる主なノードは以下の通りです(公式ドキュメント参照):

ノード名 役割
Start ワークフローの入力を定義するエントリーポイント
LLM LLMへのプロンプト送信と出力取得
Knowledge Retrieval ナレッジベースからのRAG検索
Question Classifier 入力テキストをカテゴリに分類
IF/ELSE 条件によって処理を分岐
Code Python/JavaScriptコードを実行
Template テンプレート文字列の生成
HTTP Request 外部APIへのHTTPリクエスト
Variable Aggregator 複数の変数をまとめる
End ワークフローの出力を定義

これらのノードを組み合わせることで、「文書を受け取り → 要約 → 感情分析 → 結果をSlackに通知」といった複合的な処理フローを構築できます。


実践例1: ドキュメント自動要約・分類パイプライン

最初の実践例として、「長文ドキュメントを受け取り、要約と分類を行い、整形されたレポートを出力する」ワークフローを構築してみましょう。

ステップ1: Startノードで入力を定義する

Startノードでは、ワークフローが受け取る入力変数を定義します。今回は以下の変数を設定します。

  • document_text(テキスト型): 解析対象のドキュメント本文
  • language(選択型): 出力言語(ja / en)

Startノードの設定例(JSON形式でエクスポートした場合):

{
  "type": "start",
  "variables": [
    {
      "variable": "document_text",
      "label": "ドキュメント本文",
      "type": "paragraph",
      "required": true,
      "max_length": 10000
    },
    {
      "variable": "language",
      "label": "出力言語",
      "type": "select",
      "options": ["ja", "en"],
      "default": "ja",
      "required": true
    }
  ]
}

ステップ2: LLMノードで要約を生成する

Startノードの次に、最初のLLMノードを配置して要約を生成します。

プロンプト設定例:

[システムプロンプト]
あなたは優秀な文書アナリストです。与えられたドキュメントを正確に要約してください。
出力言語: {{#start.language#}}

[ユーザーメッセージ]
以下のドキュメントを200文字以内で要約してください。

---
{{#start.document_text#}}
---

要約:

ここで重要なのが、変数参照の記法です。Difyでは {{#ノードID.変数名#}} の形式で他のノードの出力を参照できます。start はStartノードのIDです。

LLMノードの設定では、使用するモデル(GPT-4o、Claude 3.5 Sonnet等)や温度パラメータも指定できます。要約タスクには温度を低め(0.2〜0.4程度)に設定することをおすすめします。

ステップ3: Question Classifierノードで文書カテゴリを判定する

要約LLMノードと並列して(または後続として)、Question ClassifierノードでLLMに文書のカテゴリを判定させます。

分類クラスの例:

  • 技術文書
  • ビジネスレポート
  • ニュース・プレスリリース
  • 法律・契約文書
  • その他

Question Classifierは入力テキストを指定したクラスのいずれかに自動分類し、後続の分岐処理に活用できます。

ステップ4: IF/ELSEノードで分岐処理

分類結果に基づいて、後続の処理を分岐させます。たとえば「技術文書」と判定された場合は技術用語の抽出処理を追加し、「法律・契約文書」の場合はリスク箇所のハイライト処理を行うといった分岐が可能です。

IF/ELSEノードの条件設定例:

条件: {{#question_classifier_1.class_name#}} == "技術文書"
→ True: 技術用語抽出LLMノードへ
→ False: 汎用フォーマットLLMノードへ

ステップ5: Codeノードでデータを整形する

各分岐の処理結果を最終的なレポート形式に整形するために、Codeノード(Python)を使います。

# Codeノード内のPythonコード例
def main(summary: str, category: str, document_text: str) -> dict:
    word_count = len(document_text)
    
    report = f"""# ドキュメント分析レポート

## 基本情報
- カテゴリ: {category}
- 文字数: {word_count}文字
- 生成日時: {__import__('datetime').datetime.now().strftime('%Y年%m月%d日 %H:%M')}

## 要約
{summary}
"""
    
    return {
        "report": report,
        "category": category,
        "word_count": word_count
    }

最後にEndノードを配置し、出力変数として report(整形されたレポート)を指定すれば、パイプラインの完成です。


実践例2: HTTP Requestノードで外部API連携を実現する

次の実践例では、ワークフロー内からHTTP Requestノードを使って外部APIと連携する方法を解説します。「文書を分析してSlack通知を送る」フローを例にとります。

HTTP RequestノードでSlack Webhookを呼び出す

Slack Incoming Webhookを使って分析結果を通知するには、HTTP Requestノードを以下のように設定します。

HTTP Requestノードの設定:

メソッド: POST
URL: https://hooks.slack.com/services/YOUR/WEBHOOK/URL
ヘッダー:
  Content-Type: application/json

ボディ (JSON):
{
  "text": "📊 ドキュメント分析が完了しました",
  "blocks": [
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "*カテゴリ:* {{#code_1.category#}}\n*文字数:* {{#code_1.word_count#}}文字"
      }
    },
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "*要約:*\n{{#llm_1.text#}}"
      }
    }
  ]
}

環境変数でWebhook URLを管理する

Slack Webhook URLのような機密情報は、Difyの環境変数(Secret Variables)機能を使って管理することを強くおすすめします。DifyのアプリコンソールにあるSecret Variables設定でURLを登録し、ノード内では {{SLACK_WEBHOOK_URL}} のように参照できます。これにより、ワークフローのエクスポートファイルにURL等の機密情報が含まれることを防げます。

外部APIからのデータ取得パターン

HTTP Requestノードはデータ取得にも使えます。たとえば、入力されたキーワードに基づいて外部の検索APIからデータを取得し、それをLLMに渡してレポートを生成するフローも構築可能です。

[HTTP Requestノード: キーワード検索API]
メソッド: GET
URL: https://api.example.com/search
クエリパラメータ:
  q: {{#start.keyword#}}
  limit: 10
  api_key: {{SEARCH_API_KEY}}

→ レスポンス: search_results(JSON配列)

[次のLLMノード]
システム: 以下の検索結果を分析してください
ユーザー: {{#http_1.body#}}

ワークフロー設計のベストプラクティス

実際にDifyワークフローを構築・運用してわかった、品質を高めるためのベストプラクティスを紹介します。

1. ノードには必ずわかりやすい名前をつける

デフォルトでは「LLM」「LLM 2」のような名前になりますが、ノードが増えるとどれがどの処理か把握しにくくなります。「要約生成」「感情分析」「Slack通知」のように日本語で具体的な名前をつけましょう。

2. エラーハンドリングを考慮する

HTTP Requestノードで外部APIを呼び出す場合、APIが一時的に利用不可になるケースがあります。IF/ELSEノードを使ってステータスコードを確認し、エラーの場合は代替処理にフォールバックするフローを設計しましょう。

[IF/ELSE: API成功判定]
条件: {{#http_request_1.status_code#}} == 200
→ True: 通常の処理フローへ
→ False: エラーログ出力LLMノードへ(または別の通知経路へ)

3. Codeノードで変数の前処理・後処理を行う

LLMの出力は自然言語であるため、後続処理で使いやすい形に整形が必要なことがあります。Codeノード(Python/JavaScript)で文字列のトリム、JSON解析、リストのフィルタリングなどの処理を挟むことで、ワークフロー全体の堅牢性が上がります。

# LLMが返したJSON文字列を安全にパースする例
def main(llm_output: str) -> dict:
    import json
    import re
    
    # コードブロックマーカーがある場合に除去
    cleaned = re.sub(r'```json\n?|```\n?', '', llm_output).strip()
    
    try:
        parsed = json.loads(cleaned)
        return {"success": True, "data": parsed}
    except json.JSONDecodeError as e:
        return {"success": False, "data": {}, "error": str(e)}

4. イテレーション(ループ処理)を活用する

Difyのワークフローには「Iteration(反復)」ノードがあり、リスト形式のデータに対して同じ処理を繰り返し適用できます。たとえば、複数のドキュメントをまとめて分析したい場合に活用できます。

[Iterationノードの構成例]
入力リスト: {{#start.document_list#}}
内部ノード:
  - LLM(個別ドキュメントの要約)
  - Code(要約の整形)
出力: 各要約のリスト

5. ワークフローのテストはステップごとに行う

Difyのワークフローエディタには、特定のノードまで実行を止めてその出力を確認できる「ステップ実行」機能があります。複雑なワークフローを構築する場合は、全体を一度に動かすのではなく、ノードを追加するたびに動作を確認する習慣をつけましょう。


ワークフローのAPI公開と外部連携

構築したワークフローは、APIとして公開して外部システムから呼び出すことができます。これにより、既存のシステムやスクリプトと組み合わせた柔軟な活用が可能になります。

APIエンドポイントの利用

Difyのアプリコンソールから「API Access」セクションに移動すると、APIキーとエンドポイントURLが確認できます。ワークフロータイプのアプリは以下のエンドポイントで呼び出せます:

curl -X POST 'https://api.dify.ai/v1/workflows/run' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "inputs": {
      "document_text": "ここに解析したいドキュメントの本文を入れます。",
      "language": "ja"
    },
    "response_mode": "blocking",
    "user": "user-001"
  }'

response_mode には以下の2つがあります:

  • blocking: 処理が完了するまで待機し、最終結果を一度に返す(短時間の処理向き)
  • streaming: Server-Sent Events形式でリアルタイムに出力をストリームする(長時間処理や進捗表示が必要な場合向き)

Pythonからワークフローを呼び出す例

import requests
import json

DIFY_API_KEY = "your-api-key-here"
DIFY_BASE_URL = "https://api.dify.ai/v1"

def run_document_analysis(document_text: str, language: str = "ja") -> dict:
    """Difyワークフローを呼び出してドキュメントを分析する"""
    
    url = f"{DIFY_BASE_URL}/workflows/run"
    headers = {
        "Authorization": f"Bearer {DIFY_API_KEY}",
        "Content-Type": "application/json"
    }
    payload = {
        "inputs": {
            "document_text": document_text,
            "language": language
        },
        "response_mode": "blocking",
        "user": "batch-processor"
    }
    
    response = requests.post(url, headers=headers, json=payload, timeout=120)
    response.raise_for_status()
    
    result = response.json()
    
    if result.get("data", {}).get("status") == "succeeded":
        outputs = result["data"]["outputs"]
        return {
            "success": True,
            "report": outputs.get("report", ""),
            "category": outputs.get("category", "不明"),
            "elapsed_time": result["data"].get("elapsed_time", 0)
        }
    else:
        return {
            "success": False,
            "error": result.get("data", {}).get("error", "不明なエラー")
        }

# 使用例
if __name__ == "__main__":
    sample_doc = """
    人工知能(AI)技術の急速な発展により、自然言語処理の分野では
    大規模言語モデル(LLM)が中心的な役割を果たしています。
    特にTransformerアーキテクチャを基盤とするモデルは、
    文章生成、翻訳、要約などの幅広いタスクで人間に匹敵する
    パフォーマンスを発揮しています。
    """
    
    result = run_document_analysis(sample_doc)
    
    if result["success"]:
        print(f"カテゴリ: {result['category']}")
        print(f"処理時間: {result['elapsed_time']:.2f}秒")
        print("\n=== レポート ===")
        print(result["report"])
    else:
        print(f"エラー: {result['error']}")

このようにPythonスクリプトからDifyワークフローを呼び出せば、バッチ処理システムやWebアプリケーションのバックエンドとの統合も容易です。


よくあるトラブルと解決策

Q: LLMノードの出力が期待通りの形式にならない

LLMは確率的に動作するため、毎回同じ形式で出力されるとは限りません。対策として:

  1. Few-shot例をプロンプトに含める: 期待する出力形式の具体例をプロンプト内に記載する
  2. 「必ず〇〇形式で出力すること」という制約を明示する: 例「結果はJSON形式のみで出力し、説明文は含めないでください」
  3. 後続のCodeノードでバリデーションと修正を行う: 前述のJSON解析コードのように、エラーを吸収する処理を挟む

Q: ワークフローの実行がタイムアウトする

複雑なワークフローや大量のデータを処理する場合、タイムアウトが発生することがあります。対策として:

  1. 処理を分割する: 大きなドキュメントを事前にチャンクに分割し、Iterationで処理する
  2. response_mode: streaming を使う: タイムアウトが起きにくいストリーミングモードを採用する
  3. LLMのmax_tokensを適切に設定する: 必要以上に大きなmax_tokensを設定しない

Q: Variable Aggregatorをうまく使えない

IF/ELSEで分岐した後に両方の経路の出力をまとめる場合、Variable Aggregatorノードが役立ちます。分岐した各経路の出力変数をVariable Aggregatorの入力として設定すると、実行された経路の出力が集約されます。条件分岐後に必ず使うべき重要なノードです。


まとめ: ワークフロー機能でDifyの真価を引き出す

Difyのワークフロー機能は、単純なチャットボット作成を超えた、エンタープライズレベルのAI自動化パイプラインを実現するための強力なツールです。

今回解説したポイントを振り返ると:

  • ノードベースのビジュアル設計で複雑なAI処理フローを直感的に構築できる
  • LLM、分類、HTTP Request、Codeなど多様なノードを組み合わせることで幅広いユースケースに対応できる
  • Codeノードを活用することで、LLMの出力を堅牢に処理・整形できる
  • APIとして公開することで、外部システムとのシームレスな統合が可能
  • ベストプラクティス(ノード命名、エラーハンドリング、ステップごとのテスト)を守ることで品質の高いワークフローが構築できる

次のアクションとして、まず小さな3〜4ノード構成のワークフロー(例: テキスト入力 → LLM要約 → 整形出力)を作ってみることをおすすめします。慣れてきたら徐々にHTTP Requestや条件分岐を加えて、業務の実課題に対応したパイプラインへと発展させていきましょう。

Difyのワークフロー機能は継続的にアップデートされており、新しいノードタイプや機能が追加され続けています。公式ドキュメントやGitHubのリリースノートをウォッチしておくことで、最新の機能をいち早く活用できます。


参考資料

この記事をシェア

XFacebookはてブ