【Amazon Bedrock × Streamlit】社内ガイドラインを利用したチャットボットアプリ開発

## はじめに

社会人1年目のAWSのサービスを何も知らない状態から、Amazon BedrockとStreamlitを用いてチャットボットアプリを開発してみました。簡易なチャットボットアプリをAWSで開発してみたい方、Streamlitに興味のある方におすすめです。この記事を読むことで、開発の大まかな流れを理解することができます。
※詳細な手順書を記載しているわけではないので、その点はご留意ください。
※誤っている記述などございましたらお気軽にコメントお待ちしております。
※エラー解決や行き詰った点に関しては別途記事にしていく予定です。

## 開発環境

- Windows 11
- Python 3.12.3
- streamlit 1.39.0
- boto3 1.35.41
- langchain 0.3.3
- langchain-aws 0.2.2
- langchain-chroma 0.1.4
- langchain-community 0.3.2
- langchain-core 0.3.11
- langchain-openai 0.2.2

## アーキテクチャ図

今回開発したアーキテクチャ図の全体像は以下の図の通りです。

![アーキテクチャ図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3987594/23ea3254-ff8b-f26d-65d3-835c445d81d2.png)



補足：セキュリティーグループとルートテーブルの通信設定に関して

セキュリティーグループ：
1. Security Group A 
インバウンドルール（タイプ/プロトコル/ポート範囲/ソース）
   カスタムTCP/TCP/8501/Security Group B
   HTTPS/TCP/443/Security Group C
アウトバウンドルール（タイプ/プロトコル/ポート範囲/送信先）
   すべてのトラフィック/すべて/すべて/0.0.0.0/0

2. Security Group B：
インバウンドルール（タイプ/プロトコル/ポート範囲/ソース）
   HTTPS/TCP/443/自宅のIPアドレス（例）
   HTTPS/TCP/443/社内プロキシ（例）
アウトバウンドルール（タイプ/プロトコル/ポート範囲/送信先）
   すべてのトラフィック/すべて/すべて/0.0.0.0/0

3. Security Group C：
インバウンドルール（タイプ/プロトコル/ポート範囲/ソース）
   HTTPS/TCP/443/Security Group A
   HTTPS/TCP/443/Security Group C
アウトバウンドルール（タイプ/プロトコル/ポート範囲/送信先）
   すべてのトラフィック/すべて/すべて/0.0.0.0/0

4. Security Group D：
インバウンドルール（タイプ/プロトコル/ポート範囲/ソース）
   HTTPS/TCP/443/Security Group A
アウトバウンドルール（タイプ/プロトコル/ポート範囲/送信先）
   すべてのトラフィック/すべて/すべて/0.0.0.0/0

ルートテーブル：
1. ルートA　
送信先/ターゲット
  0.0.0.0/0 / Internet Gateway
  VPC IP / VPC IP
明示的なサブネットの関連付け
Public Subnet

2. ルートB
送信先/ターゲット
  0.0.0.0/0 / NAT Gateway
  VPC IP / VPC IP
明示的なサブネットの関連付け
Pribate Subnet

## アプリ概要

__チャットボットによる社内文書検索と質問応答__
社内文書（ガイドラインやルールなど）をもとにしたチャットボットを開発しました。

__主な機能__
質問応答：ユーザーが入力した簡単な質問に対して瞬時に回答を提供し、業務効率を向上させます。
参考資料一覧提示：社内に保存されている文書から関連する情報を抽出します。抽出に使用した資料を参考資料一覧として提示します。
会話履歴リセット機能：Clear Conversationsボタンを押すことで、会話履歴をリセットすることができます。

__チャットボット画面（イメージ）__

![チャットボットメイン画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3987594/e4074ce9-0eca-49b0-83dc-e636df7277a7.png)


## 本記事の流れ

- 1 章：開発背景と目的
- 2 章：VPC の作成からアプリケーションのセットアップまで
- 3 章：Bedrock の設定から S3 の準備まで
- 4 章：アプリケーションの開発とブラウザからのアクセスまで
- 5 章：その他設定に関して
- 6 章：振り返り

### 1 章：開発背景と目的

__開発背景__
社員がガイドラインから必要な情報を得るまでに多くの手順が必要であり、その情報量の多さから適切な情報にたどり着くまでに困難が伴っていました。そのため、情報の検索に多くの時間と労力を費やしてしまう課題が存在していました。

