「ChatGPTで業務が楽になった」という声は多くの現場で聞かれます。OpenAIの公式情報によれば、GPT-4oなどのレガシーモデルから、より高度な推論能力やツール実行能力を備えた新モデルへと移行が進んでいます。要約や文章作成の精度が劇的に向上し、応答速度も改善されました。しかし、AIモデルがどれほど進化しても、「結局、毎回手動でプロンプトを入力し、結果を別ツールに転記するのが手間だ」という根本的な課題は残ります。
対話型のAIは非常に強力なツールですが、定型業務を完全に自動化し、ビジネスへの最短距離を描くためには「人が介在しないプロセス」を設計する必要があります。そこでシステム思考の観点から注目すべきなのが、DifyのWorkflow(ワークフロー)機能です。
本記事では、単なるチャットボット作成から一歩進み、「キーワードを入力するだけで、Web検索・情報収集・要約を行い、Slackにレポートを通知する」という一連の流れを自動化する自律型エージェントを構築します。
特に、GUI操作だけでは手が届かない複雑なデータの加工処理について、Pythonコードノードを使った具体的な実装方法に踏み込みます。システム思考で全体を設計し、細部をコードで詰める。このアプローチをとることで、実務に耐えうる堅牢なエージェントを作り上げるための基盤となります。まずは動くプロトタイプを作り、仮説を即座に形にして検証していきましょう。
1. Chatflowとは違う「Workflow」の思考法
Difyには大きく分けて「Chatflow」と「Workflow」という2つの作成モードがあります。多くの人が最初に触れるのはChatflowですが、業務プロセスの自動化を目指すならWorkflowへの意識転換が必要です。
対話型ではなく「プロセス型」で考える
Chatflowは、ユーザーとの「会話のキャッチボール」を前提としています。文脈(コンテキスト)を保持し、過去のやり取りを踏まえて回答することに長けています。しかし、これは裏を返せば「ユーザーが応答しないと処理が進まない」ということでもあります。
一方、Workflowは「入力から出力までの一方向の処理」です。工場のベルトコンベアをイメージしてください。材料(入力データ)を投入すれば、各工程(ノード)で加工され、最終製品(成果物)が出てきます。ここに人間の介在は不要です。
- Chatflow: 人間との協働作業、相談、ブレインストーミング
- Workflow: 定型業務の代行、バックグラウンド処理、API連携
業務効率化の観点では、この「プロセス型」のアプローチこそが、時間を生み出す鍵となります。
今回構築する「競合リサーチエージェント」の全貌
今回作成するエージェントの設計図(フロー)は以下の通りです。
- Start: 調査したい「キーワード」を入力
- Tool (Search): 検索エンジンで最新情報を取得
- Code (Python): 検索結果から不要な情報を削ぎ落とし、整形
- LLM: 整形された情報を元に、競合分析レポートを作成
- HTTP Request: 完成したレポートをSlackチャンネルへ通知
この流れにおいて、特に重要なのが「3」のPythonによるデータ処理です。ここをノーコードだけでやろうとすると、トークン数を無駄に消費したり、LLMが誤作動を起こしたりする原因になります。技術の本質を見極め、適切なツールを選択することが重要です。
2. Workflowの基本構造とノード設定
では、実際にDifyのキャンバス上で構築を進めていきます。まずは骨格となる基本ノードの配置です。
Startノードと変数の定義
Workflowの出発点となるStartノードでは、外部からどのようなデータを受け取るかを定義します。
今回は検索キーワードを受け取るため、以下のように設定します。
- Variable:
query - Type: String(文字列)
- Required: チェックを入れる
これで、このワークフローを実行する際に「query」という入力欄が表示されるようになります。
Toolノード(Tavily/Google Search)の連携
次に、外部情報を取得するためのToolノードを接続します。Web検索には、AIエージェント向けに最適化された検索APIである「Tavily」の使用をお勧めします(Google Search APIでも代用可能です)。
キャンバス右側のメニューから「Tools」を選び、Tavilyを追加します。入力パラメータの query には、Startノードで定義した変数を割り当てます。Difyでは {{#Start.query#}} のような形式で変数を参照します。
LLMノードによる「判断」の組み込み
検索結果をそのまま人間に見せるのではなく、AIに「判断」させる場合はLLMノードを挟みます。例えば、「検索結果の中から、今回の調査意図に最も合致する企業を3社選定せよ」といった指示です。
ここで重要なテクニックがあります。後続の処理(プログラムによる処理など)にデータを渡す場合、LLMの出力をJSON形式に指定することです。プロンプト内で「出力は必ずJSON形式にしてください」と指示し、モデル設定でも「JSON Mode」をオンにしておくと、システム間の連携エラーを減らすことができます。経営者視点で見ても、エラーの少ない安定したシステムは業務の信頼性に直結します。
3. 【コード解説】Pythonノードによるデータ構造化
ここが本記事のハイライトです。検索ツールから返ってくるデータは、往々にして雑多な情報を含んでいます。そのままLLMに投げると、トークン制限に引っかかったり、精度が落ちたりする可能性があります。
Difyの「Codeノード」を使って、Pythonスクリプトでデータをきれいに整形しましょう。
検索結果のノイズを除去するフィルタリング処理
以下は、検索結果(リスト形式の辞書データ)を受け取り、タイトルとURL、要約文だけを抽出してMarkdown形式のリストに変換するPythonコードです。
def main(search_results: list) -> dict:
"""
検索結果リストから必要な情報だけを抽出して整形する関数
Args:
search_results (list): 検索ツールから返却された生データ
Returns:
dict: 整形後のテキストと件数
"""
formatted_list = []
# 検索結果が空、またはNoneの場合のハンドリング
if not search_results:
return {
"result_text": "検索結果が見つかりませんでした。",
"count": 0
}
for item in search_results:
# 辞書から必要なキーを取得(存在しない場合はデフォルト値を設定)
title = item.get('title', 'No Title')
url = item.get('url', '#')
# コンテンツが長すぎる場合は先頭300文字にカットしてトークン節約
content = item.get('content', '')[:300]
# 読みやすい形式に整形
entry = f"### {title}\n- URL: {url}\n- Summary: {content}..."
formatted_list.append(entry)
# リストを改行で結合して一つの文字列にする
joined_text = "\n\n".join(formatted_list)
# 【重要】DifyのCodeノードは必ず辞書型(dict)でreturnする必要がある
return {
"result_text": joined_text,
"count": len(formatted_list)
}
Dify独自の入出力仕様への対応
このコードには、DifyでPythonを使う際の重要なルールが含まれています。
- 関数名は
main: エントリーポイントは必ずdef main(...)でなければなりません。 - 引数のマッピング: コードエディタの下部にある「Input Variables」で、前のノードの出力(例:
{{#Tavily.result#}})をsearch_resultsという変数名にマッピングする必要があります。 - 戻り値は辞書型:
returnは必ずdict型で行います。ここで返したキー(上記例ではresult_textとcount)が、次のノードで利用可能な変数となります。
この「構造化データへの変換」を挟むことで、後続のLLMはノイズのない純粋なテキストデータを読み込むことができ、分析の精度が向上すると考えられます。
4. 論理分岐と反復処理の実装
直線的な処理だけでなく、状況に応じた分岐を作ることで、エージェントはより賢くなります。
If-Elseノードによる条件分岐
先ほどのPythonコードで count という変数を返しました。これを使って条件分岐を行いましょう。
- If:
countが0の場合 → エラーメッセージを出力して終了 - Else:
countが1以上の場合 → LLMによる詳細分析へ進む
このように「データがない場合」のルートを作っておくことは、実運用での安定性を高めるために不可欠です。
Templateノードを使った最終レポートの整形
LLMが生成した分析結果や、Pythonで整形したソース情報を組み合わせて、最終的なレポート形式に整えます。ここで役立つのが「Templateノード」です。
Jinja2記法を使って、以下のように記述します。
# 🔍 競合調査レポート: {{ input_keyword }}
## 📊 AI分析サマリー
{{ llm_analysis }}
## 🔗 参照ソース

{{ formatted_sources }}
---
Created by Dify Agent
ここで変数を埋め込むことで、常に一定のフォーマットで出力されるようになります。これはSlackやメールで見やすさを担保するために重要です。
5. デバッグと外部アプリへの接続
最後に、作成したワークフローが正しく動くか確認し、Slackへ接続します。
各ノードの入出力をトレースする
Difyのプレビュー機能(Runボタン)を実行すると、各ノードがどのようなデータを受け取り、何を出力したかが可視化されます。
特にCodeノードでエラーが出る場合は、「Input Variables」の設定ミスか、Pythonコード内のキー名の間違いがほとんどです。ログを見て、NoneType エラーなどが出ていないか確認しましょう。
HTTP RequestノードでのSlack通知実装
ワークフローの最後に「HTTP Request」ノードを配置します。これにより、外部のAPIを叩くことができます。
- Slack側で「Incoming Webhook」のURLを発行します。
- DifyのHTTP Requestノード設定:
- Method: POST
- URL: SlackのWebhook URL
- Headers:
Content-Type: application/json - Body: JSONを選択し、以下の構造を入力
{
"text": "{{#Template.output#}}"
}
これで、Difyで生成されたレポートが、自動的にSlackチャンネルに投稿されるようになります。
まとめ
今回は、DifyのWorkflow機能とPythonコードノードを組み合わせた、AIエージェント構築手順について解説しました。
- ChatflowとWorkflowの違い: 対話ではなくプロセス自動化の視点を持つ。
- Codeノードの活用: データの整形や計算はLLMではなくコードに任せる。
- 構造化データの重要性: ノード間の受け渡しをスムーズにし、エラーを防ぐ。
この構成は、競合調査だけでなく、日報の自動生成、特定の技術ニュースの収集、あるいは社内FAQの更新など、あらゆる定型業務に応用可能です。
まずは小さなタスクから自動化し、徐々に複雑なワークフローへと発展させてみてください。「自分でコードを書ける」という強みと「ノーコードの手軽さ」を組み合わせることで、開発スピードと品質の両立が可能になります。プロトタイプ思考で「まず動くものを作る」ことから始め、ビジネスの課題解決に向けた最短距離を描いていきましょう。
コメント