👁

Bedrockにナレッジをもたせよう

この章では、Amazon Bedrock Knowledge Basesを使ったS3ドキュメントのベクトル検索とRAG構成の構築をハンズオン形式で学習します。これにより、モデルの回答をドキュメントに紐づけて返す仕組みが構築できるようになります。

1. 本章の概要

1.1 本章の目的

素のBedrockの呼び出しでは、モデルが学習済みの知識だけを頼りに回答するため、社内ドキュメントや自社仕様への回答は苦手です。RAG(Retrieval-Augmented Generation)は、事前に用意したドキュメント群から関連情報を検索して、モデルの回答を根拠づける仕組みです。BedrockのKnowledge Basesを使うと、S3にドキュメントを置くだけでRAGを構築できます。

1.2 ハンズオンの流れ

S3をデータソースにしたManaged Knowledge Basesを作成し、埋め込みモデルとベクトル検索をBedrockに任せる構成を組みます。マネジメントコンソールとLambda(boto3)の2経路から、ナレッジに基づいた回答が返ることを確認します。

1.3 事前準備

必要なツール

この章では、以下のツールを使用します。まだインストールしていない場合は、リンク先の手順に沿って準備をお願いします。

ツール名 関連箇所 理由
Python Pythonのインストール ナレッジベースを呼び出すLambda関数のコードを記述・実行するために使用する

必要なアカウント

この章では、以下のアカウントを使用します。まだ用意していない場合は、リンク先の手順に沿って準備をお願いします。

アカウント名 関連箇所 理由
AWSアカウント AWSアカウントの作成 Bedrock Knowledge Basesを構築する環境として使用する

動作確認でLambdaを使うため、Bedrockを使ってみよう の内容を先に完了しておくと、Lambda関数の作成手順や実行ロールの考え方が把握できているため理解が進みます。

2. ハンズオンの概要

2.1 RAGとは

RAGは、モデルの回答をあらかじめ用意した外部ドキュメントに紐づけて生成する仕組みです。モデル自体を再学習させることなく、社内ドキュメントや自社の仕様書など、モデルが元から知らない情報に基づいた回答を返せるようになります。

RAGの中では、質問に近い意味を持つドキュメントを探すためにベクトル検索が使われます。事前にドキュメントを小さなチャンクに分割し、埋め込みモデル(Embedding Model)で数値ベクトルに変換してベクトルストアに保存しておきます。質問が来たときも同じ埋め込みモデルでベクトル化し、意味的に近いチャンクを検索して回答生成用のモデルに渡します。

📝 チャンクとは
チャンクは、ドキュメントを検索単位に区切ったテキストの一片です。数十ページのマニュアルをそのまま扱うと、質問と関係のない部分まで丸ごとヒットしたり、モデルに渡すトークン数が膨らんで料金・レイテンシが跳ね上がるため、段落や見出し単位で数百〜千文字程度に分割します。分割の粒度は検索精度とコストに直結するため、対象ドキュメントに合わせて調整するのが一般的です。
📝 ベクトル検索とは
ベクトル検索は、テキストを数値ベクトルに変換したうえで、質問と意味的に近い文書を距離計算で探す検索方式です。単語の完全一致で探す全文検索と違い、「クレカ払い」と「クレジットカード決済」のような表現の揺れがあっても近い意味として拾えるのが特徴です。RAGでは、ユーザの質問と登録済みドキュメントをそれぞれベクトル化し、類似度が高い順にチャンクを取り出して回答生成に渡します。
💡 ポイント
Claude CodeのCLAUDE.mdやSkillsも「AIに情報を持たせる」仕組みですが、これらはセッション開始時や特定のパターンに反応してまとめて読み込む静的なコンテキストで、扱える情報量には制約があります。一方RAGは、質問時に大量のドキュメント(数百MB規模でも可)から関連部分だけを動的に検索して渡す仕組みで、扱える情報量と更新頻度の柔軟性が桁違いです。どちらか一方ではなく「共通ルールや常時参照する情報はCLAUDE.md、大規模なドキュメントはRAG」などと役割を分けて組み合わせるのが実際には一般的です。

2.2 Knowledge Basesとは

