RAKSUL TechBlog

RAKSULグループのエンジニアが技術トピックを発信するブログです

Dify応用編:RAG搭載チャットボットの「その先」へ

この記事はNovasell Advent Calendar 2025の15日目の記事です。


目次


はじめに

Novasellで主にAIプロダクトの設計・開発を行っている浅田です。今回は、私たちのチームでも積極的に活用しているDifyの応用テクニックについてお話しします。

Difyは、LLMアプリケーションをノーコード/ローコードで構築できるプラットフォームです。 RAGチャットボットやAIエージェントをGUIで手軽に作れるため、非常に人気があります(自分も最初はそのあまりの簡単さに感嘆した覚えがあります)。 dify.ai

しかし、実際の業務で活用しようとすると、さまざまな課題にぶつかることがあります。

例えば:

  • 調査用Agentを作る際、メインのLLMはClaude Opus 4.5を使いつつ、X上のユーザーの声も収集したい。しかし、grokはあくまで1つのモデルなので、デフォルトではツールとして呼び出せない。
  • 複数のアプリで同じような処理を重複して管理しており、メンテナンスがつらい。
  • アプリケーションごとのLLMコストを把握したいが、Difyではアプリ別にAPIキーを設定できないため、各アプリのコストを目視でチェックする必要がある。

本記事では、こうしたDifyの「痒いところ」を解決するテクニックを紹介します。 以下では3つのLevelに分けて解説していきます。 この3つのテクニックを使いこなせば、Difyの無限の可能性を引き出せるはずです。

なお、ここで紹介するテクニックはすべてDify上で完結します。したがって、エンジニアに限らず誰でも実践できます。


本記事の対象読者

  • Difyで基本的なチャットボットやワークフローを構築した経験がある方
  • 「動くものは作れたけど、もっと複雑なことがしたい」と感じている方
  • Difyを本番運用する上での管理・監視に課題を感じている方

Level 1:ワークフローをツールとして公開し、Agentから利用する

こんなときに使う

「調査用Agentを作っている。メインのモデルはClaude Opus 4を使いたいけど、ツールとしてgrokを呼び出したい」

「画像生成Agentを作っている。メインのモデルはClaude Opus 4を使いたいけど、ツールとしてNano Banana Proを呼び出したい」

「カスタムツールをすばやく公開して、Agentから利用できるようにしたい」

grokやNano Banana Proは1つのモデルであり、デフォルトではツールとして利用できません。こうした特定のモデルを別のAgentからツールとして呼び出したい、あるいはカスタムツールを公開してAgentに利用させたい、というニーズがあります。

こうしたケースで威力を発揮するのが、ワークフローのツール化機能です。

何ができるのか

Difyでは、作成したワークフローを「ツール」として公開し、別のAgentから呼び出すことができます。これにより、以下のような構成が可能になります。

[Agent (Claude Opus 4)]
    ├── ツール: 画像生成ワークフロー(内部でNano Banana Pro使用)
    ├── ツール: X Researchワークフロー(内部でgrok使用)
    └── ツール: ...

設定手順

Step 1: ワークフローを作成・公開する

まず、ツール化したいワークフローを通常通り作成します。開始ノードで入力変数を定義し、終了ノードで出力を返す構成にしておきます。

完成したら、右上の「公開する」ボタンからワークフローを公開します(以下はgrokのツール化の例です)。

Step 2: 「Workflow as Tool」を設定する

公開メニュー内の「ワークフローをツールとして公開する」オプションを選択し、

以下を設定します。

設定項目 説明
ツールコールの名前 Agentがツールを識別するための名前。機能を端的に表す命名を
ツールの説明 LLMがツールの使用タイミングを判断するための重要な情報
ツール入力 ワークフローに渡す入力パラメータの説明

ポイント: ツールの説明やツール入力の説明は、Agentが「いつ、どのようにこのツールを使うべきか」を判断する材料になります。ここをしっかり書くことで、Agentが適切なタイミングでツールを実行できるようになります。可能な限り詳細かつ明確に記載しましょう。

Step 3: Agentから呼び出す

新規Agentを作成し、Toolsセクションで「Workflow」タブから公開済みのワークフローを追加します。

実践Tips

💡 ワークフローの粒度を意識する

