Slack経由でRAGにコードレビューを依頼するBotを作成 〜 AWS SAM編


## はじめに

https://qiita.com/sakes9/items/3777330b25b236ac2841

前回の記事では、リーダブルコードの原則に従ったコードレビューを自動化できないものか・・と考えた結果、『RAGにリーダブルコードの原則を取り込ませてコードレビューをしてもらおう！！』という検証をしてみました。

![デモ動画.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/e3ca044c-a7bc-3014-c69b-cb06714d0f23.gif)

検証環境の構築は **AWSマネジメントコンソール** を使用していましたが、今回は **AWS SAM** を使用して、より簡単に環境構築する方法の解説を行います。

使用するリポジトリは以下になります。

https://github.com/sakes9/sam-slack-review-rag

## 事前準備

### リージョン切り替え

全ての手順は「**東京リージョン**」で実施することを前提としているため、AWSのマネジメントコンソールからリージョンを「**東京**」に変更してから手順を進めてください。

<img width="300" alt="RAG構築_1" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/16847f76-71f9-61cd-bea8-0a01b9fa32c7.png">

### Cloud9

ローカルマシンの環境を汚さないために、**Cloud9** を使用して環境構築を行います。Cloud9 には、今回の作業に必要な以下のツールが事前にインストールされているため、環境構築がスムーズに行えます。

- AWS CLI
- AWS SAM CLI
- Docker

#### Cloud9環境の作成

Cloud9コンソールにアクセスして、「環境を作成」をクリックします。

<img width="600" alt="Cloud9環境を構築する_1" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/81bf03fb-5395-08d3-2cf2-db438eb4c445.png">

名前は `sam-deploy` として、「新しいEC2インスタンス」を選択します。

<img width="600" alt="Cloud9環境を構築する_2" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/b06cebe4-4b5d-8755-285e-8e0b3bbcb20e.png">

インスタンスタイプは `t2.micro` を選択し、プラットフォームは「Amazon Linux 2023」を選択します。

<img width="600" alt="Cloud9環境を構築する_3" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/a95b1a6f-2d36-d9ec-9029-6dd208282d71.png">

「作成」をクリックします。

<img width="600" alt="Cloud9環境を構築する_4" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/1ca7fcb5-68d6-36f4-7e6d-0e851c15c6fa.png">

作成した環境を選択し「Cloud9 で開く」を選択すると、Cloud9のコンソールが開きます。本手順でコマンドを実行する際は、Cloud9のコンソールで実行してください。

<img width="600" alt="Cloud9環境を構築する_5" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/c4903c54-b8f9-2578-e04b-e62bf36a6094.png">

#### Pythonのインストール

Cloud9では「Python3.9」がデフォルトでインストールされていますが、デプロイするLambdaのランタイムは「Python3.10」にしているため、Cloud9で以下のコマンドを実行して「Python3.10」をインストールして使用できるようにします。

```bash
# 必要な開発ツールをインストール
sudo yum -y install bzip2-devel xz-devel

# pyenv を clone して設定
git clone https://github.com/pyenv/pyenv.git ~/.pyenv
echo 'export PATH="$HOME/.pyenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(pyenv init -)"' >> ~/.bashrc

# .bashrc の変更を適用
source ~/.bashrc

# Python 3.10.14 をインストールして現環境に設定
pyenv install 3.10.14
pyenv global 3.10.14

# python 3.10 の pip が使用されていることを確認
pip --version
# pip 23.0.1 from /home/ec2-user/.pyenv/versions/3.10.14/lib/python3.10/site-packages/pip (python 3.10)
```

#### IAMロールの設定

Cloud9ではAMTC（AWS managed temporary credentials）がデフォルトで有効になっており、AMTCで付与された一時クレデンシャルの権限では **IAMロールやIAMポリシーに対するアクセス権限が制限されている** ため、デプロイ時にエラーが発生してしまいます。

https://cloud5.jp/cloud9_iam/

そのため、Cloud9インスタンスのIAMロールに「**AdministratorAccess**」ポリシーを追加でアタッチすることで、IAMロールやIAMポリシーに対するアクセス権限を付与することにします。

1. Cloud9画面の右上にある「歯車アイコン」をクリックし、「AWS Settings」の「Credentials」から、「AWS managed temporary credentials」を無効にします。

    <img width="600" alt="Cloud9環境を構築する_6" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/f60e5bb8-3898-c86d-7b01-b739ce9b6528.png">