Amazon Bedrock Knowledge Basesは、RAGを構築するための一連の仕組みをBedrock側でまとめて面倒を見てくれるサービスです。データソースを指定するだけで、以下を自動で行います。

  • ドキュメントを取り込んでチャンク分割する
  • 埋め込みモデルで数値ベクトルに変換する
  • ベクトルストア(OpenSearch Serverlessなど)に保存する
  • 質問時にベクトル検索して該当チャンクを取ってくる

データソースには Amazon S3 のほかに、以下のコネクタが用意されています。社内外に散らばった情報源を1つのナレッジベースにまとめて扱えます。

データソース 主な用途
Amazon S3 ファイルサーバ的にドキュメントを置いて取り込む
Confluence Atlassian Confluence の社内ドキュメントを取り込む
Microsoft SharePoint SharePoint 上のドキュメントを取り込む
Google Drive Google Drive 上のドキュメントを取り込む
Microsoft OneDrive OneDrive 上のドキュメントを取り込む
Web Crawler 指定したドメイン配下のページをクロールして取り込む
カスタムコネクタ 上記以外の情報源を、API 経由で任意のタイミングで取り込む

さらに構造化データ側では、Amazon Redshift や AWS Glue データカタログ をデータソースとして指定することもでき、テーブル上のデータに対しても自然言語で問い合わせられる形にできます。

対応データソースの最新一覧は Create a managed knowledge base(AWS公式ドキュメント) に記載があります。

本章では、最もシンプルな Amazon S3 をデータソースにしてナレッジベースを作成します。

2.3 今回作るRAG

今回は架空の飲食店「Bistro・DevOps」の自動問い合わせシステムを想定し、店舗情報やメニューをS3にアップロードして Knowledge Bases で検索できる状態にします。その後、「営業時間は?」「ワインは何がありますか?」「ハンバーグはありますか?」といった質問を投げ、モデルがナレッジの内容に基づいて答えてくれることを確認します。

こうしたお店固有の店舗情報やメニュー・価格は、一般的な生成AIモデルの学習データには含まれていません。ネット上に公開されているケースでも、モデル学習時点との差分や情報の網羅性は保証されないため、そのまま質問しても事実と違う回答(ハルシネーション)が返ってくることがあります。「モデルが元から知らない」「頻繁に更新される」情報を確実に扱えるようにするために、あらかじめナレッジとして登録しておくのがRAGの出番です。

2.4 ハンズオン全体の流れ

S3バケットを用意し、店舗情報とメニューをまとめたサンプルドキュメントをアップロードします。Knowledge Basesを作成し、データソースを同期して、Bedrockコンソールから質問を投げてナレッジに基づいた回答が返るかを確認します。最後にLambda(boto3)からナレッジベースを呼び出して、コンソール外からもRAGを使えることを確認します。

3. Knowledge Basesを作成する

Knowledge Basesを作成することで、S3上のドキュメントをチャンク分割・ベクトル化してベクトルストアに登録し、質問時にRAGとして使える状態を一括で用意できます。まずデータソースとなるS3バケットを用意してドキュメントを配置し、次にナレッジベース本体を作成、最後にデータソースを同期する流れで進めます。

3.1 S3バケットを作成する

S3コンソールで「バケットを作成」を開き、以下の設定でバケットを作ります。

項目 設定値 設定の基準
AWS リージョン アジアパシフィック(東京) ap-northeast-1 Knowledge Basesと同じリージョンに揃える
バケットタイプ 汎用 一般的な用途向けの標準タイプを使う
バケット名 ai-platform-rag-<自分の識別子> S3バケット名は全世界で一意のため、識別子を付けて重複を避ける
パブリックアクセス すべてブロック 内部利用のみに限定する

その他の項目はデフォルトのままで問題ありません。画面下部までスクロールし、右下の「バケットを作成」をクリックします。

バケット一覧画面に戻り、「バケット『ai-platform-rag-<自分の識別子>』が正常に作成されました」というメッセージが表示されれば、バケットの作成は完了です。

3.2 restaurant.md をアップロードする

サンプルドキュメントとして、Bistro・DevOps の店舗情報とメニューをまとめた restaurant.md を用意しています。以下のボタンからダウンロードしてください。

ダウンロードした restaurant.md を、作成したS3バケットにアップロードします。バケットの詳細画面(オブジェクトタブ)を開き、右上の「アップロード」をクリックします。

