- 1 1. RAG とは・ユースケース・従来検索との違い
- 2 2. Knowledge Bases for Amazon Bedrock 概論 + データソース設計
- 3 3. OpenSearch Serverless で vector index を構築する
- 4 4. Knowledge Base 作成 + Embeddings model 選択 + Chunking 戦略
- 5 5. RetrieveAndGenerate API ハンズオン (Python boto3)
- 6 6. Bedrock Agents + Action Groups + Lambda 連携
- 7 7. 運用: Guardrails + CloudWatch Metrics + コスト最適化
- 8 8. まとめ・次回予告
1. RAG とは・ユースケース・従来検索との違い
1-1. RAG とは何か
大規模言語モデル (LLM) は学習データ外の最新情報や社内固有知識を問われると、事実と異なる回答を自信満々に生成する「幻覚 (Hallucination)」問題を抱えている。モデルをファインチューニングして知識を注入する方法もあるが、毎回の学習コストと時間が膨大になる。RAG (Retrieval-Augmented Generation) はこの問題を「外部知識を検索してプロンプトに注入する」手法で解決する。モデルの重みを変えず、最新の文書を動的に参照できるのが最大の特長だ。
処理は次の 3 段で構成される。
- Retrieve (検索) — ユーザーのクエリをベクトル化し、Vector Index から意味的に近い文書チャンクを Top-K 件取得する
- Augment (拡張) — 取得したチャンクをプロンプトテンプレートに埋め込み、LLM へのコンテキストを構築する
- Generate (生成) — LLM が注入されたコンテキストを根拠に、出典に基づいた回答と citation を生成する

主要用語:
| 用語 | 説明 |
|---|---|
| Retriever | クエリに対して関連文書チャンクを検索するコンポーネント (Vector DB + Embedding model の組み合わせ) |
| Embeddings | テキストを高次元の数値ベクトルに変換する処理・モデル。意味的な類似度をベクトル空間で表現する |
| Vector Index | Embeddings を格納し高速に近傍検索するインデックス (本記事では OpenSearch Serverless を使用) |
| Generator | コンテキストを受け取り最終回答を生成する LLM (本記事では Claude 3.5 Sonnet v2 または Amazon Nova Pro) |
なお、RAG はすべての質問に万能ではない。学習データ内の一般知識で回答できる質問やリアルタイム計算が必要な質問には RAG より直接のファインチューニングや関数呼び出しが有効な場合もある。「社内固有ドキュメントへの回答」「頻繁に更新される情報への参照」「引用元を明示した回答生成」が必要なシナリオこそが RAG の真骨頂だ。
1-2. Bedrock Knowledge Bases の位置付け
従来の RAG 自前実装では LangChain + Pinecone + OpenAI Embeddings のように複数のサービスを組み合わせ、チャンキング処理・Embedding API の認証管理・Vector Index のスケーリング・ライフサイクル管理をすべて自前で担う必要があった。Amazon Bedrock Knowledge Bases (2024 年 Q1 GA) はこれらをフルマネージドで提供し、RAG 基盤の実装負担を大幅に削減する。取込からベクトル検索・回答生成までを同一 AWS アカウント内で完結できるため、データが外部サービスに送出されないセキュリティ面の利点もある。
| 工程 | 自前実装の負担 | Bedrock KB のマネージド範囲 |
|---|---|---|
| ドキュメント取込 | S3 → 手動変換・パースコード | Ingestion Job で自動化 |
| チャンキング | コード実装・チューニング | DEFAULT / HIERARCHICAL / SEMANTIC から選択 |
| Embedding API | 認証・バッチ・リトライ管理 | モデル選択のみ・自動呼び出し |
| Vector Index 管理 | スケーリング・バックアップを自前 | OpenSearch Serverless に委譲 |
| Retrieve API | SDK 統合・プロンプト設計 | retrieve_and_generate 1 API で完結 |
- 即日開始: AWS アカウントのみで KB を作成可能。LangChain 依存解消や VectorDB 契約が不要
- 運用省力化: Embedding API 認証・Vector Index スケーリングは KB が自動管理。エンジニアの運用負担を削減
- IaC 対応:
aws_bedrockagent_knowledge_base1 リソースで Terraform 宣言的管理できる - 固定コスト注意: OpenSearch Serverless 最小 2 OCU (~$350/月) が常時発生。小規模 PoC では割高になる場合がある
1-3. ユースケース 4 選
Bedrock RAG が特に効果を発揮する代表的なユースケースを示す。
| ユースケース | データ量目安 | 更新頻度 | 精度要件 | 推奨チャンキング |
|---|---|---|---|---|
| (a) 社内 FAQ bot | 数百〜数千 Q&A | 月次 | 中 | SEMANTIC (文脈保持) |
| (b) 製品マニュアル検索 | 数十 PDF (数千ページ) | 四半期 | 高 | HIERARCHICAL (長文 PDF) |
| (c) コードレビュー補助 | リポジトリ + ガイドライン | 週次 | 中 | FIXED_SIZE (コード断片向け) |
| (d) 契約書分析 | 数百〜数千件 PDF | 随時追加 | 最高 | HIERARCHICAL + Guardrails |
各ユースケースの特性をさらに補足する。
- (a) 社内 FAQ bot は Q&A 形式のデータが多く、1 チャンクが 1 Q&A ペアに収まりやすいため SEMANTIC チャンキングが文脈を保ちやすい。Slack・Confluence・社内 Wiki を S3 に定期エクスポートして連携するパターンが一般的。
- (b) 製品マニュアル は数十〜数百ページの PDF が中心で、章をまたぐ参照関係が多い。HIERARCHICAL チャンキングはページ単位の親チャンクと段落単位の子チャンクの 2 層構造で参照精度を高める。
- (c) コードレビュー補助 はコードブロックの境界でチャンクを切る必要があり、FIXED_SIZE でトークン数を揃えつつ関数境界を意識した前処理が効果的。
- (d) 契約書分析 は誤った引用が法的リスクにつながるため、Guardrails で「根拠なき断言を禁止する」トピックフィルターを設定することが必須。
更新頻度が高いデータソースは INCREMENTAL sync を活用して取込コストを抑えることができる。チャンキング戦略の詳細は §4 で解説する。
1-4. 従来検索との違い
キーワード検索 (BM25/Lucene) とベクトル検索の根本的な違いを理解することで、自社ユースケースに最適な検索設計を選択できる。
| 検索方式 | 仕組み | 得意な質問 | 苦手な質問 |
|---|---|---|---|
| BM25 (全文検索) | キーワード出現頻度 × 逆文書頻度でスコアリング | 固有名詞・型番・正確な文字列一致 | 「〜みたいなもの」の意味的検索 |
| ベクトル検索 (kNN) | Embedding ベクトル間のコサイン類似度 | 意味・文脈の類似性 | 完全一致・レアキーワード検索 |
| ハイブリッド (BM25+kNN) | RRF (Reciprocal Rank Fusion) でスコア統合 | 両タイプの質問に対応 | チューニングが複雑になる |
2026 年 4 月時点の OpenSearch Serverless は BM25 + kNN (faiss/nmslib) + sparse neural search の 3 方式に対応している。sparse neural search は BERT 系モデルで語彙ベクトルを生成し、全文検索の語彙精度とベクトル検索の意味理解を自然に統合する手法だ。本記事のハンズオンでは kNN (dense vector) を中心に扱い、ハイブリッド検索の発展的活用は §5 のコラムで補足する。
また、LLM のコンテキストウィンドウの拡大 (Claude 3.5 Sonnet v2 は 200K tokens) により「すべてのドキュメントをプロンプトに詰め込む」アプローチも技術的には可能になっているが、検索精度・コスト・レイテンシの観点から、Vector Index による Top-K 検索は依然として RAG の推奨アーキテクチャとなっている (2026 年 4 月時点)。
1-5. この記事で到達する範囲
本記事の 8 章を通じて、terraform apply で全インフラを構築し python retrieve_and_generate.py を実行して RAG の応答と citation (出典チャンク) を確認できる状態を目指す。Agents まで進めれば「RAG 検索 + 外部 API 呼び出し」の合わせ技エージェントも体験できる。
| 章 | 内容 | 主な成果物 |
|---|---|---|
| §1 | RAG とは・ユースケース・従来検索との違い | 概念理解 |
| §2 | Knowledge Bases 概論 + データソース設計 | IAM role HCL + S3 バケット設計 |
| §3 | OpenSearch Serverless vector index 構築 | Collection + Index Terraform |
| §4 | KB 作成 + Embeddings 選定 + Chunking 戦略 | aws_bedrockagent_knowledge_base HCL |
| §5 | RetrieveAndGenerate API ハンズオン | retrieve_and_generate.py |
| §6 | Bedrock Agents + Action Groups + Lambda 連携 | Agent + Lambda Terraform |
| §7 | 運用: Guardrails + CloudWatch + コスト最適化 | Guardrail HCL + CloudWatch dashboard JSON |
| §8 | まとめ・落とし穴集・terraform destroy 完全手順 | クリーンアップ手順 |
読了時間の目安は約 120 分、terraform apply 〜 Agents 作成を含むハンズオン実機操作は 45〜60 分を見込む。各章は独立性を意識して書かれているため、OpenSearch 構築 (§3) や Agents (§6) のみを参照することも可能だ。なお、本記事全体で使用するコードは hashicorp/aws provider ~> 5.70 / Python 3.11+ / boto3 1.35.x を前提としており、バージョンが異なる場合は動作保証外となる (2026 年 4 月時点)。
1-6. 前提環境
- AWS リージョン: us-east-1 または us-west-2 (Bedrock Knowledge Bases・Agents・OpenSearch Serverless の全機能対応リージョン)
- Python: 3.11 以上 / boto3 1.35.x (2026 年 4 月時点最新安定版)
- Terraform: 1.9.x 以上 / hashicorp/aws provider ~> 5.70
- Bedrock model access 申請済み: Claude 3.5 Sonnet v2・Amazon Titan Text Embeddings v2・Cohere Embed Multilingual v3 — 未申請のまま進めると AccessDenied で詰まる
- IAM 権限: AdministratorAccess 相当 (ハンズオン用。本番環境では最小権限に絞ること)
# 前提環境確認
python --version # Python 3.11.x 以上
pip install "boto3==1.35.76"
terraform -version # Terraform v1.9.x 以上
1-7. 本記事で扱う API バージョンと範囲宣言
Bedrock Knowledge Bases・Agents・OpenSearch Serverless は 2024-2025 年にかけて API・リソースが急速に拡張されている。本記事は 2026 年 4 月時点 の GA 機能のみを対象とし、以下の機能は範囲外とする (将来記事で別途扱う):
- Agents multi-agent collaboration (2025 Q4 GA): 複数エージェント協調。本記事は single agent 構成のみ
- Prompt caching (2026 Q1 GA): プロンプトキャッシュ。本記事の
retrieve_and_generateでは未使用 - Model Distillation / Batch Inference: 推論最適化・バッチ処理系は別テーマ
また Terraform aws_bedrockagent_* リソース群 は 2024 Q3 以降に追加され、provider 5.x 内で破壊的変更が数回発生した履歴がある。本記事は hashicorp/aws ~> 5.70 でピン留めしており、terraform init -upgrade で上位に移る場合は必ず AWS Provider CHANGELOG を確認してほしい。API 仕様・料金・モデル ARN は執筆時点 (2026 年 4 月) の AWS 公式 doc を一次ソースとし、読者自身の実行タイミングで再確認することを強く推奨する。
2. Knowledge Bases for Amazon Bedrock 概論 + データソース設計
2-1. Knowledge Base / Data Source / Vector Store の 3 層モデル
Amazon Bedrock Knowledge Bases のアーキテクチャは Knowledge Base (KB) / Data Source / Vector Store の 3 層で構成されている。各層の役割を理解することで Terraform での宣言設計がスムーズになる。
- Knowledge Base (KB) — RAG 処理のトップレベルエントリーポイント。
retrieve/retrieve_and_generateAPI のエンドポイントとなり、Embeddings モデルや Vector Store の接続設定を保持する - Data Source — ドキュメントの取込元を定義する設定単位。S3 バケット・Web Crawler の URL・Confluence スペースなどを指定する。1 つの KB に複数の Data Source を関連付けられる
- Vector Store — Embedding ベクトルを格納・検索する永続ストレージ。本記事では Amazon OpenSearch Serverless を使用する。1 つの KB に対して 1 つの Vector Store が紐付く制約があることを必ず押さえておく
この 3 層の制約を図で表すと 1 KB : N Data Source : 1 Vector Store となる。複数の Data Source (例: 製品マニュアル S3 + コーポレートサイト Web Crawler) を同一 KB に登録し、Ingestion Job を実行すると、全データソースのチャンクが単一の Vector Store に蓄積される。検索時はその Vector Store 全体から Top-K 件を取得する仕組みだ。