ツール化するワークフローは、「単一責務」になるよう設計しましょう。複数の責務を持たせると、Agentが適切なタイミングで呼び出せなくなります。

💡 Chatflowはツール化できない

ツールとして公開できるのは「Workflow」タイプのみです。「Chatflow」はツール化できないので、注意してください。


Level 2:HTTPノードで外部API呼び出し

こんなときに使う

例1: 共通処理の重複管理問題

A・B・Cという3つのワークフローがあり、それぞれに「ドキュメントを解析 → RAG検索 → 結果を要約」という一連の処理が含まれているとします。

[Workflow A] ─ ドキュメント解析 → RAG検索 → 要約生成 ─ ...
[Workflow B] ─ ドキュメント解析 → RAG検索 → 要約生成 ─ ...
[Workflow C] ─ ドキュメント解析 → RAG検索 → 要約生成 ─ ...

この状態で「要約生成のロジックを変更したい」となった場合、A・B・Cすべてを修正しなければなりません。修正漏れのリスクもあります。

例2: Agentノードの制約回避

ワークフロー内でAgent的な処理をしたい場合、Difyには「Agentノード」がありますが、いくつかの制約があります。例えば、画像を入力として渡せないといった制限です。このように、Agentアプリではできるのに、Agentノードだとできないことがあります。


まとめ: どちらのケースも、HTTPノードで別のDifyアプリをAPI経由で呼び出すことで解決できます。共通処理はワークフローとして、Agent的な処理はAgentアプリとして切り出し、必要な箇所からHTTPで呼び出すパターンです。

設定手順

※以下はAgentを切り出すパターンです。

Step 1: 呼び出される側のAgentを作成・公開する

まず、呼び出したい処理をAgentアプリとして作成し、公開します。 そのうえで、APIアクセス用のAPIキーを発行しておきます。

Step 2: HTTPノードを設定する

呼び出す側のワークフローにHTTPノードを追加し、以下のように設定します。

設定項目
Method POST
URL https://api.dify.ai/v1/chat-messages(またはセルフホスト環境のURL)
Headers Authorization: Bearer {api_key}, Content-Type: application/json
Body JSON形式でクエリや入力変数を指定

重要: Agentを呼び出す場合、response_modeには必ずstreamingを指定してください。blockingではAgentモードが動作しません。

Step 3: レスポンスを処理する

streamingモードのレスポンスは、Server-Sent Events(SSE)形式で返ってきます。そのままでは使いづらいため、Codeノードでパースする処理を入れます。

ストリーミングレスポンスはdata: {...}という形式のチャンクが連続して送られてくるため、これを結合して最終的な回答を抽出するロジックが必要です。

import json

def main(output: str) -> dict:
    # Split the body into lines
    lines = output.strip().split("\n")
    
    last_thought = None
    
    # Look for all agent_thought events
    for line in lines:
        if line.startswith("data: "):
            data_str = line[6:]  # Remove "data: " prefix
            try:
                event_data = json.loads(data_str)
                if event_data.get("event") == "agent_thought" and event_data.get("thought"):
                    last_thought = event_data.get("thought")
            except json.JSONDecodeError:
                continue
    
    return {
        "result": last_thought
    }

実践Tips

💡 タイムアウト設定に注意

HTTPノードのデフォルトタイムアウトは60秒です。複雑なAgent処理を呼び出す場合、これでは足りないことがあります。セルフホスト環境では、環境変数で調整可能です。

💡 画像を送りたい場合

画像データを送る場合、Base64エンコードして直接送るよりも、File Upload APIでいったんアップロードしてからファイルIDを参照する方式の方が安定します。

自分は以下のような手順でリクエストボディを構築しています。参考までに。

  1. ファイルアップロード
  2. ID抽出
  3. リクエスト構築

⚠️ エラーハンドリングを忘れずに

HTTPノードの後には、ステータスコードをチェックする条件分岐を入れておくことをお勧めします。


Level 3:Console APIで「無双」する

こんなときに使う

Difyを本格的に運用し始めると、UI上の操作だけでは限界を感じる場面が出てきます。

  • Dify上のアプリケーションログを有効活用したい
  • アプリごとのコストを1つのワークフローで集計したい
  • ワークフローから動的にナレッジファイルを追加したい

こうした「運用・管理系」のニーズに応えるのが、Console APIです。

