【Dify】問い合わせチャットボットを最適化する
はじめに
本記事では、ローコードAIプラットフォームであるDifyを活用して、問い合わせ対応を効率化するチャットボットの構築と最適化について解説します。特に、ナレッジベースを活用した正確な回答と、LLMによる柔軟な対応を組み合わせることで、ユーザーの満足度向上を目指します。
チャットボットの要件定義
今回構築するチャットボットは、以下の要件を満たすものとします。
- 質問に対して、事前に登録されたナレッジベースの知識を用いて回答する。
- ナレッジベースで解決できない場合、LLMが回答できそうな関連性の高い質問リストを出力する。
- 提示された質問リストの中からユーザーが質問を選択した場合、LLMがその質問に対してナレッジベースの知識から回答を出力する。
質問の流れに関して
今回、質問リストの出力はボタン形式で行いました。背景としてはそのまま質問のリストを出力してもユーザーからすると、"質問内容を選ぶ⇒コピペ⇒回答"という工程を挟まなければいけないです。実用といった面でこれは使い勝手が悪く感じてしまいます。そこでボタン形式を選択することでこの1工程をなくすことで使いやすさを向上させUI/UXの向上を図っています。
オーケストレートの整形
Difyでチャットボットのオーケストレーションを実装するにあたり、2つの会話変数を利用します。
- check: ユーザー自身が質問を入力したのか、それともLLMが出力した質問候補を選択したのかを判定するために使用します。
-
record: ユーザーの質問に対するナレッジベースの検索結果や、LLMによる最初の回答などを記録します。
処理の流れは以下の図の通りです。
これを実装したのが以下のオーケストレートです。
if/elseブロックで回答の処理を分けています。
こちら上の枝から順に次の処理を行っています。
- 「いいえ」と答えられた場合に質問候補を出力する
- 「はい」と答えられた場合のテンプレートの回答
- 選ばれた質問に対して回答を出力する
- 最初の回答を作成する
このようにして問い合わせチャットボットを最適化していきます。
実際のDSLファイルはこちら
app:
description:
問い合わせチャットボット
icon: 🤖
icon_background: '#FFEAD5'
mode: advanced-chat
name: 質問候補出力_0528
use_icon_as_answer_icon: false
kind: app
version: 0.1.5
workflow:
conversation_variables:
- description: 2回目以降の質問か確認する変数
id: 05132f67-00db-4678-9110-af36329c7933
name: check
selector:
- conversation
- check
value: 0
value_type: number
- description: 知識取得を保存するための変数
id: 0522c864-16af-419c-9eb8-edbb4ca19c94
name: record
selector:
- conversation
- record
value: []
value_type: array[object]
environment_variables: []
features:
file_upload:
allowed_file_extensions:
- .JPG
- .JPEG
- .PNG
- .GIF
- .WEBP
- .SVG
allowed_file_types:
- image
allowed_file_upload_methods:
- local_file
- remote_url
enabled: false
fileUploadConfig:
audio_file_size_limit: 50
batch_count_limit: 5
file_size_limit: 15
image_file_size_limit: 10
video_file_size_limit: 100
workflow_file_upload_limit: 10
image:
enabled: false
number_limits: 3
transfer_methods:
- local_file
- remote_url
number_limits: 3
opening_statement: ''
retriever_resource:
enabled: true
sensitive_word_avoidance:
enabled: false
speech_to_text:
enabled: false
suggested_questions: []
suggested_questions_after_answer:
enabled: false
text_to_speech:
enabled: false
language: ''
voice: ''
graph:
edges:
- data:
sourceType: llm
targetType: answer
id: llm-answer
selected: false
source: llm
sourceHandle: source
target: answer
targetHandle: target
type: custom
- data:
isInIteration: false
sourceType: start
targetType: if-else
id: 1748411157898-source-1748411163886-target
selected: false
source: '1748411157898'
sourceHandle: source
target: '1748411163886'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
sourceType: knowledge-retrieval
targetType: assigner
id: 1748411314007-source-1748411332733-target
selected: false
source: '1748411314007'
sourceHandle: source
target: '1748411332733'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
sourceType: assigner
targetType: llm
id: 1748411332733-source-llm-target
selected: false
source: '1748411332733'
sourceHandle: source
target: llm
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
sourceType: if-else
targetType: knowledge-retrieval
id: 1748411163886-false-1748411314007-target
selected: false
source: '1748411163886'
sourceHandle: 'false'
target: '1748411314007'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
sourceType: if-else
targetType: llm
id: 1748411163886-true-1748411430003-target
selected: false
source: '1748411163886'
sourceHandle: 'true'
target: '1748411430003'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
sourceType: llm
targetType: assigner
id: 1748411430003-source-1748412493118-target
selected: false
source: '1748411430003'
sourceHandle: source
target: '1748412493118'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
sourceType: assigner
targetType: answer
id: 1748412493118-source-1748412415854-target
selected: false
source: '1748412493118'
sourceHandle: source
target: '1748412415854'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
sourceType: if-else
targetType: llm
id: 1748411163886-0acae8b5-0eda-4d35-87dd-d6b25be585ac-1748412516107-target
selected: false
source: '1748411163886'
sourceHandle: 0acae8b5-0eda-4d35-87dd-d6b25be585ac
target: '1748412516107'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
sourceType: llm
targetType: assigner
id: 1748412516107-source-1748439251080-target
source: '1748412516107'
sourceHandle: source
target: '1748439251080'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
sourceType: assigner
targetType: answer
id: 1748439251080-source-1748412523292-target
source: '1748439251080'
sourceHandle: source
target: '1748412523292'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
sourceType: if-else
targetType: assigner
id: 1748411163886-e419857a-9ab7-4d98-812a-9955a0926470-1748440430377-target
source: '1748411163886'
sourceHandle: e419857a-9ab7-4d98-812a-9955a0926470
target: '1748440430377'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
sourceType: assigner
targetType: answer
id: 1748440430377-source-1748440411152-target
source: '1748440430377'
sourceHandle: source
target: '1748440411152'
targetHandle: target
type: custom
zIndex: 0
nodes:
- data:
desc: ''
selected: false
title: 開始
type: start
variables: []
height: 54
id: '1748411157898'
position:
x: 173.81479720216078
y: 282
positionAbsolute:
x: 173.81479720216078
y: 282
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
context:
enabled: true
variable_selector:
- conversation
- record
desc: 最初の質問対応
model:
completion_params:
temperature: 0.7
mode: chat
name: anthropic.claude-3-5-sonnet-20240620-v1:0
provider: bedrock
prompt_template:
- id: b6978f38-6208-41f1-be70-586fca987fcf
role: system
text: "{{#context#}}の中から{{#sys.query#}}に対応する最適な直接的な回答を書き抜いてください。\n** URLがある場合は続けて書き抜いてください\
\ **\n** コンプライアンスマニュアルを参照している場合 **は参照ページも出力してください\n\nもし直接的な回答がない場合には情報から回答を予測して結論から回答を作成してください。\n\
\nまた回答はユーザーに見やすいように形を整えてください\n\n\n出力:\n - 直接的な回答がある場合\n - 回答内容:[質問に対する回答]\n\
\ - コンプライアンスマニュアルを参照した場合\n -マニュアルページ番号: [参照ページ一覧]\n\
\ - 直接的な回答がない場合\n - 結論: [質問に対する回答]を出力\n - 参考事例など: [回答に対して参考にした参考事例、理由]\n\
\ - コンプライアンスマニュアル参照ページ\n -マニュアルページ番号: [参照ページ一覧"
- id: 2a971da4-bff2-41db-96a3-99d92487fd72
role: user
text: '質問内容: {{#sys.query#}}'
selected: false
title: LLM
type: llm
variables: []
vision:
enabled: false
height: 126
id: llm
position:
x: 1284.5947934199883
y: 709.6839269295078
positionAbsolute:
x: 1284.5947934199883
y: 709.6839269295078
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
answer: '{{#llm.text#}}
解決しましたか?
<button data-message="はい">はい</button>
<button data-message="いいえ">いいえ: 質問リストを出力</button>
<button data-message="FB">FB</button>'
desc: ''
selected: false
title: 回答
type: answer
variables: []
height: 182
id: answer
position:
x: 1616.4460950649911
y: 709.6839269295078
positionAbsolute:
x: 1616.4460950649911
y: 709.6839269295078
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
cases:
- case_id: 'true'
conditions:
- comparison_operator: is
id: 16980755-f8d2-438f-b7e7-a58f3b6ea3e1
value: いいえ
varType: string
variable_selector:
- sys
- query
id: 'true'
logical_operator: and
- case_id: e419857a-9ab7-4d98-812a-9955a0926470
conditions:
- comparison_operator: is
id: ced305db-4b25-4185-a03d-75dbdda49e9d
value: はい
varType: string
variable_selector:
- sys
- query
id: e419857a-9ab7-4d98-812a-9955a0926470
logical_operator: and
- case_id: 0acae8b5-0eda-4d35-87dd-d6b25be585ac
conditions:
- comparison_operator: '='
id: c5490bd5-b11a-4fd8-a708-de8bb0ec8498
value: '1'
varType: number
variable_selector:
- conversation
- check
id: 0acae8b5-0eda-4d35-87dd-d6b25be585ac
logical_operator: and
desc: ''
selected: false
title: IF/ELSE
type: if-else
height: 222
id: '1748411163886'
position:
x: 437.16839204506675
y: 282
positionAbsolute:
x: 437.16839204506675
y: 282
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
dataset_ids:
- 7c2233da-b668-4c30-b9e3-9f7e886fe3c3
desc: ナレッジから知識取得
multiple_retrieval_config:
reranking_enable: false
reranking_mode: reranking_model
reranking_model:
model: cohere.rerank-v3-5:0
provider: bedrock
top_k: 4
query_variable_selector:
- sys
- query
retrieval_mode: multiple
selected: false
title: 知識取得
type: knowledge-retrieval
height: 120
id: '1748411314007'
position:
x: 717.6765703899466
y: 709.6839269295078
positionAbsolute:
x: 717.6765703899466
y: 709.6839269295078
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
desc: 知識取得の内容を記録する
items:
- input_type: variable
operation: over-write
value:
- '1748411314007'
- result
variable_selector:
- conversation
- record
write_mode: over-write
selected: false
title: record
type: assigner
version: '2'
height: 116
id: '1748411332733'
position:
x: 995
y: 709.6839269295078
positionAbsolute:
x: 995
y: 709.6839269295078
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
context:
enabled: true
variable_selector:
- conversation
- record
desc: '「いいえ」の場合
質問候補を出力するLLM
ボタン形式で出力する'
model:
completion_params:
temperature: 0.7
mode: chat
name: anthropic.claude-3-5-sonnet-20240620-v1:0
provider: bedrock
prompt_template:
- edition_type: basic
id: cca3033d-5b1e-4794-a002-351f1d00872e
role: system
text: "{{#context#}}の中で答えられる質問の候補をできるだけリスト形式で出力してください。ボタンで入力できるように出力してください。\n\
\n\n出力形式:\n - 回答できる質問のリスト:\n - <button data-message=\"[質問内容]\">[質問内容]</button>"
- id: e2cf3da8-f34a-495a-84b7-70537da16870
role: user
text: '質問内容: コンテキストの中の質問のリストを教えてください。'
selected: false
title: LLM_質問出力
type: llm
variables: []
vision:
enabled: false
height: 158
id: '1748411430003'
position:
x: 717.6765703899466
y: 82.29388626463248
positionAbsolute:
x: 717.6765703899466
y: 82.29388626463248
selected: true
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
answer: '{{#1748411430003.text#}}
<br>
**他の質問をする場合は必ず押してください**
<button data-message="他の質問へ">他の質問をする</button>'
desc: 質問候補を出力する
selected: false
title: 回答_質問出力
type: answer
variables: []
height: 178
id: '1748412415854'
position:
x: 1268.5130164500297
y: 82.29388626463248
positionAbsolute:
x: 1268.5130164500297
y: 82.29388626463248
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
desc: check=1
items:
- input_type: constant
operation: set
value: 1
variable_selector:
- conversation
- check
write_mode: over-write
selected: false
title: check_on
type: assigner
version: '2'
height: 116
id: '1748412493118'
position:
x: 999.594793419988
y: 82.29388626463248
positionAbsolute:
x: 999.594793419988
y: 82.29388626463248
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
context:
enabled: true
variable_selector:
- conversation
- record
desc: 'check=1の場合
2回目以降の回答を出力する'
model:
completion_params:
temperature: 0.7
mode: chat
name: anthropic.claude-3-5-sonnet-20240620-v1:0
provider: bedrock
prompt_template:
- id: 08edbacb-74ee-4a96-abc2-beb651127ac3
role: system
text: '{{#context#}}をもとに回答を作成してください'
- id: 3111a21f-9ecd-4973-bbb4-67f5403686c0
role: user
text: '質問内容: {{#sys.query#}}'
selected: false
title: LLM_質問回答2
type: llm
variables: []
vision:
enabled: false
height: 142
id: '1748412516107'
position:
x: 717.6765703899466
y: 481.4646289196917
positionAbsolute:
x: 717.6765703899466
y: 481.4646289196917
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
answer: '{{#1748412516107.text#}}
解決しましたか?
<button data-message="はい">はい</button>
<button data-message="いいえ">いいえ: 質問リストを出力</button>
<button data-message="FB">フィードバックをする</button>'
desc: ''
selected: false
title: 回答_2回目
type: answer
variables: []
height: 182
id: '1748412523292'
position:
x: 1289.1895868399763
y: 481.4646289196917
positionAbsolute:
x: 1289.1895868399763
y: 481.4646289196917
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
desc: check=0
items:
- input_type: constant
operation: set
value: 0
variable_selector:
- conversation
- check
write_mode: over-write
selected: false
title: check_off
type: assigner
version: '2'
height: 116
id: '1748439251080'
position:
x: 995
y: 481.4646289196917
positionAbsolute:
x: 995
y: 481.4646289196917
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
answer: 'ご利用ありがとうございました。
引き続き入力いただくことでご利用いただけます。
※押し間違えてしまった場合はこちら
<button data-message="いいえ">質問出力</button>'
desc: はいの場合の回答
selected: false
title: 回答_はい
type: answer
variables: []
height: 192
id: '1748440411152'
position:
x: 999.594793419988
y: 282
positionAbsolute:
x: 999.594793419988
y: 282
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
desc: check=0
items:
- input_type: constant
operation: set
value: 0
variable_selector:
- conversation
- check
write_mode: over-write
selected: false
title: check_off
type: assigner
version: '2'
height: 116
id: '1748440430377'
position:
x: 717.6765703899466
y: 282
positionAbsolute:
x: 717.6765703899466
y: 282
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
viewport:
x: 332.71848733935377
y: 168.19454696909278
zoom: 0.4575753398790304
ボタンの実装に関して
解決しましたかボタンを実装するときにボタンの機能を実装する必要があります。まず、その実装方法について説明します。Difyでは一部のHTMLの機能を使うことができます。HTMLにはbuttonタグというものがあり、それを用いて解決しましたかボタンを実装します。次の文章を回答ノードに入力してみてください。
解決しましたか?
<button data-message="はい">はい</button>
<button data-message="いいえ">いいえ: 質問リストを出力</button>
出力は次のようになります。
解決しましたかボタン
続いてこれの応用としてLLMで出力した質問候補をボタン形式で出力してみましょう。LLMの出力方法に関してはLLMノードのシステムプロンプトで制御します。出力形式を設定することでボタンの出力を実装することができました。その簡易的なプロンプトと出力のイメージを次に示します。なおLLMの種類によっては出力形式の指示を無視する場合があるのでLLMの選定も重要になってきます。特に、ナレッジにないような質問を出力してしまうと次のLLMが回答できない状況があるので気を付けましょう。
input:
//ナレッジから取得されたデータの説明//
goal:
inputのデータから回答できる質問のリストをできる限り挙げボタン形式で出力する
output:
- 回答できる質問のリスト:
- <button data-message="[質問内容]">[質問内容]</button>
注: ここでのぞんだ回答がないので他の質問を手打ちしてしまうかもしれないのでLLMからの出力のほかに必ず次の文言を回答ノードに追加しておく。
**他の質問をする場合は必ず押してください**
<button data-message="他の質問へ">他の質問をする</button>
出力のイメージ
このようにして質問候補をLLMによりボタン形式で出力できました。
まとめ
Difyで問い合わせボットを作成する際に、LLMによる質問候補を生成し、それをユーザーインターフェース上でボタン形式で提示することで、ユーザーはより直感的かつストレスなく目的の回答にたどり着けるようになります。LLMからの出力でUIを指定しておけば出力時にも反映されるのでMarkdown形式と組み合わせて使うとさらにUI/UXの向上が見込めます。
Discussion