Denodo AI SDK を Slack から使用する

こんにちは！インサイトテクノロジーの松尾です。

本記事では、Denodo Platform 9.1 の新機能「Denodo AI SDK」を Slack から利用する手順をご紹介します。

先日の記事では、Denodo AI SDK のチャットボットサンプルを動かす方法をご紹介しました。

https://qiita.com/takumats/items/434b596efc5b8e71357d

しかし、専用アプリではなく、普段使いのツール（例: Slack）から使いたい方も多いのではないでしょうか？

そこで、Denodo 公式ドキュメントを参考に、Slack との連携を試してみました。この記事では、その手順を詳しく説明します。

* [Integrating the Denodo AI SDK in a Slack bot](https://community.denodo.com/kb/en/view/document/Integrating%20the%20Denodo%20AI%20SDK%20in%20a%20Slack%20bot?utm_source=partner_lead_InsightTech)

### 私の実行環境

* Windows 11 24H2
* WSL2 Ubuntu 22.04.5 LTS
* Slack フリープラン

## アーキテクチャ概要

動作確認のため、以下のアーキテクチャで構築しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/86a9321c-eb87-d089-0837-dbd6765c4607.png)

動作は以下の通りです。

* Slackからのメッセージは、ローカル PC 上で動作する Python プログラム（"Slack IF"）で処理します
* "Slack IF"が Denodo AI SDK にリクエストを送信します
* Denodo AI SDKは、Denodo Platform および生成 AI から回答を生成します
* 最終的に "Slack IF" が Slack に応答を返します

ではさっそく進めていきましょう。

## Slack と Python プログラム（ "Slack IF" ）のやり取り

まず、Slack と Python プログラムがメッセージをやり取りできるよう設定します。この部分は Denodo とは直接関係なく、Slack 社の公式ドキュメントに基づいて進めます。ローカル PC と Slack のやり取りの方法として Denodo 社の記事では localtunnel を使って Flask とやり取りするような手順となっていますが、本投稿では Slack 社のドキュメントに従い Socket Mode を使って行います。まずは、以下のドキュメントに従い、ローカルの Python コードが Slack とやり取りできるようにしてみましょう。

https://tools.slack.dev/bolt-python/ja-jp/getting-started/

### Slack アプリの作成

まず、「Create New App」からアプリを追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/003778e1-cb62-66e8-a069-99f418b203e4.png)

「From scratch」を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/18396838-8830-ee92-2d3b-7793cb3605d4.png)

アプリ名を入力し、ワークスペース名を選択し、「Create App」を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/89d8d609-104a-3e2a-31b7-1468339f62dc.png)

### トークンの作成

必要なトークン(ボットトークンとアプリレベルトークン)を作成します。

左から「OAuth & Permissions」を選択し、「Bot Token Scopes」で「Add an OAuth Scope」を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/7f4ef822-f5f6-085b-40c2-b9783e5492db.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/568721a2-7f38-c0a0-082d-e25b491d1bb2.png)

`chat:write` というのを探して追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/0c114a36-cb48-a6c4-0a00-34ce0611be2c.png)

一番上の「Install App to Workspace」を選択します。表示される画面で指定したワークスペースへのアプリのインストールを実行します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/343d20c5-1295-3495-ab15-8b69a6981c78.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/44b4cb0a-e37e-7774-3319-b0e4479aff22.png)

「Bot User OAuth Access Token」が確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/cec98db7-1827-20bc-dbc4-0512aa3b949a.png)

この `xoxb-xxxxxxxxxxxxxxxxxxx` がボットトークンです。

続いて、「Basic Informationのページ」で、アプリレベルトークンの「Generate Token and Scopes」を選択し、アプリレベルトークンを作成します。このトークンに `connections:write` のスコープを付与します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/34f992f8-442b-807c-0233-c574b9ab8ae3.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/2739afa4-9b2c-71d3-94f7-8401de50a869.png)

アプリレベルトークン `xapp-xxxxxxxxxxxxxxxxxxxxxx` が生成されました。

### Socket Mode の有効化とイベントサブスクリプションの設定

左のメニューから Socket Mode を選択し、「Enable Socket Mode」から Socket Mode を有効にします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/2ba8a814-4d3f-50c8-ffb7-1158bc0ec0e2.png)

参照するイベントを設定します。「Event Subscriptions」を選択します。
以下のように、Scope を選択し、下部にある「Save Changes」を選択して保存します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/7d90c07e-f84d-d782-137e-83e0ccce08ec.png)

これで準備は整いました。

念のため、以下からアプリを再起動します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/a6a8be4b-6ece-97fc-dfbe-8774397cfcb7.png)

:::note info
上記再インストールの手順はドキュメントへの記載はありませんが、私が実施したときは再インストールしないと動作しなかったので、念のため、、
:::

