【Dify】独自ツールを作ってみた！（セルフホスト版）

## はじめに

Difyは、AIアプリケーションを簡単に構築できるプラットフォームとして注目を集めています。特に、非エンジニアの方でも直感的に操作できる点が魅力です。提供している機能は既に多いですが、カスタマイズが可能なセルフホスト版を使えばさらに自分が欲しいツールを作成してより多彩な用途に対応できます。本記事では、Difyをセルフホスト環境で活用し、独自ツールを作成して動かすまでの手順を解説します。

## Difyツールとは

ツールは、ネット検索、科学計算、画像の描画などの機能を追加し、大規模言語モデル(LLM)の能力を拡張して外部世界と接続する力を与えます。ツールを活用すれば、再利用可能なAPI連携を構築したり、複数なLLMやツールを組み合わせて複雑な処理を実装したりすることが可能です。

## 独自のツール作成手順

本記事では、自社商品検索をするツールを作ります。完了したらワークフローなどに取り込めます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935351/ad9da425-1a17-d3fa-415f-1ed78d04bd5d.png)

まず、作業用のフォルダーを作成します。

```bash
mkdir ~/providers && cd ~/providers/
```

### 1. プロバイダー設定

プロバイダーとは、Difyにツールの機能を追加するための基盤です。これを使うことで外部APIや自社システムと連携できます。プロバイダーを通じて、Difyが外部APIやデータベースと連携できるようになります。

必要なフォルダーやファイルを作成します。

```bash
mkdir my-provider && cd my-provider

touch my-provider.yaml
touch my-provider.py
mkdir _assets
mkdir tools
```

`my-provider.yaml`に基本情報を記述します。

```yaml
identity:
  author: YAOYA株式会社
  name: my-provider
  label:
    en_US: My Provider
  description:
    en_US: 自社データを扱うプロバイダーです。
  icon: icon.png
```

- `name` : フォルダー名と一致しないといけません
- `icon` : `_assets`フォルダーに配置する必要があります

次は、`my-provider.py`を編集します。今回は認証が要らないのでかなりシンプルです。

```py
from typing import Any
from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController

class MyProvider(BuiltinToolProviderController):
    def _validate_credentials(self, credentials: dict[str, Any]) -> None:
        return None
```

### 2. ツール設定

ツールは、Difyが実行する「個々の作業」や「機能」を指します。
先ほどのCRM連携を例にすると、「顧客データを取得するツール」や「顧客情報を更新するツール」を作成することで、Difyが具体的なタスクを実行します。

まず、必要なファイルを作成します。

```bash
touch tools/search-products.yaml
touch tools/search-products.py
```

ツールの基本情報、パラメーター、出力などを `search-products.yaml` ファイルに記述します。

```yaml
identity:
  name: search-products
  author: YAOYA株式会社
  label:
    en_US: Search Products
description:
  human:
    en_US: 自社商品を検索する。
  llm: Searches for products.
parameters:
  - name: query
    type: string
    required: true
    label:
      en_US: Query string
    human_description:
      en_US: クエリ
    form: llm
```

そえぞれの項目の説明は[公式サイト](https://docs.dify.ai/ja-jp/guides/tools/quick-tool-integration#id-3-tsrunoyamlwo)に確認できますが、個人的に以下のパラメータを注目してほしいです。

- **name**：ファイル名と一致しないといけないです
- **llm**：LLMに渡す紹介文。LLMがこのツールをよりよく理解して使用できるように、ここにはできるだけ詳細な情報を書いておくといいです
- **parameters**：パラメーターリスト。少し難解ですが、既存のツールの中身を見て真似て書きましょう

基本情報を記述した後に、具体的な「動き方」を定義する`search-products.py`を編集します。今回は以下の流れを実現しています：

1. **クエリを受け取る**：ユーザーが入力したクエリをツールが取得します（例: 検索キーワード）
2. **外部APIを呼び出す**：外部APIにクエリを送信して結果を取得します
3. **レスポンスを加工する**：APIの結果をフォーマットし、Difyが利用できる形式（JSONなど）に整えます
4. **Difyに返す**：整形されたデータがDifyに渡され、次の処理に活用されます

```python
from typing import Any, Dict, List, Union
from core.tools.tool.builtin_tool import BuiltinTool
from core.tools.entities.tool_entities import ToolInvokeMessage


class SearchProductsTool(BuiltinTool):
    def _invoke(
        self, user_id: str, tool_parameters: Dict[str, Any]
    ) -> Union[ToolInvokeMessage, List[ToolInvokeMessage]]:
        # 1. クエリを受け取る
        q = tool_parameters["query"]

        # 商品検索をします。
        # 本来はAPIなどから取得しますが、割愛します。
        products = [
            {"product_name": "商品A", "price": "1000円", "availability": "在庫あり"},
            {"product_name": "商品B", "price": "1500円", "availability": "在庫なし"},
            {"product_name": "商品C", "price": "2800円", "availability": "取り寄せ"},
        ]

        data = list(filter(lambda p: p["product_name"] == q, products))

        # `json` フィールドに結果を入れてDifyに返す
        return [self.create_json_message(d) for d in data]
```

### 3. Dify設定

最後に、Dify側の設定を行います。

まず、`docker-compose.yaml`を編集して、apiコンテナにプロバイダーフォルダをマウントします。

```yaml
services:
    api:
        volumes:
            # ここに下記の行を追加
            - /home/<user>/providers/my-provider:/app/api/core/tools/provider/builtin/my-provider
```

- プロバイダーは `/app/api/core/tools/provider/builtin` にあります。かなり参考になりますので他のプロバイダーの中身を確認しましょう！

その後、Difyを再起動します。

```shell
docker compose down
docker compose up -d
```

以上です。ワークフローで使えることをツール一覧から確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935351/5ee1fe12-ba70-4d49-24ad-181245d44332.png)


ツールが認識されない場合は、`docker-compose.yaml`のパス設定が正しいことを確認し、Difyを再起動してくださいまた、`/app/api/core/tools/provider/builtin`内のフォルダ名が一致していることを確認してください。

## まとめ

本記事では、Difyのセルフホスト版を使って外部APIと連携する独自のツールを構築する手順を実践しました。プロバイダーやツールのそれぞれの役割はご理解を頂いたでしょうか？難しい部分があるかもしれませんが、Difyの[公式ドキュメント](https://docs.dify.ai/ja-jp/guides/tools/quick-tool-integration)や[サンプルコード](https://github.com/langgenius/dify/tree/8d4bb9b40d5bb1d6b1ae46b1b35c3680ce28d18d/api/core/tools/provider/builtin)を参照しながら実装すれば、実現は十分可能です。プロバイダーやツールを活用することで、Difyの活用できる幅が大きくなります！