__開発目的__
ガイドラインの利用に伴うハードルを下げ、社員が必要な情報を迅速かつ容易に得られるようにすることを目指しました。これにより、情報検索にかかる時間と労力を削減し、作業効率を向上させ、情報の理解にかかる負担軽減を目的としました。

### 2 章：VPC の作成から アプリケーションのセットアップまで

この章では、VPCの作成からアプリケーションのセットアップまでの手順を説明します。以下のステップに従って私は進めました。各ステップには、私が初めてAWSマネジメントコンソールを触りながら作成した際、参考にした記事のリンクも掲載しているので、必要に応じて確認してください。
※大まかな作業内容を記載しているため、詳細な説明は省略しています。ご了承ください。また、最新の情報はAWSの公式ドキュメントを読むことを強くお勧めします。

ステップ1：VPCとSubnetの構築
VPCと3つのSubnetを構築しました。- 参考①

ステップ2：EC2インスタンスの作成
VPC内にEC2インスタンスを作成しました。- 参考②

ステップ3：Internet Gatewayの設置
VPC内にInternet Gatewayを設置し、Public SubnetとPrivate Subnetに分ける設定を行いました。（Internet GatewayとつながっているSubnetがPublic Subnet）- 参考②

ステップ4：NAT Gatewayの設置
2つのSubnetに、それぞれ1つずつNAT Gatewayを設置しました。- 参考②

ステップ5：ルートテーブル作成とルーティング設定
Internet GatewayとNAT Gatewayを使うために、Subnetのルートテーブルを変更する必要があるため行いました。- 参考②

ステップ6：Route 53で新規ドメインのリクエスト
Route 53で新規パブリックドメインの申請・取得のリクエストを行いました。- 参考③

ステップ7：サブドメインの登録とパブリック証明書の発行
サブドメインをRoute 53に登録し、証明書をリクエストしました。- 参考④

メモ：AWS Certificate Manager(ACM)で証明書をリクエストしたがステータスが「保留中の検証」から変化しない場合の対応：登録済みドメインと同じ名前のホストゾーンに登録したいレコード名とNSを追加することで解決しました。

ステップ8：ALB用のセキュリティグループ、ターゲットグループの作成
Application Load Balancer(ALB)用のセキュリティグループを作成しました。- 参考⑤

メモ：セキュリティーグループは、インターネットからALBへの通信。言い換えると、AWS外部からのインバウンド通信に該当します。ターゲットグループは、ALBからEC2への通信。言い換えると。AWS内部で完結する通信に該当します。

ステップ9：ALB作成
Application Load Balancer (ALB)を作成しました。- 参考⑤

ステップ10：ターゲットグループにターゲットを追加
ターゲットグループにターゲットを追加しました。- 参考⑤

ステップ11：EC2へのSession Managerを使ったアクセス
Private Subnetに配置されたEC2インスタンスに対して、Session Managerを使って接続しました。- 参考⑥, ⑦

ステップ12：アプリケーションのセットアップ
Python、StreamlitのインストールとStreamlitの起動を行いました。
参考記事でPythonインストール後に、以下のコマンドを実行しました。- 参考⑧

```python
pip install streamlit 
streamlit hello
```

メモ：次回以降は、以下のコマンドを入力すれば、EC2インスタンスでのStreamlitの起動と停止が行える。(1行ずつ実行)

```Linux
sudo su - ec2-user
nohup streamlit hello &
ps -ef | grep streamlit
kill -9 確認したPID
```

最後に...
上記参考記事以外で、以下の記事がわかりやすくまとまっているので共有しておきます。- 参考⑨

ここまで完了した際のアーキテクチャ図：

![アーキテクチャ図2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3987594/db670dbe-cd21-94b5-4336-34d409dbf49c.png)


