SlackでVertex AIボットアプリをつくる

# はじめに

以前OpenAI APIを利用したSlackのChatGPTボットアプリを作成しました。

https://qiita.com/yahayuta/items/ce20827faf78664c4f5e

今回はGCP Vertex AIのPython SDKを使って似たようなアプリをつくってみようと思います。

:::note
9/20時点でVertex AIの言語生成AIが突然日本語に対応してたので記事を修正してます
:::

# 処理仕様

おおまかな処理仕様はOpenAI版とほぼ同じとなります。

https://qiita.com/yahayuta/items/ce20827faf78664c4f5e

今回はポイントとなるところのみを説明していきます。
動作環境は同じGCPのCloud Runを利用します。

Vertex AIを利用するためにはGCPのアカウントが必要です。
APIと連携するためにアプリ用のサービスアカウントを作成し「**Vertex AI ユーザー**」ロールを付与します。
サービスアカウントをCloud Runで作成したアプリに割り当ててください。
GCP以外の環境で動作させるにはサービスアカウントの鍵ファイルを作成して`GOOGLE_APPLICATION_CREDENTIALS`という環境変数に鍵ファイルのパスを指定することでAPIを動作させることができました。

# 処理内容

今回必要なPythonライブラリは以下となります。

```ssh
pip install google-cloud-aiplatform
# pip install googletrans==3.1.0a0
```
~~googletransはなぜかバージョン指定をする必要があったので・・・ということで~~ 

```python
import vertexai

from vertexai.preview.language_models import ChatModel, ChatMessage
# from googletrans import Translator
```

今回はテキストチャットのAPIを利用します。
会話履歴の取得と送信が必要になるのでSlackのスレッドから履歴を取得します。
~~プロンプトが日本語に対応していないので翻訳ライブラリを利用して「日本語→英語」と「英語→日本語」の変換を行います。~~

# スレッド情報取得

```python
    message_history = []
    input = ''
    # スレッドモードの場合スレッド内の発言をAIに送る
    if thread_ts != '':
        payload = {'token':APP_TOKEN_PALM, 'channel':channel_id, 'ts': thread_ts}
        res = requests.post("https://slack.com/api/conversations.replies", data=payload)
        res_json = res.json()
        print(res_json)
        messages = res_json['messages']
        for message in messages:

            # アプリへのメンションの場合
            if APP_USER_ID_PALM in message['text']:
                # 制御文字を抜く
                input = message['text'].replace(APP_USER_ID_PALM, '')
                # 何もない場合は無視する
                if input == '':
                    continue
                # 全て無条件に英語に訳す
                # input = traslate_ja_to_en(input)
                q_message = ChatMessage(author="user", content=input)
                message_history.append(q_message)
            # AIの回答の場合
            elif APP_USER_ID_PALM_RAW in message['user']:
                input = message['text']
                # 全て無条件に英語に訳す
                # input = traslate_ja_to_en(input)
                a_message = ChatMessage(author="bot", content=input)
                message_history.append(a_message)
```
会話履歴の作成処理です。
Slackからスレッドの内容を全て取得しユーザの質問とAIの回答のみを抽出していきます。
`ChatMessage`というオブジェクトを作成ユーザーの質問とAIボットの回答を交互に配列へ格納していきます。
~~スレッドから取得したメッセージをgoogletransライブラリにて全て英語に翻訳します。~~

```python
#def traslate_ja_to_en(text):
#    translator = Translator()
#    translated = translator.translate(text, src='ja', dest='en')
#    print(translated)
#    return translated.text

#def traslate_en_to_ja(text):
#    translator = Translator()
#    translated = translator.translate(text, src='en', dest='ja')
#    print(translated)
#    return translated.text
```

# プロンプト送信＆回答をSlackに投稿

```python
    text = text.replace(APP_USER_ID_PALM, '')

    # 全て無条件に英語に訳す
    # text = traslate_ja_to_en(text)

    # 一番最後のメッセージは質問としてAIに送るため削除する
    message_history.pop()

    print(message_history)
    print(text)

    vertexai.init(project="GCPのプロジェクト名", location="us-central1")
    chat_model = ChatModel.from_pretrained("chat-bison@001")
    parameters = {
        "temperature": 0.2,
        "max_output_tokens": 1024,
        "top_p": 0.8,
        "top_k": 40
    }

    answer = ''

    try:
        chat = chat_model.start_chat(message_history=message_history)
        response = chat.send_message(text, **parameters)
        print(f"Response from Model: {response.text}")
        answer = response.text
    except Exception as e:
        answer = f"An exception occurred: {e}"
        print(answer)

    # 全て無条件に日本語に訳す
    # answer = traslate_en_to_ja(answer)
```

先ほど作成した会話履歴は`message_history`へセットします。
データ構造は「ユーザ、ボット、ユーザ、ボット、、、」の組み合わせの順番になる必要があるのですが
スレッド内の全てのメッセージを取得しているため最後のユーザの発言を`pop()`関数で外します。

:::note
会話履歴データの異常エラーとしてデータ数は偶数になってないといけないとか交互になっていないなど例外が飛んできます
- There should be odd number of messages for correct alternating turn.
- Messages must alternate between authors.
:::


Slackから送信されたユーザの発言自体は`text`に格納していますので~~それを英訳して~~APIに送信します。
~~AIから戻ってきた`answer`を和訳してからSlackに送信する感じになります。~~

# おわりに

GCPのVertex AIコンソールプレイグラウンド上でサンプルソースも簡単に出力できるのでこれは楽勝かなと思いきや
履歴の積み上げサンプルが公式リファレンス上全く情報として見つけられなかったで最後は結局SDKのコードを読んで発見した感じですね。
https://github.com/googleapis/python-aiplatform