RAGアプリをAWS環境で構築した

背景や目的


1.生成AIへの期待は益々高まっていますが、一般のITシステムでは企業独自の情報を回答させる事が重要な観点だと感じます。
2.別途実施した独自モデル作成実験で、追加学習では誤情報の排除は無理!と考えたその後に、RAG(検索拡張生成)の話題が増え、それを試した次第です。実際にシステムにする時の知見にしたく、AWSで作成してみました。
3.ここでは、その構築方法をまとめました。AWSで組んでみましたが、デプロイ方法の1つの例として、生成AI関連のエンジニアの方々に、ご参考になれば幸いです。また、より良い方法等のご意見も頂けると嬉しいです。

RAGアプリの構成


以下のような処理にしました。
1.LLMは、総合的に最も注目度が高いChatGPTを利用。
2.検索部はAWS KendraとOpenAI Embeddingsの2つを試す。
3.デプロイ環境はAWS Lambdaを利用(サーバレス環境を試す)。

RAGアプリ環境設定サマリー


上の処理を実現する環境設定は以下の通り。
この中で、外部データの設置も実施している。
1.Amazon S3 バケット作成とファイルアップ
2.Lambdaでレイヤーを追加 (open ai、numpyのimport用)
3.VPCの設定 (EFS、外部インターネットアクセス、S3アクセスを可能にする基礎設定)
4.EFSの設定 (sklearnのimport用)
5.Lambdaの設定 (VPCとEFS利用に関わる事)
6.LambdaでS3参照するための追加設定
7.Amazon Kendra インデックス作成、データソース作成、sync、削除
8.RAGアプリの最終構成

Amazon S3 バケット作成とファイルアップ(検索対象ファイル設置用)


検索対象ファイルをS3に設置します。

1.検索窓に”S3″を入力し「S3」を押下。Amazon S3の画面に遷移。
2.メニュー「バケット」で「バケットを作成」を押す。

3.「バケットを作成」画面では以下設定値を登録する。

Kendra用データの設定値
バケットタイプ汎用バケット
バケット名tsh-kendra1
オブジェクト所有者 ACL無効(デフォルト)
embeddings用データの設定値
バケットタイプ汎用バケット
バケット名tsh-embeddings1
オブジェクト所有者 ACL無効(デフォルト)
Kendra用データの設定値
パブリックアクセスパブリックアクセスをすべてブロック(デフォルト)
バージョニング無効にする(デフォルト)
embeddings用データの設定値
パブリックアクセスパブリックアクセスをすべてブロック(デフォルト)
バージョニング無効にする(デフォルト)
Kendra用データの設定値
タグ (Key)Name,(Value)tsh-kendra
暗号化タイプAmazon S3 マネージドキー使用・・ (SSE-S3)(デフォルト)
バケットキー有効にする。(デフォルト)
ストレージクラススタンダード
アップするファイル学習済QA.pdf
操作説明書.pdf
embeddings用データの設定値
タグ(Key)Name,(Value)tsh-embeddings
暗号化タイプAmazon S3 マネージドキー使用・・ (SSE-S3)(デフォルト)
バケットキー有効にする。(デフォルト)
ストレージクラススタンダード
アップするファイルembeddings_data.json

Lambdaでレイヤーを追加 (OpenAI、numpyのimport用)


OpenAIは、テキスト生成等で、numpyはコサイン類似度の計算で、それぞれ使いました。Lambdaでimportするために、レイヤーを追加する必要があります。

1.検索窓に”Lambda”を入力し「Lambda」を押下。Lambdaの画面に遷移。
2.上部の「Cloud Shell」アイコンを押し、シェルを開き、以下コマンドでzipファイルを2つ作成。

AWS Cloudshell
//コマンド:
[cloudshell-user@** ~]$pip install -t ./python/ numpy
[cloudshell-user@** ~]$zip -r numpy.zip ./python
[cloudshell-user@** ~]$rm -r ./python/
[cloudshell-user@** ~]$pip install -t ./python/ --upgrade openai 
[cloudshell-user@** ~]$zip -r openai.zip python
[cloudshell-user@** ~]$rm -r python

3.作成されたファイルは手元にダウンロードしておく

4.Lambda画面の「レイヤー」メニューを開き、「レイヤーの作成」を押す。

5.その作成画面内で以下の値を入力し、作成を完了させる(2つ作成)。

作成1:設定値(openai)
名前openai
ファイル.zip ファイルをアップロードにチェックし、openai.zipをアップロード
レイヤ作成)レイヤー互換アーキテクチャx86_64
レイヤ作成)互換性のあるランタイムPython3.9
作成2:設定値(numpy)
名前 numpy
ファイル.zip ファイルをアップロードにチェックし、numpy.zipをアップロード
レイヤ作成)レイヤー互換アーキテクチャx86_64
レイヤ作成)互換性のあるランタイムPython3.9

