Lex+BedrockでSlackボットを作りました！

# やりたいこと
・社内規定などの文書をS3に格納し、それをBedRockのナレッジベースのデータソースとして活用する
・Lexを使用して自由会話形式のボットを作成し、社員が社内規定に関する質問をすると、ボットが回答する仕組みを構築する。これにより、大量の文書から必要な情報を探す時間を短縮する
・LexをSlackと連携させ、SlackのDM上で会話できるようにする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/5cc63b59-5514-7e0c-9ccf-3bdf9d03877a.png)

# S3の作成＋社内文書の格納
S3を開き、「バケットを作成」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/c6173a67-a8ca-4695-52d4-cf0afc0b54c2.png)

バケット名を入力し、その他設定をデフォルトのままで、「バケットを作成」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/ff7a2d80-3bc0-8263-4312-c6786d2556d9.png)

社内規定のpdfファイルをアップロード
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/a5310069-26ac-4511-6890-ece985978c74.png)

# Bedrockナレッジベースの作成
「Bedrock」を開き、左メニューから「ナレッジベース」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/f9054ff7-a032-325a-6d09-c645d8803599.png)

「ナレッジベースを作成」をクリックし、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/aa056bf2-8acd-b24d-1b7d-9477e8d14a32.png)

「Knowledge Base with vector store」を選択
（「Knowledge Base with structured data store」は構造化データの場合に使う）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/69940378-ffdf-61b3-fc12-bb397b1a6dfa.png)

ナレッジベース名は自動で付与されるが、変えても大丈夫
IAM許可は事前に作っていないので、「新しいサービスロールを作成して使用」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/49a3a134-7878-5025-970f-bc4ae887f75d.png)

「Data source details」は「S3」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/c1976aae-3c43-d82b-c88c-cdcf195c5ceb.png)

その他設定はデフォルトのままで、「次へ」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/9f797679-645e-7526-4abb-926a07eeefd3.png)

「S3を参照」をクリックし、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/71680ff2-5aae-edad-7b2b-43695842dfa7.png)

先ほど作ったS3バケットを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/a40949c3-055f-4312-2065-f5a385c897aa.png)

その他設定はデフォルトのままで、「次へ」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/326c4347-fab7-330b-db7c-9d53fe07d49e.png)

「埋め込みモデル」について、社内で「Titan Embeddings G1 - Text v1.2」を試しているメンバーがいるので、他モデル精度検証のため「Embed Multilingual v3」を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/575448c0-aa72-2802-6741-cce817aea530.png)

「ベクトルデータベース」について、「Amazon Aurora PostgreSQL Serverless」にして、「次へ」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/bae469c1-aa0e-c557-3400-e150bf1928f5.png)

設定内容を確認し、「ナレッジベースを作成」をクリック（数十分かかる）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/1b167e40-26de-c9cb-ebd9-6c48a03ad6c0.png)

# モデルの有効化
左メニューの「モデルアクセス」をクリックし、利用できるベースモデルの画面を開きます

初めてBedrockを使う方は、各モデルの「アクセスのステータス」がすべて灰色の「リクエスト可能」になっていると思います。

モデルを利用する際は、AWS Marketplaceで課金が発生するため、事前に利用するモデルの有効化を行う必要があります。

使うモデルを選択し、「モデルアクセスをリクエスト」をクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/96ec9249-050c-f0f6-ac7b-9b5396080360.png)

すると、このようなメールが飛んでくる

今回は他者のハンズオンブログを参照しながら作成するので、ブログと同じく「Anthropic > Claude 3.5 Sonnet（回答文作成のモデル）」と「Cohere > Embed Multilingual（S3ファイル取り込み時のモデル）」を検証したく、両方とも有効化をしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/1caa28d7-e383-78c2-01a4-237e9dd49b3b.png)

# S3とナレッジベースの同期
左メニューの「ナレッジベース」を再度クリックし、先ほど出来上がったナレッジベースをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/c5eb79ed-93cb-7fe2-1732-b3ebfe637fbf.png)

画面右側にある「ナレッジベースをテスト」には、「1 つ以上のデータソースが同期されていません。」と表示されています
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/d6470c6b-85a7-cdd1-71b7-4c78d0721d81.png)

画面真ん中あたりにある「データソース」に、作られたデータソースをクリックすると、S3と紐づけているのですが、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/018416dd-e641-cb79-7104-fd28ccfd43b6.png)

S3に格納されたファイルとの「同期」はまだ行われていませんので、「Document」が空状態。「同期」クリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/dad0a63e-bac9-fc14-9cd7-8044360e9708.png)