アップロード画面が開いたら、「ファイルを追加」ボタンから、あるいは画面上部のエリアにドラッグ&ドロップで restaurant.md を追加します。ファイル一覧に restaurant.md が表示されたら、右下の「アップロード」をクリックします。

「アップロードに成功しました」の緑バナーが表示され、ファイル一覧で restaurant.md のステータスが「成功しました」になっていれば、アップロードは完了です。

3.3 ナレッジベース本体を作成する

S3にドキュメントの配置ができたので、これをデータソースにしてナレッジベース本体を作成します。Bedrockコンソールの左メニューで「ナレッジベース」を開き、右上の「Create Managed KB」ボタンをクリックします。

Create Managed Knowledge Base (KB) 画面が開くので、以下の項目を入力します。

項目 設定値 設定の基準
ナレッジベース名 ai-platform-rag-kb 用途がひと目で分かる名前をつける
データソース名 ai-platform-rag-kb-source ナレッジベースとの対応が分かる名前をつける
Data source type Amazon S3 今回のハンズオンで使うデータソースを指定する
データソースの場所 このAWSアカウント 同じアカウント内のS3バケットを使う
S3 のURI s3://ai-platform-rag-<自分の識別子> 前のステップで作成したバケットを指定する

上記以外の設定項目は、デフォルトのままで問題ありません。ハンズオンで押さえておくポイントは以下です。

  • 埋め込みモデル(KB details の追加設定)は Managed embeddings model - recommended がデフォルトで選ばれており、Managed KBが追加費用なしで用意する埋め込みモデルで日本語にも対応する
  • IAM Permissions(KB details の追加設定)は 新しいサービスロールを作成して使用 がデフォルトで選ばれており、Bedrockが自動で AmazonBedrockExecutionRoleForKnowledgeBase_... というロールを作成する
  • ベクトルストアは Managed KB では明示的な選択項目がなく、内部で自動的にマネージドベクトルストアが用意される

画面下部までスクロールし、右下の「ナレッジベースを作成」をクリックします。

ナレッジベースの作成には数分〜10分ほどかかります。ステータスが「使用可能」になると、続いてデータソースの初回同期が自動で始まります。

💡 ポイント
Managed KBが自動で用意するベクトルストアは、有効化されている間は常時稼働のコストが発生します。ハンズオンで作りっぱなしにするとコストが積み重なるため、後の「不要リソースの削除」で忘れずに片付けてください。

3.4 データソースの初回同期を待つ

Managed KBは、ナレッジベースの作成直後にデータソースの初回同期(S3上のドキュメントを取り込んでチャンク分割・ベクトル化する処理)を自動で開始します。

ナレッジベースの詳細画面下部の「データソース」欄で、ステータスが「同期中」になっていることを確認します。しばらく待つとステータスが「使用可能」に変わり、ベクトル検索が使える状態になります。今回のような小さなドキュメントであれば、数分以内に完了します。

なお、後からS3のドキュメントを追加・更新した場合は、同じ画面の「同期」ボタンから手動で再同期を実行します。

3.5 コンソールから動作確認する

初回同期が完了すると、ナレッジベース詳細画面の上部に「データソースの同期が完了しました」の緑バナーが表示され、データソース欄のステータスも「利用可能」に変わります。この状態になっていれば、動作確認に進めます。

まずはBedrockコンソールから、実際に質問を投げてナレッジに基づいた回答が返ってくることを確認します。ナレッジベース詳細画面の右上にある「ナレッジベースをテスト」ボタンをクリックします。

テスト画面(Test KBs)が開きます。左側の「設定」パネルでは、APIモードとして Agentic retrieval with answer generation(Managed KBのデフォルト。検索と回答生成をエージェンティックに行う方式)と、応答生成に使うモデル Managed model - Equivalent to Claude Sonnet 4.6 or better(Managed KBが用意する、Claude Sonnet相当のモデル)が自動選択されています。設定はそのまま触らずに進めます。

右側「テスト」パネル下部のプロンプト欄に、以下の質問を入力して「実行」をクリックします。

営業時間を教えて

数秒待つと、ドキュメントの内容に基づいた応答が返ります。