6.Lambda>関数>今回作成関数 を選択し、「レイヤーの追加」を押す。

7.その画面で以下の値を入力し、作成を完了させる(2つ追加)。

レイヤーを選択設定
レイヤーソースカスタムレイヤー
カスタムレイヤー上で作成したレイヤー(openai,numpy) を選択

VPCの設定(EFS、外部インターネットアクセス、S3アクセスを可能にする基礎設定)


コサイン類似度の計算用にsklearnを使いたく、一部EC2を利用した事もあり、LambdaはVPC上で使っています。

1.検索窓に”VPC”を入力し「VPC」を押下。VPC画面に遷移。
2.「サブネット」メニューで「サブネットを作成」を押し、以下の設定値にて、サブネットを2件作成する。

設定値(public-lambda)
サブネット名public-lambda
IPv4 subnet CIDR block172.31.48.0/20
設定値(private-lambda)
サブネット名private-lambda
IPv4 subnet CIDR block172.31.64.0/20

3.続けて、VPC画面の「インターネットゲートウェイ」メニューを開く。
4.ここでは1件作成済である事を確認する。(無い場合は作成する(説明省略))。

5.続けて、VPC画面の「NATゲートウェイ」メニューを開く。
6.開いた画面で、以下の設定値にて、NATゲートウェイを作成する。

設定値(NATゲートウェイ)
名前タグATD-lambda-NATGW
サブネットpublic-lambda (先ほどpublic用に作成したもの)
接続タイプパブリック
ElasticIP割当ID52.**.**.**(「ElasticIPを割り当て」を押し、出てきた値)

7.続けて、VPC画面の「ルートテーブル」メニューを開き、「ルートテーブルを作成」を押す。

8.開いた画面で、以下の設定値にて、ルートテーブルを2件作成する。

設定値(public-route-table)
名前public-route-table
VPCvpc-******(既存のVPCを選択。今回は既存のVPCを使用する。)
ルートを編集/ルートを追加/送信先0.0.0.0/0
ルートを編集/ルートを追加/ターゲットigw-******(先ほど確認した既存のインターネットゲートウェイ)
サブネットの関連付けを編集public-lambda (先ほど作成したサブネットを関連付けする)
設定値(private-route-table)
名前private-route-table
VPCvpc-******(public-route-tableと同じ)
ルートを編集/ルートを追加/送信先0.0.0.0/0
ルートを編集/ルートを追加/ターゲットnat-******(先ほど作成したNATゲートウェイ)
サブネットの関連付けを編集private-lambda (先ほど作成したサブネットを関連付けする)

EFSの設定 (sklearnのimport用)


sklearnは、検索用(ベクター値比較用)のコサイン類似度の計算で使いたく、セットアップしました。設置場所で使っているEC2は、既に存在している前提で説明しております(ご容赦下さい)。

1.検索窓に”Lambda”を入力し「Lambda」を押下。Lambdaの画面に遷移。
2.上部の「Cloud Shell」アイコンを押し、シェルを開き、以下コマンドでzipファイルを1つ作成。

AWS Cloudshell
//コマンド:
pip3 install -t ./python/ -U scikit-learn
zip -r sklearn_aws_all.zip python
rm -r ./python/

3.作成されたファイルは手元にダウンロードしておく。

4.AWSのEC2画面の「セキュリティグループ」メニューを開き、「セキュリティグループの作成」を押し、「セキュリティグループを作成」を開く。

5.その作成画面内で以下の値を入力し、作成を完了させる。

設定値
セキュリティーグループ名security-efs
インバウンドルール/タイプNFS
ソースsg-****** (今回使うEC2インスタンスのセキュリティグループ ID)
sg-****** (Lambda側のセキュリティグループ ID)
アウトバウンドルール(そのまま)
タグ(Key)Name, (Value)security-efs

6.AWSのEFS画面の「ファイルシステム」メニューを開き、「ファイルシステムの作成」を押す。

7.その作成画面内で以下の値を入力し、作成を完了させる。

設定値
名前efs-for-lambda
Virtual Private Cloud (VPC)(そのまま。EC2と同じVPCのまま)
ネットワーク/セキュリティーグループEC2のサブネット(172.31.32.0/20)と
Lambdaのサブネット(172.31.64.0/20)の2つを指定
セキュリティグループは、上で作成したsecurity-efsを設定
アクセスポイント/名前accesspoint1
アクセスポイント/(それ以外) (全てデフォルト)

8. 作成したファイルシステム画面で「アタッチ」ボタンをおす。EFSアタッチコマンドのコピーボタンを押し、コピーする。

9.AWSのEC2画面の「インスタンス」メニューを開き、今回利用するインスタンスに接続する。(※EC2インスタンスは既に作成済の前提です。)