Ingestion Job (データ取込処理) は Data Source 単位でトリガーする。Job 実行中に S3 に新しいドキュメントを追加しても、次回 Ingestion Job まで Vector Store には反映されない点に注意が必要だ。自動化には EventBridge Scheduler + Lambda で定期実行するパターンが一般的で、§7 で扱う。

2-2. データソース 3 種の比較
Knowledge Bases が対応するデータソースの種類は 2026 年 4 月時点で S3 / Web Crawler / Confluence (SharePoint・Salesforce を含む) の 3 カテゴリがある。自社のドキュメント管理形態に応じて選択する。
| データソース | 対応ファイル形式 | 更新方式 | 料金モデル |
|---|---|---|---|
| S3 | PDF・TXT・HTML・DOCX・Markdown・CSV | FULL_SYNC / INCREMENTAL | S3 ストレージ + Embedding API 従量 |
| Web Crawler | HTML (クロール対象ページ) | FULL_SYNC のみ (2026-04 時点) | Embedding API 従量 (クロール料金なし) |
| Confluence | Confluence Page・Space | FULL_SYNC / INCREMENTAL | Confluence API アクセス料 + Embedding API 従量 |
FULL_SYNC は全ドキュメントを再スキャンするためドキュメント削除・移動にも対応するが、大規模 KB では Embedding コストが増加する。INCREMENTAL は S3 の ETag・更新タイムスタンプで差分検出するため取込コストを抑えられる。更新頻度の高いデータソースには INCREMENTAL の使用を推奨する。
- S3 を選ぶとき: 社内 PDF・Word・Markdown など静的ドキュメント群を管理している場合。最も柔軟でファイル形式対応が広い。機密データは S3 バケットポリシーで閉域化できる
- Web Crawler を選ぶとき: 製品の公開ドキュメントサイト・コーポレートサイトを KB 化したい場合。S3 への手動エクスポートが不要になる。社外サイトをクロールする際は robots.txt および利用規約を必ず確認し、法務レビューを経ること
- Confluence を選ぶとき: 社内ナレッジが Confluence Space に集積されている場合。スペース単位で取込範囲を制御できる。SharePoint・Salesforce も同様に Enterprise Connector として利用可能 (2025 Q2 GA)
2-3. S3 データソース設計
S3 データソースは最も汎用的かつ対応ファイル形式が豊富なため、初めて Knowledge Base を構築する際の第一選択肢となる。設計上の重要ポイントを以下に示す。
バケット構造の推奨例:
s3://my-kb-bucket/
docs/
product-manuals/
v1.0/
product-a.pdf
product-a.pdf.metadata.json ← メタデータサイドカー
v2.0/
product-a.pdf
faq/
support-faq.html
legal/
contract-templates/
nda-template.pdf
prefix を用途別に分けることで、Data Source の inclusionPrefixes / exclusionPrefixes フィルターを使って取込範囲を精緻に制御できる。例えば docs/legal/ のみを対象とした法務専用 Data Source を作ることも可能だ。
各ドキュメントと同名 + .metadata.json 拡張子のサイドカーファイルを S3 に配置することで、チャンクに department / version / language などのカスタムメタデータを付与できる。Retrieve 時のメタデータフィルタリングに活用する。
Terraform — S3 バケット:
resource "aws_s3_bucket" "kb_bucket" {
bucket = "${local.project_name}-kb-docs"
tags= local.common_tags
}
resource "aws_s3_bucket_versioning" "kb_bucket" {
bucket = aws_s3_bucket.kb_bucket.id
versioning_configuration { status = "Enabled" }
}
resource "aws_s3_bucket_public_access_block" "kb_bucket" {
bucket= aws_s3_bucket.kb_bucket.id
block_public_acls = true
block_public_policy = true
ignore_public_acls= true
restrict_public_buckets = true
}
2-4. Web Crawler データソース設計
Web Crawler データソースは 2024 年 GA となった機能で、指定した seed URL を起点にページを再帰的にクロールし、HTML コンテンツを Knowledge Base に取り込む (2026 年 4 月時点)。
設計のポイント:
- seed URL — クロールの起点 URL を最大 10 件まで指定できる。サイトのトップページを指定すると配下のページを自動探索する
- crawl scope —
HOST_ONLYはシードと同じホスト内のみ、SUBDOMAINSはサブドメインも含めてクロールする。外部 URL へのリンクは常に除外される - rate limit — デフォルトは 300 ミリ秒間隔。対象サイトの robots.txt に
Crawl-delayが設定されている場合はその値を優先すること - 法務確認 — 社外の公開サイトをクロールする場合、そのサイトの利用規約・robots.txt を確認し、法務部門のレビューを必ず経ること。
User-Agent: Bedrock-Knowledge-Base-Crawlerとして識別される (2026 年 4 月時点)
Terraform — Web Crawler Data Source HCL スケルトン:
resource "aws_bedrockagent_data_source" "web_crawler" {
knowledge_base_id = aws_bedrockagent_knowledge_base.main.id
name = "corporate-site-crawler"
data_source_configuration {
type = "WEB"
web_configuration {
source_configuration {
url_configuration {
seed_urls {
url = "https://docs.example.com/"
}
}
}
crawler_configuration {
crawler_limits {
rate_limit = 300
}
scope = "HOST_ONLY"
}
}
}
vector_ingestion_configuration {
chunking_configuration {
chunking_strategy = "DEFAULT"
}
}
}
2-5. IAM ロール設計
Knowledge Base が AWS サービスを操作するための KB service role を適切に設計することが、Ingestion Job の成功と最小権限原則の両立に不可欠だ。
KB service role に必要な権限:
| IAM アクション | 対象リソース | 用途 |
|---|---|---|
bedrock:InvokeModel | Titan / Cohere モデル ARN | Embedding API 呼び出し |
s3:GetObject | KB バケット | ドキュメント取得 |
s3:ListBucket | KB バケット | Ingestion Job でのオブジェクト列挙 |
aoss:APIAccessAll | OpenSearch Serverless Collection | Vector Index への書き込み・読み取り |
trust policy の principal は bedrock.amazonaws.com を指定する。sts:AssumeRole の Condition で aws:SourceAccount を設定することで、他 AWS アカウントからの不正利用を防ぐことができる。
Terraform — IAM ロール + 最小権限ポリシー:
data "aws_caller_identity" "current" {}
resource "aws_iam_role" "kb_service_role" {
name = "${local.project_name}-kb-service-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Service = "bedrock.amazonaws.com" }
Action = "sts:AssumeRole"
Condition = { StringEquals = {
"aws:SourceAccount" = data.aws_caller_identity.current.account_id
}}
}]
})
tags = local.common_tags
}
resource "aws_iam_role_policy" "kb_service_policy" {
name= "kb-service-policy"
role= aws_iam_role.kb_service_role.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid= "BedrockEmbedding"
Effect= "Allow"
Action= ["bedrock:InvokeModel"]
Resource = [
"arn:aws:bedrock:${local.region}::foundation-model/amazon.titan-embed-text-v2:0",
"arn:aws:bedrock:${local.region}::foundation-model/cohere.embed-multilingual-v3",
]
},
{
Sid= "S3Access"
Effect= "Allow"
Action= ["s3:GetObject", "s3:ListBucket"]
Resource = [aws_s3_bucket.kb_bucket.arn, "${aws_s3_bucket.kb_bucket.arn}/*"]
},
{
Sid= "AOSSAccess"
Effect= "Allow"
Action= ["aoss:APIAccessAll"]
Resource = aws_opensearchserverless_collection.vector_store.arn
}
]
})
}
- bedrock:InvokeModel: 使用する Embedding モデル ARN のみに絞ること。ワイルドカード
bedrock:*は避ける - s3:GetObject / s3:ListBucket: KB 専用バケットの ARN に限定する。
/*を忘れると ListBucket は通っても GetObject が失敗する - aoss:APIAccessAll: OpenSearch Serverless の Data Access Policy と IAM ポリシーの両方で許可が必要。片方だけでは認可エラーになる
- iam:PassRole: KB を作成する IAM ユーザー / ロールは
iam:PassRoleで KB service role を渡す権限が必要。Terraform 実行ロールに付与を忘れずに - Condition の SourceAccount: trust policy に
aws:SourceAccount条件を設定し、confused deputy 攻撃を防ぐ
2-6. Chunking 戦略の概論
Knowledge Base へのデータ取込時、ドキュメントは チャンク (chunk) と呼ばれる小単位に分割されてから Embedding される。チャンクの粒度と方式が Retrieve 精度に直結するため、データの性質に合わせた選択が重要だ。
4 つの Chunking 方式 (2026 年 4 月時点):
| 方式 | 概要 | デフォルト設定 | 適するユースケース |
|---|---|---|---|
| DEFAULT | 固定 300 トークン・20% オーバーラップ | なし (選択時の初期値) | 汎用・短文ドキュメント |
| FIXED_SIZE | トークン数とオーバーラップ率を手動指定 | 任意 | コードブロック・構造化テキスト |
| HIERARCHICAL | 親チャンク (大)・子チャンク (小) の 2 層構造 | 親 8192 / 子 300 tokens | 長文 PDF・章をまたぐ参照が多い文書 |
| SEMANTIC | 文の意味的まとまりで動的に分割 | モデル依存 | FAQ・対話ログ・意味単位が重要な文書 |
HIERARCHICAL は 2024 年 Q3・SEMANTIC は 2025 年 Q4 にそれぞれ GA となった (2026 年 4 月時点)。最新の対応状況は Knowledge Bases 公式ドキュメント で確認してください。Chunking 方式の選定フローと Terraform での設定方法は §4 で詳しく解説する。§3 では Knowledge Base の Embedding ベクトルを格納する OpenSearch Serverless vector index の構築を行う。
3. OpenSearch Serverless で vector index を構築する
3-1. OpenSearch Serverless 概観 — OCU と料金構造
Amazon OpenSearch Serverless はクラスター管理が不要なフルマネージド OpenSearch サービスです。Bedrock Knowledge Bases のベクトルストアとして最も簡単に統合できる選択肢です。プロビジョンドの OpenSearch Service と比較すると、容量計画が不要な代わりに OCU (OpenSearch Compute Unit) 単位で時間課金されます。
OCU は vCPU 2 コアと RAM 8 GB に相当するリソース単位で、indexing 専用と search 専用の 2 種類があります。Bedrock Knowledge Bases では最小 2 OCU (indexing 1 + search 1) が必要です。2024 年以前は 1 OCU 最小が可能でしたが廃止されているため注意してください。OCU 単価は $0.24/OCU/時間 (2026 年 4 月時点 / us-east-1)。月額最低コスト: 2 OCU × 730 時間 × $0.24 = $350.40/月。OCU は 24/7 で課金されるため、検証環境では作業後に collection を削除することを強く推奨します。最新料金は AWS OpenSearch Service Pricing でご確認ください。
3-2. Collection 作成 — Terraform 定義
aws_opensearchserverless_collection で VECTORSEARCH タイプの collection を作成します。encryption_policy と network_policy は collection より先に作成が必要なため depends_on で順序制御します。
resource "aws_opensearchserverless_security_policy" "encryption" {
name = "${local.project_name}-enc"
type = "encryption"
policy = jsonencode({
Rules = [{ ResourceType = "collection", Resource = ["collection/${local.project_name}-vector"] }]
AWSOwnedKey = true
})
}
resource "aws_opensearchserverless_security_policy" "network" {
name = "${local.project_name}-net"
type = "network"
policy = jsonencode([{
Rules = [
{ ResourceType = "collection", Resource = ["collection/${local.project_name}-vector"] },
{ ResourceType = "dashboard", Resource = ["collection/${local.project_name}-vector"] }
]
AllowFromPublic = true
}])
}
resource "aws_opensearchserverless_collection" "kb_vector" {
name = "${local.project_name}-vector"
type = "VECTORSEARCH"
tags = local.common_tags
depends_on = [
aws_opensearchserverless_security_policy.encryption,
aws_opensearchserverless_security_policy.network,
]
}
AWSOwnedKey = true は AWS 所有 KMS キー、AllowFromPublic = true は開発用パブリックアクセスです。本番環境では CMK (KmsARN 指定) と VPC エンドポイント経由のプライベートアクセスへの切り替えを推奨します。
3-3. Access Policy 設計 — KB service role への権限付与
Bedrock KB は専用の service role で OpenSearch Serverless にアクセスします。aws_opensearchserverless_access_policy でそのロールに aoss:* 権限を付与します。
resource "aws_opensearchserverless_access_policy" "kb_access" {
name = "${local.project_name}-kb-access"
type = "data"
policy = jsonencode([{
Rules = [
{
ResourceType = "index"
Resource = ["index/${local.project_name}-vector/*"]
Permission= ["aoss:*"]
},
{
ResourceType = "collection"
Resource = ["collection/${local.project_name}-vector"]
Permission= ["aoss:*"]
}
]
Principal = [aws_iam_role.bedrock_kb_role.arn]
}])
}
Principal には §2 で作成した Bedrock KB service role の ARN を指定します。index と collection の両レベルへの権限が必要です。aws_bedrockagent_knowledge_base の apply 前に access policy が完成していることを確認してください。
3-4. Vector Index 構築 — Terraform 未対応のため curl で作成する
vector index の作成は hashicorp/aws provider では未対応 (2026 年 4 月時点) です。curl または Python で OpenSearch REST API を直接呼び出して作成します。
KB 用必須フィールド (3 点)
| フィールド名 | 型 | 説明 |
|---|---|---|
bedrock-knowledge-base-default-vector | knn_vector (dim=1024) | ベクトル埋め込みを格納 |
AMAZON_BEDROCK_TEXT_CHUNK | text | チャンク化された原文テキスト |
AMAZON_BEDROCK_METADATA | keyword | S3 URI などのメタデータ |

ENDPOINT=$(terraform output -raw opensearch_collection_endpoint)
INDEX_NAME="bedrock-kb-index"
curl -XPUT "https://${ENDPOINT}/${INDEX_NAME}" \
--aws-sigv4 "aws:amz:us-east-1:aoss" \
--user "${AWS_ACCESS_KEY_ID}:${AWS_SECRET_ACCESS_KEY}" \
-H "Content-Type: application/json" \
-H "x-amz-security-token: ${AWS_SESSION_TOKEN}" \
-d '{
"settings": { "index": { "knn": true } },
"mappings": {
"properties": {
"bedrock-knowledge-base-default-vector": {
"type": "knn_vector",
"dimension": 1024,
"method": { "name": "hnsw", "engine": "faiss" }
},
"AMAZON_BEDROCK_TEXT_CHUNK": { "type": "text" },
"AMAZON_BEDROCK_METADATA":{ "type": "keyword" }
}
}
}'
engine は faiss (高速・大規模向け) と nmslib (精度重視) から選択できます。Knowledge Bases の推奨は faiss です。dimension は Titan v2 / Cohere v3 の標準次元数 1024 に合わせます。Cohere 384 次元モデルを使う場合は "dimension": 384 に変更してください。
- bedrock-knowledge-base-default-vector — knn_vector 型 (dimension=1024)。ベクトル埋め込みを格納するメインフィールド。
- AMAZON_BEDROCK_TEXT_CHUNK — text 型。Bedrock がチャンク後に格納するテキスト原文。
- AMAZON_BEDROCK_METADATA — keyword 型。S3 URI やメタデータ JSON を格納するフィールド。
この 3 フィールド名は Bedrock KB の仕様で固定です。大文字・小文字・ハイフン・アンダースコアを一字も変更してはいけません。
Python 版は requests-aws4auth で SigV4 署名を付与します。
import boto3, requests
from requests_aws4auth import AWS4Auth
def create_kb_index(endpoint: str, index_name: str, dimension: int = 1024, region: str = "us-east-1"):
creds = boto3.Session().get_credentials().get_frozen_credentials()
auth = AWS4Auth(creds.access_key, creds.secret_key, region, "aoss", session_token=creds.token)
body = {
"settings": {"index": {"knn": True}},
"mappings": {
"properties": {
"bedrock-knowledge-base-default-vector": {
"type": "knn_vector", "dimension": dimension,
"method": {"name": "hnsw", "engine": "faiss"},
},
"AMAZON_BEDROCK_TEXT_CHUNK": {"type": "text"},
"AMAZON_BEDROCK_METADATA":{"type": "keyword"},
}
},
}
resp = requests.put(f"https://{endpoint}/{index_name}", auth=auth, json=body)
resp.raise_for_status()
return resp.json()
3-5. コンソール代替手順 — Quick create でフォールバックする
Terraform での vector index 作成に問題が発生した場合、Bedrock コンソールの “Quick create” で OpenSearch Serverless を自動構成できます。
- AWS マネジメントコンソール → Amazon Bedrock → Knowledge Bases → Create knowledge base
- Vector store セクションで “Quick create a new vector store” を選択
- collection と vector index が自動作成される (index フィールド設定も自動)
- KB 作成後、collection ID をコンソールで確認し
terraform importで Terraform 管理に取り込む
Quick create は IaC としての管理性が失われます。security_policy・network_policy・access_policy もそれぞれ個別に import が必要なため、最初から Terraform で構築する方がトータルコストは低くなります。
3-6. コールドスタートとレイテンシ — 本番運用で必ず考慮する
OpenSearch Serverless は一定時間リクエストがない場合に休眠状態になり、初回リクエストで 30〜60 秒のレイテンシ が発生する場合があります (AWS 公式ドキュメント記載・2026 年 4 月時点)。
import boto3
def warmup_knowledge_base(kb_id: str, region: str = "us-east-1") -> None:
"""OpenSearch Serverless のコールドスタートを防ぐ warmup 関数"""
client = boto3.client("bedrock-agent-runtime", region_name=region)
try:
client.retrieve(
knowledgeBaseId=kb_id,
retrievalQuery={"text": "warmup"},
retrievalConfiguration={"vectorSearchConfiguration": {"numberOfResults": 1}},
)
except Exception:
pass # warmup のため結果は無視
- warmup ping を実装する: Lambda 起動時やアプリ初期化時にダミークエリを投げて OpenSearch を活性化する。
- タイムアウトを長く設定する: boto3 の
read_timeoutを 90 秒以上に設定し、初回クエリのタイムアウトを防ぐ。 - EventBridge Scheduler でキープアライブ: 5 分間隔でダミークエリを送ることで常時アクティブ状態を維持できる。
- ユーザーへのフィードバック: UI 側でコールドスタート中は “回答を準備中…” のローディング表示を設ける。
4. Knowledge Base 作成 + Embeddings model 選択 + Chunking 戦略
4-1. Knowledge Base Terraform 定義
aws_bedrockagent_knowledge_base が正式リソース名です(aws_bedrock_knowledge_base は誤り)。locals でモデル ARN と次元数を変数化しておくと、Embeddings model の切り替えが容易になります。
locals {
region = "us-east-1"
embedding_models = {
titan_v2 = {
arn = "arn:aws:bedrock:${local.region}::foundation-model/amazon.titan-embed-text-v2:0"
dimension = 1024
}
cohere_multilingual_v3 = {
arn = "arn:aws:bedrock:${local.region}::foundation-model/cohere.embed-multilingual-v3"
dimension = 1024
}
}
selected_model = local.embedding_models["titan_v2"]
}
resource "aws_bedrockagent_knowledge_base" "this" {
name = "my-rag-kb"
role_arn = aws_iam_role.kb_role.arn
knowledge_base_configuration {
type = "VECTOR"
vector_knowledge_base_configuration {
embedding_model_arn = local.selected_model.arn
}
}
storage_configuration {
type = "OPENSEARCH_SERVERLESS"
opensearch_serverless_configuration {
collection_arn = aws_opensearchserverless_collection.this.arn
vector_index_name = "bedrock-kb-index"
field_mapping {
vector_field= "bedrock-knowledge-base-default-vector"
text_field = "AMAZON_BEDROCK_TEXT_CHUNK"
metadata_field = "AMAZON_BEDROCK_METADATA"
}
}
}
tags = { Project = "bedrock-rag-handson", ManagedBy = "Terraform" }
}
field_mapping の 3 フィールド名(bedrock-knowledge-base-default-vector / AMAZON_BEDROCK_TEXT_CHUNK / AMAZON_BEDROCK_METADATA)は Bedrock KB のデフォルト値です。§3 で作成した OpenSearch Serverless index の mapping と完全一致させてください。
4-2. Embeddings model 2 択
2026 年 4 月時点で選ぶべき Embeddings model は主に 2 種類です。
| 項目 | Titan Text Embeddings v2 | Cohere Embed Multilingual v3 |
|---|---|---|
| 対応言語 | 英語・日本語・中国語等 | 100 言語以上 |
| 次元数 | 1024(固定) | 1024 / 512 / 384(選択可) |
| 料金(2026年4月時点) | $0.00002 / 1K input tokens | $0.0001 / 1K tokens |
| コスト比較 | 1/5 のコスト | 多言語で精度優位 |
- 100 言語以上の混在ドキュメントを扱う → Cohere Embed Multilingual v3
- 日本語・英語中心でコスト優先 → Titan Text Embeddings v2(Cohere の 1/5 コスト)
- 次元数を 384 に削減してストレージ節約したい → Cohere v3(dimension=384 オプション)
- 迷ったら → Titan v2 から始めて、精度不足なら Cohere v3 に移行
※ 料金は 2026 年 4 月時点の概算です。最新単価は AWS Bedrock Pricing で必ず確認してください。
4-3. Chunking 戦略 4 方式
2026 年 4 月時点で 4 方式が利用可能です。
| 方式 | chunk size | GA 時期 | 適用ユースケース |
|---|---|---|---|
| DEFAULT | 300 tokens(overlap 20%) | 2024 Q1 | 汎用・まず試すならこれ |
| FIXED_SIZE | 任意指定 | 2024 Q1 | 均一サイズが必要な場合 |
| HIERARCHICAL | parent 1500 / child 300 tokens | 2024 Q3 GA | 長文 PDF・技術マニュアル |
| SEMANTIC | 文脈で自動分割 | 2025 Q4 GA | FAQ・Q&A・文脈保持重要 |
HIERARCHICAL は親 chunk(文脈保持)と子 chunk(検索精度)の 2 層構造で長文 PDF に有効です。SEMANTIC は文章の意味境界で自動分割するため、FAQ や Q&A 形式のドキュメントに特に有効です(2025 年 Q4 GA・2026 年 4 月時点)。
resource "aws_bedrockagent_data_source" "this" {
knowledge_base_id = aws_bedrockagent_knowledge_base.this.id
name = "product-manuals"
data_source_configuration {
type = "S3"
s3_configuration {
bucket_arn= aws_s3_bucket.kb_docs.arn
inclusion_prefixes = ["docs/"]
}
}
vector_ingestion_configuration {
chunking_configuration {
chunking_strategy = "HIERARCHICAL"
hierarchical_chunking_configuration {
level_configuration { max_tokens = 1500 }
level_configuration { max_tokens = 300 }
overlap_tokens = 60
}
}
}
}
- DEFAULT: 300 tokens 均等分割。最初の検証・汎用途に
- FIXED_SIZE: chunk_size を明示指定。均一サイズが必要な場合
- HIERARCHICAL(2024 Q3 GA): 長文 PDF・技術マニュアルに強い。親/子 2 層で文脈を保持
- SEMANTIC(2025 Q4 GA): 意味境界で自動分割。FAQ・Q&A で文脈保持精度が向上
4-4. Data Source 登録 + Ingestion job
terraform apply でデータソースを登録後、手動で ingestion job を起動する必要があります(terraform apply だけでは自動取り込みされません)。
# Knowledge Base ID と Data Source ID を取得
KB_ID=$(terraform output -raw knowledge_base_id)
DS_ID=$(terraform output -raw data_source_id)
# Ingestion job を起動
aws bedrock-agent start-ingestion-job \
--knowledge-base-id "$KB_ID" \
--data-source-id "$DS_ID" \
--region us-east-1
4-5. Ingestion job ステータス監視
get-ingestion-job でステータスを確認します。ステータス遷移: STARTING → IN_PROGRESS → COMPLETE または FAILED。
# ポーリングスクリプト例
while true; do
STATUS=$(aws bedrock-agent get-ingestion-job \
--knowledge-base-id "$KB_ID" \
--data-source-id "$DS_ID" \
--ingestion-job-id "$JOB_ID" \
--region us-east-1 \
--query 'ingestionJob.status' \
--output text)
echo "Status: $STATUS"
if [ "$STATUS" = "COMPLETE" ] || [ "$STATUS" = "FAILED" ]; then break; fi
sleep 30
done
# 失敗時は詳細を確認
aws bedrock-agent get-ingestion-job \
--knowledge-base-id "$KB_ID" \
--data-source-id "$DS_ID" \
--ingestion-job-id "$JOB_ID" \
--region us-east-1 \
--query 'ingestionJob.{status: status, stats: statistics, reasons: failureReasons}'
100 ドキュメント(平均 10K tokens)の場合、取り込みには 3〜5 分程度かかります(2026 年 4 月時点の目安。ドキュメント数・サイズ・chunking 方式により変動します)。
ingestion job が COMPLETE になったら、次の §5 で retrieve_and_generate API を使って実際にクエリを投げていきましょう。
5. RetrieveAndGenerate API ハンズオン (Python boto3)
5-1. boto3 クライアント初期化
Bedrock の Python SDK には 2 種類のクライアントがあります。混同するとエラーになるため最初に整理します。
| クライアント名 | 用途 | 主な API |
|---|---|---|
bedrock-agent | 管理系(KB・Agent CRUD / ingestion job) | start_ingestion_job, list_knowledge_bases |
bedrock-agent-runtime | 呼び出し系(検索・生成・エージェント実行) | retrieve, retrieve_and_generate, invoke_agent |
import boto3
from botocore.config import Config
def make_runtime_client(region: str = "us-east-1"):
return boto3.client(
"bedrock-agent-runtime",
region_name=region,
config=Config(
retries={"max_attempts": 5, "mode": "adaptive"},
read_timeout=60,
connect_timeout=10,
),
)
retries={"mode": "adaptive"} により ThrottlingException 発生時は SDK が自動リトライします。read_timeout=60 は LLM 生成が長引く場合のタイムアウト防止です。
5-2. RetrieveAndGenerate API 基本呼び出し
retrieve_and_generate は「ベクトル検索 + LLM 生成」を 1 API コールで完結させる最もシンプルな RAG パターンです。

KB_ID = "XXXXXXXXXX"
MODEL_ARN = "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-3-5-sonnet-20241022-v2:0"
def retrieve_and_generate(client, query: str, kb_id=KB_ID, model_arn=MODEL_ARN, session_id=None):
req = {
"input": {"text": query},
"retrieveAndGenerateConfiguration": {
"type": "KNOWLEDGE_BASE",
"knowledgeBaseConfiguration": {"knowledgeBaseId": kb_id, "modelArn": model_arn},
},
}
if session_id:
req["sessionId"] = session_id
return client.retrieve_and_generate(**req)
client = make_runtime_client()
resp = retrieve_and_generate(client, "ECS Blue/Green デプロイの手順を教えてください")
print(resp["output"]["text"])
for c in resp.get("citations", []):
for ref in c.get("retrievedReferences", []):
print(ref.get("location", {}).get("s3Location", {}).get("uri", "N/A"))
print(f"sessionId: {resp.get('sessionId')}")
主なレスポンスフィールド(2026年4月時点): output.text(回答テキスト)/ citations[].retrievedReferences[].content.text(根拠 chunk)/ citations[].retrievedReferences[].location.s3Location.uri(出典 URI)/ sessionId(マルチターン継続用)。
input.text: ユーザーの質問文(文字列)retrieveAndGenerateConfiguration.type:"KNOWLEDGE_BASE"固定knowledgeBaseConfiguration.knowledgeBaseId: §4 で作成した KB IDknowledgeBaseConfiguration.modelArn: 生成モデル ARN(Claude 3.5 Sonnet v2 または Nova Pro 推奨)sessionId(任意): マルチターン会話時は前回の sessionId を引き回す
5-3. Retrieve 単体 API
LLM を自前で選びたい場合や、プロンプトを細かく制御したい場合は retrieve 単体 API を使います。
import json
def retrieve_and_invoke(client, bedrock_rt, query: str, kb_id: str) -> str:
# Step 1: ベクトル検索で関連 chunk を取得
chunks = client.retrieve(
knowledgeBaseId=kb_id,
retrievalQuery={"text": query},
retrievalConfiguration={"vectorSearchConfiguration": {"numberOfResults": 5}},
)["retrievalResults"]
# Step 2: chunk をコンテキストに組み込んで自前 invoke_model
context = "\n\n".join(
f"[出典: {c.get('location', {}).get('s3Location', {}).get('uri', 'N/A')}]\n{c['content']['text']}"
for c in chunks
)
body = json.dumps({
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 1024,
"messages": [{"role": "user", "content": f"<context>\n{context}\n</context>\n\n質問: {query}"}],
})
resp = bedrock_rt.invoke_model(
modelId="anthropic.claude-3-5-sonnet-20241022-v2:0", body=body
)
return json.loads(resp["body"].read())["content"][0]["text"]
5-4. セッション継続
初回応答の sessionId を次回リクエストに渡すことで、前の質問の文脈を保ちながらマルチターン会話が可能です。
session_id = None
for question in ["ユースケースを教えて", "コストが高いのはどれ?", "削減策は?"]:
resp = retrieve_and_generate(client, query=question, kb_id=KB_ID,
model_arn=MODEL_ARN, session_id=session_id)
session_id = resp.get("sessionId")
print(f"Q: {question}\nA: {resp['output']['text']}\n")
セッション TTL は 24 時間(2026 年 4 月時点)。TTL 超過後に同じ sessionId を使うとエラーになるため、長期運用では有効期限管理が必要です。
5-5. エラーハンドリング
主な例外: ThrottlingException(TPS 超過・backoff でリトライ)/ ResourceNotFoundException(KB ID 誤り)/ ValidationException(入力長超過)/ AccessDeniedException(IAM / モデルアクセス未許可)。
import functools, random, time, logging
from botocore.exceptions import ClientError
log = logging.getLogger(__name__)
RETRYABLE = {"ThrottlingException", "ServiceUnavailableException", "InternalServerException"}
def with_retry(max_attempts: int = 5):
def decorator(fn):
@functools.wraps(fn)
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return fn(*args, **kwargs)
except ClientError as e:
code = e.response["Error"]["Code"]
if code not in RETRYABLE or attempt == max_attempts - 1:
raise
delay = min(2 ** attempt + random.random(), 30)
log.warning("retry %d after %.1fs: %s", attempt + 1, delay, code)
time.sleep(delay)
return wrapper
return decorator
@with_retry(max_attempts=5)
def safe_retrieve_and_generate(client, query: str, kb_id: str, model_arn: str):
return client.retrieve_and_generate(
input={"text": query},
retrieveAndGenerateConfiguration={
"type": "KNOWLEDGE_BASE",
"knowledgeBaseConfiguration": {"knowledgeBaseId": kb_id, "modelArn": model_arn},
},
)
- ThrottlingException: backoff + jitter で最大 5 回リトライ(delay 上限 30 秒)
- ResourceNotFoundException:
terraform output -raw knowledge_base_idで KB ID を再確認 - ValidationException: クエリを 512 tokens 以下に短縮(2026年4月時点推奨)
- AccessDeniedException: Bedrock コンソール → Model access でモデルアクセスを申請
5-6. 回答精度の評価観点
retrieve_and_generate の精度は citations の内容を元ドキュメントと照合して評価します。3 つの観点で人手確認するのが基本です。
- Answer Relevance(回答が質問に答えているか): 質問と
output.textを読み比べる - Context Relevance(検索 chunk が質問に関連しているか):
citations[].retrievedReferences[].content.textを確認 - Groundedness(幻覚がないか):
output.textと chunk テキストを照合
for i, c in enumerate(resp.get("citations", []), 1):
for ref in c.get("retrievedReferences", []):
print(f"[chunk {i}] {ref['content']['text'][:100]}...")
print(f" 出典: {ref.get('location',{}).get('s3Location',{}).get('uri','N/A')}")
精度が低い場合は、Chunking を HIERARCHICAL / SEMANTIC に変更して再 ingestion、または numberOfResults を増やして検索幅を拡大してください。§5 で動作確認ができたら、次の §6 では Bedrock Agents を使って外部 API 呼び出しも組み合わせた発展構成を構築します。
6. Bedrock Agents + Action Groups + Lambda 連携
6-1. Bedrock Agents とは
§5 の retrieve_and_generate は「KB 検索 → LLM 1 回」という単方向フローでした。Bedrock Agents はこれを拡張し、ReAct (Reasoning + Acting) パターンで「推論 → ツール呼び出し → 観察 → 再推論」のループを繰り返します。外部 API (Action Group = Lambda) と Knowledge Base 検索を組み合わせた複合アクションを実現できます。
GA タイムライン (2026 年 4 月時点):
– 2024 年 Q3: Bedrock Agents GA
– 2025 年 Q4: multi-agent collaboration GA
本記事の範囲:
aws_bedrockagent_agent_collaboratorを使う multi-agent collaboration は Terraform provider 側の追従がバージョンによって差があります。本記事は single agent 構成のみを扱い、multi-agent collaboration は範囲外とします。
| 比較軸 | KB 単体 (retrieve_and_generate) | Bedrock Agents |
|---|---|---|
| 外部 API 呼び出し | 不可 | 可能 (Action Group = Lambda) |
| LLM 呼び出し回数 | 1 回 | 2〜5 回 (ReAct ループ) |
| コスト | 低 | 高め (複数回 invoke) |
6-2. Agent Terraform 定義
Agent の IAM ロールには bedrock:InvokeModel と lambda:InvokeFunction の両権限が必要です。trust policy の principal は bedrock.amazonaws.com です。
resource "aws_iam_role" "bedrock_agent" {
name= "${local.project_name}-agent-role"
assume_role_policy = jsonencode({
Version= "2012-10-17"
Statement = [{ Effect = "Allow", Principal = { Service = "bedrock.amazonaws.com" }, Action = "sts:AssumeRole" }]
})
}
resource "aws_iam_role_policy" "bedrock_agent" {
name= "${local.project_name}-agent-policy"
role= aws_iam_role.bedrock_agent.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{ Effect = "Allow", Action = ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream"],
Resource = "arn:aws:bedrock:${local.region}::foundation-model/*" },
{ Effect = "Allow", Action = "lambda:InvokeFunction",
Resource = aws_lambda_function.action_handler.arn }
]
})
}
resource "aws_bedrockagent_agent" "main" {
agent_name = "${local.project_name}-agent"
agent_resource_role_arn = aws_iam_role.bedrock_agent.arn
foundation_model = "anthropic.claude-3-5-sonnet-20241022-v2:0"
idle_session_ttl_in_seconds = 600
instruction = <<-EOT
まず Knowledge Base を使って関連情報を検索してから回答してください。
Knowledge Base に情報がない場合のみ、在庫確認ツールを使って補完してください。
回答は簡潔かつ正確に、日本語で返してください。
EOT
tags = local.common_tags
}
instruction に「KB を使ってから回答」と明記することで、Agent が KB-first な動作をするよう誘導できます。この指示が ReAct ループの推論品質に直結します。
6-3. Action Group + Lambda
OpenAPI 3.0 schema で Action Group のエンドポイント仕様を宣言し、Lambda で実処理します。
resource "aws_s3_object" "openapi_schema" {
bucket = aws_s3_bucket.kb_bucket.id
key = "agent/openapi-schema.yaml"
content= file("${path.module}/openapi-schema.yaml")
content_type = "application/x-yaml"
}
resource "aws_bedrockagent_agent_action_group" "inventory" {
agent_id = aws_bedrockagent_agent.main.agent_id
agent_version = "DRAFT"
action_group_name = "InventoryActionGroup"
description = "在庫確認を行うアクショングループ"
action_group_executor {
lambda = aws_lambda_function.action_handler.arn
}
api_schema {
s3 {
s3_bucket_name = aws_s3_bucket.kb_bucket.id
s3_object_key = aws_s3_object.openapi_schema.key
}
}
depends_on = [aws_bedrockagent_agent.main]
}
Action Group OpenAPI schema 最小構成
- openapi: “3.0.0” — 3.0 系のみ対応 (3.1 系は 2026-04 時点未対応)
- operationId — 必須。Agent がツールを識別する際に使用
- description (paths 配下) — Agent の推論に使用。具体的に書くと精度向上
- parameters[].description — 各パラメータの説明も Agent が読む。省略不可
- schema は S3 アップロード (推奨) または inline 文字列の 2 方式をサポート
- 参照: Bedrock Agents API schema 公式ガイド
Lambda handler は OpenAPI 準拠のレスポンス構造 を返す必要があります:
import json, logging
logger = logging.getLogger()
INVENTORY = {
"PROD-001": {"quantity": 42, "warehouse": "東京DC"},
"PROD-002": {"quantity": 0, "warehouse": "大阪DC"},
}
def lambda_handler(event: dict, context) -> dict:
logger.info("event: %s", json.dumps(event))
action_group = event.get("actionGroup", "")
api_path = event.get("apiPath", "")
http_method = event.get("httpMethod", "GET")
# parameters は [{"name": "product_id", "type": "string", "value": "PROD-001"}] 形式
params = {p["name"]: p["value"] for p in event.get("parameters", [])}
if api_path == "/check-inventory" and http_method == "GET":
product_id = params.get("product_id", "")
data = INVENTORY.get(product_id, {"error": f"商品 '{product_id}' は見つかりません"})
status = 200
else:
data = {"error": f"Unknown path: {api_path}"}
status = 400
return {
"actionGroup": action_group,
"apiPath": api_path,
"httpMethod": http_method,
"httpStatusCode": status,
# responseBody."application/json".body には json.dumps() で文字列化した値を入れる
"responseBody": {
"application/json": {
"body": json.dumps(data, ensure_ascii=False)
}
},
}
レスポンス構造の注意点: responseBody."application/json".body には JSON オブジェクトをそのまま入れるのではなく、json.dumps() で文字列化した値を入れます (二重エンコードが正しい仕様です)。
6-4. Knowledge Base の Attach
§4 で作成した KB を Agent に紐付けます:
resource "aws_bedrockagent_agent_knowledge_base_association" "main" {
agent_id = aws_bedrockagent_agent.main.agent_id
agent_version = "DRAFT"
knowledge_base_id = aws_bedrockagent_knowledge_base.main.id
knowledge_base_state = "ENABLED"
description = "社内ナレッジベース。製品マニュアル・FAQ・社内規程を格納"
}
knowledge_base_state = "DISABLED" に変えると KB を参照せず Action Group のみで応答します。instruction に「まず Knowledge Base を検索してから」と明記することで KB-first な ReAct ループを促進できます。
6-5. Agent Alias と prepare
Agent の設定変更後は prepare が必要です。prepare は内部で DRAFT をコンパイルして呼び出し可能な状態にします。2026 年 4 月時点の prepare 所要時間は 1〜2 分 です。terraform apply 直後に invoke すると ConflictException が返ることがあるため、ステータスが PREPARED になるのを待ってから呼び出してください。
resource "aws_bedrockagent_agent_alias" "dev" {
agent_id= aws_bedrockagent_agent.main.agent_id
agent_alias_name = "dev"
routing_configuration {
agent_version = "DRAFT"
}
tags = local.common_tags
}
# prepare 実行
aws bedrock-agent prepare-agent --agent-id <AGENT_ID> --region us-east-1
# PREPARED になるまで確認 (1〜2 分待つ)
aws bedrock-agent get-agent --agent-id <AGENT_ID> --query "agent.agentStatus"
# → "PREPARED" で呼び出し可能
本番環境では DRAFT ではなく数値バージョン ("1" / "2") のエイリアスを使い、バージョン切り替えでダウンタイムなし更新を行います。
6-6. InvokeAgent による実行
bedrock-agent-runtime クライアントで Agent を呼び出します。レスポンスは EventStream 形式のストリーミングです。
import boto3, json, uuid, logging
from botocore.config import Config
logger = logging.getLogger(__name__)
def make_agent_client(region: str = "us-east-1"):
return boto3.client(
"bedrock-agent-runtime",
region_name=region,
config=Config(
retries={"max_attempts": 3, "mode": "adaptive"},
read_timeout=120, # ReAct ループで複数回 LLM を呼ぶため長めに設定
),
)
def invoke_agent(client, agent_id: str, alias_id: str,
session_id: str, message: str) -> str:
response = client.invoke_agent(
agentId=agent_id,
agentAliasId=alias_id,
sessionId=session_id,
inputText=message,
enableTrace=True,# ReAct ログを有効化
)
chunks = []
for event in response["completion"]:
if "chunk" in event:
chunks.append(event["chunk"]["bytes"].decode("utf-8"))
elif "trace" in event:
_log_react_trace(event["trace"])
return "".join(chunks)
def _log_react_trace(trace: dict) -> None:
orch = trace.get("trace", {}).get("orchestrationTrace", {})
if "rationale" in orch:
logger.info("[推論] %s", orch["rationale"].get("text", "")[:200])
if "invocationInput" in orch:
logger.info("[ツール呼び出し] %s", json.dumps(orch["invocationInput"]))
if "observation" in orch:
logger.info("[結果] type=%s", orch["observation"].get("type", ""))
trace log で ReAct ループを可視化する
enableTrace=True を設定すると、各 EventStream イベントの trace フィールドに ReAct の思考過程が含まれます。
- orchestrationTrace.rationale.text — 「Knowledge Base を検索して在庫情報を取得する」などの LLM 推論テキスト
- orchestrationTrace.invocationInput — KB 検索クエリまたは Action Group の
apiPathとparameters - orchestrationTrace.observation — ツールの返却値。KB なら retrieved chunks、Lambda なら
responseBody - ループが 2〜5 サイクル繰り返されるのが典型的なパターン
- 本番環境では
enableTrace=Falseにしてレイテンシとコストを削減 - 参照: Bedrock Agents trace events 公式ガイド

7. 運用: Guardrails + CloudWatch Metrics + コスト最適化
7-1. Guardrails 概論
Amazon Bedrock Guardrails は 2024 年に GA し、LLM の入出力を複数フィルタで自動検査・ブロックできます (2026 年 4 月時点)。Knowledge Base / Agent / InvokeModel に guardrail_id + guardrail_version を指定するだけで紐付けられ、追加コードは不要です。
4 つのフィルタ概要:
| フィルタ | 機能 |
|---|---|
| Content filter | 有害コンテンツを NONE / LOW / MEDIUM / HIGH の強度でブロック |
| Denied topics | 特定トピック (競合比較・法律相談等) を name + definition + examples で禁止 |
| Word filter | キーワードリスト + AWS マネージドリスト (profanity 等) でブロック |
| Sensitive info filter | 氏名・電話番号・メールアドレス等を ANONYMIZE または BLOCK |
Guardrails を Knowledge Base に紐付けると retrieve_and_generate の input/output 両方に自動適用されます。Agent に紐付けると ReAct ループ内の全 LLM 呼び出しに適用されます。まず Agent で動作確認し、問題なければ KB にも展開するフローを推奨します。
7-2. Guardrail Terraform 定義
resource "aws_bedrock_guardrail" "main" {
name = "${local.project_name}-guardrail"
blocked_inputs_messaging = "この質問にはお答えできません。"
blocked_outputs_messaging = "回答を生成できませんでした。"
content_policy_config {
filters_config {
type= "SEXUAL"
input_strength = "HIGH"
output_strength = "HIGH"
}
filters_config {
type= "HATE"
input_strength = "HIGH"
output_strength = "HIGH"
}
}
topic_policy_config {
topics_config {
name = "CompetitorComparison"
definition = "競合他社のサービスと自社サービスを比較する質問"
examples= ["AWSとGCPを比較して", "他社のRAGサービスとの違いは?"]
type = "DENY"
}
}
sensitive_information_policy_config {
pii_entities_config {
type= "EMAIL"
action = "ANONYMIZE"
}
pii_entities_config {
type= "PHONE"
action = "ANONYMIZE"
}
}
tags = local.common_tags
}
resource "aws_bedrock_guardrail_version" "main" {
guardrail_id = aws_bedrock_guardrail.main.guardrail_id
description = "production v1"
}
DRAFT バージョンは開発時のみ使用し、本番では必ず aws_bedrock_guardrail_version でバージョンを固定してください。
7-3. Guardrail 料金注記
- Content filter: $0.75 / 1M text units (1,000 文字 = 1 text unit 換算)
- Sensitive info filter (PII masking): $0.10 / 1M text units
- 1,000 クエリ × 2KB テキスト = 約 2,000 text units → 月額 $0.0015 程度
- 大量クエリ (月 100 万件以上) では Guardrail コストも積み上がります。必ず AWS Pricing Calculator (https://calculator.aws) で最新の料金を確認してください。
- 公式 Pricing ページ: https://aws.amazon.com/bedrock/pricing/
7-4. CloudWatch Metrics
Bedrock は AWS/Bedrock namespace にメトリクスを自動送信します (追加設定不要)。主要 8 種類を把握しておきましょう。
| メトリクス | 説明 | 推奨アラーム |
|---|---|---|
Invocations | モデル呼び出し回数 | — |
InputTokenCount | 入力トークン数 (コスト試算に使用) | — |
OutputTokenCount | 出力トークン数 (コスト試算に使用) | — |
InvocationLatency | レスポンス全体のレイテンシ (ms) | p99 > 30,000ms でアラート |
InvocationClientErrors | 4xx エラー (認証失敗・入力不正等) | 5分間 > 5件 |
InvocationServerErrors | 5xx エラー (Bedrock 側障害) | 5分間 > 1件 |
InvocationThrottles | スロットリング回数 | 5分間 > 10件 |
KnowledgeBaseRetrieve | KB retrieve 呼び出し数 | — |
CloudWatch Alarm の Terraform 定義 (スロットリング監視):
resource "aws_cloudwatch_metric_alarm" "bedrock_throttle" {
alarm_name = "${local.project_name}-bedrock-throttle"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 1
metric_name= "InvocationThrottles"
namespace = "AWS/Bedrock"
period = 300
statistic = "Sum"
threshold = 10
alarm_description= "Bedrock スロットリングが5分間で10件超"
alarm_actions = [aws_sns_topic.alerts.arn]
dimensions = {
ModelId = "anthropic.claude-3-5-sonnet-20241022-v2:0"
}
}
Dashboard JSON 断片 (スロットリング + p99 レイテンシ監視):
{
"widgets": [
{
"type": "metric",
"properties": {
"title": "Bedrock InvocationThrottles",
"metrics": [["AWS/Bedrock", "InvocationThrottles",
"ModelId", "anthropic.claude-3-5-sonnet-20241022-v2:0"]],
"period": 300, "stat": "Sum", "region": "us-east-1"
}
},
{
"type": "metric",
"properties": {
"title": "InvocationLatency p99",
"metrics": [["AWS/Bedrock", "InvocationLatency",
"ModelId", "anthropic.claude-3-5-sonnet-20241022-v2:0"]],
"period": 300, "stat": "p99", "region": "us-east-1"
}
}
]
}
7-5. コスト最適化
コスト見積もり表 (2026 年 4 月時点・概算)
重要: 下表は 2026 年 4 月時点の公式料金に基づく概算です。必ず AWS Pricing Calculator で最新料金を確認してください。
| カテゴリ | 計算式 | 月額概算 (USD) |
|---|---|---|
| OpenSearch Serverless 2 OCU × 730h | $0.24/OCU-h | $350.40 |
| Embeddings 初回取込 (Titan v2 / 1M tokens) | $0.00002/1K | $0.02 |
| Embeddings クエリ (Titan v2 / 100K tokens) | $0.00002/1K | $0.002 |
| Generation input (Claude 3.5 Sonnet v2 / 2M tokens) | $0.003/1K | $6.00 |
| Generation output (Claude 3.5 Sonnet v2 / 500K tokens) | $0.015/1K | $7.50 |
| Guardrail content filter (2,000 text units) | $0.75/1M | $0.0015 |
| S3 / Lambda / CloudWatch | Free tier / 微量 | ≈$0.05 |
| 合計 | — | 約 $364/月 |
前提: 1,000 クエリ/月・100 docs (1 doc = 10K tokens)・Claude 3.5 Sonnet v2・Titan v2・us-east-1。OpenSearch Serverless (2 OCU) がコスト全体の 96% を占めます。
- Embedding model を Titan v2 に統一する
Cohere Embed v3 ($0.0001/1K) に対して Titan v2 は $0.00002/1K (約 1/5)。日本語単言語用途では Titan v2 で十分な精度が得られる事例が多い。 - Chunking を FIXED_SIZE に統一し overlap を削減する
DEFAULT (300 tokens / 20% overlap) → FIXED_SIZE (200 tokens / 0%) に変更すると Embedding API 呼び出し回数を約 25% 削減できる。 - dev 環境の OpenSearch OCU を業務時間帯に絞る
業務時間のみ稼働 (8h × 22日 = 176h) にすると月額約 $84 と本番比 76% 削減。現状 stop/start API がないため collection 削除→再作成の運用が必要。
この試算も 2026 年 4 月時点の概算です。AWS Pricing Calculator での最新確認を推奨します。
7-6. 運用チェックリスト
[ ] Guardrail が KB / Agent に正しく紐付いているか確認
aws bedrock-agent get-knowledge-base --knowledge-base-id <ID>
→ guardrailConfiguration フィールドを確認
[ ] CloudWatch アラームが設定済みか
→ InvocationThrottles (5分間 > 10件)
→ InvocationServerErrors (5分間 > 1件)
→ InvocationLatency p99 (> 30,000ms)
[ ] AWS Cost Anomaly Detection が設定済みか
→ 前月比 20% 増加で通知する Monitor を作成
[ ] KB sync が定期実行されているか
→ EventBridge Scheduler → Lambda → start-ingestion-job
→ aws bedrock-agent list-ingestion-jobs で最終成功日時を確認
[ ] Bedrock model access が有効か
→ Bedrock コンソール > Model access > 使用モデルが "Access granted"
8. まとめ・次回予告
8-1. 到達点サマリ
本記事 (§1〜§7) を完走することで、以下が実現できるようになります。
- RAG の 3 段構造 (Retrieve → Augment → Generate) と Bedrock Knowledge Bases の位置付けを説明できる
- S3 / Web Crawler / Confluence の 3 種データソースを使い分けられる
aws_opensearchserverless_collection+ vector index を Terraform で構築できるaws_bedrockagent_knowledge_base+aws_bedrockagent_data_sourceを Terraform で定義できる- Titan v2 / Cohere Embed v3 の特性を理解し、ユースケースに合わせた Embeddings model を選択できる
- FIXED_SIZE / HIERARCHICAL / SEMANTIC の Chunking 戦略を使い分けられる
bedrock-agent-runtime.retrieve_and_generateで質問→回答+citation を取得できるaws_bedrockagent_agent+ Action Group + Lambda で外部 API を呼び出す Agent を構築できる- Guardrails の 4 フィルタを Terraform で設定し KB / Agent に紐付けられる
- CloudWatch
AWS/Bedrocknamespace のメトリクスでコストとレイテンシを監視できる
8-2. 落とし穴集
ハンズオン中に詰まりやすいポイント 8 選です。
| # | 症状 | 原因 | 対策 |
|---|---|---|---|
| a | AccessDeniedException on InvokeModel | Bedrock model access 未申請 | Bedrock コンソール > Model access でモデルを申請 |
| b | aoss:APIAccessAll permission denied | OpenSearch Serverless の data access policy 漏れ | aws_opensearchserverless_access_policy に KB service role を追加 |
| c | vector index 作成失敗 | Terraform が index mapping をネイティブサポートしない | curl -XPUT または opensearch provider で手動作成 |
| d | KB sync 後も検索結果が空 | start-ingestion-job 未実行 or 失敗 | aws bedrock-agent list-ingestion-jobs でステータス確認 |
| e | ResourceNotFoundException on KB ID | リージョンが Bedrock 非対応 or KB ID の誤り | us-east-1 / us-west-2 のどちらかを確認 |
| f | OpenSearch コストが月 $350 超 | OCU が最小 2 OCU でも 24/7 稼働 | dev 環境は業務時間のみ (collection 削除→再作成) で削減 |
| g | Agent から回答が返ってこない | aws_bedrockagent_agent_alias の prepare 未完了 | Alias 作成後 1〜2 分待機してから invoke_agent を呼ぶ |
| h | マルチターンで文脈が引き継がれない | sessionId の引き回し忘れ | 初回レスポンスの sessionId を次回リクエストに必ず渡す |
8-3. 次回予告
本記事で構築した Bedrock RAG 基盤をベースに、以下を Vol2 候補として検討しています。
- Bedrock multi-agent collaboration — 複数 Agent が協調するオーケストレーション (2025 年 Q4 GA・2026 年 4 月時点)
- Bedrock Prompt Flow — GUI でプロンプトパイプラインを設計するビジュアルビルダー
- Bedrock Model Distillation — 大型モデルの出力を小型モデルに転移してコスト削減
- Bedrock Batch Inference + S3 — 非同期の大量推論ジョブで 50% コスト削減
公開予定は未定ですが、本記事にコメントいただければ優先度を上げます。
8-4. terraform destroy 完全手順
Bedrock Agent 関連リソースは依存関係が逆順削除不可のケースがあります。
terraform destroy 前に以下の順序でリソースを手動削除してください。依存関係に従った削除順序:
# 1. Agent と KB の関連付けを先に削除
aws bedrock-agent disassociate-agent-knowledge-base \
--agent-id <AGENT_ID> \
--agent-version DRAFT \
--knowledge-base-id <KB_ID>
# 2. Agent Alias を削除
aws bedrock-agent delete-agent-alias \
--agent-id <AGENT_ID> \
--agent-alias-id <ALIAS_ID>
# 3. Agent 本体を削除
aws bedrock-agent delete-agent \
--agent-id <AGENT_ID>
# 4. Data Source → Knowledge Base を削除
aws bedrock-agent delete-data-source \
--knowledge-base-id <KB_ID> \
--data-source-id <DS_ID>
aws bedrock-agent delete-knowledge-base \
--knowledge-base-id <KB_ID>
# 5. terraform destroy で残りのリソースを削除
# (OpenSearch Serverless collection + policies / IAM roles / S3 等)
terraform destroy
フォールバック手動削除 (Terraform が state conflict でエラーになった場合):
# OpenSearch Serverless collection 削除
aws opensearchserverless delete-collection \
--id <COLLECTION_ID>
# IAM role のポリシーデタッチ → ロール削除
aws iam detach-role-policy \
--role-name bedrock-rag-kb-role \
--policy-arn <POLICY_ARN>
aws iam delete-role --role-name bedrock-rag-kb-role
# S3 バケット内オブジェクトを空にしてから削除
aws s3 rm s3://<BUCKET_NAME> --recursive
aws s3api delete-bucket --bucket <BUCKET_NAME>