Bistro・DevOpsの営業時間は以下の通りです。
ランチ
・11:30〜14:30(ラストオーダー 14:00)
ディナー
・17:30〜22:00(ラストオーダー 21:30)
なお、定休日は毎週火曜日、および月末の水曜日となっています。

回答本文の途中に [1] [2] のような引用番号が付き、応答の下部に「View source chunks」と「Trace details」のリンクが表示されます。

「View source chunks」をクリックすると、画面右側に「ソースチャンク」パネルが開き、restaurant.md から実際に取り出されたテキストの中身を確認できます。モデルが自分の記憶ではなく、登録したドキュメントの内容から回答を組み立てていることが具体的に見えます。

同様に「ワインは何がありますか?」「ハンバーグはありますか?」などの質問も試して、ドキュメントの内容に沿った回答が返ることを確認してみてください。

一方、ドキュメントに書かれていない質問(例: 「駐車場はありますか?」)を投げると、モデルは「Bistro・DevOpsの店舗情報には駐車場に関する記載がありません」とドキュメントに事実がないことを正直に伝えたうえで、「渋谷駅周辺には複数のコインパーキングがある」といったモデル自身の一般知識で補足を添える形で応答します。

ドキュメントに根拠がないことは勝手に断定せずハルシネーションを避けつつ、利用者に役立つ情報も返せているのがRAGの強みです。

4. Lambdaから呼び出す

コンソールで確認できたRAGを、次はコンソール外からも使えることを確認します。実運用ではAPIから呼び出すケースが大半なので、Lambdaからナレッジベースに質問を投げて回答が返ることを確認します。

Lambda 側の処理は、RAGの基本パターンに沿って 2ステップ で組みます。

  1. ナレッジベースに質問を投げて、関連するチャンクを取得する(bedrock-agent-runtime の retrieve API)
  2. 取得したチャンクをプロンプトに埋め込んで、Bedrockを使ってみよう で使った converse API に渡し、回答を生成させる

前のセクションと同じ converse 呼び出しに、ナレッジ検索の1ステップを前に足しただけ の構成です。

4.1 IAM ロールと Lambda 関数を CloudFormation で作成する

Lambda 関数と、Bedrock 呼び出し権限を持たせた実行ロールを、CloudFormation テンプレートで一括作成します。関数の器作りをテンプレートに任せることで、本章の主題である RAG のコードに集中できます。

以下のテンプレートをダウンロードしてください。

テンプレートには、以下の2つのリソースが定義されています。

リソース 内容
rag-query-lambda-role Lambda 実行ロール。ナレッジベースからチャンクを取得する bedrock:Retrieve、Converse API を呼び出す bedrock:InvokeModel、および CloudWatch Logs 出力用のAWS管理ポリシー AWSLambdaBasicExecutionRole を持つ
rag-query Lambda 関数(Python 3.14 / arm64 / タイムアウト60秒)。この時点では中身は空のコードで、この後コンソール側で本物のコードに置き換える

CloudFormationのダッシュボードでスタックの作成 > 新しいリソースを使用(標準)をクリックし、スタック作成ウィザードを開きます。

ステップ1: スタックの作成

「前提条件 - テンプレートの準備」で既存のテンプレートを選択を選び、「テンプレートの指定」の「テンプレートソース」でテンプレートファイルのアップロードを選択します。「テンプレートファイルのアップロード」のファイルの選択からダウンロードした rag-lambda.yaml を指定し、次へをクリックします。

ステップ2: スタックの詳細を指定

「スタック名」に以下の値を入力します。

設定項目 値 設定の基準
スタック名 ai-platform-rag ハンズオン用に分かりやすい名前をつける

入力できたら次へをクリックします。

ステップ3: スタックオプションの設定

そのまま画面を下にスクロールし、「機能」セクションのAWS CloudFormation によって IAM リソースがカスタム名で作成される場合があることを承認します。にチェックを入れて次へをクリックします。

ステップ4: 確認して作成

内容を確認して、最下部の送信をクリックしてスタックを作成します。

スタックの作成完了確認

スタックのステータスが CREATE_COMPLETE になったら、スタック詳細画面の「リソース」タブに、RagQueryFunction(rag-query 関数)と RagQueryLambdaRole(rag-query-lambda-role ロール)の2つが CREATE_COMPLETE で並んでいることを確認します。

