0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Difyのツール機能を使ってシノニムマップ機能を足してみる

Posted at

はじめに

この記事をご覧いただいているということは、Difyに興味をお持ちの方、もしくはすでに使っている方かもしれませんね。たまたま目に留まって開いてみた、という方も大歓迎です!

Dify(ディファイ)は、ノーコードで生成AIアプリを作れるオープンソースのプラットフォームです。プログラミングの知識がなくても、直感的な操作でAIアプリを構築できるため、ビジネスユーザーや非エンジニアの方々にもおすすめです。

今回はそんなDifyというノーコードツールに独自の機能を足してみたいと思います。

参考文献など

Difyには、ツールと呼ばれる機能が存在し、この機能で外部のAPIを呼び出したりできます。
今回はこのAPIを簡単に実装し、シノニムマップを作るところまで行きたいと思います。

Difyを立ち上げる

上記を参考にDifyを立ち上げてログインできる状態にまでしておきます。

APIを作る

今回使用するAPIフレームワークはFastAPIです。
こちらでサクッと実装します。

まずは、FastAPIのひな形を作ります。
ここで、"url": "http://192.168.11.123:8000"に設定してあるIPの内容を自分のローカルIPと合せてください。

from fastapi import FastAPI

app = FastAPI(
    title="Dify Tool API",
    description="これはDifyのサンプルToolのAPIです。",
    servers=[
        {
            "url": "http://192.168.11.123:8000",  # 自分のローカルIPを指定
            "description": "Local server",
        }
    ],
)

@app.post("/api/synonym")
async def replace_synonym():
    return {"message": "Hello"}

シノニムとは

今回の実装では、単純に置き換えるではなく、形態素解析をして置き換えます。
なぜこんなことをするか。
例えば、「確認」のシノニムマップを「チェック」としていた場合を考えてみましょう。

部分一致による単純な置換

単純に部分一致で「確認」を置き換える処理を行った場合、次のような誤動作が発生します。

  • 入力: "不確認状態です"
  • 出力: "不チェック状態です"

この例では、「確認」だけを部分一致で置き換えてしまったため、「不確認」という単語が分解されて不自然な日本語になっています。

シノニム処理による改善

形態素解析を活用することで、「不確認」全体を適切に解析し、「確認」のみをシノニム辞書に基づいて置き換えることができます。

  • 入力: "不確認状態です"
  • 出力: "不確認状態です"

この場合、「不確認」は一つの単語として認識され、シノニム辞書に存在しないため置換されません。

一方で、例えば「確認します」という文が入力された場合は次のように変換されます。

  • 入力: "確認します"
  • 出力: "チェックします"

形態素解析を用いることで、文脈に応じた正確な置換が可能となり、不自然な結果を回避できます。

シノニム部分を実装する

それでは、辞書を作ります。
先ほどの例の確認:チェックを入れておきます。

# シノニム辞書の定義
synonym_dict = {
    "確認": "チェック",
}

形態素分析器の最小トークンごとに置き換えてそれをつなぎ合わせて返却しようかと思いましたが、接頭辞がついている場合、例えば先ほどの例の"不確認"の場合、 "不"と"確認"に分類されてしまい思うようにいい結果が得られませんので、1文字+名詞または形容詞の場合はそれを一単語とみなすようにします。

@Language.component("merge_prefixed_tokens")
def merge_prefixed_tokens(doc):
    """
    接頭辞を動的に判定し、次のトークンと結合する関数。
    """
    with doc.retokenize() as retokenizer:
        for i, token in enumerate(doc[:-1]):
            # 動的に接頭辞を判定(1文字であり、次のトークンが名詞/形容詞の場合)
            if len(token.text) == 1 and doc[i + 1].pos_ in {"NOUN", "ADJ"}:
                span = doc[i:i + 2]
                retokenizer.merge(span)
    return doc

# カスタムコンポーネントをパイプラインに追加
nlp.add_pipe("merge_prefixed_tokens", last=True)

API受け取り部分は、そのまま出力されたトークンごとに見て行って、シノニムにヒットするものが置き換えて結合し、文字列データを返します。

@app.post("/api/synonym")
async def replace_synonym(text: str):
    """
    GiNZAを用いて文章を形態素解析し、ヒットした単語をシノニムマップの単語と置き換える
    """
    # GiNZAで形態素解析
    doc = nlp(text)

    # トークンをシノニム辞書で置き換えつつ結合
    replaced_text = "".join(
        synonym_dict.get(token.lemma_, token.text) for token in doc
    )

    return replaced_text

全体コード

from fastapi import FastAPI
import spacy
from spacy.language import Language

app = FastAPI(
    title="Dify Tool API",
    description="これはDifyのサンプルToolのAPIです。",
    servers=[
        {
            "url": "http://192.168.11.123:8000",  # 自分のローカルIPを指定
            "description": "Local server",
        }
    ],
)

# GiNZAの日本語モデルをロード
nlp = spacy.load("ja_ginza")