10.そのコンソールで以下コマンドにて作成と確認をする。

AWS Cloudshell
//コマンド:
$sudo su ec2-user
[ec2-user@** ~]$ sudo yum install -y amazon-efs-utils
[ec2-user@** ~]$ cd /home/ec2-user/
[ec2-user@** ~]$sudo mkdir /home/ec2-user/efs
[ec2-user@** ~]$ sudo mount -t efs -o tls fs-******:/ /home/ec2-user/efs
 ★前の手順「アタッチ」でコピーしたコマンド
[ec2-user@** ~]$ df -T
Filesystem     Type            1K-blocks     Used        Available Use% Mounted on
devtmpfs       devtmpfs          4013952        0          4013952   0% /dev
tmpfs          tmpfs             4031812        4          4031808   1% /dev/shm
tmpfs          tmpfs             4031812      524          4031288   1% /run
tmpfs          tmpfs             4031812        0          4031812   0% /sys/fs/cgroup
/dev/nvme0n1p1 xfs              16764908 12746084          4018824  77% /
tmpfs          tmpfs              806364        0           806364   0% /run/user/0
127.0.0.1:/    nfs4     9007199254739968        0 9007199254739968   0% /home/ec2-user/efs
ec2-user@** efs]$ ls -la
total 8
drwxr-xr-x  2 root     root     6144 Dec 26 11:25 .
drwx------ 14 ec2-user ec2-user 4096 Dec 26 12:13 ..
[ec2-user@** efs]$ sudo chmod go+rw .
[ec2-user@** efs]$ ls -la
total 8
drwxrwxrwx  2 root     root     6144 Dec 26 11:25 .
drwx------ 14 ec2-user ec2-user 4096 Dec 26 12:13 ..

11.作成済のsklearn_aws_all.zipを前の手順で作成したディレクトリへアップする。

12.引き続きコンソールで以下コマンドを実行し、sklearnをセットする。

AWS Cloudshell
//コマンド:
[ec2-user@** efs]$ unzip ./sklearn_aws_all.zip
[ec2-user@** efs]$ ls
python  sklearn_aws_all.zip
[ec2-user@** efs]$ rm ./sklearn_aws_all.zip
[ec2-user@** efs]$ ls
python
[ec2-user@** efs]$ mv ./python/ ./sklearn/
[ec2-user@** efs]$ ls
sklearn     *******

Lambdaの設定 (VPCとEFS利用に関わる事)   


これまでに作成したVPC上でLambdaが動作し、またEFSと接続されるように、Lambdaの設定をします。

1.検索窓に”Lambda”を入力し「Lambda」を押下。Lambdaの画面に遷移。

2.「関数」メニューで今回作成した関数を選択し、「設定>VPC」を選択後、「編集」を押下し、以下の値で設定する。

設定値
VPCvpc-******
(今回EFSで参照するEC2インスタンスが属するVPCをセット)
サブネットprivate-lambda(172.31.64.0/20)の1つだけ設定(重要)
セキュリティーグループdefault VPC security group
(EFS側設定と一致させる点に注意)

3.続けて、lambda画面の「設定>アクセス権限」を押し、設定済のロール名(事前に設定済の想定です)のリンクをクリックする。

4.IAM画面が開くので、そこで「許可を追加>ポリシーをアタッチ」を押し、以下のポリシーを追加する。

設定値
追加ポリシーAWSLambdaVPCAccessExecutionRole

5.Lambda画面に戻り、「設定>ファイルシステム」を選択後、「ファイルシステムの追加」を押下し、以下の値で設定する。

設定値
EFSファイルシステムefs-for-lambda
アクセスポイントaccesspoint1
ローカルマウントパス /mnt/efs

6.同様に、「設定>環境変数」を選択し、「編集」を押下し、  以下の値を設定する。

設定値
キーLD_LIBRARY_PATH
/lib64:/usr/lib64:$LAMBDA_RUNTIME_DIR:$LAMBDA_RUNTIME_DIR/lib:$LAMBDA_TASK_ROOT:$LAMBDA_TASK_ROOT/lib:/opt/lib:/mnt/efs

LambdaでS3参照するための追加設定


VPCを使う事も考慮し、接続可能な状態にします。

1.検索窓に”Lambda”を入力し「Lambda」を押下。Lambdaの画面に遷移。

2.「関数」メニューで今回作成した関数を選択し、「設定>アクセス権限」を押し、設定済のロール名のリンクをクリックする。

3.IAM画面が開くので、そこで「許可を追加>ポリシーをアタッチ」を押し、以下のポリシーを追加する。

設定値
追加ポリシーAmazonS3ReadOnlyAccess

