この記事は、Qiita Dify Advent Calendar 2024 の16日目の記事です。
qiita.com
はじめに
本記事では、DiscordのSlash Command を AWS Lambda で受け取り、そのイベントを Dify に転送する仕組みを紹介します。
Discordアプリを作成した経験がある方はご存知かもしれませんが、Slash Commandが実行されると、あらかじめ設定したInteractions Endpoint URL
にイベントが送信されます。しかし、このURLは1つしか設定できないため、1つのDiscordアプリから複数のDifyアプリを呼び出すには少しだけ工夫が必要です。本記事では、その実現方法を紹介します。
また、Discordアプリを常駐させるとコスト面で不利になるため、必要なときにのみイベントを受けDifyに連携できるようAWS Lambdaを採用しています。
前提条件
- AWSアカウントがあること
- AWS SAM CLIをインストール済みで、初期設定が終わっていること
- 連携したいDifyアプリ(workflow)を作成済みであること
- Discordのアプリを作成済みで、下記の権限(bot, applications.commands)を有効にして任意のサーバーに入れていること
注意事項
- 本記事で対象にしているDifyアプリはworkflowのみです。chatbotを利用したい場合はリクエスト先を変更することで動くはずです。
- Slash CommandとLambdaの連携については最低限のみ記載します。詳細は本記事で紹介するソースコードや他の方の記事を見ていただければと思います。
Slash Commandとは?
Slash Commandは、Discord上で「/」を入力することで呼び出せるコマンド機能です。
例えば、「/」を入力すると使用可能なコマンド一覧が表示され、「/neko」などのコマンドを実行できます。
上記の例は"text"を入力するようにしていますが、オプションで指定できApplication Commandsあたりに詳しく書いてあります。
システム構成
- Discordでコマンドを実行
- Lambdaがイベントを受け取り、どのDify Appに送信するかを識別してリクエスト
- Dify Appが動作し、Discordにメッセージ送信
ソースコード
SAMでデプロイできるようにしています。コードを自分用に一部書き換える必要があるので、該当箇所だけ記載します。他の箇所はSlash CommandとLambdaの連携についてなので、割愛します。 github.com
- register-commands:コマンドの登録用Lambda
- discord:Discordからのイベントを受けるLambda
register-commands
DiscordのSlash Commandは事前にコマンドの登録をするLambdaです。今回は複数人で開発をすることを想定しLambdaで動かしていますが、ローカルで実行でも大丈夫です。
- コマンドは下記のように記載してください。optionsはApplication Commandsあたりを参照し、自身のDifyアプリに合わせてください。
commands = [ { 'name': 'neko', 'description': 'にゃーん', 'options': [ { "name": "text", "description": "string", "type": 3, "required": True, } ] }, { 'name': 'summary', 'description': '要約', 'options': [ { "name": "url", "description": "string", "type": 3, "required": True, } ] } ]
discord
Discordからイベントを受け取った際に、コマンド名を取得し正しいDifyアプリへリクエストを送信、DifyからのレスポンスをDiscordへ送信するLambdaです。Lambdaの関数URLを発行します。
- 自身のコマンド名に合わせ、下記のコードを変更してください。
inputs
はDifyアプリの開始ノードに合わせてください。command_input
は入力内容をDiscordに返すための変数です。(Difyアプリの実行には影響しません)
例:/neko
ではquery
を設定
inputs = {} if command_name == 'neko': command_input += f'text={body['data']['options'][0]['value']}' inputs = { 'query': body['data']['options'][0]['value'] } elif command_name == 'summary': command_input += f'url={body['data']['options'][0]['value']}' inputs = { 'url': body['data']['options'][0]['value'] }
- Dify アプリの終了ノードは
text
で返すようにしてください。 変更したい場合は、下記のdata
を終了ノードの出力に合わせてください。
response = send_dify_workflow(inputs, body['member']['user']['username'], DIFY_API_KEY) logger.info(f'dify response: {response}') url = f'https://discordapp.com/api/channels/{channel_id}/messages' headers = { 'authorization': f'Bot {DISCORD_BOT_TOKEN}', 'content-type': 'application/json' } data = { 'content': response['data']['outputs']['text'], } r = requests.post(url, headers=headers, json=data)
- (変更不要です。紹介のみ。)DifyアプリをLambdaで振り分けるロジックは下記の部分です。
command_name
にはregister-commands
で登録するname
が入ってきます。
DIFY_API_KEY = get_ssm_parameter(f'/dify/app/{command_name}')
デプロイ & Discordアプリ設定
準備
登録手順を参考に下記の値をパラメータストアにに登録してください。(SAMでデプロイする前でも後でもOK)
名前 | 値 |
---|---|
/discord/dify/DISCORD_APP_ID | Discord Developer Portal > Discordアプリ > General Information > Application ID |
/discord/dify/DISCORD_BOT_TOKEN | Discord Developer Portal > Discordアプリ > Bot > TOKEN |
/discord/dify/PUBLIC_KEY | Discord Developer Portal > Discordアプリ > General Information > Public Key |
/dify/ENDPOINT | Difyのエンドポイント |
/dify/app/[command name] | DifyアプリのAPI KEY。必要なコマンドの分だけ登録してください。この記事では/dify/app/neko 、/dify/app/summary を登録しています。 |
登録手順
- パラメータの作成
- 赤枠を入力・選択して「パラメータを作成」をクリック
デプロイ
$ sam build $ sam deploy
デプロイが成功すると下記のようにLambdaの関数URLが出力されるのでコピーします。
Discordアプリ設定
Discord Developer Portal > Discordアプリ > General Information > Interactions Endpoint URLに上記でコピーした関数URLをペーストし、「Save Changes」をクリック
下記のメッセージが表示されればOK。
コマンド登録
register-commandsのLambda(そのままデプロイしていれば、discord-slashcommand-CommandRegisterFunction-xxxxxxxxxxxx
のような関数名)を開き、テスト実行してください。statusCode": 200
が返ってきていればコマンド登録はできているはずです。
関数URLを発行してcurlで実行しても良いし、そもそもLambdaにせずローカルで実行してもよいです。おまかせします。
コマンド実行
"/[command name]"で下記のようにコマンドが表示されていればOKです。
実行しましょう!アプリから返答があればOKです!(/nekoは猫語で返答してくれるworkflow)
まとめ
DiscordのSlash CommandからDifyを呼び出す仕組みについて紹介しました。
コマンド登録の部分は改良したい。