4.2 環境変数を設定する

作成した Lambda 関数に、呼び出し先のナレッジベース ID と、回答生成に使うモデルの推論プロファイル ID を、環境変数として渡します。

まずナレッジベース ID を Bedrock コンソールから取得します。ナレッジベース詳細画面(ai-platform-rag-kb)の右側「ナレッジベースの概要」→「ナレッジベース ID」(APNGXQR7KQ のような文字列)の、右のコピーアイコンから控えます。

推論プロファイル ID は、Bedrockを使ってみよう で控えたものと同じ値(例: jp.anthropic.claude-sonnet-4-6)を使います。

Lambda コンソールで rag-query 関数を開き、「設定」タブ→「環境変数」を選択して「編集」をクリックします。「環境変数の追加」から、以下の2つを設定して「保存」します。

キー 値
KNOWLEDGE_BASE_ID 先ほど控えたナレッジベース ID
MODEL_ID 推論プロファイル ID(例: jp.anthropic.claude-sonnet-4-6)

4.3 Lambda関数のコードを書き換える

CloudFormation で作成された rag-query 関数は、中身が空のコードになっています。これを Bedrock Knowledge Bases を呼び出すコードに書き換えます。

rag-query 関数の「コード」タブを開き、エクスプローラーで index.py を選択して、以下の内容に置き換えます。

💡 ポイント
Bedrockを使ってみよう では lambda_function.py を編集しましたが、こちらは index.py になっています。これは CloudFormation でインラインコードを持つ Lambda 関数を作成すると、パッケージ内のファイル名が index.py になる仕様のためです。ハンドラも index.lambda_handler を指す形で作成してあるので、そのまま index.py の中身を置き換える形で問題ありません。
import os
import boto3

KNOWLEDGE_BASE_ID = os.environ["KNOWLEDGE_BASE_ID"]
MODEL_ID = os.environ["MODEL_ID"]
REGION = "ap-northeast-1"

agent_runtime = boto3.client("bedrock-agent-runtime", region_name=REGION)
bedrock = boto3.client("bedrock-runtime", region_name=REGION)


def lambda_handler(event, context):
    question = event.get("question", "こんにちは")

    retrieved = agent_runtime.retrieve(
        knowledgeBaseId=KNOWLEDGE_BASE_ID,
        retrievalQuery={"text": question},
    )
    knowledge = "\n\n".join(
        r["content"]["text"] for r in retrieved["retrievalResults"]
    )

    prompt = f"参考情報:\n{knowledge}\n\n質問: {question}"
    response = bedrock.converse(
        modelId=MODEL_ID,
        messages=[{"role": "user", "content": [{"text": prompt}]}],
    )
    answer = response["output"]["message"]["content"][0]["text"]
    return {"answer": answer}

コードを解説します。

agent_runtime = boto3.client("bedrock-agent-runtime", region_name=REGION)
bedrock = boto3.client("bedrock-runtime", region_name=REGION)

boto3のクライアントを2つ用意しています。ナレッジベースの検索は bedrock-agent-runtime クライアント、モデルの呼び出しは bedrock-runtime クライアントを使います。RAG は「検索」と「生成」という2種類の処理を組み合わせる仕組みなので、それぞれ担当するクライアントも別になっています。

retrieved = agent_runtime.retrieve(
    knowledgeBaseId=KNOWLEDGE_BASE_ID,
    retrievalQuery={"text": question},
)

retrieve API で、ナレッジベースから質問に近いチャンクを取得しています。retrievalQuery に渡した質問文が Bedrock 側で自動的にベクトル化され、意味的に近いチャンクが検索されて返ります。

knowledge = "\n\n".join(
    r["content"]["text"] for r in retrieved["retrievalResults"]
)

retrieve の戻り値 retrievalResults は、検索でヒットしたチャンクのリストです。各チャンクの本文テキストを \n\n で連結して、モデルに渡す knowledge としてまとめています。

prompt = f"参考情報:\n{knowledge}\n\n質問: {question}"

モデルに投げるプロンプトを組み立てています。「参考情報:」として先ほど検索したチャンクを貼り付け、その後に「質問: 」として元の質問文を続ける形です。モデルはこの参考情報を踏まえて回答を組み立てます。