4.続けて、VPC画面を開き、「エンドポイント」メニューを開き、「エンドポイントを作成」を押す。

5.その画面では、以下の設定値を入力し、エンドポイントを1つ作成する。

設定値
名前タグatd-s3
サービスカテゴリAWSサービス(デフォルト)
サービスcom.amazonaws.ap-northeast-1.s3 (タイプ=Gatewayの方)
VPCvpc-******(今回全体で使用しているvpc)
ルートテーブルprivate-route-table(private-lambda(172.31.64.0/20)の制御用テーブル)

Amazon Kendraインデックス作成、データソース作成、sync、削除


検索方法AパターンのKendraを準備します。インデックス(DBみたいなもの)を作成すれば、完了です。余談ですが、課金が気になり、作成と削除を何回か繰り返しました。

1.検索窓に“kendra”を入力し「Amazon Kendra」を押下、Amazon Kendraの画面に遷移。

2.Amazon Kendraの画面に「create an index」を押す。

3.作成画面内で以下の値を入力し、作成を完了させる。

設定値
Index nameaitest1
IAM rolecreate a new role -> aitest1(2回目以降は、AmazonKendra-ap-northeast-1-aitest1を選択、で大丈夫)
Encryptionそのまま
タグ (Key)Name, (Value)kendra-aitest1
Access control settingsNo(デフォルト)
User-group expansionNone(デフォルト)
Provisioning editionsDeveloper edition(デフォルト)
index作成(create押下~完了)30分かかったり、1分位で終わる事もある。
(Add data sources押下後)
Data sources
Amazon S3 connector を選択
Data source nameaitest1-s3-test1
Default languageJapanese
(Data sourceの)IAM rolecreate a new role -> aitest(n)
  (★(n)は今まで未使用の数値。2回目以降も、必ず新規作成する。しないとsyncでエラー発生(原因不明))
Data-source/VPC vpc-****** (既存のVPCを選択。)
Data-source/VPC/subnetsubnet-****** (172.31.64.0/20) | ap-northeast-1c, private-lambda  (lambda/VPC設定値と同一)
Data-source/VPC/VPC security groupssg-****** (lambda/VPCの設定値と同じ)
Enter the data source locations3://tsh-kendra1
Sync modeNew, modified, or deleted content sync
Sync run scheduleRun on demand
S3 field mappingそのまま
datasource作成(Add data source押下~完了)1分位で終わる。
Syncする(Sync now押下~完了)15分~20分位、かかっていた。

Lambdaの方で、KendraにアクセスできるようにIAMポリシーを追加しておきます。

1.検索窓に”Lambda”を入力し「Lambda」を押下。Lambdaの画面に遷移。

2.「関数」メニューで今回作成した関数を選択し、「設定>アクセス権限」を押し、設定済のロール名のリンクをクリックする。

3.IAM画面が開くので、そこで「許可を追加>ポリシーをアタッチ」を押し、以下のポリシーを追加する。

設定値
追加ポリシーAmazonKendraFullAccess

RAGアプリの最終構成


  最終的な構成は以下の通り。

以下は、実行してわかった事です。
1.EFS利用は、コサイン類似度の計算をするための苦肉の策
Lambdaでは、必要なライブラリのインストール時に「レイヤー」を利用しますが、50MB制限の問題で、EFSを利用しました。初回アクセス時に、ロード(おそらくEFS経由のsklearn)で1分位かかるので、正直あまり良くないです。(動作はします。)
2.VPCを使い対応箇所が増えるも良い経験に
EFSを使う都合でLambdaをVPC内で動作させる必要が生じ、設定箇所がとても増えました。ただ、そのおかげで、色々勉強になりました。
3.Kendraは課金額に注意
インデックスが存在するだけで、1時間あたり約170円(2024/6/18時点)かかるので、終業時に毎回削除していました。うっかり放置し、想像以上の額にならないよう、お使いになる際には、ご注意ください。最新の価格はこちらをご覧ください。

画面


ちなみに画面はこんな感じです。

まとめ


1.RAGシステムをサーバレス環境で構築でき、良い勉強になりました。

2.コサイン類似度の計算をするために苦労した点は、想定外でしたが、この辺りは、色んなツールが出ているようなので、今後試してみたいと思います。

3.テキスト生成と検索部分で外部連携がありますが、そこは概ね想像通りの動作でした(精度は別として)。

4.精度としては、ベクター検索が良さそうでした。精度の詳細(確認実験)は、こちらでやっていますので、どうぞご参照ください。

ご連絡フォーム


フィードバックを是非お願いします。
本記事の方法での問題点や、よりよい方法のアイデアを頂けると大変助かります。

この記事に関して

その他のご連絡

DevAIsをもっと見る

今すぐ購読し、続きを読んで、すべてのアーカイブにアクセスしましょう。

続きを読む