すると、このようにS3のファイルが取り込まれました。
（S3ファイルの変更がある場合、再度「同期」する必要があります）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/ec54f359-abaf-7224-5621-d0b61f62c694.png)

取り込まれたS3ファイルは、Bedrock側でベクトル変換します。変換済みのデータはAurora Serverlessに格納されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/18c3d684-5fa3-47e8-c341-20838bac57ca.png)

# Bedrockにおけるモデル検証
再びナレッジベースの画面に戻ります。画面右側にある「ナレッジベースをテスト」は初期状態で入力できないですが、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/97bd023b-ba15-6cf4-0c4c-f4283353535f.png)

「回答を生成」にチェックを外すと、このように入力できるようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/67971027-15c4-14fb-dcca-e78c3242811a.png)

しかし、「有給は何日取れる」って質問すると、こんな感じでよくわからない回答文が帰ってきました。。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/2f7eab07-74f5-cfe9-8534-881b90ed94a9.png)

「回答文生成」のモデルをまだ選択していないからです！
「回答を生成」にチェックを入れた状態で「モデルを選択」をクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/1ee52421-987c-3406-98c5-7941b2131db9.png)

先ほど有効化したAnthropic社とCohere社のモデルが選択肢として出てきました。「Claude 3.5 Sonnet  v1」（回答文を作成するモデル）を選び、「適用」をクリック。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/4cc6ab4c-8a7b-6688-1f83-5c0870f1f9a6.png)

すると「Claude 3.5 Sonnet　v1　オンデマンド」が表示されました。
もう一度同じ質問をすると、今度はまとまった回答文が帰ってきました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/73ee200a-4ddb-faf6-ac18-b9c8c5cefada.png)

# Lambda作成
Lambdaを開き、「関数の作成」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/5b33aae1-3998-f2ef-6e2c-243efdc41b03.png)

「一から作成」を選択し、ランタイムは「Python3.13」にし、「関数の作成」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/527b1038-5008-b36a-e4cb-2a580a2237dd.png)

作成されたLambda関数をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/962bb3ce-144c-7201-b2ee-a44f37447c31.png)

以下のソースをデプロイします。
（Lexから質問文を受け付け、Bedrockナレッジベースへ渡し、最後は回答文をLexへ返す）
※knowledge_base_idはナレッジベース画面を参照し書き換えます。
※modelArnは、以下の方法で取得できます。

https://wp-kyoto.net/list-base-model-from-bedrock-by-aws-cli/

```
import boto3
import json

bedrock_agent_runtime_client = boto3.client('bedrock-agent-runtime')

def lambda_handler(event, context):

    # Lexからの入力イベントを確認
    print("Event received from Lex:", json.dumps(event))

    user_prompt = event.get('inputTranscript')
    
    # Knowledge Base IDに置き換えてください
    knowledge_base_id = 'xxxx'

    # Bedrockのモデル
    modelArn = 'anthropic.claude-3-5-sonnet-20240620-v1:0'

    response = bedrock_agent_runtime_client.retrieve_and_generate(
        input={
            'text': user_prompt,
        },
        retrieveAndGenerateConfiguration={
            'type': 'KNOWLEDGE_BASE',
            'knowledgeBaseConfiguration': {
                'knowledgeBaseId': knowledge_base_id,
                'modelArn': modelArn,
            }
        }
    )

    print("Received response:" + json.dumps(response, ensure_ascii=False))

    response_output = response['output']['text']

    response = {
        "messages": [
                {
                    "contentType": "PlainText",
                    "content": response_output
                }
            ],
        "sessionState": {
            "dialogAction": {
                "type": "Close"
            },
            "intent": {
                "name": "Lexインテント名に書き換えてください",
                "state": "Fulfilled"
            }
        }
    }


    return response

```

「設定」＞「一般設定」＞「編集」で、「タイムアウト」時間を30秒へ変更します。
（Bedrockの回答文生成に時間がかかる場合があり、初期設定の3秒は足りない恐れがあります。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/23f65edf-63d0-fbd4-9034-2bb74a0fe542.png)

「設定」＞「アクセス権限」＞「ロール名」で、自動作成されたIAMロールへ遷移します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/7e39aa57-defd-46dd-9f5e-92f429db9958.png)

初期状態は「AWSLambdaBasicExecutionRole」（CloudWatch関連の権限）しかないはずですが、「許可ポリシー」＞「許可の追加」で 「AmazonBedrockFullAccess」ポリシーを付与し、ナレッジベースへのアクセス権限を与えます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/d17bb9aa-0f95-dd95-67de-126b1290e326.png)

これで設定完了！

