13億パラメータの日本語特化GPT言語モデルを使ってSlackAIチャットボットを作ってみた2～SlackBot実装編～

# 1.はじめに
当記事は[「文章生成機能編」](https://qiita.com/milky04/items/24d7d2abf90e90417d93)の続きです。
先にそちらを読んでいただけると幸いです。

当記事ではSlackBotの実装について記載します。

:::note warn
初心者なため、間違った部分や理解の足りていない部分もあるかと思います。
そういった部分がありましたらコメントまたは[Twitter](https://twitter.com/as9784048914215)で指摘して頂けると幸いです。
:::

# 2.SlackBot初期設定
SlackBotの登録や設定、作成に関しては以下の記事が非常にわかりやすいため、そちらを参考に行ってください。[^SlackBot]
(タイトルの構成が似てしまいました…)

- [初心者がPythonでSlackbotを作ってみた！1 〜Slack初期設定編〜](https://qiita.com/hayahaya2/items/0787225d9fa66dbcdf4f)
- [初心者がPythonでSlackbotを作ってみた！2 〜Slackbot起動編〜](https://qiita.com/hayahaya2/items/d952455ff5fb20808262)
- [初心者がPythonでSlackbotを作ってみた！3 〜Slackbot機能編〜](https://qiita.com/hayahaya2/items/9e2692ffe7695377aafe)

上記記事に無い点や補足、差異についてを以下に記述します。

### 2-1.イベントや権限の設定

![スクリーンショット 2022-07-03 233014.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/768789/2b0ac41f-0d8a-2379-508a-bcb3d003bfef.png)
`Event Subscriptions`の`Enable Events`を`ON`にします。
そして、`Subscribe to bot events`に以下のイベントを追加します。

- `message.channels`
このイベントを追加することで、チャンネルにメッセージが投稿された際のイベント実行が可能になります。

今回の場合、追加するイベントは上記1つのみでokです。

![スクリーンショット 2022-07-03 232900.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/768789/b685472e-0975-d5bb-c9b1-fd7ddd4d05ff.png)
`OAuth & Permissions`の`Scopes`の`Bot Token Scopes`で以下の権限を設定します。

- `channels:history`
`bot`がチャンネルのメッセージ履歴にアクセスできるようになる権限です。
この権限を追加することで、`bot`がメッセージを読み取れるようになります。
こちらの権限は`Subscribe to bot events`に`message.channels`を追加すると自動で追加されます。

- `chat:write`
`bot`がチャンネルにメッセージを書き込むことができるようになる権限です。

今回の場合、設定する権限は上記2つのみでokです。

### 2-2.チャンネルにアプリを追加

![スクリーンショット 2022-07-03 233703.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/768789/ce63628f-7085-1157-dd70-a4861efad761.png)
`bot`をアプリとして追加するチャンネルを右クリックし、`チャンネル詳細を表示する`を選択します。

![スクリーンショット 2022-07-03 233910.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/768789/fdfb8d8f-2655-64ca-9bb2-89488bf88df8.png)
表示されたポップアップの`インテグレーション`タブに移動し、`App`の`アプリを追加する`を選択します。

![スクリーンショット 2022-07-03 234008.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/768789/1b8641cd-1319-96d2-69c8-02c191c0fb30.png)
遷移先のページにある`追加`ボタンでアプリを追加します。

以上で`bot`の初期設定が完了しました。

# 3.ライブラリインストール
```
pip install slack_bolt slack_sdk
```
必要なライブラリをインストールします。

### 3-1.slack_bolt
イベント実行によるメッセージの送受信を行うために`slack_bolt`をインストールします。

### 3-2.slack_sdk
メッセージの送信を行うために`slack_sdk`をインストールします[^slack_sdk]。

# 4.コード解説
### 4-1.import
```Python:ChatBot.py
from slack_sdk import WebClient
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
import re
import RinnaJapaneseGPT1b as rinna
```
必要なライブラリを`import`します。
[3](https://qiita.com/drafts/f1a3344844e85fe9bc73/edit#3%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)でインストールしたライブラリに加え、正規表現を使用するため`re`も`import`します。
[前回作成した文章生成機能](https://qiita.com/milky04/items/24d7d2abf90e90417d93#3-5%E3%82%B3%E3%83%BC%E3%83%89%E5%85%A8%E6%96%87)である`RinnaJapaneseGPT1b`も`import`します。

### 4-2.botのTokenやチャンネルIDを指定
```Python:ChatBot.py
SLACK_BOT_TOKEN = "Bot User OAuth Tokenを記述"
SLACK_APP_TOKEN = "Socket ModeのTokenを記述"

CHANNEL_ID = "botを動かすチャンネルIDを記述"

token = WebClient(SLACK_BOT_TOKEN)
```
`SLACK_BOT_TOKEN`に`Bot User OAuth Token`を格納します。[^token]
`SLACK_APP_TOKEN`に`Socket Mode`の`Token`を格納します。

`CHANNEL_ID`に`bot`を動かすチャンネルIDを格納します。

`WebClient`に`SLACK_BOT_TOKEN`を指定し、`token`に格納します。

### 4-3.bot起動時の処理
```Python:ChatBot.py
token.chat_postMessage(text="ログインしました。このチャンネルで話しかけてください。", channel=CHANNEL_ID)

app = App(token=SLACK_BOT_TOKEN)
```
`token.chat_postMessage`で`bot`起動時にメッセージを送信します。
`text`に`"ログインしました。このチャンネルで話しかけてください。"`というメッセージを指定します。
`channel`に送信先のチャンネルを指定します。

`app = App(token=SLACK_BOT_TOKEN)`で`bot`を初期化(イニシャライズ)します。

### 4-4.メッセージが投稿された時の処理
```Python:ChatBot.py
@app.message(re.compile("(.*)"))
def reply(message, context, say):
    user = message["user"]
    token.chat_postMessage(text="文章生成中…", channel=CHANNEL_ID)
    greeting = context['matches'][0]
    response = rinna.generate_text(greeting)
    text = f'<@{user}>{response}'
    say(text=text, channel=CHANNEL_ID)
```
正規表現を使い、`@app.message(re.compile("(.*)"))`でメッセージが投稿された際に処理をイベント実行します。

メンションする為に`message["user"]`でメッセージを投稿したユーザーを判定し、`user`に格納します。

文章生成開始を明示する為に`token.chat_postMessage`で`"文章生成中…"`というメッセージを指定チャンネルに送信します。

正規表現のマッチ結果が`context.matches`に設定されるため、`context['matches'][0]`でマッチ結果である投稿されたメッセージを`greeting`に格納します。

`rinna.nlp(greeting)`で投稿されたメッセージを`rinna`に与え、生成された文章を`response`に格納します。

メンション付きメッセージを`f'<@{user}>{response}'`で設定し、`text`に格納します。

`say`でメンション付きメッセージを指定チャンネルに返します。

### 4-5.bot起動
```Python:ChatBot.py
if __name__ == ""__main__"":
  SocketModeHandler(app, SLACK_APP_TOKEN).start()
```
`bot`を起動します。

### 4-6.コード全文
```Python:ChatBot.py
from slack_sdk import WebClient
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
import re
import RinnaJapaneseGPT1b as rinna

SLACK_BOT_TOKEN = "Bot User OAuth Tokenを記述"
SLACK_APP_TOKEN = "Socket Mode のTokenを記述"

CHANNEL_ID = "botを動かすチャンネルIDを記述"

token = WebClient(SLACK_BOT_TOKEN)

token.chat_postMessage(text="ログインしました。このチャンネルで話しかけてください。", channel=CHANNEL_ID)

app = App(token=SLACK_BOT_TOKEN)

@app.message(re.compile("(.*)"))
def reply(message, context, say):
    user = message["user"]
    token.chat_postMessage(text="文章生成中…", channel=CHANNEL_ID)
    greeting = context['matches'][0]
    response = rinna.generate_text(greeting)
    text = f'<@{user}>{response}'
    say(text=text, channel=CHANNEL_ID)

if __name__ == "__main__":
  SocketModeHandler(app, SLACK_APP_TOKEN).start()

```
上記コピペでそのまま使用できます。

# 5.実行
それではいよいよ`bot`の実行をしていきます。
以下の画像がSlack上での`bot`の実行結果です。

![スクリーンショット 2022-07-03 232141.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/768789/a353560c-a040-755d-9b5b-c5d7d16d99b9.png)
無事実行出来ていますね。
これでSlack上でAIチャットボットとの会話を楽しむことができます。

![スクリーンショット 2022-07-03 232437.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/768789/6da24b24-1da2-3874-a5f3-3125db4e9449.png)
だいぶすごい(小並感

余談ですが、実行時にもし必要な権限が無かった場合はなんとエラーで必要な権限を教えてくれます。
以下は権限を何も設定していない状態で実行した時のエラーの最終行です。

```
The server responded with: {'ok': False, 'error': 'missing_scope', 'needed': 'chat:write:bot', 'provided': 'incoming-webhook'}  
```
全てを教えてくれているわけではありませんが、権限に`chat:write`が必要と教えてくれています。優しいですね。

# 6.おわりに
SlackBotと[rinna/japanese-gpt-1b](https://huggingface.co/rinna/japanese-gpt-1b)を使うことで簡単にAIチャットボットを作成することができました。
今回は行っていませんが、seed値を固定したりFine Tuningしたりなど、様々な楽しみ方が出来ると思うのでぜひ調べてみてください。

私も開発し、学んでいて非常に楽しく、自然言語処理(機械学習)の取っ掛かりとしても良かったと感じました。
[BLOOM](https://bigscience.huggingface.co/blog/bloom)も最近リリースされたので、こちらも試してみたいと思います。

当記事を通じて自然言語処理に少しでも興味を持っていただけたなら幸いです。

# 7.参考記事
- [Slack API](https://api.slack.com/)
- [Bolt 入門ガイド](https://slack.dev/bolt-python/ja-jp/tutorial/getting-started)
- [初心者がPythonでSlackbotを作ってみた！1 〜Slack初期設定編〜](https://qiita.com/hayahaya2/items/0787225d9fa66dbcdf4f)
- [初心者がPythonでSlackbotを作ってみた！2 〜Slackbot起動編〜](https://qiita.com/hayahaya2/items/d952455ff5fb20808262)
- [初心者がPythonでSlackbotを作ってみた！3 〜Slackbot機能編〜](https://qiita.com/hayahaya2/items/9e2692ffe7695377aafe)
- [【最新版slackアプリ開発】久しぶりにslackアプリを作った時にslackbotライブラリでは使えなかったのでBoltに移行した](https://www.sassy-blog.com/entry/20211105/1636086081)
- [Python で Slack API や Webhook を扱うなら公式 SDK（slack-sdk/slack-bolt）を使おう](https://qiita.com/seratch/items/8f93fd0bf815b0b1d557)
- [Slack ソケットモードの最も簡単な始め方](https://qiita.com/seratch/items/1a460c08c3e245b56441)

[^SlackBot]:「Slack bot」等で検索すると上位に出てくる情報は古いものが多いため注意が必要です。
[^slack_sdk]:`slack_bolt`をインストールすれば`slack_sdk`も自動的にインストールされるそうなので、`slack_bolt`のインストールだけで良いかもしれません。
[^token]:今回構成を簡単にするために`Bot User OAuth Token`や`Socket Mode`の`Token`、チャンネルIDを直接コードに記述していますが、本来であれば機密性や脆弱性等の観点から直接記述しない方が良いです。
`Token`やチャンネルIDを直接コードに記述している関係で、今回Pythonの仮想環境は使用していません。