response = bedrock.converse(
    modelId=MODEL_ID,
    messages=[{"role": "user", "content": [{"text": prompt}]}],
)
answer = response["output"]["message"]["content"][0]["text"]

Bedrockを使ってみよう で使ったのと同じ converse の呼び出しです。違いは、渡すプロンプトが「参考情報付き」に差し替わっているだけで、呼び出し方や応答の取り出し方は変わりません。

コードを貼り付けたら、左サイドバーの DEPLOY セクションにある「Deploy」ボタン(ショートカット: ⇧⌘U / Windows は Ctrl+Shift+U)を押して、Lambda に反映します。

4.4 動作確認する

Lambda コンソールの「テスト」タブで、以下のテストイベントを作成して実行します。イベント名は任意(例: rag-test)です。

{
  "question": "ハンバーグはありますか?"
}

以下のような実行結果が返ります。

{
  "answer": "はい、ハンバーグはございます。ランチの『国産牛のハンバーグプレート』(1,800円、手ごねハンバーグ・季節のサラダ・ライスまたはパン付き)と、一品料理の『手ごねハンバーグ』(1,600円)がございます。"
}

コンソールのテスト画面で確認したのと同じ回答が、Lambda(API)からも取得できていることが分かります。この Lambda を API Gateway 経由で公開すれば、そのまま Q&A API として外部から使える構成にもできます。

5. 不要リソースの削除

Managed KBのマネージドベクトルストアは有効化されている間は常時稼働のコストが発生するため、ハンズオン後は忘れずに削除します。

5.1 CloudFormationスタックを削除する

CloudFormationのダッシュボードで、ai-platform-rag スタックのラジオボタンを選択し、右上のスタックを削除をクリックします。ステータスが DELETE_COMPLETE になれば、Lambda 関数と実行ロールがまとめて削除されます。

5.2 ナレッジベースを削除する

Bedrockコンソールの「ナレッジベース」一覧で、ai-platform-rag-kb のチェックボックスをオンにし、右上のメニューから削除をクリックします。ナレッジベースを削除すると、Managed KBが内部で用意したベクトルストアも自動で片付けられます。

5.3 S3バケットを削除する

S3バケットは、バケット内のオブジェクトを空にしてから、バケット自体を削除する2ステップで進めます。

S3コンソールの「汎用バケット」一覧で、検索フィールドに ai-platform-rag と入力してバケットを絞り込みます。バケットのラジオボタンを選択し、上部の空にするをクリックして、画面の指示に従ってオブジェクトをすべて削除します。

空にしたら再度同じバケットを選択し、今度は削除をクリックしてバケット自体を削除します。

5.4 ナレッジベース用IAMロールを削除する

IAMコンソールの「ロール」一覧で、検索フィールドに AmazonBedrockExecutionRole と入力してロールを絞り込みます。Bedrock が自動作成した AmazonBedrockExecutionRoleForKnowledgeBase_*(末尾の数文字はランダム)にチェックを入れて、右上の削除をクリックします。

6. まとめ

この章では、Bedrock Knowledge BasesでS3上のドキュメントを使ったRAG構築を体験しました。

  • S3のドキュメントを取り込み、埋め込み→ベクトルストア保存までManaged Knowledge Basesにまとめて任せられる
  • Managed KBは作成直後に初回のデータソース同期が自動で始まり、ステータスが「使用可能」になれば検索できる
  • コンソールの「ナレッジベースをテスト」画面で、質問と回答・引用元チャンクをインタラクティブに確認できる
  • Lambdaからは、「ナレッジベースからチャンクを取得する retrieve」→「取得したチャンクをプロンプトに埋めて呼び出す converse」の2ステップでRAGを組める

次の章では、LambdaによるBedrockの応用操作(マルチターン会話・システムプロンプト・ツール呼び出しループ)をハンズオン形式で体験します。

この教材は役に立ちましたか?

いいねをたくさんいただけると、制作者の励みになり、より多くのセクションが作れるようになります。

感想を一言(任意)

いただいたコメントは次の制作のヒントになります。ぜひお気軽にご投稿ください。

このコメントは他の受講生には公開されません。DevOps Camp運営が、教材改善のために確認します。

0 / 2000