【Dify】Self-Routeを使用した、数学の回答・解説生成のアプリケーションの実装

# はじめに
今回はSelf-Routeの技術を使用した数学の回答解説生成のアプリケーションを、Difyで実装してみました。

**●Self-Routeについて**
・Self-Routeとは
ユーザーの質問に対し、RAGを行うべきか、長文コンテクスト(Long-Context)対応LLM、(以後、LC対応LLM)のプロンプトに情報を全て与えて処理を行うべきかを、AIが判定し最適な方を選んで処理してくれる手法で、2024年の7月下旬に発表されました。

Self-Routeを使用することで、計算コストを減らしたうえで、LC対応LLMと同じくらいの質で処理を行うことができます。
詳しくは以下の論文をご参照ください。

https://www.arxiv.org/abs/2407.16833


**●Difyについて**
Difyは独自のアプリケーションを作成できる、生成型AIツールの開発プラットフォームです。

https://dify.ai/jp


また、今回の実装にあたって、以下の動画を参考にしました。

https://youtu.be/b5zlZkRLh5Y?si=brVXe5FBjTQS21Xf



# 導入

アプリケーションの全体像は以下のようになっています。
今回は、「チャットボット」のChatflow機能で作成しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806967/e6ef0991-de16-5e08-a8f6-ad3cd10c5a02.png)



**①質問の入力**
**②ナレッジの取得**
**③LLMでユーザーからの質問に回答できるかどうかを判断**
**④条件分岐(A～Cの3つ)**

**A.参照されたナレッジとは関係ない質問が来た場合**
A-1:LLMでの回答
A-2:回答の出力

**B.参照されたナレッジだけでは回答できないと判断した場合**
B-1ナレッジに登録された文書の読み込み
B-2:LLMでの回答
B-3:回答の出力


**C.参照されたナレッジだけで回答できると判断した場合**
C-1:LLMを使って数式を見やすく変換
C-2:RAGを使った回答の出力

という流れになっています。

また、今回アプリケーションの作成にあたって、ナレッジの作成が必要だったので、作成方法について共有します。
今回ナレッジには、以下の数学の問題集を使用しています。

https://web.math-aquarium.jp/rennsyuu-sikinotennkaitoinnsuubunnkai-1.pdf


ナレッジを作成
↓
ファイルのアップロード
↓
保存して処理
↓
ドキュメントに移動


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806967/3e62b2f9-7fb4-3d46-3985-cd66e8ad4ec8.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806967/40c979eb-e854-599d-1f38-a05d23d53d61.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806967/5cba50ec-a277-3a95-fe9c-943945285b1d.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806967/06a8a083-2356-8fc0-4359-416ed0a2330b.png)


アプリケーションの詳細について説明します。

**①質問の入力**
「開始」ノードでは、ユーザからの質問を入力します。


**②ナレッジの取得**
「知識取得」ノードでは、ナレッジから情報を取得し、参照します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806967/ca4e451c-6e8d-5c68-5b25-f6e944e843da.png)


**③LLMでユーザーからの質問に回答できるかどうかを判断**
「LLM(Self-Route)」ノードでは、ユーザーの質問に対し、RAGを行うべきか、LC対応LLMのプロンプトに情報を全て与えて処理を行うべきかの判断をしてもらいます。

プロンプト内にある、「出力ルール」について説明します。
・質問が文脈から答える必要がない場合: no need
これは、「知識取得」ノードで得た情報(今回は数学の問題集)に関係ない質問がきた場合を指します。
この場合、LLMは no need という文字列のみを出力します。

・文脈から正しい回答を出力できない場合: unanswerable
LLMがナレッジ(抽出された情報)を基に、ユーザーの質問に対して正確に回答できないと判断した場合を指します。
この場合、LLMは unanswerable という文字列のみを出力します。

・文脈から正しい回答を出力できる場合: 正しい答えを丁寧に回答する
ナレッジを基に、ユーザーの質問に対して正確に回答できると判断した場合を指します。
この場合、LLMは 正しい答えを丁寧に回答します。つまりRAGをおこないます。

