Amazon Bedrock ナレッジベースを利用して、簡易的なRAGを作ってみた

# はじめに
ナレッジベースを利用して、簡単にRAGを構築できました。
実装の流れをご紹介します。

ナレッジベースに関するAWSのドキュメントは以下です。
https://docs.aws.amazon.com/bedrock/latest/userguide/knowledge-base.html

### 受講したUdemyの講座
https://www.udemy.com/course/amazon-bedrock-aws-generative-ai-beginner-to-advanced/?srsltid=AfmBOoqvqRPw-UfYYKlepb3BgkaTu5onJOzzjRt5-6RtOKxotJ0N81Gb&couponCode=ST21MT121624

Bedrockを使ってみたいと思って、ハンズオンを含むUdemyの講座を受講しました。
当講座の難易度は高くないと思います。AWS、Python、英語いずれも初心者の私でも楽しみながら取り組めました。

### 実装するシステム構成
:::note warn
今回構築する構成では、Amazon OpenSearch Serviceを使っている間、1時間ごとに料金が発生します。
記事の最後にコレクションの削除手順を紹介します。
:::
上記のUdemy講座に含まれるユースケースを基に、簡易的なRAGアプリケーションを実装します。
ベクトルDBにはBedrockのユーザーガイドを格納して、Bedrockについて回答してくれるアプリを実装します。
～Use Case 5 : Serverless e-Learning App using Bedrock Knowledge Base + Claude FM + AWS Lambda + API Gateway～
![simpleRAG_architect.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445312/572cd4e7-0caf-4c33-d128-647ae14ba7c7.png)

# 大まかな実装の流れ
* データストアを用意する（S3）
* ナレッジベースを作成する（Bedrock）
* Lambda関数を書く
* APIを作る
## データストアを用意する
1. S3バケットを作成します
    * リージョンは「バージニア北部」にしてみました。東京リージョンは使えるモデルが少ないので。
    * 設定はすべてデフォルトで問題ないと思います

2. Bedrockのユーザーガイド（PDF）を一度、ダウンロードします
https://docs.aws.amazon.com/bedrock/?icmpid=docs_homepage_ml
![S3source.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445312/9ea44f48-877a-7028-3fa2-ee27d72287de.png)

3. 上記のファイルを1.で作成したバケットにアップロードします

## ナレッジベースを作成する
1. Bedrockのページを開く
1. 左のメニュー最下部の、Bedrock configurations の下にある「モデルアクセス」をクリックする
1. 「すべてのモデルを有効にする」か「特定のモデルを有効にする」ボタンをクリックして、使用したいモデルのアクセスをリクエストする
![modelacsess.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445312/8765aad5-05d3-9d7e-5289-d3230ad24c8e.png)

2. 左のメニュー中段の、オーケストレーションの下にある「ナレッジベース」をクリックする
2. 「ナレッジベースを作成」というオレンジ色のボタンをクリックして、「Knowledge Base with vector store」を選択する
![create-knowledgebase.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445312/b741bb28-ad2f-435f-5f45-3f0fb9e00718.png)

4. ナレッジベースの詳細を入力
    * IAMロールは「新しいサービスロールを作成して使用」
    * Data source details は「Amazon S3」
    * 「次へ」をクリック

5. データソースを設定
    * 作成したS3を設定する
    * チャンキング戦略は「デフォルトチャンキング」で問題ないと思います
    * 「次へ」をクリック

6. 埋め込みモデルを選択し、ベクトルストアを設定する
    * 埋め込みモデルは「Titan Text Embeddings v2」にしてみました
    * ベクトルデータベースは「新しいベクトルストアをクイック作成 - 推奨」
    * 「次へ」をクリック

7. 設定内容を確認して、問題がなければ「ナレッジベースを作成」をクリック
    * ベクトルデータベースの作成完了まで、数分待ちます

8. ナレッジベースが作成完了したら、データソースを選択して同期
![datasourcesync.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445312/8bbe9916-2557-1c9a-27af-6574b0235249.png)
    * 同期完了まで数分待ちます

9. 右側のウィンドウでコンソール上からテストしてみます
    * モデルを選択。「Claude 3 Sonnet v1」にしてみました
    * テストしてみます
![testmodel.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445312/d27366d9-67c8-9ab3-2a16-b6102fa36efa.png)
動きました！

## Lambda関数を書く
1. 関数の作成
    * リージョンは「バージニア北部」
    * ランタイムは「Python 3.13」を選択

2. タイムアウト設定を伸ばしておく
    * Bedrockの応答は多少時間がかかるため、デフォルトの3秒のままだとタイムアウトになってしまう
![timeout.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445312/1a8ffcc9-3364-8f01-70b3-c497a6c029ad.png)

3. アクセス権限の編集
    * 対象のロール名をクリックすると、IAMのページが開きます
![role.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445312/ac6718b2-a8c5-788d-c0a4-764083cba3d1.png)
    * 「AmazonBedrockFullAccess」ポリシーをアタッチします
![bedrockfullacess.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445312/0b743bb6-c741-427d-21bf-e005fee9cdae.png)