### Python プログラムの実行

[参考ページ](https://tools.slack.dev/bolt-python/ja-jp/getting-started/) に記載されている以下のサンプルを実行してみましょう。

```python
import os
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler

# ボットトークンを渡してアプリを初期化します
app = App(token=os.environ.get("SLACK_BOT_TOKEN"))

# 'こんにちは' を含むメッセージをリッスンします
# 指定可能なリスナーのメソッド引数の一覧は以下のモジュールドキュメントを参考にしてください：
# https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html
@app.message("こんにちは")
def message_hello(message, say):
    # イベントがトリガーされたチャンネルへ say() でメッセージを送信します
    say(f"こんにちは、<@{message['user']}> さん！")

if __name__ == "__main__":
    # アプリを起動して、ソケットモードで Slack に接続します
    SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start()
```

以下のようにトークンを環境変数セットしてから実行します。

```bash
(venv) $ export SLACK_APP_TOKEN=xapp-xxxxxxxxxxxxxxxxxxx
(venv) $ export SLACK_BOT_TOKEN=xoxb-xxxxxxxxxxxxxxxxxxx
(venv) $ python3 app.py
⚡️ Bolt app is running!
```

そして、Slack から以下のように「こんにちは」と呼び掛けると、応答が返ってきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/b48c2805-fb9f-7c94-2892-15ad83214c14.png)

このサンプルは「こんにちは」という文字列が入っているメッセージに反応するようになっています。
毎回「こんにちは」と入力するのも面倒なので、すべてのメッセージに反応するよう、以下のように作り替えておきましょう。

```diff_python
11c11
< @app.message("こんにちは")
---
> @app.event("message")
```

すると、すべてのメッセージに反応するようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/7ffb5d5f-b9d4-40ca-01ca-546d2a01da4c.png)

## Denodo AI SDK への問い合わせと返信処理の追加

### Denodo AI SDK の起動

もちろん、事前に Denodo AI SDK を起動しておく必要があります。起動は以下の記事を参考に Docker で起動するのが手っ取り早いです。

https://qiita.com/takumats/items/434b596efc5b8e71357d

```bash
$ docker compose -f docker-compose-sample-chatbot.yml up
```

で起動します。

### Denodo AI SDK への問い合わせ処理と返信処理の実装

Python プログラムに Denodo AI SDK への問い合わせ処理を Denodo 社のサンプルに従って組み込みます。
以下は、質問を Denodo AI SDK に送信し、その結果を取得する例です。

```python
import os
import requests;
from requests.auth import HTTPBasicAuth
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler

# ボットトークンを渡してアプリを初期化
app = App(token=os.environ.get("SLACK_BOT_TOKEN"))

# 質問を Denodo AI SDK へ投げて応答を返却
def call_answer_question_api(question):
    url = "http://localhost:8008/answerQuestion"
    params = {
        "question": question,
    }

    response = requests.get(url, params=params, auth=HTTPBasicAuth('admin', 'admin'))
    if response.status_code == 200:
        data = response.json()
        print(data)
        return f"{data['answer']}\n{data['sql_query']}"
    else:
        response.raise_for_status()

# Error events
@app.error
def error_handler(error, body, logger):
    logger.error(f"Error: {error}")

# メッセージに対する処理
@app.event("message")
def handle_message_events(body, say):
    event = body['event']
    user_input = event.get('text')
    response = call_answer_question_api(user_input)
    say(response)

if __name__ == "__main__":
    # アプリを起動して、ソケットモードで Slack に接続
    SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start()
```

すると以下のように、Slack から Denodo AI SDK とやり取りできるようになりました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/e74dbc75-8404-b19e-c0db-d3992d889ba8.png)

## おわりに

本記事では、Denodo AI SDK を Slack から操作する手順を、Denodo 社のドキュメントを参考に紹介しました。専用の UI を使用せず、普段使いのツールから操作できることで、利用の敷居が大きく下がると感じています。
今回は動作確認のサンプル構築にとどまりましたが、Slack を介して Denodo のデータを直接クエリーできる未来を想像すると、とてもワクワクしますね！

### もう少し詳しく知りたい方へ

弊社インサイトテクノロジーは、Denodo 社の公式パートナーとして、Denodo Platform の紹介、販売、導入支援を行っています。ご興味のある方は、ぜひ以下のリンクからお問い合わせください。

https://www.insight-tec.com/products/denodo/

また、「これからデータ統合基盤を整備したい」「どのようにデータを統合すればよいか迷っている」といった初期段階のお悩みや、「現在進行中のプロジェクトで特定の課題がある（例: Snowflake に SAP からリアルタイムデータを取り込みたい）」といった具体的なご相談も、ぜひお気軽にお問い合わせください。

https://www.insight-tec.com/products/consulting/