プロンプトの入力例です。
```
あなたには、下記のような文脈情報が与えられています。
あなたのタスクは、下記の文脈からユーザーの質問に回答できるかを判定して、
回答できる場合は、回答を行い、回答できない場合は回答できない旨を出力することです。
下記のルールに従ってください。

### 文脈
コンテキスト

### 出力ルール
下記の場合に応じてコロン以降の回答を出力してください
- 質問が文脈から答える必要がない場合: no need
- 文脈から正しい回答を出力できない場合: unanswerable
- 文脈から正しい回答を出力できる場合: 正しい答えを丁寧に回答する

上記の出力以外の説明などは不要です。
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806967/6f39a7f1-c1b6-bc41-2ab7-48a632089fd8.png)



**④条件分岐**
**③LLMでユーザーからの質問に回答できるかどうかを判断**で判断された結果に基づいて、3つのうち1つの処理をおこないます。
それぞれの処理について説明します。

**A.参照されたナレッジとは関係ない質問が来た場合**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806967/425347e7-11b1-fcb1-b077-806e6377bba8.png)
A-1:LLMでの回答
「LLM2」ノードでは、「知識取得」ノードで得た情報に関係ない質問に対して、回答します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806967/df73d22d-0369-a5e5-8934-0a7b255c9d7d.png)
A-2:回答の出力
「回答(LLM2使用)」ノードでは、回答を出力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806967/7d2572e7-0db9-1200-dc88-dce6553403af.png)


**B.参照されたナレッジだけでは回答できないと判断した場合**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806967/f122602c-f15e-2d70-74ae-2954f74927ff.png)
B-1ナレッジに登録された文書の読み込み
「原文」ノードでは、数学の問題集のテキストを全て貼り付けています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806967/181d5d5c-4e31-7dd9-71fb-e6e75fb61e1a.png)
B-2:LLMでの回答
**「LLM3」ノードでは、「原文」ノードで得たテキストを基に、質問に回答してもらいます。
今回は数学の問題集を取り扱っていることもあり、数式の表記について苦戦しました。
具体的には、例えばxの3乗について、x³と出力したいのを、x^3と出力してしまう点です。
ユーザーにとって見やすいと感じてもらえるように、数式の表記に注意してプロンプトを作成しています。**
以下はプロンプトの内容です。
```
下記の文脈が与えられています。
あなたのタスクは、下記の文脈に関する、ユーザーの質問に回答することです。
下記のルールに従ってください。

### 文脈情報
コンテキスト

### 出力ルール
数式に関して、特に乗数の表記方法については、ユニコードの上付き文字を使用した方法を使用してください。
また、$$で囲う、LaTeX形式は使わないでください。
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806967/544f627e-8b5a-e882-ca39-c5a9f5171810.png)
B-3:回答の出力
「回答(LC使用)」ノードでは、回答を出力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806967/588af75e-827b-6524-b010-cd3276f3cef1.png)


**C.参照されたナレッジだけで回答できると判断した場合**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806967/441ba926-f319-0b33-feec-24a412ba44a7.png)
C-1:LLMを使って数式を見やすく変換
「LLM4」ノードでは、「LLM3」ノード同様、数式の表記を見やすくするための処理をおこないます。
しかし、既に「LLM(Self-Route)」ノードで回答を得ているため、回答の数式の表記を見やすくするための処理だけをするようなプロンプトにしています。
```
LLM(Self...｛x｝textの内容を何も変更を加えずに出力してください。
ただし、数式がある際は、特に乗数の表記方法については、ユニコードの上付き文字を使用した方法を使用してください。
また、$$で囲う、LaTeX形式は使わないでください。
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806967/8914f343-caeb-b84a-f1bc-f5da40a5161c.png)

C-2:RAGを使った回答の出力
「回答(RAG使用)」ノードでは、回答を出力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806967/7fac0337-c2a6-1f2d-fc60-6629efda3b20.png)


# 実行結果
**A.参照されたナレッジとは関係ない質問が来た場合**
この場合は、質問に対応した回答を出力します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806967/e4ef0575-2f2f-c229-839f-3bffd24d45d0.png)


**B.参照されたナレッジだけでは回答できないと判断した場合**
質問例：ナレッジに登録した問題集の問題のうち、大問1について解説して
大問1の問題について回答を生成してくれているうえ、数式の表記も見やすく表記できています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806967/3985f316-c433-3e8e-65ae-d8c2d8bc8a3b.png)

また、処理に掛かった時間と、トークンについては以下のようになっています。
| 処理に掛かった時間(s) | トークン(Tokens) |
|:-----------|------------:|
| 10.436   | 3170        |

情報を全て与えて処理をしている分、時間が掛かっています。

![1004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806967/6b599f96-c58f-5d2d-b9e0-8cc98753f7ba.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806967/ffd50c6c-94bd-ce73-eed4-d153d6acbb4e.png)


**C.参照されたナレッジだけで回答できると判断した場合**
質問例：３ 次の式を展開せよ。(1) (a－2b)2 (2) (3＋2x)(3－2x)(3) (a－5)(a＋7) (4) (5x－4y)(3x＋2y)これらのナレッジに登録された問題について、解説お願いします。
これらの問題について解説してくれているうえ、数式の表記もバッチリです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806967/de009ccc-0b25-567a-8409-343ef4203b18.png)


また、処理に掛かった時間と、トークンについては以下のようになっています。
| 処理に掛かった時間(s) | トークン(Tokens) |
|:-----------|------------:|
| 5.708   | 1440       |

処理に掛かった時間、トークンともに情報を全て与えて処理をした例よりも少なくなっています。

![1004-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806967/549c0b43-5699-c61e-fd3f-5a099f90ba97.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3806967/cefa8e78-9f84-f311-2f94-4f90fc10cb7b.png)



# まとめ
今回は、DifyでSelf-Routeの技術を使用した、数学の回答解説生成のアプリケーションを作成してみました。
出力にかかる時間に関しては、プロンプトの調整次第で、もっと早くなる可能性があると感じました。
今回の例では、RAGをおこなった場合、LC対応LLMを行った場合の性能面の差はあまり感じられませんでした。
数学の回答・解説生成とは違った例を考えて、実装することも考えています。

また、今回の作成にあたり、数式の表記に関する出力ルールを考えるのにとても苦戦しましたが、良い経験になりました。
今後もDifyの新機能の検証など、様々なことに取り組んでいけたらなと考えています。