参考記事とリンク
参考①：[AWS VPC/サブネットを作成する(2022年)](https://qiita.com/kosimaru1997/items/0d2d5598560217dfeb47)
参考②：[実際に構築してみる(Internet gateway と NAT gateway)](https://qiita.com/miyuki_samitani/items/d0929af503f8ccb8d563)
参考③：[【初心者】Amazon Route53 でドメインを取得する](https://qiita.com/mksamba/items/e50a423b8ab12271eb73)
参考④：[Route53にサブドメインを登録する手順](https://qiita.com/n4rvs3_squld/items/43adcc34c28f61e05d57)
参考⑤：[【AWS】ALB-EC2-RDSで3層アーキテクチャを作成する](https://zenn.dev/shimiyu/articles/80bd299fbd404d)
参考⑥：[AWSのプライベートサブネットに配置されたEC2へのSession Managerを使ったアクセス](https://zenn.dev/kazutech/articles/3559db9605d198)
参考⑦：[AWS Session Managerを使ってプライベートサブネット内のEC2インスタンスに接続する](https://developers.prtimes.jp/2022/11/08/aws-session-manager/#index_id8)
参考⑧：[Amazon Linux2023にpyenvを利用してpython3.12をインストール](https://qiita.com/YujiHamada3/items/e1124b78f7d89b53c498)
参考⑨：[初学者向けのAWS基本設計と構築、EC2インスタンスによるインターネット接続確認](https://qiita.com/free-honda/items/e9cbaadb13c79a03b14f)


### 3 章：Bedrock の設定から S3 の準備まで
この章では、Bedrock の設定から S3 の準備までの手順を説明します。以下のステップに従って私は進めました。参考にした記事とリンクも掲載しているので、必要に応じて確認してください。
※大まかな作業内容を記載しているため、詳細な説明は省略しています。ご了承ください。また、最新の情報はAWSの公式ドキュメントを読むことを強くお勧めします。

ステップ1：Bedrockの設定
Bedrockサービスの有効化をおこないました。- 参考⑩

補足：今回はanthropicのclaude-3-haikuのモデルを使用しました。
（modelId = 'anthropic.claude-3-haiku-20240307-v1:0'）

ステップ2：Kendraの設定
Amazon Kendraのインデックスを作成しました。- 参考⑪

ステップ3：S3の設定
S3バケットの作成と検索対象データの準備を行いました。- 参考⑫

ステップ4：ローカルからEC2内への接続～sshセッション接続（主にファイル転送用）
社内PCからSession Managerを用いたEC2への接続の設定およびsshセッション接続を行いました。- 参考⑬, ⑭

補足①：aws-cliのインストール
社内のPCから接続する場合は、AWSのコマンドラインツールaws-cliが必要なため、インストールしました。また、別途session-manager-pluginが必要なので、こちらもインストールしました。- 参考⑬

補足②：アクセスキーの払い出し
アクセスキーとシークレット・アクセスキーを取得は以下の記事が参考になりました。- 参考⑮

メモ：うまくいかない時は、以下の記事が有用でした。- 参考⑯, ⑰

まとめ：この章では、Bedrock, Kendra, S3の設定とローカルからのSession Managerを用いたEC2接続の設定を行いました。また、ローカルからのssh接続によるファイル転送が可能となりました。

最後に...
上記参考記事以外で、以下の記事がわかりやすくまとまっているので共有しておきます。- 参考⑱

ここまで完了した際のアーキテクチャ図：

![アーキテクチャ図2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3987594/ab386e64-f055-e02d-c28c-ddd28f2b374b.png)


参考記事とリンク
参考⑩：[とりあえずBedrockを個人利用としてLambdaで実行するまで](https://qiita.com/ryome/items/2585c50e8c6a8831022c)
参考⑪：[Amazon KendraでPowerPoint, Excel, Wordファイルの全文検索を試してみた](https://dev.classmethod.jp/articles/full-text-search-of-powerpoint-excel-and-word-with-amazon-kendra/)
参考⑫：[AWS: AWS入門(S3編1) - バケットを作成する](https://qiita.com/frozencatpisces/items/27d8f7f3483df797ad21#%E5%AE%9F%E6%96%BD%E7%92%B0%E5%A2%83)
参考⑬：[AWS Session Manager ことはじめ](https://qiita.com/tuken24/items/17d9ea4fbb1a4e40f78e)
参考⑭：[Windows での Session Manager プラグインのインストール](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/install-plugin-windows.html)
参考⑮：[AWSのアクセスキーを取得する方法](https://qiita.com/yamasakk/items/3060d22faeed8e05ebe4)
参考⑯：[AWS CLI SSM接続時のエラー【 [SSL: CERTIFICATE_VERIFY_FAILED]】対処法メモ](https://qiita.com/bol-belt/items/44fa18a59c676f54dcdd)
参考⑰：[SSH で Permission Denied となる傾向と対策](https://qiita.com/youcune/items/2f427979403771f2e03a)
参考⑱：[Amazon EC2インスタンスにSession Managerを使用して接続する手順](https://qiita.com/hisato_imanishi/items/9993c86e02f5cbf4deae)


### 4 章：アプリケーションの開発とブラウザからのアクセスまで
この章では、アプリケーションの開発とブラウザからのアクセスまでの手順を説明します。以下のステップに従って私は進めました。参考にした記事とリンクも掲載しているので、必要に応じて確認してください。
※大まかな作業内容を記載しているため、詳細な説明は省略しています。ご了承ください。また、最新の情報はAWSの公式ドキュメントを読むことを強くお勧めします。


ステップ1：アプリケーション開発
以下の記事を参考にアプリを実装しました。
主に、StreamlitとLangchainを用いることでコードを実装しました。（後日、記事にする予定）
具体的なコードに関しては、今回の記事では割愛させていただきますが、基本的にこれらの参考資料をベースに完成させることができるはずです。- 参考⑲, ⑳, ㉑, ㉒, ㉓

メモ：うまくいかない時は、以下の記事が有用でした。- 参考㉔, ㉕

ステップ2：作成したPythonファイルをEC2内に転送
PythonファイルをローカルマシンからEC2インスタンスに転送しました。（方法は3章ステップ4を参照）

ステップ3：ブラウザからのアプリのアクセス
インターネット経由でドメイン名を使ってアクセスできるようコマンドを入力し、EC2内でアプリを立ち上げました。その後、ブラウザからのアプリを検索しアクセスできることを確認しました。（方法は2章ステップ12を参照）

メモ：ローカルマシンで開発したアプリをEC2内に転送し、立ち上げからkillするまでのコマンドまとめ（Windows PowerShellを使用）

```Linux
# Session Manager経由でEC2に接続を自動化
# 下記コマンドを打つと、token-codeを求められるので、Authenticatorで認証する
$json = (aws sts get-session-token --serial-number arn:aws:iam::<自身の値を入力>:mfa/Authenticator --token-code "$(Read-Host token-code)" --no-verify | ConvertFrom-Json); aws configure set aws_access_key_id $json.Credentials.AccessKeyId --profile mfa; aws configure set aws_secret_access_key $json.Credentials.SecretAccessKey --profile mfa; aws configure set aws_session_token $json.Credentials.SessionToken --profile mfa

# Session Manager経由でEC2接続する
aws ssm start-session --target i-<インスタンスID> --profile mfa --no-verify

# sh-5.2$に切り替わるのを確認後exitする
sh-5.2$exitで抜ける。

#ローカルからファイルを転送する
scp -i "C:\Users\<自分の名前>\.ssh\chatbot-ec2-key.pem" <自分の作成したアプリのパス> ec2-user@i-<インスタンスID>:~/

#EC2内にSSH接続で入る
ssh -i C:\Users\<自分の名前>\.ssh\chatbot-ec2-key.pem ec2-user@i-<インスタンスID>

#転送したファイルがあるか確認
ls

#アプリの立ち上げ（環境変数の永続化）
nohup streamlit run /home/ec2-user/<アプリ名.py> &

#killしたい番号を表示させる
ps -ef | grep streamlit

#killする
kill -9 確認したPID
```

ここまで完了した際のアーキテクチャ図：

![アーキテクチャ図4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3987594/4b023033-e3d8-d9f1-79cf-84223a4f889c.png)


参考⑲：[Amazon BedrockとStreamlitで簡単チャットアプリを作成する方法](https://qiita.com/moritalous/items/c9e8fce7514345af5cd2)
参考⑳：[Amazon BedrockとStreamlitでチャットボットを作成してみた](https://qiita.com/tatsuki-tsuchiyama/items/df1465a8c8fa8ac0a690)
参考㉑：[LangChainからKendraでRAGする（その1）-Amazon Bedrock APIで始めるLLM超入門⑤](https://qiita.com/cyberBOSE/items/5cdec3580eb22b567d39)
参考㉒：[LangChainからKendraでRAGする（その2）-Amazon Bedrock APIで始めるLLM超入門⑥](https://qiita.com/cyberBOSE/items/836dca81c090b1194f55)
参考㉓：[Streamlit](https://python.langchain.com/docs/integrations/memory/streamlit_chat_message_history/)
参考㉔：[AmazonKendraRetriever "Could not load credentials" error in latest release #7571](https://github.com/langchain-ai/langchain/issues/7571)
参考㉕：[RunnableWithMessageHistory to output source documents in RAG #20041](https://github.com/langchain-ai/langchain/discussions/20041)

### 5 章：その他設定に関して
ステップ1：EC2の自動起動・停止
EventBridgeを用いて、EC2インスタンスの停止と起動の設定を行いました。- 参考㉖

ステップ2：アプリ立ち上げの自動化
EC2起動時にアプリが自動で呼び出されるよう設定しました。- 参考㉗, ㉘

ステップ3：Cognitoの設定とALBとCognito連携
Cognitoを用いて認証や認可の設定を行いました。その後、ALBのホスト名をブラウザーで開いたときに認証画面を出すよう設定しました。- 参考㉙, ㉚

ステップ4：CloudWatchでのアプリケーション監視
CloudWatchを用いて、アプリのログを収集するよう設定しました。- 参考㉛, ㉜

メモ：うまくいかない時は、以下の記事が有用でした。- 参考㉝

ステップ5：コスト分析のためのAmazon Bedrockの利用トークン数集計
Amazon Bedrockの利用トークン数をCloudWatch Logs Insightsを用いて集計できるよう設定しました。- 参考㉞

ステップ6：SNSトピックとサブスクリプションの作成とCloud watchとの連携
CloudWatchアラームとSNSを用いて、自分のメール宛に通知出す設定をしました。- 参考㉟


ここまで完了した際のアーキテクチャ図：

![アーキテクチャ図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3987594/23ea3254-ff8b-f26d-65d3-835c445d81d2.png)


参考㉖：[EventBridge Schedulerでインスタンスを平日12~16時に起動するよう設定してみた](https://dev.classmethod.jp/articles/EventBridge-Scheduler-ec2-ks/)
参考㉗：[Streamlitで作ったアプリのEC2ホスティング&デプロイ自動化](https://qiita.com/take314/items/51c58859dde262916b47)
参考㉘：[AWS の EC2(Amazon Linux 2023) で 起動時にコマンド実行](https://qiita.com/sueasen/items/87ef8b616307644476d0)
参考㉙：[[AWS]cognito認可から認証まで](https://zenn.dev/taichi_tech/articles/f79af5ed38adf6)
参考㉚：[ALBにCognitoでの認証を付ける](https://qiita.com/mkt1234/items/e580628afe167c826adf)
参考㉛：[CloudWatch エージェント設定ファイルを手動で作成または編集する](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/CloudWatch-Agent-Configuration-File-Details.html)
参考㉜：[CloudWatch Logs で AWS EC2 インスタンスのログを管理したい](https://blog.denet.co.jp/aws-ec2-server-log-send-to-cloudwatch-logs/)
参考㉝：[Why is the /var/log directory missing logs in my EC2 Amazon Linux 2023 instance?](https://repost.aws/knowledge-center/ec2-linux-al2023-find-log-files)
参考㉞：[Amazon Bedrockの利用トークン数をCloudWatch Logs Insightsで集計してみる](https://techblog.nhn-techorus.com/archives/28616)
参考㉟：[CloudWatchアラームとSNSでメール通知を導入する方法](https://atmarksol.jp/tech/cloudwatch/)

### 6 章：振り返り

AWSのサービスを触ることが初めての初心者が、様々な試行錯誤をしながら社内向けのチャットボットを開発することができました。決して自分1人の力でできたわけではないですが、この開発経験を通じてAWSやStreamlitに関する知見をたくさん増やすことができました。（開発を通じた振り返りは今後記事にしていく予定です）
この経験を活かして、AWS SAA試験を受験したところ1発合格をすることができました！
最後に、チャットボット開発を進めるにあたり多くの助言と指導を行ってくださった先輩社員のHさんにここで感謝申し上げます。ありがとうございました！！