2. EC2コンソールに移動して、Cloud9のインスタンスに紐づくIAMロールを選択します。

    「aws-cloud9-sam-deploy〜」という名前のインスタンスがあるので、そのインスタンスを選択し、「セキュリティ」から「IAMロール」を選択します。

    <img width="600" alt="Cloud9環境を構築する_7" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/f106614a-d32a-f2e3-cfd4-ed70dc4c407a.png">

3. IAMロールにポリシーをアタッチします。

    「許可の追加」から「ポリシーをアタッチ」を選択します。

    <img width="600" alt="Cloud9環境を構築する_8" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/833d3acc-3af0-6710-ae48-d274abc15528.png">

    「AdministratorAccess」ポリシーにチェックを入れて、「許可を追加」をクリックします。

    <img width="600" alt="Cloud9環境を構築する_9" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/f927a798-58b4-d33a-c0d3-063a758c2bb5.png">

### Bedrock

#### モデルアクセスの有効化

東京リージョンでBedrockを使用するために、モデルアクセスを有効化します。

1. Bedrockコンソールに移動して「モデルアクセス」を選択します。

    <img width="600" alt="モデルアクセスの有効化_1" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/d026f95f-1c32-6fd6-4e3f-11aa05b5332d.png">

2. Anthropic社のモデルを使用するには「ユースケースの詳細」を提出する必要があるため、「ユースケースの詳細」をクリックします。

    <img width="300" alt="モデルアクセスの有効化_2" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/70d3973a-786e-d33d-58ca-ffa29c005564.png">

3. ユースケースの詳細を入力して、「送信」をクリックします

    <img width="400" alt="モデルアクセスの有効化_3" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/73c4fc43-2dba-16d6-d5dc-33545dfc3384.png">

4. 「モデルアクセスを管理」をクリックします

    <img width="600" alt="モデルアクセスの有効化_4" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/1706dd1e-ff22-1793-bdff-6d69b1d4b89b.png">

5. 「Claude」にチェックを入れて、「モデルアクセスをリクエスト」をクリックします。

    <img width="600" alt="モデルアクセスの有効化_5" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/7845921d-413b-2963-0db1-dd4a9f2e4a98.png">

6. アクセスが付与されたことを確認します。

    <img width="600" alt="モデルアクセスの有効化_6" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/ae999264-2400-c5c1-be7e-bfb08722db27.png">

### Slack

#### ワークスペースの作成

既存のワークスペースを使用するか、新規でワークスペースを作成してください。
私は `RAG` というワークスペースを作成しています。

#### アプリを作成する