# Lexボットの作成
Lexを開き、「ボット作成」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/21341f76-78c7-b3bd-96ae-2cbb3fdb9889.png)

「空のボットを作成します」を選択、ボット名を設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/34ee2262-e0c4-7506-3753-42a75812e235.png)

IAMを新たに作成します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/cb3baca0-39a9-3375-29bb-9ef591bbf51a.png)

児童オンラインプライバシー保護法 (COPPA)に「はい」、「次へ」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/89eab2aa-ee10-7cd0-0d6c-5756bf7a6236.png)

「Japanese」を選択し、好きな音声を一つ選び、「完了」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/1dc1ef70-1e70-20cb-b4a0-bd964fff7ec1.png)

すると、「ボット (EmployeeBot) が正常に作成されました」が表示され、「インテント」作成の画面に遷移します。

まず、「インテント」名を入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/d3f5fcab-d91b-372a-ed46-f5f150151b60.png)

「サンプル発話」とは、ユーザーが会話をスタートするときに、入力すると予想される代表的なフレーズ。

「社内規定を知りたい」を入力すると、ボットは新しい会話フローをスタートし、応答をはじめます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/75ef346a-f054-f9fc-4789-6c596faae742.png)

スロットに「スロットを追加」をクリックし、スロットを追加

今回はユーザーが自由会話形式で質問できるようにしたいので、「AMAZON.FreeFormInput」を選択し、想定した定型の返答文を「社内規定に関するご質問にお答えいたします。何かご不明な点がございましたら、お気軽にお尋ねください。」で設定します。

ちなみにですが、Lexのほうで、様々な学習済みの「スロットタイプ」があり、例えば「AMAZON.City」を選択すると、会話中から自動で都市名らしき言葉を認識できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/e800f92d-5d77-6c4a-7fb7-04a4941d6eb9.png)

「フルフィルメント」＞「詳細オプション」でクリックし、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/ddf0e653-cf7f-1d51-c52b-a7435c804a0d.png)

「フルフィルメントに Lambda 関数を使用」にチェックを入れます。
そうすると、ユーザーが質問を完了すると、Lambda関数がトリガーされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/5a762f54-3586-a553-bff3-147cd8bd2eed.png)

ちなみにですが、もし会話の開始時や途中に随時Lambdaをトリガーしたい場合、画面一番下の「初期化と検証に Lambda 関数を使用」にチェックを入れる必要があります。
今回はその想定がないため設定しません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/76595b1e-cd63-ffeb-a760-5f7e371733af.png)

その他設定は今回いらないので、画面右下の「インテントを保存をクリック」して保存したら完了です。

インテント設定画面の一番上にスクロールすると、設定されて会話フローが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/24608586-3ef9-8d74-5f49-7c7aa9327982.png)

# Lambdaと連携
再び「Lex」に戻り、「ボット」＞「ボット名」＞「デプロイ」＞「エイリアス」でクリックし、言語の「Japanese」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/2fce2d05-056d-e2ed-c8e8-46ba30bb72fd.png)

先ほどのLambda関数を設定して、「保存」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/0d64342c-6794-20a2-21fc-c991cf8262bc.png)

再び「インテント」設定の画面に戻り、「構築」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/f81c418c-f220-3ba5-4e55-e9af3f8de2a5.png)

# Lexボットのテスト
構築が完了すると、「インテント」設定の画面の「テスト」をクリックし、「社内規定を知りたい」と入力

すると、会話がスタートされ、スロットで設定した定型文が返されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/f45ab144-5e1a-9551-0107-37142636fa5f.png)

Bedrockのテストと同じく「有給は何日取れる」って質問すると、さっきとほぼ同じの回答が返されました！

裏では「Lex→Lambda→Bedrockナレッジベース」で動いているのがわかりました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/966576be-4100-5121-665d-225fca650130.png)


# Slackアプリの作成＋Lexボット連携
最後は、このLexボットをSlack上にも使いたいので、Slackアプリを作成し、Lexボットと連携します！

この前作ったブログを参照して設定してください。

https://qiita.com/curlneko/items/4c739610a944d32476fc

# Slackの検証
以上の設定が完了したら、SlackのDMでアプリを検索し、DMを投げてみましょう！

このようにSlackのDMでLexボットと会話できるようになりました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/88829b4a-5a62-91d0-a921-12f8bf61b9f6.png)


# 参考サイト
https://qiita.com/minorun365/items/24dfb0ea3afde6ed0a56

https://dev.classmethod.jp/articles/bedrock-knowledge-base-aoss-s3-lambda/

https://dev.classmethod.jp/articles/update-reinvent2023-bedrock-rag/