Bedrock Knowledge BasesとS3 Vectorsでキャラクター別のRAGをやってみた
こんにちは、ゲームソリューション部のsoraです。
今回は、Bedrock Knowledge BasesとS3 Vectorsを使って、キャラクターチャットアプリに簡易なナレッジ検索(RAG)を追加してみました。
単一のKnowledge Basesにメタデータフィルタを組み合わせ、キャラ別に参考情報を引けるようにしています。
検証構成
今回作るアプリの全体像は以下です。

題材は、ミケ(三毛猫)とポチ(柴犬)と話せるキャラクターチャットアプリです。
Flutter + API Gateway + Lambda(Go)+ DynamoDBで構築したチャットアプリに、Bedrock関連のリソースを追加してRAG対応します。
- データソース:S3バケット
- ベクトルストア:S3 Vectors(Knowledge Bases作成時に生成)
- 埋め込みモデル:Titan Text Embeddings V2
- Knowledge Bases:1個(キャラはメタデータで区別)
LambdaはユーザーからのメッセージでRetrieveを叩き、結果をPrompt Managementテンプレの{{context}}変数に流し込んでConverse APIに渡します。
キャラの性格・口調・好物といった基本情報はシステムプロンプトに、「ミケはどこに住んでいるの?」のような設定情報はS3上のドキュメントにと、役割分担しています。
Knowledge Basesで使えるベクトルストアの選定
今回はAWS純正のベクトルストアから選びます。
検証用途のためコストの低いS3 Vectorsを使用します。
- OpenSearch Serverless / Managed Cluster
- Aurora PostgreSQL Serverless v2
- Neptune Analytics(GraphRAG)
- S3 Vectors
単一Knowledge Bases+メタデータフィルタの構成
キャラごとにKnowledge Basesを作る方法もありますが、キャラ数だけ管理対象が増えてしまいます。
今回は単一のKnowledge BasesにキャラIDをメタデータとして付与し、Retrieve時にフィルタする方式にしました。
キャラを増やすときも運用が楽です。
データ準備
キャラ別のQ&A集
キャラごとのQ&A集をMarkdownで作ります。
# ミケのプロフィール
Q: ミケはどこに住んでいますか?
A: ミケは古民家風カフェの軒先に住んでいます。日当たりがよくて居心地のいい場所です。
Q: ミケの仲良しの友達は誰ですか?
A: ミケの仲良しは隣の家のシャム猫「シャム子」です。毎日塀の上で挨拶を交わしています。
Q: ミケの好きな季節はいつですか?
A: ミケは秋が一番好きです。暑すぎず寒すぎず、日向ぼっこに最適だからです。
各キャラ3問だけにしました。
種別・性格・口調・好物のようなシステムプロンプトと重複する内容はあえて入れず、RAG固有の情報だけにする方針です。
メタデータファイル
キャラIDを付与するため、本文ファイルと対になるJSONファイルを用意します。
{
"metadataAttributes": {
"characterId": "mike"
}
}
ファイル名は<filename>.metadata.json形式が必須です。
これでRetrieve APIでcharacterId == mikeのチャンクだけに絞れるようになります。
Knowledge Basesを作る
Bedrockコンソールにて、作成ボタンからUnstructured data - ベクトルストアを含むナレッジベースを選択します。

データソース
データソースにはS3を選択します。

S3には、先ほどキャラ別のQ&A集やメタデータを作成して配置したバケットを指定します。

主な設定項目は以下のとおりです。
| 項目 | 値 | 補足 |
|---|---|---|
| 解析戦略 | Bedrock デフォルトパーサー | テキストのみ処理、無料 |
| チャンキング戦略 | デフォルトチャンキング | 約300トークンに自動分割 |
| 変換関数(Lambda) | 未設定 | PII除去や独自メタデータ付与が必要なときに使用 |
| データ削除ポリシー | Delete | データソース削除時にベクトルストア側も削除 |
画像入りPDFなどを扱うときはパーサーとしての基盤モデルを選ぶこともできますが、追加料金がかかります。
今回はテキストだけなのでデフォルトのままにしています。
埋め込みモデルとベクトルストア
埋め込みモデルにはTitan Text Embeddings V2を選択します。


新しいベクトルストアをクイック作成を選び、ベクトルストアとしてAmazon S3 Vectorsを指定します。
ベクトルバケットとベクトルインデックスはKnowledge Basesが自動で作成してくれます。

作成完了
内容を確認して作成すると、Knowledge Basesの概要画面に遷移します。

Knowledge Basesへの同期
作成したKnowledge Basesを開き、データソースから該当ソースを選んで同期ボタンを押します。

同期処理が、S3からのファイル取得・ベクトル化・ベクトルストアへの保存までを自動で実行してくれるため、Lambdaでベクトル化の処理を実装する必要はありません。
完了するとベクトル化されたチャンクがS3 Vectorsに保存され、同期履歴にステータスが記録されます。

Prompt Managementの設定
Prompt Managementの基本的な使い方は以下の記事で詳しく扱っているため、ここではRAG用の差分のみを記載します。
各キャラのプロンプトのシステム命令側の末尾に、RAG用の指示と{{context}}変数を追加します。
以下に参考情報が与えられた場合は、質問に直接関係する部分だけを使って簡潔に答えてください。
質問されていないことは付け加えないこと。
情報がない、または質問に関係ない場合は、無視して通常通り答えてください。
キャラの口調は崩さないこと。
参考情報:
{{context}}

ユーザーメッセージ欄について、{{user_message}}などの変数や空欄にしているとエラーになったため、無害な静的テキストを埋めています。
# {{user_message}}にしたときのエラー
ValidationException: Prompt resource couldn't be rendered, please fix prompt resource
# 空欄にしたときのエラー
ValidationException: The text field in the ContentBlock object at messages.0.content.0 is blank.
LambdaからRetrieveを呼び出す
Lambda側でやることは大きく2つです。
- ユーザーのメッセージで
bedrock-agent-runtimeのRetrieveAPIを叩き、characterIdでメタデータフィルタをかけてチャンクを取得する - 取得したテキストをConverseの
PromptVariablesに{"context": "..."}の形で渡す
Retrieveはbedrockruntimeではなくbedrockagentruntimeパッケージにある点だけ注意が必要です。
動作確認
flutter runで起動して、動作確認してみます。
選択画面

ミケ
最初の「あなたの名前は?」の質問に関しては、Prompt Managementを参照して回答が返ってきており、レスポンスも早かったです。
2つ目以降の質問からは、Q&A集で渡した情報を参照して回答が返ってきています。
Knowledge Basesに見に行っているため、最初の質問と比較してレスポンスは遅かったです。
質問に応じて、Prompt ManagementとKnowledge Basesに分岐して回答を生成していることが確認できました。

ポチ
ポチも同じ修正を入れて、こちらもキャラ口調を保ったままRAG経由の回答ができるようになりました。
Knowledge Basesは1つしか用意していませんが、メタデータを使用してのキャラごとの分岐もできています。

最後に
今回は、Bedrock Knowledge BasesとS3 Vectorsで、キャラクターチャットアプリにメタデータフィルタ付きのRAGを追加してみました。
この記事がどなたかの参考になれば幸いです。