1. [Slack API](https://api.slack.com/) にアクセスして、「Your apps」をクリックします。

    <img width="600" alt="アプリを作成する_1" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/eb7ae6fd-0a8f-a414-9471-226f0b9ab344.png">

2. アプリを作成する

    「Create an App」をクリックします。

    <img width="400" alt="アプリを作成する_2" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/a760cf9d-c52a-9c00-a1d4-04f6a3b20a06.png">

    「From scratch」を選択する

    <img width="400" alt="アプリを作成する_3" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/0840ded2-9ccc-c909-23fd-9449f844d48c.png">

    App Name（アプリ名）は `Reviewer` と入力し、作成したワークスペースを選択し、「Create App」をクリックします。

    <img width="400" alt="アプリを作成する_4" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/6ee20eef-e30a-885f-b9a5-b07761f0401d.png">

3. アプリのBot権限を追加する

    OAuth & Permissions を選択します。

    <img width="600" alt="アプリを作成する_5" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/cec00544-4bb9-7477-6355-cd44c5bde2be.png">

    Scopes の 「Add an OAuth Scope」をクリックします。

    <img width="600" alt="アプリを作成する_6" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/f806a578-87be-ae75-fe49-957ae33a7edb.png">

    メンションメッセージを読み取り、レスポンスを返すための権限を追加する

    - app_mentions:read
    - channels:read
    - chat:write

    <img width="600" alt="アプリを作成する_7" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/3e43d591-f8bb-9bbb-62b5-20c93ddb8cef.png">

4. ワークスペースにアプリをインストールする

    OAuth & Permissions の OAuth Tokens for Your Workspace にある Install to Workspace をクリックします。

    <img width="600" alt="アプリを作成する_8" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/714f64ee-db64-d103-92a8-d9db3cd0f94d.png">

    許可するをクリックします。

    <img width="400" alt="アプリを作成する_9" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/84c5dd7d-ae16-54b7-7457-cdf5ee983f9c.png">

## デプロイ

Cloud9で作業するため、Cloud9コンソールにアクセスして、作成した環境を開きます。

### プロジェクトのクローン

1. リポジトリをクローンする

    ```bash
    git clone https://github.com/sakes9/sam-slack-review-rag.git
    ```

2. リポジトリに移動する

    ```bash
    cd sam-slack-review-rag
    ```

### 環境変数を設定する

Slackの「Bot User OAuth Token」と「Signing Secret」の値を `template.yaml` の **Environment** に設定します。

 1. OAuth & Permissions にある Bot User OAuth Token をメモしておきます。

     <img width="600" alt="環境変数を設定する_1" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/2230b2a9-3409-786a-ce71-5b20e7602478.png">

 2. Basic Information にある Signing Secret をメモしておきます。

    <img width="600" alt="環境変数を設定する_2" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/bba6b22b-3c84-129f-5cf6-d47537813d71.png">

    <img width="600" alt="環境変数を設定する_3" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/0342e322-63e7-0d46-93dd-d6c387e55c6f.png">

 3. `template.yaml` の以下の環境変数にSlackの「Bot User OAuth Token」と「Signing Secret」の値を設定して保存します。

    <img width="600" alt="環境変数を設定する_4" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/60138389-02ba-5cd5-4441-875bdb485a0b.png">

    | 環境変数 | 値 |
    | --- | --- |
    | SLACK_BOT_TOKEN | {Bot User OAuth Token の値} |
    | SLACK_SIGNING_SECRET | {Signing Secret の値} |

    ```yaml
    ReviewReadableCodeFunction:
        Type: AWS::Serverless::Function
        Properties:
        FunctionName: "review-readable-code-function"
        ・・・
        Environment:
            Variables:
            ・・・
            SLACK_BOT_TOKEN: "xoxb-XXXXXXXXXXXXX" # Slack Bot Token
            SLACK_SIGNING_SECRET: "XXXXXXXXXXXXX" # Slack Signing Secret
        ・・・
    ```

### ビルド〜デプロイ

1. ビルドします

    ```bash
    sam build
    ```

2. デプロイします

    S3バケット名はリージョン内で一意である必要があるため、データソース格納バケット名は `DataSourceBucketName` パラメータに **一意の名前** を指定してデプロイする。

    ```bash
    sam deploy --region ap-northeast-1 \
        --parameter-overrides DataSourceBucketName="readable-code-data-source-{ランダムな文字列}"
    ```

### Kendraのデータソースを同期します

1. data-sourceフォルダ内のPDFファイルをS3にアップロードします。

    Cloud9で以下のコマンドを実行し、データソース格納バケットにPDFファイルをアップロードします。

    ```bash
    aws s3 cp data-source/[第Ⅰ部][2章]表面上の改善_名前に情報を詰め込む.pdf s3://{データソース格納バケット名}
    ```

2. Kendraのデータソースを同期する

    1. Kendraコンソールで `readable-code-index` インデックスを選択します。
    2. データソースから `readable-code-data-source` を選択します。
    3. 「Sync now」をクリックして、データソースを同期します。

        <img width="600" alt="データソースの作成_1" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/faa8b858-1a10-da36-3c5a-0cfaf440eaff.png">

        同期完了まで時間がかかるため、しばらく待ちます。

        <img width="600" alt="データソースの作成_2" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/b31eac77-7383-e1f1-4f7e-f52915af5d9e.png">

        同期が完了しました。

        <img width="600" alt="データソースの作成_3" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/2bb5e495-4121-11c4-fa52-e5126c726cb0.png">

### メンション時のイベントを設定する

1. Slack API から「Event Subscriptions」を選択して、Enable Events を ON にします。

    <img width="600" alt="メンション時のイベントを設定する_1" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/28f032cd-41ca-23f4-7b7c-d67f2429c916.png">

2. Lambdaの関数URLをコピーします。

    <img width="600" alt="メンション時のイベントを設定する_2" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/67eaa195-4d97-9cba-789e-0ee675ba9761.png">

3. Request URL にコピーした関数URLを貼り付けます。

    一度目の検証で失敗するが、Retryしたら2回目で成功する（理由はわからない）

    <img width="600" alt="メンション時のイベントを設定する_3" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/cc1a2cd0-52f7-c351-0f00-82cb49a6f67e.png">

      検証に成功すると Verified にチェックがつきます。

    <img width="600" alt="メンション時のイベントを設定する_4" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/b4898f0c-8440-52be-21f1-510b3467c7fc.png">

4. 「Subscribe to bot events」の Add Bot User Event をクリックします。

    <img width="600" alt="メンション時のイベントを設定する_5" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/ff3877eb-906b-e997-c195-cd9300d1972f.png">

5. 「app_mention」を追加します。

    <img width="600" alt="メンション時のイベントを設定する_6" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/cebaf0da-98e0-0f41-f9ad-e8952ccc1d30.png">

6. 「Save Changes」をクリックします。

    <img width="600" alt="メンション時のイベントを設定する_7" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/204ec90d-14d8-d3dd-eaa1-78eb835ee142.png">

## 動作検証

### コードレビューの提出

1. [Slack](https://slack.com/) にアクセスします
2. Slack App をインストールしたワークスペースを開きます
3. チャンネルを作成します
   私は `review` というプライベートチャンネルを作成しました。

   <img width="600" alt="動作検証_1" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/b6ceac87-1d83-bfb2-5a16-e9a69e54fff2.png">

4. Slack Appを招待します

    `@Reviewer` をメンションして、Slack App を招待します。

   <img width="600" alt="動作検証_2" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/979cd764-bed3-e2fd-2fde-400e5fb32457.png">

    「招待する」をクリックします。

   <img width="600" alt="動作検証_3" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/64e434fa-72f6-4650-cf24-bc6d16356fb1.png">

    Slack App がチャンネルに招待されました。

   <img width="400" alt="動作検証_4" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/dac31551-8bf5-dc85-0b7f-509307a1aedc.png">

    招待時の初回メッセージに対するレスポンスは無視してください。

   <img width="400" alt="動作検証_5" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/37100b4a-bd8b-e874-ab6f-81b148106181.png">

5. レビュー依頼を出します

    `@Reviewer` をメンションして、コードをレビューしてもらうために、以下のメッセージを送信すると、レビューが開始されます。

    ```text
    @Reviewer
    for (var i = 0; i < clubs.length; i++) {
        for (var j = 0; j < clubs[i].members.length; j++) {
            for (var k = 0; k < clubs[i].members[j].users.length; k++) {
                if (clubs[i].members[j].users[k].id === userId) {
                    console.log(`${clubs[i].members[j].users[k].name} が所属しているクラブは ${clubs[i].name}`);
                }
            }
        }
    }
    ```

   <img width="400" alt="動作検証_6" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/085d311b-b0ff-4413-8bed-cc64eb0758ef.png">

6. レビュー結果が返却されます

   <img width="600" alt="動作検証_7" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/3344bd81-1721-590d-184e-c9af76a4a7c4.png">

   <img width="600" alt="動作検証_8" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087076/c84d4822-8f39-d3ba-83c6-4e1d77de6756.png">

## スタック削除

:::note warn
注意
動作検証後は、必ずリソースの削除を行ってください。
リソースが残っていると課金が発生します。
特にKendraは高額な課金が発生するため、削除を忘れないようにしてください。
:::

スタックを削除することで、スタックに関連するリソースが削除されます。
以下の手順を実行してスタックを削除します。

1. S3バケットの中身を空にする

    S3バケットはオブジェクトが存在すると削除できないため、以下のコマンドを実行してスタック削除前にバケットの中身を空にしておく。

    ```bash
    aws s3 rm s3://{データソース格納バケット名} --recursive
    ```

2. スタック削除

    以下のコマンドを実行することで、デプロイしたスタックを削除できます。

    ```bash
    sam delete --region ap-northeast-1
    ```

3. Cloud9環境の削除

    Cloud9コンソールにアクセスして、作成したCloud9環境とIAMロール（AWSCloud9SSMAccessRole）を削除します。

## まとめ

**AWS SAM** を使用して、Slack経由でコードレビューを依頼するRAG環境を構築する方法について解説しました。[前回の記事](https://qiita.com/sakes9/items/3777330b25b236ac2841)ではAWSマネジメントコンソールから環境を構築していましたが、手動作業には **ヒューマンエラーのリスク** が伴い、またAWSリソースが テンプレート化されていない（コード化されていない） ため、作成した環境の **再現性が低くなる** といったデメリットがあります。
AWS SAM を利用することで、サーバーレスアプリケーションの構築が簡単になり、異なる環境への迅速なデプロイが可能になります。