Service API と Console API の違い

Difyには2種類のAPIがあります。

Service API Console API
用途 アプリの実行・利用 アプリの管理・運用
認証 アプリごとのAPIキー ユーザーログイン(email/password)
主な操作 チャット送信、ワークフロー実行 アプリ作成、ログ取得、コスト取得
ドキュメント 公式ドキュメントあり 公式ドキュメントなし(ソースコード参照)

認証の仕組み

Console APIは、まずログインしてアクセストークンを取得し、そのトークンを使って各種操作を行う方式です。

1. POST /console/api/login でログイン
   → access_tokenを取得

2. 各APIリクエストのヘッダーに
   Authorization: Bearer {access_token} を付与

具体例:チャット履歴を取得する

実際のコード例を見てみましょう。以下は、Console APIを使って特定アプリのチャット履歴を取得し、Markdown形式に変換するスクリプトです。

import requests
from typing import List, Dict, Any


def main(conversation_data: str, dify_password: str, dify_email: str, target_app_id) -> str:
    base_url = "https://your-dify-instance.com/console/api"
    email = dify_email
    password = dify_password
    app_id = target_app_id
    limit = 100
    conversation_id = conversation_data.get("id")

    base_url = base_url.rstrip('/')
    session = requests.Session()

    # ログイン
    login_url = f"{base_url}/login"
    response = session.post(login_url, json={
        "email": email,
        "password": password
    })
    response.raise_for_status()
    access_token = response.json()['data']['access_token']

    # チャットメッセージ取得
    messages_url = f"{base_url}/apps/{app_id}/chat-messages"
    headers = {'Authorization': f'Bearer {access_token}'}
    params = {'conversation_id': conversation_id, 'limit': limit}

    response = session.get(messages_url, headers=headers, params=params)
    response.raise_for_status()
    messages = response.json().get('data', [])

    # Markdown形式に変換
    markdown = _convert_to_markdown(messages)

    return {"conversation": markdown}


def _convert_to_markdown(messages: List[Dict[str, Any]]) -> str:
    lines = []

    for msg in messages:
        query = msg.get('query', '').strip()
        answer = msg.get('answer', '').strip()

        if query:
            lines.append(f"Q: {query}\n")
        if answer:
            lines.append(f"A: {answer}\n")

    return '\n'.join(lines)

このコードのポイント:

  1. ログイン処理: POST /console/api/loginでemail/passwordを送信し、access_tokenを取得
  2. 認証ヘッダー: 取得したトークンをAuthorization: Bearer {token}として付与
  3. データ取得: GET /console/api/apps/{app_id}/chat-messagesでチャット履歴を取得

このスクリプトをDifyのCodeノードに配置すれば、ワークフロー内から別アプリの利用状況を集計する、といった処理が可能になります。

Console APIを活用することで、ログ取得以外にも、コストの取得、ナレッジの追加、設定ファイルのダウンロードなどが可能になります。用途に合わせて上記のコードを応用してみてください。

注意点

⚠️ 公式サポートではない

Console APIは公式ドキュメントに記載されておらず、バージョンアップで仕様が変わる可能性があります。本番運用で使う場合は、この点を理解したうえで利用してください。

⚠️ Cloud版での利用

Dify Cloud(SaaS版)でもConsole APIは存在しますが、一部機能が制限されている可能性があります。セルフホスト環境の方がフル機能を利用できます。

💡 エンドポイントの探し方

Console APIのエンドポイントは、GitHubリポジトリのapi/controllers/console/配下を見ると把握できます。また、ブラウザの開発者ツールでDifyのUIを操作しながらネットワークタブを確認すると、実際に呼ばれているAPIがわかります。

参考: https://github.com/langgenius/dify/tree/main/api/controllers/console


まとめ

本記事では、Difyの応用的な使い方を3つのレベルに分けて紹介しました。

Level テクニック 解決できる課題
1 ワークフローのツール化 複数モデルの使い分け、処理のモジュール化
2 HTTPノードでのAPI呼び出し Agentノードの制約回避、共通処理の切り出し
3 Console APIの活用 運用自動化、コスト管理、CI/CD

RAGチャットボットを作れるようになったら、ぜひこれらのテクニックを使って、より実践的なAIアプリケーションに挑戦してみてください。


参考リンク