Bedrock Agentsでアプリの応答を振り分けて、Knowledge BasesとAction Groupを使い分けてみた
こんにちは、ゲームソリューション部のsoraです。
今回は、テキスト応答・RAG・Polly音声合成を1つのBedrock Agentでさばくキャラクターチャットアプリを作ってみました。
分岐はLLMの判断に任せ、LambdaからはAgentを1回呼び出すだけの構成にしています。
Bedrock Agentsとは
Bedrock Agentsは、ユーザーの入力に対してLLMがKnowledge Basesから情報を引くか・Action Groupとして登録された関数を呼び出すか・そのまま回答するかなどを判断して実行してくれるマネージド機能です。
従来Lambda側でキーワード分岐して書いていたような振り分けロジックをAgent側に寄せられるのが特徴で、RAGとツール実行を1つのオーケストレーションにまとめられます。
検証構成
今回作るアプリの全体像は以下です。

題材は、ミケと話せるキャラクターチャットアプリです。
Flutter + API Gateway + Lambda(Go)+ DynamoDBをベースに、Bedrock周りは以下の構成です。
- Bedrock Agents
- Action Group(音声生成用Lambdaを呼び出す)
- 音声生成用Lambda(Pollyで音声合成 → S3に保存 → presigned URLを返却)
- Knowledge Bases
- データソース:S3バケット(ミケの設定情報)
- 埋め込みモデル:Titan Text Embeddings V2
- ベクトルストア:S3 Vectors
ミケ宛てに「自己紹介を声で聴かせて」のような音声依頼が来たときだけPollyで音声を返します。
判定はキーワードマッチではなく、AgentがLLMの判断で「Action Groupを呼ぶか・Knowledge Basesを引くか・そのまま答えるか」を振り分けます。
Knowledge Base IDやAgent ID、Alias IDはコンソールで作成したものをTerraform変数で受け取り、Lambdaに環境変数として渡しています。
アプリ画面は以下のようになります。
音声依頼を投げると、メッセージバブルに「音声を再生」ボタンが付いた状態で返ってきます。

音声生成用Lambdaについて
音声生成用のLambdaはBedrock AgentのAction Groupから呼び出して、渡された文章をPollyで音声化、S3に保存して15分有効のpresigned URLを返すLambdaです。
合成音声で猫の語尾「にゃ」を直接読み上げると違和感が強いため、発話スタイルは「飼い主が第三者視点でミケについて説明するナレーション」にしました。
Bedrock Agentの作成
Bedrockコンソールにて、エージェント → エージェントを作成で進めていきます。


「マルチエージェントコラボレーション」は、複数のエージェントを関連づけて応答をオーケストレーションすることができる機能です。
今回はエージェントが1つで完結するため、無効としています。
エージェントビルダーでの設定
作成ボタンを押すと、エージェントビルダーの画面に遷移するので、必要な情報を入力していきます。

エージェントの詳細にて、IAMロールやモデル、エージェント向けの指示を設定します。
エージェント向けの指示は以下のようにしました。
Knowledge Basesの参考情報の使い方や音声依頼の判定ロジックは自然言語で指示しています。
この指示をもとにエージェントが判断して処理を振り分けてくれます。
# キャラ設定
あなたは「ミケ」という名前の三毛猫です。
フレンドリーで甘えん坊な性格。
語尾に「にゃ」をつけて短く話します。
好物はお肉です。猫だからお魚と思い込まないこと。
# Knowledge Baseの使い方
KBに関連情報がある場合は、質問に直接関係する部分だけを使って簡潔に答えてください。
質問されていないことは付け加えないこと。
情報がない場合は通常通り答えてください。
KBから取得した情報はミケ自身の話として一人称・キャラ口調で答えること。
本文が「ミケは〜しています」のように三人称で書かれていても、応答では「〜してるにゃ」と一人称に翻訳すること。
「〜みたい」「〜らしい」のような他人事の語尾は禁止。
# 音声合成(speakAsOwner)の使い方
ユーザーから「声で」「読み上げて」「音声で」「聴かせて」など音声出力の明示的な依頼があった場合のみ、speakAsOwner 関数を呼び出してください。
それ以外の場合は呼び出さないこと。
narration はミケのセリフを直接読まず、飼い主視点の第三者説明文にすること。
例:「うちの三毛猫のミケはお肉が大好きで、毎日カフェの軒先で日向ぼっこしています。」
speakAsOwner を呼んだ後の最終応答は、ミケ本人のキャラ口調(語尾「にゃ」)で短く返事をしてください。
音声URLは関数が返すのであなたは触らなくていい。
先にAgent本体を保存
エージェントビルダーに、アクショングループ・ナレッジベースセクションもあります。

