Dify 触ってみたメモ

Difyとは
DifyはノーコードでLLMを組み込んだアプリケーション(チャットボット、AIエージェントアプリ等)を簡単に作成できる開発ツール。GPTsの超上位互換のような感じ。
複雑なフローが必要なLLMアプリケーションも簡単に素早く作成することができるので、技術検証・PoCに大いに役立ちそう。

料金
オープンソースのためローカルにダウンロードして使用すれば無料。
言語モデルを利用する際にAPI料金のみ発生する。
DifyのWebページ上で利用することも可能。ただ、その場合料金が発生してしまう。
一応、無料プランでも200メッセージ/月の制限付きで利用できるが、ローカルで利用すれば完全無料のためローカルをオススメする。

ローカルでの実行
Readmeのクイックスタートに従うことで簡単に実行できる。
https://github.com/langgenius/dify/blob/main/README_JA.md
git clone https://github.com/langgenius/dify.git
cd docker
cp .env.example .env
docker compose up -d
上記コマンドを実行後、ブラウザでhttp://localhost/install
にアクセスする。

ライセンス
商用利用可能なOSSであるが、一部条件があることがライセンスに記載されている。
条件は以下
- マルチテナントSaaSサービス運営にDifyを用いてはいけない
- Difyを使用する際に、Difyコンソール内のロゴや著作権情報を削除または変更してはいけない
マルチテナントSaaSサービスかどうかの判断基準は、「ワークスペースが複数あるかどうか」で決まるらしい。

使える言語モデル
OpenAIのGPTシリーズやAnthropicのClaudeシリーズなど主要なモデルは網羅されている。
各公式サイトでAPIキーを取得し、セットアップ画面でキーを入力することでモデルが利用可能となる。
まずは、比較的料金の安いGPT-4o-mini、無料で利用できるgroqのモデル、一定は無料のGeminiなどのモデルを使うのがおすすめ。

アプリケーションタイプ
Difyでは4つのアプリケーションタイプを作成することができる。
それぞれのタイプの説明は以下。
基本的には、チャットボット(chatflow)、ワークフローを使うことになりそう。

RAGの準備(知識ベース)
(1)ナビゲーションバー「ナレッジ」で、「ナレッジを作成」をクリックすることでRAG参照ドキュメントをアップロードできる。
(2)ファイルをアップロードする画面となるので、参照させたいドキュメントを選択する。
(3)RAGの設定が表示されるので、お好みの設定を選択する。
チャンク設定
- 自動:チャンクの長さ、ルールを自動で設定してくれる。初めはこれがおすすめ。
- カスタム:最大チャンク長、チャンクのオーバーラップなどの手動で設定できる。
- Microsoftの記事によると最大チャンク長は512、チャンクのオーバーラップは最大チャンク長の25%程度(512トークンであれば128トークン)の精度が良いらしい。
インデックスモード
- 高品質:ベクトル検索を行うため高精度。その分コストがかかる。
- 経済的:Dify用意した無償のインデックス方法が使用される。
検索設定
-
ベクトル検索:ベクトル表現が最も類似するテキストチャンクを検索する
- 意味的に近い文章を検索するため、直接的な質問でなくても検索可能
- ただ、LLMが知らない固有名詞や単語が含まれる場合に弱い
-
全文検索:ユーザーのキーワードベースで検索を行う
- 正確な照合(固有名詞、番号等)の検索には強いが、意味的な検索には弱い
-
ハイブリット検索(オススメ):全文検索とベクトル検索を同時に実行し、ユーザーのクエリに最適な結果を選択する
- ベクトル検索、全文検索の欠点を補う方法
※ rerankモデル
rerankモデルとは、複数の情報の中から、生成モデルが最適な回答を生成するために、重要な情報を再選別・再評価するモデル。より精度の高い検索結果を望むことができる。
Cohereのモデルが有名かつ無料で使えるのでオススメ。

