1. 社内の問い合わせ対応を効率化したい!
社内での問い合わせ対応は意外と工数がかかっていると思っています。
理由としては
- 同じような質問に何度も答えている
- 問い合わせに対応する人の手が空かない(属人化)
- 窓口が分からない
- 検索してもでてこない
などがあるかと思います。
今回はこの問い合わせ対応を効率化したいと思い、ユーザー操作が全てSlackで完結するような社内問い合わせシステムをDifyとAWS Lambdaを用いて構築してみました。
2. できたもの
このようにslackで問い合わせると15秒ほどで回答が自動で返信が来ます。
ナレッジベースに含まれない内容だとこのように担当者に通知されます。
具体的なシステムの流れは以下になります。
システムの流れ
- Slackからユーザーが問い合わせ
- AWS Lambdaがイベント受信
- Dify APi呼び出し
- 問い合わせを分類(体組成計、歩数計、その他)
- 体組成計・歩数計はナレッジベース検索、その他は担当者にSlack通知
- 検索結果があるかのIF/ELSE
- IF:LLMで回答を生成してSlackに出力
- ELSE:担当者にSlack通知
マーメイド図にすると以下のようになります。
3. 事前準備
3.1. ナレッジベースの準備
体組成計と歩数計それぞれのナレッジベースを作成します。
- Difyのトップメニューから「ナレッジ」をクリック
- 「ナレッジを作成」をクリック
- ナレッジ名を入力
Markdownファイルのアップロードと設定
- 用意したMarkdownファイルをアップロード
- 分割選定
- チャンク最大長:500
- チャンク重複:50
- インデックス認定:高品質
- 埋め込みモデル:
text-embedding-3-large
3.2. Slack連携の設定
3.2.1. Slack Appの作成
こちらにアクセスします。
- 「Create New App」→「From scratch」を選択
- App名とワークスペースを選択して「Create App」をクリック
3.2.2. Incoming Webhookの有効化
- 左メニューの「Incoming Webhooks」をクリック
- 「Activate Incoming Webhooks」をONに変更
- 「Add New Webhook to Workspace」をクリック
- 回答を投稿するチャンネルを選択し、許可するをクリック
- 生成された文章をWebhook URLをコピーして保存
担当者に通知するための別チャンネル用のWebhook URLも同様に作成します。
3.2.3. Event Subscriptionsの設定(質問取得用)
Slackでの質問をLambda経由でDifyに送信するための設定です
この設定はLambda関数のデプロイ後に行います
- 左メニューの「Event Subscriptions」をクリック
- 「Enable Events」をONに切り替え
- 「Reuest URL」にLambdaの関数URLを入力、「✓ Verified」と表示されればOK
-
message.channelsを追加 - 右下の「Save Changes」をクリック
3.2.4. Appをチャンネルに招待
Slackの質問を受け付けるチャンネルで以下を入力する。
/invite @Dify FAQ Bot
4. ワークフローの構築
4.1. 新規ワークフローの作成
- スタジオ → 最初から作成
- ワークフローを選択
- 名前を記入して作成をクリック
4.1. 開始ノードの設定
Slackからの質問はLambda経由でDify APIに送信されます。APIリクエストのinputsパラメータで渡される値を受け取るための入力フィールドを設定します。
- 変数名:
user_question - フィールドタイプ:段落
- ラベル:質問を入力してください
4.2. 質問分類器ノードの設定
設定:
- 入力変数:
ユーザー入力 / {x}user_question - 推論モデル:
gpt-4o-mini
クラス(分類)を追加:
- クラス1(体組成計)
体組成計、体重計、体脂肪率、BMI、筋肉量、内臓脂肪、基礎代謝、体年齢、骨量に関する質問 - クラス2(歩数計)
歩数計、万歩計、歩数、消費カロリー、歩行距離、活動量計に関する質問 - クラス3(その他)
上記に該当しない質問、一般的な問い合わせ、担当者への直接連絡が必要な内容
「その他」は必ず最後(一番下)に配置してください。
4.3. 知識検索ノードの設定
質問分類器の「体組成計」出力から接続:
設定:
- ナレッジ:「体組成計FAQ」を選択
- クエリ変数:
ユーザー入力 / {x}user_question
同様に「歩数計」用の知識検索ノードも作成します。
4.4. IF/ELSEノードの設定
IF条件:
- 変数:
知識検索 / {x}result - 条件:「空でない」
次のステップ
- IF:LLM回答生成ノード
- ELSE:担当者通知ノード
4.5. LLM回答生成ノードの設定
設定:
- モデル;
gpt-4o-mini - コンテキスト:知識検索ノードの'result'を選択
- SYSTEM
以下のルールに従って回答してください: 提供された参考情報から回答部分のみを抜き出してください。 - USER
【提供された情報】 コンテキスト
4.6. 担当者通知ノードの設定(情報不足時)
ナレッジベースで情報が見つからなかったとき、担当者に通知します。
設定:
- Slack Incoming Webhook URL:担当者通知用のWebhook URL
- content
:rotating_light: *対応依頼* 以下の問い合わせがFAQ Botで対応できませんでした。 担当者の確認をお願いします。 --- *質問内容:* {{#ユーザー入力.user_question#}} *分類結果:* 体組成計 または 歩数計(該当する方) *理由:* ナレッジベースに該当する情報がありませんでした ---
4.7. Slack送信ノードの設定
「APIキー認証設定」で:
- 認証名:任意の名前
- Slack Webhook URL:手順3.2.2.でコピーしたURL
設定:
- content:LLMノードの出力
- Slack Incoming Webhook url:手順3.2.2.でコピーしたURL
4.8. 担当者通知ノードの設定(その他の場合)
Slack Incoming Webhook URL:担当者通知用のWebhook URL
content:
:rotating_light: *対応依頼*
以下の問い合わせがFAQ Botで対応できませんでした。
担当者の確認をお願いします。
---
*質問内容:*
{{#ユーザー入力.user_question#}}
*分類結果:* その他
---
4.9. ワークフローの完成
最終的にできるDifyのワークフローはこちらです。
※ 開始がユーザー入力となっていますが、AWS Lambdaから変数を受け取っています。
5. AWS Lambda連携の構築(Slackからの質問取得)
5.1. DifyのAPI情報を取得
- ワークフローを公開
- 左メニューの「APIアクセス」をクリック
- APIエンドポイントとAPIキーをメモ
5.2. Lambda関数の作成
環境変数に
- DIFY_API_KEY:DifyのAPIキー
- DIFY_API_ENDPOINT:(例:
https://api.dify.ai/v1/workflows/run)
関数URLの有効化
- Lambda関数の設定タブ → 関数URL
- 認証タイプ:「NONE」
- 保存
- 生成された関数URLをコピー(事前準備の3.2.3.で用いる)
コードは以下になります。
import json
import urllib.request
import os
DIFY_API_KEY = os.environ['DIFY_API_KEY']
DIFY_API_ENDPOINT = os.environ['DIFY_API_ENDPOINT']
# 処理済みイベントを記録(簡易的な重複排除)
processed_events = set()
def lambda_handler(event, context):
print("=== Received event ===")
print(json.dumps(event))
# Slackのリトライを無視
if event.get('headers', {}).get('x-slack-retry-num'):
print("Ignoring Slack retry")
return {'statusCode': 200, 'body': 'ok'}
if 'body' not in event:
print("No body in event")
return {'statusCode': 200, 'body': 'no body'}
body = json.loads(event['body'])
print("=== Parsed body ===")
print(json.dumps(body))
# Slack URL検証(初回のみ)
if body.get('type') == 'url_verification':
print("URL verification request")
return {
'statusCode': 200,
'body': body['challenge']
}
# イベントIDで重複チェック
event_id = body.get('event_id')
if event_id in processed_events:
print(f"Duplicate event: {event_id}")
return {'statusCode': 200, 'body': 'ok'}
processed_events.add(event_id)
# メッセージイベント処理
event_type = body.get('event', {}).get('type')
print(f"Event type: {event_type}")
if event_type == 'app_mention':
# Bot自身のメッセージは無視
if body['event'].get('bot_id'):
print("Ignoring bot message")
return {'statusCode': 200, 'body': 'ok'}
user_question = body['event']['text']
print(f"User question: {user_question}")
# Dify ワークフローAPI呼び出し
data = json.dumps({
"inputs": {"user_question": user_question},
"response_mode": "blocking",
"user": body['event']['user']
}).encode('utf-8')
print(f"Calling Dify API: {DIFY_API_ENDPOINT}")
req = urllib.request.Request(
DIFY_API_ENDPOINT,
data=data,
headers={
'Authorization': f'Bearer {DIFY_API_KEY}',
'Content-Type': 'application/json'
}
)
try:
response = urllib.request.urlopen(req)
response_body = response.read().decode('utf-8')
print(f"Dify response: {response_body}")
except Exception as e:
print(f"Dify API error: {str(e)}")
return {'statusCode': 200, 'body': 'ok'}
6. まとめ
今回はDifyを用いて社内問い合わせシステムを構築しました。
ナレッジベースは適当に作ったMarkdownファイルを用いたので、ちゃんとしたものを用いて構築してみようと思います。