4. コードを書きます
    サンプルコードは以下です。
    ```Python:lambda_function.py
    import json
    #1 import boto3
    import boto3
    #2 create client connection with bedrock
    client_bedrock_knowledgebase = boto3.client('bedrock-agent-runtime')
    
    def lambda_handler(event, context):
        #3 store the user prompt
        print(event['prompt'])
        user_prompt=event['prompt']
        #4 use retrieve and generate API
        client_knowledgebase = client_bedrock_knowledgebase.retrieve_and_generate(
            input={
                "text": user_prompt
                },
            retrieveAndGenerateConfiguration={
                "type": 'KNOWLEDGE_BASE',
                "knowledgeBaseConfiguration": {
                    "knowledgeBaseId": 'KNOWLEDGEBASE_ID',
                    "modelArn": 'model_arn'
                }
            }
        )
        response_kbase_final=client_knowledgebase['output']['text']
        return {
            'statusCode': 200,
            'body': response_kbase_final
        }
    ```
    ポイントをお伝えします。
    * (#2) clientを定義します。詳細は[こちら](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/bedrock-agent-runtime.html "AgentsforBedrockRuntime")に。
    * (#4) retrieve_and_generateを使用します。RAGにおけるretrieveとgenerateの両方をまとめて実行してくれるAPIです。詳細は[こちら](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/bedrock-agent-runtime/client/retrieve_and_generate.html "retrieve_and_generate")。
        * サンプルコードでは最低限必要なフィールドを埋めてあります
        * knowledgeBaseIdはナレッジベースの概要からコピペしてください
        * modelArnはBedrockのModel catalog からモデルを選んで、Model IDを指定してください。例えば、Anthropic の Claude 3 Sonnet なら"anthropic.claude-3-sonnet-20240229-v1:0"です
    ![modelcatalog.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445312/0d68acf0-2af9-bc09-611e-9d4d2034275a.png)

1. テストしてみます
    ```
    {
      "prompt": "What is the retrieve_and_generate API?"
    }
    ```
    ```
    Response:
    {
      "statusCode": 200,
      "body": "The RetrieveAndGenerate API is an operation in Amazon Bedrock that combines several steps to query a knowledge base and generate responses based on the retrieved information. It internally uses the GenerateQuery operation (if the knowledge base is connected to a structured data store), the Retrieve operation to retrieve relevant information from the data sources, and the InvokeModel operation to generate a response based on the retrieved information. The RetrieveAndGenerate API allows you to configure the retrieval and generation process by specifying details such as the type of data source (knowledge base or external sources), external source configuration, and knowledge base configuration. It returns the generated response text as output."
    }
    ```
    問題なさそうです！

## APIを作る
1. API Gatewayのページを開く
1. 「APIを作成」をクリック
    * REST APIの「構築」をクリック
    * 設定はデフォルトのままで問題ないです。「APIを作成」をクリック
1. 「メソッドを作成」をクリック
    * メソッドタイプは「GET」
    * Lambda関数に今回作成したものを指定します
    * 「メソッドを作成」をクリック
1. メソッドリクエストの設定から「編集」をクリック
    * リクエストバリデーターを「本文、クエリ文字列パラメータ、およびヘッダーを検証」に変更
    * ▶URL クエリ文字列パラメータ を展開して、「クエリ文字列を追加」をクリック
    * 名前を`prompt`として「保存」をクリック
1. 統合リクエストの設定から「編集」をクリック
    * ▶マッピングテンプレート を展開して、「マッピングテンプレートの追加」をクリック
    *  コンテンツタイプを`application/json`にします
    *  テンプレート本文を以下の内容にして、「保存」をクリック
    ```
    {
    "prompt":"$input.params('prompt')"
    }
    ```
1. 「APIをデプロイ」をクリックして、「新しいステージ」を選択し`dev`という名前にして、「デプロイ」をクリック
1. コンソールからテストしてみます
    ![test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445312/b60094bd-57a1-da35-a337-b48872378ee2.png)

```
レスポンス本文
{"statusCode": 200, "body": "You can use the following embedding models in Amazon Bedrock Knowledge Bases:\n\n- Amazon Titan Embeddings G1 - Text (supports floating-point vectors)\n- Amazon Titan Text Embeddings V2 (supports floating-point and binary vectors)\n- Cohere Embed (English) (supports floating-point and binary vectors)\n- Cohere Embed (Multilingual) (supports floating-point and binary vectors)"}
``` 
成功です！🎉

## ナレッジベースとOpenSearch Serviceコレクションの削除
Amazon OpenSearch Serviceを使っている間は1時間ごとに料金がかかります。
削除手順を紹介します。

### ナレッジベースの削除
Bedrockのページから、該当のナレッジベースのを選択して削除します。
    ![knowledgebasetestdelede.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445312/7050f78d-00de-9707-ec22-032f1a391fde.png)

### OpenSearch Serviceコレクションの削除
Amazon OpenSearch Serviceのページを開いて、サーバレスのダッシュボードから該当のコレクションを選択して削除します。
    ![delete.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445312/4bbc661b-3dfc-1627-da1e-c1c23b93c3c6.png)


# まとめ
以上、Amazon Bedrock ナレッジベースを利用して、簡易的なRAGを構築する手順をご紹介いたしました。
![simpleRAG_architect.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/445312/572cd4e7-0caf-4c33-d128-647ae14ba7c7.png)

この構成だととってもお手軽にRAGを構築できる反面、OpenSearch Service を使用している間、料金がかかり続けてしまいます。
ご紹介したUdemyの講座では、自分でベクトルDBへの埋め込みや回答生成を実装するユースケースのハンズオンも含まれています。もし興味を持たれた方は、試してみてください！