RAG ChatBot作ってみた
以下の動作をするChatBotを作成してみた。
1時間程度でフローが完成したので、割と簡単にChatBotを作れると思う。
- GPT-4oに関する質問 → GPT4-oのページを元に、GPT-4o-miniが回答
- Gemini 1.5に関する質問 → Gemini 1.5の論文(知識ベースにアップロード済み)を元に、Gemini 1.5 Flashが回答
- Claude 3に関する質問 → Claude 3のページを元に、claude3-haikuが回答
- その他の質問 → 「すみません、その質問にはお答えできません🤗」と返信
動作確認
Gemini 1.5 proの最大入力トークンについて質問したところ、論文中を適切に参照し、100Mトークンであると回答してくれた。
質問分類器も適切に動作しており、CommandR+について質問しても「答えられません」と返してくれる。
補足情報
PDFの参照(Geminiの論文)はアップロードした知識ベースを用いているが、Web情報の参照にはJinaReaderというツールを用いてWebページの情報を取得している。
JinaReaderはURLやウェブ検索からLLM に適した入力を得られる便利なやつらしい。
DSLファイル出力
作成したフローはDify独自のDSL形式と呼ばれるyaml形式で書き出せるよう。
参考に書き出したDSLを以下に貼っておく。
DSL
app:
description: ''
icon: 🤖
icon_background: '#FFEAD5'
mode: advanced-chat
name: LLM-Question-RAG
kind: app
version: 0.1.1
workflow:
conversation_variables: []
environment_variables: []
features:
file_upload:
image:
enabled: false
number_limits: 3
transfer_methods:
- remote_url
- local_file
opening_statement: ''
retriever_resource:
enabled: true
sensitive_word_avoidance:
configs: []
enabled: false
type: ''
speech_to_text:
enabled: false
suggested_questions: []
suggested_questions_after_answer:
enabled: false
text_to_speech:
enabled: false
language: ''
voice: ''
graph:
edges:
- data:
isInIteration: false
sourceType: start
targetType: question-classifier
id: start-source-1723968002564-target
source: start
sourceHandle: source
target: '1723968002564'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
sourceType: question-classifier
targetType: answer
id: 1723968002564-1-1723968831471-target
source: '1723968002564'
sourceHandle: '1'
target: '1723968831471'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
sourceType: question-classifier
targetType: answer
id: 1723968002564-2-17239688395370-target
source: '1723968002564'
sourceHandle: '2'
target: '17239688395370'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
sourceType: question-classifier
targetType: answer
id: 1723968002564-1723968123909-17239688695710-target
source: '1723968002564'
sourceHandle: '1723968123909'
target: '17239688695710'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
sourceType: question-classifier
targetType: answer
id: 1723968002564-1723968806226-17239688613540-target
source: '1723968002564'
sourceHandle: '1723968806226'
target: '17239688613540'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
sourceType: answer
targetType: tool
id: 1723968831471-source-1723971377306-target
source: '1723968831471'
sourceHandle: source
target: '1723971377306'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
sourceType: tool
targetType: llm
id: 1723971377306-source-1723986187151-target
source: '1723971377306'
sourceHandle: source
target: '1723986187151'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
sourceType: llm
targetType: answer
id: 1723986187151-source-1723992268204-target
source: '1723986187151'
sourceHandle: source
target: '1723992268204'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
sourceType: answer
targetType: knowledge-retrieval
id: 17239688395370-source-1723995080221-target
source: '17239688395370'
sourceHandle: source
target: '1723995080221'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
sourceType: knowledge-retrieval
targetType: llm
id: 1723995080221-source-1723995105317-target
source: '1723995080221'
sourceHandle: source
target: '1723995105317'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
sourceType: llm
targetType: answer
id: 1723995105317-source-1723995195840-target
source: '1723995105317'
sourceHandle: source
target: '1723995195840'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
sourceType: answer
targetType: tool
id: 17239688695710-source-17239955048040-target
source: '17239688695710'
sourceHandle: source
target: '17239955048040'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
sourceType: tool
targetType: llm
id: 17239955048040-source-17239955198490-target
source: '17239955048040'
sourceHandle: source
target: '17239955198490'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
sourceType: llm
targetType: answer
id: 17239955198490-source-17239956005570-target
source: '17239955198490'
sourceHandle: source
target: '17239956005570'
targetHandle: target
type: custom
zIndex: 0
nodes:
- data:
selected: false
title: START
type: start
variables: []
height: 53
id: start
position:
x: 30
y: 349
positionAbsolute:
x: 30
y: 349
type: custom
width: 244
- data:
classes:
- id: '1'
name: GPT-4oに関する質問
- id: '2'
name: Gemini 1.5に関する質問
- id: '1723968123909'
name: Claude 3 Haikuに関する質問
- id: '1723968806226'
name: その他の質問
desc: ''
instructions: ''
model:
completion_params:
temperature: 0.7
mode: chat
name: gemini-1.5-flash-latest
provider: google
query_variable_selector:
- start
- sys.query
selected: false
title: 質問分類器
topics: []
type: question-classifier
height: 263
id: '1723968002564'
position:
x: 334
y: 349
positionAbsolute:
x: 334
y: 349
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
answer: 'GPT-4oに関する質問ですね!
以下質問の回答です。
---
'
desc: ''
selected: false
title: 回答(GPT-4o)
type: answer
variables: []
height: 119
id: '1723968831471'
position:
x: 638
y: 349
positionAbsolute:
x: 638
y: 349
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
answer: 'Gemini 1.5に関する質問ですね!
以下質問の回答です。
---
'
desc: ''
selected: false
title: 回答 (Gemini)
type: answer
variables: []
height: 119
id: '17239688395370'
position:
x: 638
y: 687
positionAbsolute:
x: 638
y: 687
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
answer: すみません、その質問にはお答えできません🤗
desc: ''
selected: false
title: 回答 (その他)
type: answer
variables: []
height: 119
id: '17239688613540'
position:
x: 638
y: 1108.2857142857142
positionAbsolute:
x: 638
y: 1108.2857142857142
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
answer: 'Claude 3に関する質問ですね!
以下質問の回答です。
---
'
desc: ''
selected: false
title: 回答 (Claude)
type: answer
variables: []
height: 119
id: '17239688695710'
position:
x: 638
y: 854.0000000000001
positionAbsolute:
x: 638
y: 854.0000000000001
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
desc: ''
provider_id: jina
provider_name: jina
provider_type: builtin
selected: false
title: JinaReader
tool_configurations:
gather_all_images_at_the_end: 0
gather_all_links_at_the_end: 0
image_caption: 0
max_retries: 3
no_cache: 0
proxy_server: null
summary: 0
target_selector: null
wait_for_selector: null
tool_label: JinaReader
tool_name: jina_reader
tool_parameters:
request_params:
type: mixed
value: ''
url:
type: mixed
value: https://openai.com/index/hello-gpt-4o/
type: tool
height: 297
id: '1723971377306'
position:
x: 942
y: 349
positionAbsolute:
x: 942
y: 349
selected: true
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
context:
enabled: true
variable_selector:
- '1723971377306'
- text
desc: ''
model:
completion_params:
temperature: 0.7
mode: chat
name: gpt-4o-mini
provider: openai
prompt_template:
- id: 4982c67c-d3a9-4ea0-aef3-591cc6dc5a9a
role: system
text: 'あなたは世界中で信頼されているQAシステムです。
事前知識ではなく、常に提供されたコンテキスト情報を使用してクエリに回答してください。'
- id: cd454d55-d944-44a4-84f8-83a190b344ae
role: user
text: '## コンテキスト
{{#1723971377306.text#}}
## QA
Q: {{#sys.query#}}
A: '
selected: false
title: LLM
type: llm
variables: []
vision:
configs:
detail: high
enabled: false
height: 97
id: '1723986187151'
position:
x: 1246
y: 349
positionAbsolute:
x: 1246
y: 349
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
answer: '{{#1723986187151.text#}}'
desc: ''
selected: false
title: 回答 5
type: answer
variables: []
height: 105
id: '1723992268204'
position:
x: 1550
y: 349
positionAbsolute:
x: 1550
y: 349
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
dataset_ids:
- e0afefa8-445e-4a54-b0a3-9d15a6c0816d
desc: ''
multiple_retrieval_config:
reranking_enable: true
reranking_mode: weighted_score
top_k: 4
weights:
keyword_setting:
keyword_weight: 0.3
vector_setting:
embedding_model_name: text-embedding-3-large
embedding_provider_name: openai
vector_weight: 0.7
query_variable_selector:
- start
- sys.query
retrieval_mode: multiple
selected: false
title: 知識取得
type: knowledge-retrieval
height: 91
id: '1723995080221'
position:
x: 942
y: 687
positionAbsolute:
x: 942
y: 687
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
context:
enabled: true
variable_selector:
- '1723995080221'
- result
desc: ''
model:
completion_params:
temperature: 0.7
mode: chat
name: gemini-1.5-flash-latest
provider: google
prompt_template:
- id: 7375c7da-6f26-49e9-a578-aed316441a80
role: system
text: 'あなたは世界中で信頼されているQAシステムです。
事前知識ではなく、常に提供されたコンテキスト情報を使用してクエリに回答してください。'
- id: e59edd08-dd59-4cd3-b970-8a313bc4ea99
role: user
text: '## コンテキスト
{{#context#}}
## QA
Q: {{#sys.query#}}
A: '
selected: false
title: LLM 2
type: llm
variables: []
vision:
configs:
detail: high
enabled: false
height: 97
id: '1723995105317'
position:
x: 1253.142857142857
y: 687
positionAbsolute:
x: 1253.142857142857
y: 687
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
answer: '{{#1723995105317.text#}}'
desc: ''
selected: false
title: 回答 6
type: answer
variables: []
height: 105
id: '1723995195840'
position:
x: 1550
y: 687
positionAbsolute:
x: 1550
y: 687
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
desc: ''
provider_id: jina
provider_name: jina
provider_type: builtin
selected: false
title: JinaReader (1)
tool_configurations:
gather_all_images_at_the_end: 0
gather_all_links_at_the_end: 0
image_caption: 0
max_retries: 3
no_cache: 0
proxy_server: null
summary: 0
target_selector: null
wait_for_selector: null
tool_label: JinaReader
tool_name: jina_reader
tool_parameters:
request_params:
type: mixed
value: ''
url:
type: mixed
value: https://www.anthropic.com/news/claude-3-family
type: tool
height: 297
id: '17239955048040'
position:
x: 942
y: 854.0000000000001
positionAbsolute:
x: 942
y: 854.0000000000001
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
context:
enabled: true
variable_selector:
- '1723971377306'
- text
desc: ''
model:
completion_params:
temperature: 0.7
mode: chat
name: claude-3-haiku-20240307
provider: anthropic
prompt_template:
- id: 4982c67c-d3a9-4ea0-aef3-591cc6dc5a9a
role: system
text: 'あなたは世界中で信頼されているQAシステムです。
事前知識ではなく、常に提供されたコンテキスト情報を使用してクエリに回答してください。'
- id: cd454d55-d944-44a4-84f8-83a190b344ae
role: user
text: '## コンテキスト
{{#17239955048040.text#}}
## QA
Q: {{#sys.query#}}
A: '
selected: false
title: LLM 3
type: llm
variables: []
vision:
configs:
detail: high
enabled: true
height: 97
id: '17239955198490'
position:
x: 1246
y: 854.0000000000001
positionAbsolute:
x: 1246
y: 854.0000000000001
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
answer: '{{#17239955198490.text#}}'
desc: ''
selected: false
title: 回答 5 (1)
type: answer
variables: []
height: 105
id: '17239956005570'
position:
x: 1550
y: 854.0000000000001
positionAbsolute:
x: 1550
y: 854.0000000000001
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
viewport:
x: 53.64783660411365
y: -158.8198288148194
zoom: 0.9267961931891546

AWS lightsail上にアプリケーションデプロイ
以下の手順でAWS lightsailにデプロイした。
VMの作成
-
インスタンスイメージはUbuntuを選択。
-
インスタンスサイズは2GBメモリ、2 vCPUsを選択。3ヶ月のフリートライアルあり。
-
SSH keyの作成
Create NewからSSH keyを発行。keyの名前は「dify-key」など何でもOK。
発行したprivate keyは後ほど使うので、ダウンロード。
-
Create Instanseでインスタンスを作成
Difyのインストール
-
インスタンスの作成が完了したら、SSH接続を行う。
-
Difyインストールのため、SSH先で以下のコマンドを実行。
コマンド実行には割と時間がかかります。
# Install Docker
curl -fsSL https://get.docker.com -o install-docker.sh
sh install-docker.sh
# Clone the GitHub repository
git clone https://github.com/langgenius/dify.git
# Navigate to the desired directory
cd dify/docker
# Run Docker Compose
sudo docker compose up -d
Difyの起動
デプロイ済みLightsailのhttpアドレスをブラウザに入力することでアクセス可能。
※ httpsではアクセスできないので注意
http://[Public IPv4 address]/install
あとはセットアップを進めていけばOK。
httpsの設定
以下の方法でhttps設定を行えるよう。