@Language.component("merge_prefixed_tokens")
def merge_prefixed_tokens(doc):
    """
    接頭辞を動的に判定し、次のトークンと結合する関数。
    """
    with doc.retokenize() as retokenizer:
        for i, token in enumerate(doc[:-1]):
            # 動的に接頭辞を判定(1文字であり、次のトークンが名詞/形容詞の場合)
            if len(token.text) == 1 and doc[i + 1].pos_ in {"NOUN", "ADJ"}:
                span = doc[i:i + 2]
                retokenizer.merge(span)
    return doc

# カスタムコンポーネントをパイプラインに追加
nlp.add_pipe("merge_prefixed_tokens", last=True)

# シノニム辞書の例
synonym_dict = {
    "確認": "チェック",
}

@app.post("/api/synonym")
async def replace_synonym(text: str):
    """
    GiNZAを用いて文章を形態素解析し、ヒットした単語をシノニムマップの単語と置き換える
    """
    # GiNZAで形態素解析
    doc = nlp(text)

    # トークンをシノニム辞書で置き換えつつ結合
    replaced_text = "".join(
        synonym_dict.get(token.lemma_, token.text) for token in doc
    )

    return replaced_text

作ったAPIをテストする

curl --location --request POST 'http://127.0.0.1:8000/api/synonym?text=不確認状態です'

image.png

上記のようになっていればOK

image.png
👆の場合のみ変換されているので要件は満たせている。

よさそう!!

Difyへのつなぎこみ

前準備

openapiの出力
まず、Difyで簡単にツールの登録ができるようにopenapiのコードを入れる必要があります。
幸い、FastAPIには、openapiを出力する機能があります。

http://127.0.0.1:8000/docs

のように/docsを付けるだけで、下記からダウンロードできます。
image.png

注意
今回は単純なものなので、Difyがそのまま読み込めますが、JSONBodyで入力を期待したりすると軒並みエラーを吐きます...

{
    "openapi": "3.1.0",
    "info": {
        "title": "Dify Tool API",
        "description": "これはDifyのサンプルToolのAPIです。",
        "version": "0.1.0"
    },
    "servers": [
        {
            "url": "http://192.168.11.123:8000",
            "description": "Local server"
        }
    ],
    "paths": {
        "/api/synonym": {
            "post": {
                "summary": "Replace Synonym",
                "description": "GiNZAを用いて文章を形態素解析し、ヒットした単語をシノニムマップの単語と置き換える",
                "operationId": "replace_synonym_api_synonym_post",
                "parameters": [
                    {
                        "name": "text",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "title": "Text"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {}
                            }
                        }
                    },
                    "422": {
                        "description": "Validation Error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/HTTPValidationError"
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "HTTPValidationError": {
                "properties": {
                    "detail": {
                        "items": {
                            "$ref": "#/components/schemas/ValidationError"
                        },
                        "type": "array",
                        "title": "Detail"
                    }
                },
                "type": "object",
                "title": "HTTPValidationError"
            },
            "ValidationError": {
                "properties": {
                    "loc": {
                        "items": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "integer"
                                }
                            ]
                        },
                        "type": "array",
                        "title": "Location"
                    },
                    "msg": {
                        "type": "string",
                        "title": "Message"
                    },
                    "type": {
                        "type": "string",
                        "title": "Error Type"
                    }
                },
                "type": "object",
                "required": [
                    "loc",
                    "msg",
                    "type"
                ],
                "title": "ValidationError"
            }
        }
    }
}

こんなものが落とせたでしょうか?
この文字列をコピーします。

Difyのツールに設定

では次にDifyの設定に進みます。
「ツール」を選択後「カスタムツールを作成する」を押下します。
image.png

すると、、

image.png
こんなものが出てきますので、名前を任意の好きな名前に、スキーマを先ほどコピーしたopenapiのYAMLを貼り付けます。
※今回は認証設定をしていないので、認証方法なしに設定します。

Difyでのテスト

image.png
上記を押下します。

image.png
text部分の値に、確認するものがありません!と入力すると、チェックするものがありません!が帰ってくるはずです。

image.png
ここまで出来たら、保存しておきます。

image.png
こんなのができましたね。

実際に使用する

実際にスタジオから、チャットボットを作成します。
オプションとして、「Chatflow」を選択して作成してください。※これがないと今回のツールが使えません
image.png

今回作ったツールをノードエディタ上に追加し、下記のように入力変数を設定します。
image.png

LLMノードでは下記のようにUSER部分を編集します。
image.png

下記のようになればOK
image.png

ちなみに「ユーザーメッセージにsys.queryが必要です」と出ていますが無視できますので大丈夫です。

プレビュー

プレビューを実行するとこうなります。
image.png

以上、Difyでシノニムマップを作ってみたでした。
他にも、この要領でいろんな機能を足して行けそうです。

また、簡易的なAPIひな形を作っておいておきます。
今回のシノニムの機能が入っています。
これをベースに機能を増やしていってもOK

ちなみに
JSON Bodyでリクエストを受け取るコードを作りFastAPIでOpenAPIを生成すると読み込まなくなりますが、
このテンプレートで、

poetry run generate-openapi

で出てきたOpenAPIはDifyにそのまま読み込ませられるようになっていますので、ご活用ください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?