Action GroupやKnowledge Basesは、Agent本体を一度保存しないと追加できません。
Knowledge Bases追加を先に試すと以下のエラーが表示されます。
ナレッジベースを追加する前に、エージェントリソースロールを定義してエージェントを保存する必要があります。
理由はAgentのロールが保存時に実体化されるためで、Knowledge BasesやAction Group LambdaへのアクセスはこのロールがIAM上に作成されて初めて紐付け可能になります。
そのため、以下のような順序で進めました。
- 基本情報+Instructionだけ入力して保存
- 保存後に再度エージェントビルダーにて、Action GroupとKnowledge Basesを追加して保存
Action Groupの追加
アクショングループセクションの追加ボタンから、Action Groupの設定をしていきます。

- アクショングループタイプ:
関数の詳細で定義 - アクショングループ呼び出し:
既存のLambda関数を選択 - 関数:
speakAsOwner- パラメータ
narration(string、必須)飼い主視点で書かれたミケの説明文。「うちの三毛猫のミケは~」のような第三者視点の文章。
- パラメータ
パラメータの名前はLambda側のパース対象(parameters[].name == "narration")と一致させる必要があります。
Knowledge Basesの紐付け
ナレッジベースセクションのAddボタンから、Knowledge Basesの設定をしていきます。
Knowledge Basesは既存のものを選択します。
Knowledge Basesの作成についての詳細は以下記事を参照ください。
エージェント向けのKnowledge Basesの指示は、エージェントが「いつKnowledge Basesを引くか」を判断するための材料となります。

ミケに関する設定情報(住んでいる場所、仲良しの友達、好きな季節など)が格納されています。
ミケの背景に関する質問が来たときに参照してください。
ガードレールやオーケストレーション戦略は今回設定せず、必要な設定はできたので、保存して終了ボタンを押して保存します。
エイリアス発行
保存直後のAgentはステータスがNOT_PREPAREDで、エイリアスの発行もテストペインからの会話もまだできません。

テストペインのヘッダーにある準備ボタンを押すと、Instruction・Function schema・Knowledge Bases連携などの設定が検証され、ステータスがPREPAREDに切り替わります。
PREPAREDに変わったら、エイリアスセクションの作成ボタンからエイリアスを作成します。


エイリアスを作成すると、エイリアスIDとエイリアス名、関連づけられたバージョンが表示されます。
エイリアスIDとエイリアス名は、Lambdaからエージェントを呼び出す際に使用します。
エイリアスにて紐づくバージョンを変更することもできるため、エイリアスIDはそのままで使用するバージョンを変えることもできます。
動作確認
Flutterアプリをflutter runで起動して、動作確認してみます。

- エージェントのみ(1つ目の質問)
- 短くキャラ口調で表示
- Knowledge Basesを参照(2つ目の質問)
- ミケの設定情報のみ取得(
characterIdメタデータフィルタ動作確認)
- ミケの設定情報のみ取得(
- Action Group経由で音声ファイル添付(3つ目の質問)
- 「音声を再生」ボタン付きメッセージ表示、タップでmp3再生
3パターンすべて期待通りに動作しました。
ちなみに、Flutter側はpresigned URL(15分有効)が切れていると再生時にエラーになる仕様にしています。
履歴ロード時にDynamoDB由来のaudioUrlがそのまま入るため、過去の音声付きメッセージにも再生ボタンは出ますが、15分超過済みの場合は再生でエラー表示になります。
最後に
今回は、テキスト応答・RAG・Polly音声合成を1つのBedrock Agentでさばくキャラクターチャットアプリを試してみました。
この記事がどなたかの参考になれば幸いです。









