AWS ML/AI本番運用Vol2|Bedrock×Embedding×RAG×KB×Agents 完全ガイド

目次

1. なぜML/AI本番運用 Vol2 か — Bedrock基礎からの架橋 + 4本柱選定の現実解

fig01: ML/AI本番運用 4本柱 全体俯瞰 (Embedding × RAG × KB × Agents)

本記事のナビゲーション (AI本番運用シリーズ Vol2 / AWS本番運用)

1-1. 本記事のゴール

Bedrock が提供する Embedding / RAG / Knowledge Bases / Agents for Bedrock の4本柱を、本番運用視点で横断網羅するのが本記事の目的だ。

AWS 公式ドキュメントには各機能の API リファレンスは揃っているが、「実際の本番システムで Embedding 次元数をどう選ぶか」「Chunk 戦略をどう設計するか」「Re-ranking の導入コストと精度向上のトレードオフは何か」といった実践的な意思決定基準が散在している。本記事ではこれらを一気通貫で整理し、設計パターン・Terraform 実装例・コスト試算を具体的に提示する。

本記事で得られる知見を先に俯瞰しておこう:

  • Embedding 設計: model 選定 (Titan v2 vs Cohere Embed)・次元数決定・Cosine Similarity 安定化の実践基準
  • RAG 構成: Naive → Advanced RAG へのステップアップ・Chunk 戦略の本番デフォルト設計・Re-ranking 導入判断
  • Knowledge Bases: Vector Store 選定 (OpenSearch Serverless / Aurora pgvector)・Sync 戦略・Query API 比較
  • Agents: Action Group 設計・Prompt template カスタマイズ・Trace 可視化による幻覚抑制
  • Cost 試算: 各柱の月額シミュレーションと Vector Store の隠れコスト

4本柱をバラバラに学ぶのではなく、本番システムでどのように組み合わせるかという視点で整理する。PoC 成功後に本番化で躓くポイントを先回りして潰すための一冊として使ってほしい。

1-2. 読者像

本記事が最も刺さるのは次のような読者だ。

  • Bedrock 基礎は経験あり: モデル選定・Prompt 設計・API 呼び出しは実装したことがある
  • 本番化の壁にぶつかっている: Embedding 選定・Chunk 戦略・KB Sync・Agent Trace など細部で迷っている段階
  • Terraform 実装例が欲しい: インフラを IaC で管理したいが、Bedrock 周りの Terraform resource がどう書けるか手本がない
  • コスト試算を正確にしたい: OpenSearch Serverless の OCU モデルや Embedding API の月額を事前にシミュレーションしたい

前提知識のレベル感: AWS 基礎 (IAM / Lambda / S3) + Python/boto3 基礎 + Terraform 入門レベル。Bedrock の初歩は AI Vol1 Bedrock Agents統合入門 で補完できる。

Database の知識については Database Vol1 の Aurora pgvector セクションも役立つ。Vector Store として Aurora pgvector を選択する場合の基礎知識が得られる。

Bedrock の初期設定・モデルアクセス許可・基本的な Prompt 呼び出しは Vol1 に譲り、本記事では本番運用に直結する設計判断から始める。

本記事の想定読者が直面している典型的な問いは「Embedding はどのモデルで何次元を使えばいいか」「RAG の Chunk size をどう決めるか」「Knowledge Bases の Sync は Full か Incremental か」といったものだ。本記事はこれらに対して「なぜそうするのか」の根拠と「具体的にどう設定するか」の実装例を合わせて提示する。本番運用の現場で迷わないための判断軸を手に入れてほしい。

1-3. AI Vol1 (Agents統合入門) からの架橋 + 差別化軸

Vol1とVol2の役割分担 — 本記事の差別化軸

  • Vol1 (Agents統合入門): Bedrock Agents の Action Group / KB / OpenAPI / Lambda 統合の導入。「まず動かす」フェーズ
  • Vol2 (本記事): 4本柱 (Embedding/RAG/KB/Agents) の本番運用 知見統合 — Cost試算・Chunk戦略・Re-ranking・Trace 詳解。「壊れない運用にする」フェーズ
  • 差別化ポイント: Embedding選定 × Chunk戦略 × Vector Store選定 × Agent Trace を一気通貫で扱う日本語ガイドは希少。PoC → 本番化の壁を突破するための判断軸と Terraform 実装例を網羅
  • Vol3 予告: Multi-modal RAG (画像+テキスト混在) / Fine-tuning Custom Model / Guardrails for Bedrock / Bedrock + Step Functions 大規模ワークフロー

AI Vol1 で構築した Bedrock Agents の基礎知識を前提に、Vol2 では次のレイヤーに踏み込む。Vol1 が「動かし方」なら、Vol2 は「本番品質で動かし続ける方法」だ。

特に、コスト最適化 Vol1 で学んだコスト意識を Bedrock に適用する際、Embedding のトークン試算と Vector Store の OCU (OpenSearch Serverless) コストが大きな比重を占める。本記事では各ステップで具体的な月額試算を提示し、コスト設計の見落としを防ぐ。

ML/AI本番運用 痛点5選 — PoC成功 → 本番化で躓くパターン

  • ①Embedding次元数選定ミス: 256次元で PoC 成功後、本番データ (100万文書) で精度低下 → 1024次元への全件 Re-embedding コストが数百ドル規模に膨らむ。次元数は最初から正しく選ばないと後戻りコストが大きい
  • ②Chunk size設計ミス: Chunk が大きすぎてノイズ混入 / 小さすぎて文脈分断。固定500トークンでは技術文書と法務文書を同一設定で処理できず、RAG の回答品質がコーパス依存で揺れる
  • ③Knowledge Bases Sync戦略の誤り: Full Sync 月次でデータ陳腐化 → 最新情報が Knowledge Base に反映されず、LLM が古い情報を自信を持って回答するという最悪の状態になる
  • ④Agents for Bedrock の幻覚抑制不足: Prompt template をデフォルトのまま運用 → Action Group の戻り値を無視した回答が頻発。本番障害につながるケースが多く、Trace 可視化がないと原因特定もできない
  • ⑤Re-ranking未導入による精度低下: Vector 検索 top-5 をそのまま LLM に渡す設計 → 意味的に近いが質問と無関係な文書が混入し、回答品質が不安定。Re-ranking は精度向上の費用対効果が高い施策だ

2. Embedding本番運用 — Titan Embeddings v2 × Cohere Embed × 次元数選定

fig02: Embedding model比較 (Titan v2 / Cohere Embed / 次元数 / コスト)

2-1. Embedding model選定 — Titan v2 vs Cohere Embed

Bedrock で利用できる主要 Embedding モデルは 2 つだ。用途・言語・コストの3軸で選定する。

amazon.titan-embed-text-v2:0

Amazon が提供するネイティブ Embedding モデルで、Knowledge Bases のデフォルト設定もこのモデルだ。

  • 次元数: 1024 / 512 / 256 (可変 — Matryoshka Representation Learning 対応)
  • 対応言語: 英語中心 + 多言語対応 (25言語)
  • 価格: $0.0001 / 1K tokens (2025年現在)
  • 特徴: normalize=true で Cosine Similarity が安定。Knowledge Bases との AWS ネイティブ統合がシームレス

cohere.embed-multilingual-v3

Cohere 社の多言語特化 Embedding モデル。日本語処理で Titan v2 より優位なケースが多い。

  • 次元数: 1024 固定
  • 対応言語: 104言語 — 日本語精度が Titan v2 より高い
  • 価格: $0.0002 / 1K tokens (Titan v2 の2倍)
  • 特徴: 多言語混在コーパスで特に有効。日本語技術文書 RAG では精度差が顕著に出る

用途別選定マトリクス

ユースケース推奨モデル理由
英語中心の社内文書 RAGTitan v2 (1024次元)コスト半額 / Knowledge Bases デフォルト統合
日本語混在の技術文書 RAGCohere Embed Multilingual v3日本語トークン精度が優位
大規模バッチ処理 (1億トークン超)Titan v2 + Batch APIコスト削減優先 ($0.0001 × Batch割引)
PoC・小規模検証Titan v2 (256次元)低コスト / 検証速度優先
多言語 (5言語以上混在)Cohere Embed Multilingual v3104言語対応の強み

選定の実務判断: まず Titan v2 で PoC を構築し、日本語精度が不十分な場合のみ Cohere に移行する段階的アプローチが現実的だ。コスト2倍の判断は精度データを見てから行うこと。

Embedding model の切り替えコスト: 一度 Titan v2 で全文書を Embedding して Vector Store に格納した後、Cohere に変更する場合は全件 Re-embedding が必要だ。100万文書規模では Titan v2 → Cohere への切り替えに $100 の追加コスト + Vector Store 再構築の工数が発生する。モデル選定は PoC 初期段階で十分な精度比較を行い、確定してから本番投入することを強く推奨する。

aws:bedrock の model アクセス許可: Bedrock コンソールでモデルアクセスを事前に有効化しておく必要がある。Titan v2 と Cohere Embed Multilingual v3 は同一アカウントでも個別に申請が必要だ。本番環境のアクセス許可設定は IaC (Terraform) で管理するより、AWS コンソールで手動設定する運用が現時点では一般的だ。

2-2. 次元数選定 — 1024 vs 512 vs 256

Embedding 次元数は「精度」「ストレージコスト」「検索レイテンシ」の三つ巴トレードオフだ。選定ミスは全件 Re-embedding という高コストの手戻りを生む。

各次元数の特性比較

次元数精度ストレージ (100万文書)検索速度推奨用途
1024次元★★★ 高約 4GB本番 RAG / 高精度要件
512次元★★☆ 中約 2GBバランス重視 / 中規模システム
256次元★☆☆ 低約 1GB最速PoC / 低レイテンシ絶対優先

※ストレージは float32 (4バイト/次元) × 文書数で計算。実際は Vector Store の Index overhead が加算される。

Matryoshka Representation Learning (MRL) とは

Titan v2 がサポートする MRL は、1024次元で生成したベクトルから 512次元・256次元を切り出しても品質が保たれる学習手法だ。従来の Embedding モデルは次元数固定だったが、MRL により一つのモデルで複数の次元数を使い分けられる。

運用上の利点:

  • 本番環境: 1024次元で高精度を維持
  • PoC・テスト環境: 同一モデルの 256次元で低コスト検証
  • コスト段階的最適化: Vector Store の Index 設定変更なしに次元数を調整可能

本番推奨設定: 日本語・英語混在の技術文書 RAG は 1024次元固定。精度よりコストを重視するシステムでも 512次元を下限 とし、256次元は PoC 検証段階に限定することを強く推奨する。

次元数変更の手戻りコスト: 例えば 256次元 → 1024次元への変更は、全文書の Re-embedding + Vector Store の再構築 + Index の再作成が必要だ。100万文書なら Titan v2 で追加 $50 + OpenSearch の再 Index 時間コストが発生する。

日本語コーパス固有の注意点

日本語は英語と比べてトークン効率が低く、同じ文章量でもトークン数が多くなる傾向がある。1文書 500 tokens の試算は英語文書を前提にしており、日本語文書では同じ文字数で 700〜900 tokens になるケースがある。コスト試算時は日本語のトークン効率 (1文字 ≈ 1〜2 tokens) を考慮して上振れバッファを設けること。

次元数の精度影響も言語によって異なる。英語では 512次元でも十分な精度が出るケースが多いが、日本語の語彙多様性・同音異義語・略語の多さから、日本語 RAG では 1024次元の方が精度安定性が高い。

2-3. セマンティック検索基礎 — Cosine Similarity と Dot Product の選択

Embedding ベクトル間の類似度計算には 2 つの主要な距離関数がある。Vector Store の設定で誤ると精度に直接影響するため、正確に理解しておきたい。

Cosine Similarity

ベクトルの長さ (magnitude) を正規化した上で内積を計算する。長さの影響を除外するため、文書の長さが異なっても安定した類似度計算ができる。

  • normalize=true で Embedding を生成した場合: Cosine Similarity = Dot Product となる (ベクトルの大きさが1に揃うため)
  • Titan v2 で normalize=true を指定すると、Vector Store の距離関数がどちらでも同じ結果になる

Dot Product (内積)

正規化なしの場合、ベクトルの長さが類似度に影響する。同一テキストでも長い文書と短い文書でスコアが変わるため、文書長が均一なコーパス以外では推奨しない。

実装上の重要な注意点

  • Titan v2 では normalize=true を必ず明示指定: API の default は False。指定なしだと Dot Product での精度が文書長依存で不安定になる
  • OpenSearch Serverless: Index 作成時の space_type"cosinesimil" を指定
  • Aurora pgvector: <=> 演算子 (Cosine Distance) を使用。<-> (L2 Distance) との混同に注意

k-NN vs ANN の選択

手法精度レイテンシスケール上限推奨場面
k-NN (完全探索)★★★ 完全高 (文書数に比例)〜10万文書小規模 / 高精度優先
ANN (HNSW/IVF)★★☆ 近似低 (対数時間)100万文書超本番大規模システム

本番 RAG では HNSW (Hierarchical Navigable Small World) アルゴリズムを使う ANN が標準だ。OpenSearch Serverless は HNSW をデフォルトでサポートし、追加設定なしで使える。

ANN の精度パラメータ (ef_construction, m) のチューニングは、本番移行前にベンチマークを取ることを推奨する。精度 (Recall@10) と レイテンシのトレードオフは文書の特性によって異なる。

OpenSearch Serverless での Vector Store 設定例

OpenSearch Serverless で kNN Index を作成する際の設定ポイント:

  • ef_construction: 512 — Index 構築精度 (高いほど精度 UP / 構築時間 UP)
  • m: 16 — HNSW グラフの接続数 (16〜32 が本番標準。大きいほどメモリ消費増)
  • space_type: "cosinesimil" — Cosine Similarity を距離関数に指定
  • dimension: 1024 — Titan v2 の次元数と一致させること

Index 作成後は次元数変更不可のため、Titan v2 の次元数 (1024/512/256) を決定してから Index を作成すること。次元数変更の場合は Index の削除・再作成が必要になる。

Observability については Observability Vol1 の「分散トレーシング」も参照。Vector Store のクエリレイテンシを X-Ray でトレースする設計が有効だ。

2-4. Embedding Cost試算

Embedding コストは「初期投入コスト」と「継続更新コスト」の2フェーズで試算する。見落としがちなのは Vector Store のストレージコストが Embedding API コストを大幅に上回るケースが多い点だ。

Titan v2 の価格体系 (2025年現在)

  • 標準 API: $0.0001 / 1K tokens = $0.10 / 1M tokens
  • Batch API 活用時: 最大 5倍効率化 (並列処理によるスループット向上。コスト自体は同単価だがスループット改善)

初期投入コスト試算 — Titan v2

規模文書数平均トークン数総トークン数Titan v2 コストCohere コスト
小規模1万文書500 tokens5M tokens$0.50$1.00
中規模10万文書500 tokens50M tokens$5.00$10.00
大規模100万文書500 tokens500M tokens$50.00$100.00
超大規模1000万文書500 tokens5B tokens$500.00$1,000.00

100万文書規模でも Embedding API コストは $50 程度と低い。コスト設計の主役は Vector Store 側だ

月次継続コスト試算 (中規模システム)

コスト項目月額試算備考
Embedding API (月 1万件更新)$0.505M tokens × $0.0001/1K
OpenSearch Serverless (2 OCU)$3452 OCU × $0.24/h × 720h
S3 (Data Source 保存)$2〜510GB × $0.023/GB
Lambda (Embedding 生成)$1〜3実行回数依存
合計約 $350Vector Store が支配的

コスト全体像の把握は コスト最適化 Vol1 の「マネージドサービスのコストモデル分解」セクションも参照してほしい。

Batch Embedding API 活用パターン

初期の大規模 Embedding (10万文書以上) は必ず Batch API を使う。リアルタイム API と Batch API の選択基準を整理する。

観点リアルタイム APIBatch API
レイテンシ即時応答 (100ms〜)非同期 (分〜時間)
スループット低 (throttling 制限あり)高 (並列処理)
ユースケース新規文書の逐次追加初期投入・全件 Re-embedding
コスト同単価同単価 (スループット向上によるコスト効率改善)

Batch Embedding のワークフロー: S3 に入力テキストを JSONL 形式で配置 → Bedrock Batch Job API を呼び出し → 完了を EventBridge で検知 → 出力 JSONL を S3 から取得 → Vector Store に格納。このパイプラインを Step Functions で管理すると、大規模バッチの冪等性と再試行が保証される。

Terraform + Lambda — Embedding API 呼び出し実装例

resource "aws_lambda_function" "embedding_generator" {
  function_name = "bedrock-embedding-generator"
  role = aws_iam_role.lambda_bedrock.arn
  runtime = "python3.12"
  handler = "index.handler"
  filename= data.archive_file.embedding_lambda.output_path
  timeout = 300
  memory_size= 512

  environment {
 variables = {
BEDROCK_REGION  = "us-east-1"
EMBEDDING_MODEL = "amazon.titan-embed-text-v2:0"
DIMENSIONS= "1024"
 }
  }
}

resource "aws_iam_role_policy" "lambda_bedrock_policy" {
  name = "bedrock-embedding-policy"
  role = aws_iam_role.lambda_bedrock.id

  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [
{
  Effect = "Allow"
  Action = [
 "bedrock:InvokeModel"
  ]
  Resource = "arn:aws:bedrock:us-east-1::foundation-model/amazon.titan-embed-text-v2:0"
}
 ]
  })
}

resource "aws_iam_role" "lambda_bedrock" {
  name = "bedrock-embedding-lambda-role"

  assume_role_policy = jsonencode({
 Version = "2012-10-17"
 Statement = [
{
  Effect = "Allow"
  Principal = { Service = "lambda.amazonaws.com" }
  Action = "sts:AssumeRole"
}
 ]
  })
}

resource "aws_iam_role_policy_attachment" "lambda_basic" {
  role = aws_iam_role.lambda_bedrock.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

Lambda 関数内の Python 実装:

import json
import boto3

def handler(event, context):
 client = boto3.client("bedrock-runtime", region_name="us-east-1")

 text = event["text"]
 dimensions = int(event.get("dimensions", 1024))
 normalize = event.get("normalize", True)

 body = json.dumps({
  "inputText": text,
  "dimensions": dimensions,
  "normalize": normalize
 })

 response = client.invoke_model(
  modelId="amazon.titan-embed-text-v2:0",
  contentType="application/json",
  accept="application/json",
  body=body
 )

 result = json.loads(response["body"].read())
 return {
  "embedding": result["embedding"],
  "inputTextTokenCount": result["inputTextTokenCount"]
 }

normalize=True を明示することで、Vector Store 側の距離関数が Cosine Similarity / Dot Product のどちらでも安定した検索結果が得られる。

Embedding 本番運用ベストプラクティス

  • Titan v2 は normalize=true を明示指定: API 呼び出し時に "normalize": true を必ずセット。デフォルト False のまま Vector Store に格納すると、文書長が揃っていないコーパスで Cosine Similarity の精度が不安定になる
  • Batch API で初期投入を効率化: 100万文書の初期 Embedding は Batch API を活用。リアルタイム API より最大5倍のスループット向上 → 同じコストでより短時間に初期投入が完了する。夜間バッチで実行するのが定番パターン
  • Embedding Cache で再生成コストを削減: 同一テキストへの繰り返し Embedding を防ぐため、DynamoDB に text_hash → embedding_vector のキャッシュテーブルを設ける。Knowledge Bases の Full Sync 時に変更のないドキュメントをスキップできる
  • 次元数は本番から逆算して決定: MRL を活用することで PoC は 256次元、本番は 1024次元という使い分けは可能だが、Vector Store の Index は次元数変更時に再作成が必要。最初の段階で本番次元数を決定し、PoC も同一次元で検証することを推奨する
Embedding コスト警戒 — 見落としがちな3つのコスト要因

  • Re-embedding 全件再実行リスク: モデルバージョン変更・次元数変更・normalize 設定変更のたびに全件 Re-embedding が発生する。100万文書 × Titan v2 = $50 の追加コスト。移行計画には必ず Re-embedding コストを織り込み、変更の影響を事前にステージング環境で検証すること
  • Vector Store ストレージが支配的コスト: 100万文書 × 1024次元 × 4バイト (float32) ≈ 4GB のベクトルデータ。OpenSearch Serverless は最小 2 OCU = 月額 $345 〜。Embedding API コスト ($50 初期) より Vector Store の月額コストが大きいケースが多い。コスト試算では Vector Store を先に計上すること
  • 多言語 Cohere は単価2倍の判断を慎重に: 日本語精度向上のために Cohere Embed Multilingual v3 を選ぶ場合、コストは Titan v2 の2倍 ($0.0002/1K tokens)。日英混在コーパスでは Titan v2 + 日本語チューニングした Chunk 戦略で精度を補える場合もある。ベンチマークデータに基づいて判断し、感覚での Cohere 採用は避けること

3. RAG本番運用 — 構成パターン × Chunk戦略 × Re-ranking × 評価指標

fig03: RAG処理フロー (Ingestion → Embedding → Vector Store → Retrieval → Re-ranking → LLM)

RAGの本番運用では、構成パターンの選定からChunk戦略・Re-ranking・評価指標まで体系的に設計する必要がある。単純なベクトル検索 (Naive RAG) で精度が不十分な場合、Advanced RAGへの移行判断と実装コストの試算が重要になる。本章では4つの観点から本番RAGの設計指針を示す。

3-1. RAG構成パターン比較 — Naive / Advanced / Modular

RAGには実装の複雑度と精度のトレードオフによって3つの主要な構成パターンが存在する。

Naive RAG (単純ベクトル検索型)

最もシンプルな構成。ユーザークエリをEmbedding変換し、Vector Storeで上位k件を検索してLLMに直接入力する。実装コストが低く、小規模なPoCに適しているが、クエリが曖昧な場合や文書が多様な場合に精度が低下する。

Advanced RAG (ハイブリッド検索型)

Naive RAGの精度不足を補う本番向け構成。Hybrid Search (ベクトル検索+BM25キーワード検索の組み合わせ)・Re-ranking (Cohere Rerank等)・Query Rewriting (クエリの言い換えで検索精度向上) を組み合わせる。実装工数は増えるが、精度向上幅が大きく本番の標準構成として採用されることが多い。

Modular RAG (Agent型RAG)

検索ルーティング・クエリ分解・複数ソース統合などを自律的に処理するエージェント型の構成。Agents for Bedrockと組み合わせることで、単一のKnowledge Baseでは対応できない複雑な多段検索・分岐処理を実現する。実装複雑度は高いが、複数ドメインのドキュメントを横断して回答生成する用途に有効。

パターン精度コスト実装複雑度推奨用途
Naive RAG★★☆PoC / 小規模・単一ドメイン
Advanced RAG★★★中〜高本番標準 / 多様な文書
Modular RAG★★★複数ドメイン横断 / Agent型

graph TD
  Q[クエリ入力] --> E[Embedding変換]
  E --> VS[Vector Store検索 top-20]
  VS --> RR{Re-ranking?}
  RR -->|Naive RAG| LLM1[LLM直接入力 top-5]
  RR -->|Advanced RAG| CR[Cohere Rerank top-20→5]
  CR --> QR{Query Rewriting?}
  QR -->|No| LLM2[LLM入力]
  QR -->|Yes| QW[クエリ書き換え] --> VS
  LLM1 --> R[回答生成]
  LLM2 --> R

図: RAG処理パイプライン詳細 — Naive RAG vs Advanced RAG の分岐フロー


3-2. Chunk戦略 — 実装判断マトリクス

Chunk戦略は検索精度に直結する最重要設計項目。文書の性質に合わせて4種類から選定する。

Fixed Size Chunking (固定長分割)

最もシンプル。300〜500 tokens単位でテキストを固定長に分割し、隣接チャンク間に50 tokensのオーバーラップを設ける。Knowledge Basesのデフォルト設定であり、均質なFAQ文書・マニュアル文書に適している。

# 設計例
chunk_size  = 400 tokens  # 300-500の中間値
overlap  = 50 tokens# 文脈の連続性確保

Recursive Character Splitting (再帰的文字列分割)

段落 → 文 → 単語の階層境界を優先してチャンクを分割する。コード文書・技術ドキュメントのような構造化テキストに有効で、セクション境界をまたぐチャンクの発生を最小化できる。LangChain等のライブラリで標準実装されている。

Semantic Chunking (意味境界分割)

Embeddingの類似度変化点を意味境界として分割する高精度手法。テーマが明確に変わる箇所で分割するため検索精度が高いが、分割処理自体にEmbedding APIコールが必要でコストが増大する。大規模コーパスへの適用はコスト試算が必須。

Parent-Child Chunking (親子チャンク)

検索時は小さなチャンク (child: 100〜200 tokens) で精度高く検索し、LLMへの入力はその親チャンク (parent: 500〜1000 tokens) で文脈を補完する2段階方式。精度とコンテキスト充実度を両立できるが、Vector StoreとDocument Storeの2重管理が必要。

戦略推奨文書タイプコスト精度実装工数
Fixed SizeFAQ・均質マニュアル★★☆
Recursive技術文書・コード★★★
Semantic多様なWebコンテンツ★★★
Parent-Child長文・学術論文★★★

本番デフォルト推奨: Fixed Size (chunk_size=400, overlap=50) → 精度不足が確認された段階でRecursive / Parent-Childへ移行する段階的アプローチが費用対効果に優れる。

RAG設計ミス — Chunk戦略3大失敗パターン

  • Chunk size過大 (1500 tokens固定): 1チャンクに複数トピックが混在し、無関係な内容がLLMコンテキストに混入。Faithfulnessスコアが0.6以下まで低下する事例が多い
  • Overlap未設定 (overlap=0): 段落境界でチャンクが切断され、文脈が分断される。特に「前述の〜」「上記の〜」といった参照を含む技術文書で致命的
  • 全文書に同一戦略を適用: FAQにSemantic Chunking → コスト過剰。長文論文にFixed Size → 文脈分断。文書タイプ別に戦略を分けることが重要
  • Re-ranking未導入: top-5直接入力では関連性の低い文書がLLMコンテキストに混入。Answer Relevanceスコアが0.7以下になりやすい
  • Query Rewriting未実装: 曖昧なクエリや口語的な質問で検索精度が大幅に低下。「それって何?」「あれの使い方」等のゼロショットクエリが失敗しやすい

3-3. Re-ranking — Cohere Rerank API × Knowledge Bases Retrieve統合

初回ベクトル検索はコサイン類似度ベースのため、意味的に近くても質問への関連性が低い文書が上位に入ることがある。Re-rankingはクロスエンコーダー型モデルで「クエリ×文書ペア」の関連性を精密に再評価し、順位を組み替える処理。

Cohere Rerank API の統合フロー

Step 1: Knowledge Bases Retrieve API → top-20件取得
Step 2: Cohere Rerank API (rerank-english-v3.0 / rerank-multilingual-v3.0)
  入力: query + 20件のchunk text
  出力: relevance_score降順に再順位付け
Step 3: 上位5件のみLLMコンテキストに渡す
Step 4: BedrockのRetrieveAndGenerate APIで最終回答生成

Latency vs 精度のトレードオフ

Re-ranking追加による処理時間増加は300〜500ms程度。Answer Relevanceスコアは0.75 → 0.90程度まで向上する実測値が多い。チャットボット用途 (応答速度重視) では追加Latencyが課題になるが、内部ナレッジベース検索 (精度重視) では費用対効果が高い。

多言語対応: 日本語文書には rerank-multilingual-v3.0 を選択。英語専用の rerank-english-v3.0 より応答品質が高く、Knowledge Bases上の日本語ドキュメントとの相性も良好。

関連: Serverless Vol1 — Lambda × Bedrock連携パターン

3-4. RAG評価指標 — RAGAS × CloudWatch カスタムメトリクス

RAGの品質を定量化するためにRAGAS (Retrieval Augmented Generation Assessment) フレームワークを活用する。4つの主要指標を定期計測し、CloudWatchカスタムメトリクスで継続監視する構成が本番標準。

Faithfulness (事実整合性)

LLMが生成した回答が検索されたコンテキストに基づいているかを検証する指標。値が低い場合、LLMが検索結果を無視して学習知識で回答 (幻覚) していることを示す。

  • 目標値: ≥0.8
  • 改善策: Knowledge Basesの文書品質向上 / Prompt templateでコンテキスト参照を明示的に指示

Answer Relevance (回答関連性)

生成された回答がユーザーの質問に直接答えているかを評価する。Chunk size過大や Re-ranking未導入で低下しやすい。

  • 目標値: ≥0.85
  • 改善策: Re-ranking導入 / Chunk sizeを300〜500 tokensに調整

Context Precision (検索精度)

Retrievalで返された文書のうち、実際に回答生成に使われた文書の割合。値が低い場合、無関係な文書が多数混入している。

  • 目標値: ≥0.7
  • 改善策: Re-ranking + top-kを20→5に絞る / Metadata Filterで検索範囲を限定

Context Recall (文脈再現率)

正解に必要な情報がRetrievalで取得できているかを測定する。Knowledge Basesのデータソース不足やSync遅延で低下する。

  • 目標値: ≥0.75
  • 改善策: Data Sourceの充実 / Incremental Sync頻度向上

関連: Observability Vol1 — CloudWatch カスタムメトリクス設計


# Knowledge Bases + Lambda Retrieval統合 Terraform実装
resource "aws_bedrockagent_knowledge_base" "rag_kb" {
  name  = "${var.env}-rag-knowledge-base"
  role_arn = aws_iam_role.kb_role.arn

  knowledge_base_configuration {
 type = "VECTOR"
 vector_knowledge_base_configuration {
embedding_model_arn = "arn:aws:bedrock:ap-northeast-1::foundation-model/amazon.titan-embed-text-v2:0"
 }
  }

  storage_configuration {
 type = "OPENSEARCH_SERVERLESS"
 opensearch_serverless_configuration {
collection_arn = aws_opensearchserverless_collection.vector_store.arn
vector_index_name = "${var.env}-rag-index"
field_mapping {
  vector_field= "embedding"
  text_field  = "AMAZON_BEDROCK_TEXT_CHUNK"
  metadata_field = "AMAZON_BEDROCK_METADATA"
}
 }
  }
}

resource "aws_bedrockagent_data_source" "s3_source" {
  knowledge_base_id = aws_bedrockagent_knowledge_base.rag_kb.id
  name  = "${var.env}-s3-data-source"

  data_source_configuration {
 type = "S3"
 s3_configuration {
bucket_arn  = aws_s3_bucket.knowledge_docs.arn
inclusion_prefixes= ["documents/", "manuals/"]
 }
  }

  vector_ingestion_configuration {
 chunking_configuration {
chunking_strategy = "FIXED_SIZE"
fixed_size_chunking_configuration {
  max_tokens= 400
  overlap_percentage = 12
}
 }
  }
}

# Lambda Retrieval統合 — Cohere Re-ranking付き
resource "aws_lambda_function" "rag_retriever" {
  function_name = "${var.env}-rag-retriever"
  runtime = "python3.12"
  handler = "retriever.handler"
  role = aws_iam_role.lambda_rag_role.arn
  filename= data.archive_file.retriever_zip.output_path
  timeout = 30
  memory_size= 512

  environment {
 variables = {
KNOWLEDGE_BASE_ID = aws_bedrockagent_knowledge_base.rag_kb.id
MODEL_ID = "anthropic.claude-3-5-sonnet-20241022-v2:0"
RERANK_MODEL_ID= "cohere.rerank-v3-5:0"
TOP_K_RETRIEVE = "20"
TOP_K_RERANK= "5"
AWS_REGION_NAME= var.aws_region
 }
  }
}

# S3 Data Source用バケット (Storage Vol1参照)
resource "aws_s3_bucket" "knowledge_docs" {
  bucket = "${var.env}-rag-knowledge-docs-${random_id.suffix.hex}"
}

resource "aws_s3_bucket_versioning" "knowledge_docs" {
  bucket = aws_s3_bucket.knowledge_docs.id
  versioning_configuration {
 status = "Enabled"
  }
}

# Knowledge Bases同期 EventBridge Schedule (日次Incremental Sync)
resource "aws_scheduler_schedule" "kb_sync" {
  name = "${var.env}-kb-sync-daily"
  group_name = "default"

  flexible_time_window {
 mode = "OFF"
  }

  schedule_expression = "cron(0 2 * * ? *)"

  target {
 arn= "arn:aws:scheduler:::aws-sdk:bedrockagent:startIngestionJob"
 role_arn = aws_iam_role.scheduler_role.arn

 input = jsonencode({
KnowledgeBaseId = aws_bedrockagent_knowledge_base.rag_kb.id
DataSourceId = aws_bedrockagent_data_source.s3_source.data_source_id
 })
  }
}

# IAM Role for Knowledge Bases
resource "aws_iam_role" "kb_role" {
  name = "${var.env}-kb-execution-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" "kb_policy" {
  name = "kb-bedrock-s3-opensearch"
  role = aws_iam_role.kb_role.id

  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [
{
  Effect= "Allow"
  Action= ["bedrock:InvokeModel"]
  Resource = "arn:aws:bedrock:*::foundation-model/amazon.titan-embed-text-v2:0"
},
{
  Effect= "Allow"
  Action= ["s3:GetObject", "s3:ListBucket"]
  Resource = [
 aws_s3_bucket.knowledge_docs.arn,
 "${aws_s3_bucket.knowledge_docs.arn}/*"
  ]
},
{
  Effect= "Allow"
  Action= ["aoss:APIAccessAll"]
  Resource = aws_opensearchserverless_collection.vector_store.arn
}
 ]
  })
}
RAG本番運用ベストプラクティス

  • Chunk設計: Fixed Size chunk_size=400 / overlap=50 tokensを本番デフォルトとし、精度不足が確認された段階でRecursive / Parent-Childへ段階移行する
  • Re-ranking必須化: Cohere Rerank (top-20 → top-5) を本番構成の標準とする。追加Latency 300〜500msに対してAnswer Relevance +0.15以上の改善が見込める
  • 多言語対応: 日本語文書には rerank-multilingual-v3.0 を選択。Titan Embed v2との組み合わせで日本語RAGの精度が安定する
  • RAGAS定期評価: Faithfulness ≥0.8 / Answer Relevance ≥0.85 / Context Precision ≥0.7 を週次でCloudWatchカスタムメトリクスに記録し、アラートで劣化を検知する
  • Incremental Sync: S3 Data SourceはEventBridge Scheduleで日次Incremental Syncを設定。Full SyncはEmbedding再生成コストが大きいため月次以下に留める
  • Metadata Filter活用: データソースにメタデータ (department / document_type等) を付与してRetrieve時に絞り込むことで、Context Precisionを大幅に改善できる
Re-ranking コスト・Latency 警戒ポイント

  • Cohere Rerank API単価: 検索クエリ × top-20件 = 20 search unitsを消費。月間10万クエリで約$200程度の追加コスト試算 (2024年時点レート)
  • Latency増加: 追加300〜500ms はモバイルユーザー向けチャットボットでは体感される。非同期表示 (Streaming) との組み合わせで UX劣化を緩和できる
  • Semantic Chunking のコスト罠: 100万トークン規模の文書コーパスにSemantic Chunkingを適用すると、分割処理だけで数十ドルのEmbedding APIコストが発生する。先にFixed Sizeで検証してから移行を判断すること
  • Vector Store容量試算: 1024次元 × 100万チャンク = 約4GB。OpenSearch Serverless OCUコスト (最小2 OCU: Indexing + Search) と合わせて月額コストを事前試算すること
§3 関連記事クロスリンク


4. Knowledge Bases for Bedrock本番運用 — Data Source × Vector Store × Sync戦略 × Query API

fig04: KB for Bedrock構成 (Data Source + Vector Store + Sync + Query API)

Knowledge Bases for Bedrock は、ドキュメントの取り込みからベクトル化・検索・LLM応答生成までを一元管理するマネージドRAG基盤だ。Data Source選定・Vector Store選定・Sync戦略・Query API の4層を正しく設計することが本番品質を左右する。

4-1. Data Source選定 — S3 / Web Crawler / Confluence / SharePoint

Knowledge Bases for Bedrock は複数の Data Source タイプをサポートしており、ドキュメントの所在と更新頻度に応じて最適なタイプを選択する。

S3 (推奨 — 最も柔軟)

S3 は最も汎用性が高く、Terraform による IaC 管理も完全対応している。PDF・DOCX・HTML・Markdown・CSV など幅広いファイル形式をサポートし、プレフィックスフィルタで特定フォルダのみを対象にできる。Incremental Sync との相性も良く、EventBridge + Lambda でファイル変更を検知して自動Syncを組むことが本番標準パターンとなる。

関連: Storage Vol1 — S3 本番設計

Web Crawler (パブリックWebコンテンツ)

公開Webサイトのコンテンツを直接クロールしてインデックス化できる。シード URL を指定し、クロール深度を制御する。外部ドキュメントサイトや製品マニュアルページをRAGに取り込む用途で有効だが、クロール頻度の制御が重要となる。

Confluence (エンタープライズWiki)

Atlassian Confluenceのスペースをデータソースとして直接接続できる。OAuth2.0認証でスペース/ページを選択し、定期Syncで最新状態を維持する。社内ナレッジベースをRAGに取り込む際の標準選択肢となる。

SharePoint (Microsoft 365環境)

Microsoft 365 の SharePoint サイトをデータソースとして接続できる。Teams 連携ドキュメントや部門別ドライブのコンテンツを RAG に統合できる。Microsoft 365 ライセンスが前提となるためエンタープライズ環境限定の選択肢だ。

Data Source 選定マトリクス

Data Source用途Terraform対応Sync方式メンテナンス負荷
S3全般 (推奨)Full/Incremental
Web Crawler公開WebコンテンツFull
Confluence社内Wiki△ (Secrets Manager要)Full/Incremental
SharePointMicrosoft 365△ (Secrets Manager要)Full/Incremental
Data Source 選定ベストプラクティス

  • 本番環境では S3 を基点に設計する — Terraform管理・バージョニング・アクセスログ・ライフサイクルポリシーが全て制御可能
  • Confluence / SharePoint を使う場合は Secrets Manager でクレデンシャルを管理し、プレーンテキスト埋め込みを避ける
  • S3 プレフィックスフィルタで Knowledge Base ごとにデータ境界を明確に分離する (テナント分離にも有効)
  • 複数 Data Source を1つの Knowledge Base に混在させる場合は Metadata で出所を記録しておくとフィルタ検索が容易になる

4-2. Vector Store選定 — OpenSearch Serverless × Aurora Postgres pgvector × Pinecone

Knowledge Bases for Bedrock が対応する Vector Store は複数あり、コスト・精度・運用負荷・Bedrock統合深度で使い分ける。

OpenSearch Serverless (Bedrock native統合 — 推奨)

Amazon OpenSearch Serverless は Knowledge Bases for Bedrock とのネイティブ統合を提供しており、コンソール/Terraform どちらからでも設定できる最も標準的な選択肢だ。

OCU (OpenSearch Compute Unit) は Indexing と Search の2系統を最低各1OCU (合計2OCU) から利用できる。自動スケールにより検索トラフィック増加に対応するが、2OCU 最小保証のためコストは常に発生する。

月額コスト試算:
– Indexing OCU: 1 OCU × $0.24/時間 × 720時間 = 約$173/月
– Search OCU: 1 OCU × $0.24/時間 × 720時間 = 約$173/月
– Storage: $0.024/GB/月 (10GB時 $0.24/月)
合計: 最小 約$346/月 ($700前後は余裕をみた見積もり)

OpenSearch Serverless OCU コスト試算 (実務参考値)

  • 最小構成 (Indexing 1OCU + Search 1OCU): 約$346/月
  • 中規模 (Indexing 2OCU + Search 2OCU): 約$692/月
  • OCU 単価: $0.24/OCU/時間 (2026年5月時点 us-east-1)
  • ストレージ料金: $0.024/GB/月 (OCU料金とは別途)
  • 開発環境では使用後に Collection を削除しておくとコスト節約になる

Aurora Postgres pgvector (既存RDS活用 / コスト効率)

Aurora PostgreSQL で pgvector 拡張を有効化することで、SQL インターフェースでのベクトル検索が可能になる。Knowledge Bases for Bedrock も Aurora Postgres をネイティブVector Storeとして対応している。

既存の Aurora クラスタがある環境では追加コストを抑えて Knowledge Bases を導入できる。vector 型カラムと HNSW/IVFFlat インデックスを組み合わせることで高速な近似最近傍探索を実現する。Aurora Serverless v2 であれば ACU 単位での自動スケールも可能だ。

関連: Database Vol1 — RDS Aurora 本番設計

Pinecone (外部サービス / 高精度 / カスタム統合)

Pinecone は外部のマネージドVector Databaseサービスであり、Knowledge Bases for Bedrock のネイティブ統合には含まれない。Lambda 経由のカスタムコネクタとして組み込む必要があり、レイテンシと運用複雑性が増す。精度・スケーラビリティは高いが、AWS ネイティブ統合を優先する環境では優先度は下がる。

Vector Store 選定マトリクス

Vector StoreBedrock統合月額コスト目安精度運用負荷推奨場面
OpenSearch Serverlessネイティブ$346〜新規・スケール優先
Aurora Postgres pgvectorネイティブ$50〜 (既存流用)中〜高既存RDS活用 / コスト重視
PineconeカスタムLambdaサービス料金既存Pinecone資産がある場合

4-3. Sync戦略 — Full Sync vs Incremental Sync

Knowledge Bases の Sync戦略は、データ鮮度とコストのトレードオフで決まる。

Full Sync: 全データ再インデックス

全ドキュメントを再度取り込み、ベクトル化し直してVector Storeを更新する。設定が単純で確実だが、ドキュメント数が多いほどコストと時間がかかる。週次・月次など低頻度バッチ向きの運用パターンだ。

  • 利用場面: 初回インポート / 大規模リビジョン / Embedding model変更時の全件再ベクトル化
  • コスト: ドキュメント数に比例 (例: 100万tokens × $0.0001 = $0.10/回)
  • 実行時間: データ量によるが大規模データは数時間以上かかる場合がある

Incremental Sync: 変更分のみ更新 (推奨)

前回Sync以降に追加・変更・削除されたドキュメントのみを処理する。S3 の変更イベントを EventBridge で検知し、Lambda 経由で Sync をトリガーするアーキテクチャが本番標準パターンだ。

関連: Serverless Vol2 — EventBridge / SQS / SNS 本番設計

EventBridge + Lambda トリガー設計

import boto3
import json

def lambda_handler(event, context):
 client = boto3.client('bedrock-agent')

 response = client.start_ingestion_job(
  knowledgeBaseId='KB_ID_HERE',
  dataSourceId='DS_ID_HERE',
  description='Incremental sync triggered by S3 event'
 )

 return {
  'statusCode': 200,
  'body': json.dumps({
'ingestionJobId': response['ingestionJob']['ingestionJobId']
  })
 }

Sync失敗時のリトライ設計 (SQS Dead Letter Queue)

Sync が失敗した場合に備え、SQS Dead Letter Queue (DLQ) を組み合わせたリトライパターンを構築しておく。Lambda の最大リトライ回数を設定し、失敗メッセージを DLQ へ退避させることで手動再処理を可能にする。DLQ メッセージを処理する Lambda 関数では bedrock-agent.start_ingestion_job() を再実行し、成功したメッセージを sqs.delete_message() で削除するフローとする。

Sync失敗・Vector Store容量の落とし穴

  • Sync失敗の無通知: ingestion job の status を CloudWatch メトリクスで監視しないと失敗を見落とす。bedrock-agent:StartIngestionJob の失敗アラームを必ず設定する
  • Vector Store容量オーバー: OpenSearch Serverless はデフォルトのストレージ上限がないが、データ量に比例したコストが発生する。月次でドキュメント数とストレージ量を確認し不要データを削除する
  • Full Sync 中の検索品質低下: Full Sync 実行中は古いインデックスと新しいインデックスが混在する期間がある。メンテナンスウィンドウを設けてトラフィックを制御する
  • Incremental Sync のスキップ: Lambda が一時的に失敗した場合、その間のS3更新が反映されない。DLQ + CloudWatch Alarm でスキップ検知の仕組みを用意する
  • Embedding model変更時: モデルを変更したら必ずFull Syncで全件再ベクトル化する。旧モデルと新モデルのベクトルが混在するとRetrieval精度が著しく低下する

4-4. Query API選択 — Retrieve / RetrieveAndGenerate / Metadata Filter / Lambda統合

Knowledge Bases for Bedrock の Query API は目的に応じて使い分ける。

Retrieve API: ドキュメント取得のみ

関連ドキュメントのチャンクを取得して返すのみで、LLM の呼び出しは行わない。取得したチャンクを自分でLLMに渡す実装が必要だが、プロンプトやモデル選択を自由に制御できる。高度なRAGパイプライン (Re-ranking / Query Rewriting / Metadata加工) を独自実装する場合に選択する。

import boto3

def retrieve_documents(knowledge_base_id: str, query: str, num_results: int = 10):
 client = boto3.client('bedrock-agent-runtime')

 response = client.retrieve(
  knowledgeBaseId=knowledge_base_id,
  retrievalQuery={'text': query},
  retrievalConfiguration={
'vectorSearchConfiguration': {
 'numberOfResults': num_results
}
  }
 )

 return response['retrievalResults']

RetrieveAndGenerate API: LLM応答生成込み (最も簡単)

ドキュメント検索とLLMによる回答生成をワンショットで実行する。実装が最もシンプルで、Knowledge Bases + Bedrock LLM の組み合わせを素早く試せる。RAGの標準ユースケースではまずこちらから始めると良い。

関連: AI Vol1 — Bedrock Agents統合入門

import boto3

def retrieve_and_generate(knowledge_base_id: str, model_arn: str, query: str):
 client = boto3.client('bedrock-agent-runtime')

 response = client.retrieve_and_generate(
  input={'text': query},
  retrieveAndGenerateConfiguration={
'type': 'KNOWLEDGE_BASE',
'knowledgeBaseConfiguration': {
 'knowledgeBaseId': knowledge_base_id,
 'modelArn': model_arn,
 'retrievalConfiguration': {
  'vectorSearchConfiguration': {
'numberOfResults': 5
  }
 }
}
  }
 )

 return {
  'answer': response['output']['text'],
  'citations': response.get('citations', [])
 }

Metadata Filter: 属性フィルタリング (テナント分離)

ドキュメントのメタデータを使って検索対象を絞り込む。テナントIDや部門コードをメタデータとして付与することで、ユーザーごとに参照できるドキュメントを制限できる。マルチテナント環境でのデータ分離に必須の機能だ。Retrieve API の retrievalConfiguration.vectorSearchConfiguration.filterequals: {key: "tenant_id", value: tenant_id} を指定することで、ユーザーのテナントに属するドキュメントのみを検索対象とする。

Lambda統合パターン

API Gateway + Lambda + Knowledge Bases の構成で RAG エンドポイントを構築する。Lambda 関数内で Retrieve または RetrieveAndGenerate を呼び出し、結果をクライアントに返す。認証・レート制限・ログ記録を Lambda + API Gateway の標準機能で管理できる。

関連: Serverless Vol1 — Lambda / API Gateway 本番設計


4-5. Terraform実装 — aws_bedrockagent_knowledge_base + aws_bedrockagent_data_source

Knowledge Bases の Terraform リソースは aws_bedrockagent_knowledge_baseaws_bedrockagent_data_source の2リソースで構成される。

aws_bedrockagent_knowledge_base (OpenSearch Serverless連携)

resource "aws_bedrockagent_knowledge_base" "main" {
  name  = "production-knowledge-base"
  role_arn = aws_iam_role.bedrock_kb_role.arn

  knowledge_base_configuration {
 type = "VECTOR"

 vector_knowledge_base_configuration {
embedding_model_arn = "arn:aws:bedrock:us-east-1::foundation-model/amazon.titan-embed-text-v2:0"
 }
  }

  storage_configuration {
 type = "OPENSEARCH_SERVERLESS"

 opensearch_serverless_configuration {
collection_arn = aws_opensearchserverless_collection.kb_collection.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 = {
 Environment = "production"
 Service  = "knowledge-base"
  }
}

resource "aws_iam_role" "bedrock_kb_role" {
  name = "bedrock-kb-role"

  assume_role_policy = jsonencode({
 Version = "2012-10-17"
 Statement = [
{
  Action = "sts:AssumeRole"
  Effect = "Allow"
  Principal = {
 Service = "bedrock.amazonaws.com"
  }
}
 ]
  })
}

resource "aws_iam_role_policy" "bedrock_kb_policy" {
  name = "bedrock-kb-policy"
  role = aws_iam_role.bedrock_kb_role.id

  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [
{
  Effect = "Allow"
  Action = [
 "s3:GetObject",
 "s3:ListBucket"
  ]
  Resource = [
 aws_s3_bucket.kb_documents.arn,
 "${aws_s3_bucket.kb_documents.arn}/*"
  ]
},
{
  Effect = "Allow"
  Action = [
 "aoss:APIAccessAll"
  ]
  Resource = aws_opensearchserverless_collection.kb_collection.arn
},
{
  Effect = "Allow"
  Action = [
 "bedrock:InvokeModel"
  ]
  Resource = "arn:aws:bedrock:us-east-1::foundation-model/amazon.titan-embed-text-v2:0"
}
 ]
  })
}

aws_bedrockagent_data_source (S3 Data Source)

resource "aws_bedrockagent_data_source" "s3_documents" {
  knowledge_base_id = aws_bedrockagent_knowledge_base.main.id
  name  = "s3-document-source"

  data_source_configuration {
 type = "S3"

 s3_configuration {
bucket_arn= aws_s3_bucket.kb_documents.arn
inclusion_prefixes = ["documents/", "manuals/"]
 }
  }

  vector_ingestion_configuration {
 chunking_configuration {
chunking_strategy = "FIXED_SIZE"

fixed_size_chunking_configuration {
  max_tokens= 400
  overlap_percentage = 15
}
 }
  }
}

resource "aws_s3_bucket" "kb_documents" {
  bucket = "production-kb-documents-${data.aws_caller_identity.current.account_id}"
}

resource "aws_s3_bucket_versioning" "kb_documents" {
  bucket = aws_s3_bucket.kb_documents.id

  versioning_configuration {
 status = "Enabled"
  }
}

# EventBridge で S3 変更を検知して Incremental Sync をトリガー
resource "aws_cloudwatch_event_rule" "s3_kb_sync" {
  name  = "kb-incremental-sync-trigger"
  description = "Trigger KB incremental sync on S3 object change"

  event_pattern = jsonencode({
 source= ["aws.s3"]
 detail-type = ["Object Created", "Object Deleted"]
 detail = {
bucket = {
  name = [aws_s3_bucket.kb_documents.id]
}
 }
  })
}

resource "aws_cloudwatch_event_target" "kb_sync_lambda" {
  rule= aws_cloudwatch_event_rule.s3_kb_sync.name
  target_id = "KBSyncLambda"
  arn = aws_lambda_function.kb_sync_trigger.arn
}
Knowledge Bases 本番運用ベストプラクティス

  • OpenSearch Serverless は本番/開発で Collection を分離: 開発 Collection は使用後に削除してOCUコストを節約する。本番 Collection のみ常時稼働とする
  • Chunk size = 300-400 tokens / overlap = 15%: 400 tokens + 15% overlap が日本語ドキュメントの本番デフォルト値。大きすぎるとLLM contextにノイズが混入し、小さすぎると意味の断片化が起きる
  • Incremental Sync を EventBridge で日次実行: S3 変更イベント連動に加えて、EventBridge Schedule による日次Full検証を組み合わせることで同期漏れを防ぐ
  • Metadata Filter でテナント分離: マルチテナント環境ではドキュメントアップロード時に tenant_id メタデータを必ず付与し、Retrieve API のフィルタと組み合わせる
  • IAM 最小権限: Bedrock KB ロールには S3 GetObject + ListBucket と OpenSearch Serverless APIAccessAll のみを許可する。広すぎるポリシーは避ける

§4 まとめ: Knowledge Bases for Bedrock 設計チェックポイント

設計項目推奨値 / 方針
Data SourceS3 (基本) / 社内WikiはConfluence
Vector StoreOpenSearch Serverless (新規) / Aurora pgvector (既存RDS活用)
Chunk size300-400 tokens / overlap 15%
Sync戦略Incremental Sync (日次 EventBridge) + Full Sync (月次)
Query APIRetrieveAndGenerate (標準) / Retrieve (カスタムパイプライン)
テナント分離S3プレフィックス + Metadata Filter
コスト管理OpenSearch Serverless OCU を開発環境では削除
障害対策DLQ + CloudWatch Alarm でSync失敗を検知

5. Agents for Bedrock本番運用 — Action Group × Lambda連携 × Prompt template × Trace × Session Memory

Agents for Bedrockは、LLMが自律的に複数のAPIを呼び出しながらタスクを完遂するオーケストレーション基盤だ。本番運用では「Action Group設計の精度」「Prompt templateの最適化」「Trace可視化」「Session Memory管理」の4軸を体系化することが安定稼働の鍵となる。

§5 Agents for Bedrock — 関連記事ナビゲーション


5-1. Action Group設計 — OpenAPI Schema × Lambda連携

Action GroupはBedrockエージェントが外部APIを呼び出すためのインターフェース定義だ。OpenAPI 3.0形式のスキーマでAPIの仕様を定義し、Lambda関数が実際の処理を担当する。スキーマ設計の精度がエージェント動作の精度に直結する。

OpenAPI Schema設計のベストプラクティス

Bedrockエージェントはスキーマを解析して、どの関数を呼び出すべきかを判断する。operationIddescription の記述品質が選択精度を左右する。

openapi: "3.0.0"
info:
  title: "OrderManagementAPI"
  description: "注文管理システムへのアクセスAPI"
  version: "1.0.0"
paths:
  /orders/{orderId}:
 get:
operationId: "getOrderStatus"
description: "指定された注文IDの現在のステータスを取得する。注文の進捗確認・配送状況照会に使用。"
parameters:
  - name: orderId
 in: path
 required: true
 description: "照会対象の注文ID (例: ORD-2024-001234)"
 schema:
type: string
responses:
  "200":
 description: "注文情報の取得成功"
 content:
application/json:
  schema:
 type: object
 properties:
orderId:
  type: string
status:
  type: string
  enum: ["pending", "processing", "shipped", "delivered"]
estimatedDelivery:
  type: string
  format: date

parameter設計3原則:

  1. operationId は英語の動詞+名詞形式で一意に設定 (例: getOrderStatus, cancelOrder)
  2. description はエージェントがいつ・なぜこの関数を呼ぶべきかを自然言語で明示
  3. パラメータの description に具体的な例値・フォーマット制約を記載する

Lambda関数の入出力契約

BedrockエージェントからLambdaへの呼び出しは固定フォーマットで行われる。返却フォーマットのキー階層を一字でも間違えるとエージェントがAction Groupを無効化する。

import json

def lambda_handler(event, context):
 # Bedrock Agentから受け取るイベント構造
 action_group = event.get("actionGroup")
 api_path  = event.get("apiPath")
 http_method  = event.get("httpMethod")
 parameters= event.get("parameters", [])

 # パラメータの安全な取り出し
 param_map = {p["name"]: p["value"] for p in parameters}
 order_id  = param_map.get("orderId", "")

 # ビジネスロジック実行
 result = fetch_order_status(order_id)

 # Bedrock Agentへの返却フォーマット (必須構造)
 return {
  "messageVersion": "1.0",
  "response": {
"actionGroup": action_group,
"apiPath": api_path,
"httpMethod": http_method,
"httpStatusCode": 200,
"responseBody": {
 "application/json": {
  "body": json.dumps(result, ensure_ascii=False)
 }
}
  }
 }

エラー返却は httpStatusCode を変更するだけでよい。500を返すとエージェントがリトライを試みるため、回復不能エラーには422/404を使う。

Bedrock Agent ↔ Lambda 通信プロトコル

Bedrock AgentとLambda間の呼び出しはリソースベースのポリシーで制御される。VPC内Lambda使用時は aws_lambda_permission でBedrockサービスプリンシパルへの lambda:InvokeFunction 付与が必要だ。

複数Action Groupの組み合わせパターン

本番システムでは複数のAction Groupを組み合わせて複雑なワークフローを実現する。

パターン構成ユースケース
ドメイン分離型注文AG + 在庫AG + 顧客AGECサイト総合カスタマーサポート
段階実行型検索AG → 加工AG → 通知AGデータ分析レポート自動生成
外部統合型社内API AG + Slack AG + メールAG社内ヘルプデスク自動対応

複数Action Group利用時の注意点: エージェントはどのAction Groupを呼ぶかをOrchestration Promptで判断する。名称・説明が重複すると誤った関数を選択するため、ドメインの境界を明確にすること。


graph TD
  U[ユーザー入力] --> PP[Pre-processing]
  PP -->|バリデーションNG| E[エラー返却]
  PP -->|OK| OC[Orchestration]
  OC --> AG{Action Group?}
  AG -->|Yes| L[Lambda実行]
  L --> OC
  AG -->|No| KB{Knowledge Base?}
  KB -->|Yes| RT[Retrieve + Generate]
  RT --> OC
  KB -->|No| LLM[LLM直接応答]
  LLM --> PO[Post-processing]
  PO --> R[ユーザー返答]

Agents for Bedrock orchestration分岐フロー — Pre/Orchestration/KB/Post の4フェーズと分岐パターン


Agents for Bedrock 落とし穴 5選

  • OpenAPI Schema の description 不足: operationIdだけ設定してdescriptionを省略すると、エージェントが関数を選択できず「実行できるアクションが見つかりません」と応答する。すべての操作にいつ呼ぶかの説明を必ず書くこと。
  • Lambda応答フォーマット不整合: responseBody のキー階層が不正だとエージェントが「APIから無効な応答を受け取りました」と判断し、Action Groupが機能停止する。上記の固定構造を厳守すること。
  • Prompt template過剰カスタマイズ: Orchestration Promptにビジネスロジックを詰め込みすぎると、Bedrock内部のオーケストレーション指示とコンフリクトし、エージェントが無限ループに陥る事例が報告されている。1フェーズずつ段階的に適用する。
  • Session TTL未設定: デフォルトTTLは30分。長時間セッションを想定したユースケースでTTL延長設定を忘れると、会話履歴が切断され「前の質問の内容を覚えていません」状態になる。
  • Action Group IAM不足: BedrockエージェントのIAM Roleに bedrock:InvokeModel だけ付与し、Lambdaの lambda:InvokeFunction を忘れるパターン。エラーメッセージが曖昧なためTerraformで一括定義する。

5-2. Prompt template カスタマイズ — 4フェーズ最適化

Agents for Bedrockは4つのPrompt phaseを経てユーザー入力を処理する。各フェーズはカスタマイズ可能であり、本番品質の幻覚抑制・タスク分解精度はここで決まる。

Pre-processing prompt — クエリバリデーション

ユーザー入力を受け取り、エージェントが処理すべきリクエストかどうかを判断するフェーズ。デフォルトのままでは範囲外のクエリを処理しようとするため、カスタマイズで対象範囲を絞り込む。

あなたは注文管理システムのサポートエージェントです。
以下のカテゴリに該当するリクエストのみ処理してください:
- 注文ステータスの確認
- 配送状況の照会
- 注文のキャンセル申請

上記以外 (商品の返品交換・請求書の修正など) は
「申し訳ございませんが、この件は専門窓口へご案内します」と応答してください。

悪意あるクエリフィルタとして「データを削除して」「全ユーザー情報を出力して」等のパターンをPre-processing段階でブロックする設計が有効だ。

Orchestration prompt — タスク分解 + Few-shot injection

エージェントの推論・計画立案フェーズ。Few-shot examplesを注入することで、どのAction Groupをどの順序で呼ぶかの精度が大幅に向上する。

## タスク分解の原則
1. ユーザーの意図を1文で要約する
2. 必要なAction Groupの呼び出し順序を計画する
3. 各ステップの成功条件を明示する

## Few-shot例
ユーザー: 「注文ORD-001の配送状況を教えてください」
計画:
  Step1: getOrderStatus(orderId="ORD-001") → status確認
  Step2: status が "shipped" なら getTrackingInfo(orderId="ORD-001") → 追跡番号取得
  Step3: 取得した情報を日本語で整形して回答する

Knowledge-base prompt — 参照スタイル指定

Knowledge Baseを呼び出す際の参照スタイル・引用形式を制御する。「FAQデータベースを参照する際は出典のドキュメント名を明示すること」などを定義すると、回答の信頼性が向上する。

Post-processing prompt — 出力形式整形

エージェントの最終回答を整形するフェーズ。ブランドトーン・フォーマット統一に使用する。

回答形式の要件:
- 敬語を使用する (丁寧語レベル)
- 数値は3桁区切りのカンマ表示
- 日付は「YYYY年MM月DD日」形式
- 技術的な専門用語はカッコ内に平易な説明を追加

Override promptsのTerraform実装

resource "aws_bedrockagent_agent" "order_agent" {
  agent_name  = "order-management-agent"
  agent_resource_role_arn = aws_iam_role.bedrock_agent.arn
  foundation_model  = "anthropic.claude-3-5-sonnet-20241022-v2:0"
  description = "注文管理システム統合エージェント"
  idle_session_ttl_in_seconds = 1800

  prompt_override_configuration {
 prompt_configurations {
prompt_type = "PRE_PROCESSING"
prompt_creation_mode = "OVERRIDDEN"
inference_configuration {
  temperature = 0.0
  top_p = 1.0
  max_length  = 2048
}
base_prompt_template = file("${path.module}/prompts/pre_processing.txt")
 }

 prompt_configurations {
prompt_type = "ORCHESTRATION"
prompt_creation_mode = "OVERRIDDEN"
inference_configuration {
  temperature = 0.1
  top_p = 0.9
  max_length  = 4096
}
base_prompt_template = file("${path.module}/prompts/orchestration.txt")
 }

 prompt_configurations {
prompt_type = "POST_PROCESSING"
prompt_creation_mode = "OVERRIDDEN"
inference_configuration {
  temperature = 0.2
  top_p = 1.0
  max_length  = 2048
}
base_prompt_template = file("${path.module}/prompts/post_processing.txt")
 }
  }

  tags = {
 Environment = "production"
 Service  = "order-management"
  }
}

5-3. Trace可視化 — CloudWatch Logs + X-Ray統合

エージェントの推論過程を可視化するTrace機能は、本番障害時の原因特定と幻覚抑制の改善に不可欠だ。Observability Vol1のCloudWatch/X-Ray設定と組み合わせることで完全なオブザーバビリティを実現できる。

Agent Trace API — Step-by-stepの推論ログ取得

Bedrock Agent呼び出し時に enableTrace: true を指定することでTrace情報を取得できる。

import boto3
import json

bedrock_agent_runtime = boto3.client(
 "bedrock-agent-runtime",
 region_name="ap-northeast-1"
)

response = bedrock_agent_runtime.invoke_agent(
 agentId="YOUR_AGENT_ID",
 agentAliasId="TSTALIASID",
 sessionId="session-uuid-v4",
 inputText="注文ORD-001のステータスを確認してください",
 enableTrace=True  # Trace有効化
)

# EventStreamの処理
for event in response["completion"]:
 if "trace" in event:
  trace_data = event["trace"]["trace"]
  print(json.dumps(trace_data, ensure_ascii=False, indent=2))
 elif "chunk" in event:
  print(event["chunk"]["bytes"].decode("utf-8"))

Trace JSON構造解析

取得されるTraceはオーケストレーションの各フェーズに対応した入れ子構造になっている。

{
  "orchestrationTrace": {
 "rationale": {
"text": "注文IDを指定してgetOrderStatus関数を呼び出す必要があります",
"traceId": "trace-001"
 },
 "invocationInput": {
"actionGroupInvocationInput": {
  "actionGroupName": "OrderManagementAG",
  "apiPath": "/orders/{orderId}",
  "httpMethod": "GET",
  "parameters": [{"name": "orderId", "value": "ORD-001"}]
}
 },
 "observation": {
"actionGroupInvocationOutput": {
  "text": "{\"orderId\": \"ORD-001\", \"status\": \"shipped\"}"
}
 }
  }
}

Traceは preProcessingTrace / orchestrationTrace / postProcessingTrace の3種類で構成される。rationale.text に「推測する」「おそらく」などの不確かな表現が含まれる場合、エージェントがKBやAction Groupから情報を得られずに推測回答している可能性が高い。

CloudWatch Logs + X-Ray設定

Lambda関数内でX-Rayサブスパンを設定すると、Bedrock Agentの呼び出しと社内APIレイテンシを一つのトレースで可視化できる。

from aws_xray_sdk.core import xray_recorder, patch_all

patch_all()  # boto3を含む全ライブラリのトレースを有効化

def lambda_handler(event, context):
 with xray_recorder.in_subsegment("bedrock-action-group") as subsegment:
  subsegment.put_annotation("actionGroup", event.get("actionGroup"))
  subsegment.put_annotation("apiPath", event.get("apiPath"))

  result = process_action(event)

  subsegment.put_metadata("result_size", len(str(result)))
  return result

CloudWatch Logs Insightsで幻覚パターンをモニタリングするクエリ例:

fields @timestamp, @message
| filter @message like /推測|おそらく|不明|確認できません/
| stats count() by bin(5m)
| sort @timestamp desc
Trace活用 3つのポイント

  • 開発・本番ともにTrace有効化: 本番でTraceを無効化すると障害時の根拠が消失する。CloudWatch Logs S3エクスポート (Glacier) でコスト圧縮しながら長期保存する。
  • preProcessingTrace でバリデーション拒否率を計測: Pre-processingで弾かれるクエリ率が10%を超えたら、Prompt templateのバリデーション条件が厳しすぎるサインだ。
  • orchestrationTrace のステップ数を監視: 正常な処理は3〜5ステップ以内。10ステップを超える場合はAction Groupループや情報不足が原因の可能性が高い。アラートを設定して早期検知する。

5-4. Session Memory — Multi-turn conversation管理

マルチターン会話を実現するSession Memoryは、ユーザー体験を大きく左右する。Database Vol1のDynamoDB設計と組み合わせて長期セッション管理を実装する。

Session ID管理 — UUID v4推奨

import uuid

def create_session_id() -> str:
 # UUID v4でユニークなSession IDを生成
 return str(uuid.uuid4())
 # 例: "f47ac10b-58cc-4372-a567-0e02b2c3d479"

Session IDはクライアント側で管理し、同一会話セッションでは同じIDを使い続ける。Session IDにユーザーIDを含めるとセッション切り替え時の漏洩リスクがある。UUID v4で完全に独立したIDを発行し、DynamoDBでuser_id ↔ session_idのマッピングを管理する。

Session TTL設計

ユースケース推奨TTL理由
カスタマーサポート30分 (デフォルト)問い合わせは短時間で完結
技術相談ボット2時間調査・検討の往復が多い
分析アシスタント8時間作業セッションが長時間に及ぶ
業務ワークフロー24時間承認フローの待機時間を含む

TTLは idle_session_ttl_in_seconds で設定し、最大3600秒 (1時間) まで指定可能。1時間超が必要な場合はアプリケーション層でDynamoDBに会話コンテキストを保存する外部化アーキテクチャを採用する。

DynamoDB sessions table設計

長期コンテキスト保存が必要な場合、DynamoDBを使って外部セッション管理を実装する。Serverless Vol1のLambda + DynamoDB統合パターンと組み合わせると高いスループットでセッション管理が可能だ。

resource "aws_dynamodb_table" "agent_sessions" {
  name  = "bedrock-agent-sessions"
  billing_mode= "PAY_PER_REQUEST"
  hash_key = "session_id"
  range_key= "turn_number"

  attribute {
 name = "session_id"
 type = "S"
  }

  attribute {
 name = "turn_number"
 type = "N"
  }

  attribute {
 name = "user_id"
 type = "S"
  }

  # GSI: user_id で全セッション取得
  global_secondary_index {
 name= "UserIdIndex"
 hash_key  = "user_id"
 range_key = "session_id"
 projection_type = "ALL"
  }

  # TTL設定 (expiration_time 属性を自動削除)
  ttl {
 attribute_name = "expiration_time"
 enabled  = true
  }

  tags = {
 Environment = "production"
 Service  = "bedrock-agent"
  }
}

resource "aws_bedrockagent_agent_action_group" "order_ag" {
  agent_id = aws_bedrockagent_agent.order_agent.id
  agent_version  = "DRAFT"
  action_group_name = "OrderManagementAG"
  description = "注文管理システムへのアクセスを提供するAction Group"

  action_group_executor {
 lambda = aws_lambda_function.order_handler.arn
  }

  api_schema {
 s3 {
s3_bucket_name = aws_s3_bucket.agent_schemas.bucket
s3_object_key  = "order-management-api.yaml"
 }
  }
}

Long-term Memory (Bedrock Memory機能 2025 GA)

2025年にGAとなったBedrock Agentsのメモリ機能を使うと、セッションを跨いだ長期記憶が可能になる。

# Long-term Memoryを有効化した呼び出し
response = bedrock_agent_runtime.invoke_agent(
 agentId="YOUR_AGENT_ID",
 agentAliasId="PROD_ALIAS",
 sessionId="new-session-uuid",
 memoryId="user-persistent-memory-id",  # ユーザー固有の永続メモリID
 inputText="先週相談した件の続きを教えてください",
 enableTrace=True
)

Long-term Memoryを有効化する際は、どのユーザー情報をどの期間保持するかのプライバシーポリシーをあわせて設計すること。GDPR・個人情報保護法の観点から「明示的な同意取得」と「削除要求への対応」を実装することが求められる。


Agents for Bedrock 本番運用ベストプラクティス

  • OpenAPI Schema品質への投資: operationIdとdescriptionの記述品質がエージェント精度を直接決定する。初期設計に時間をかけること。定期的にTrace logで誤選択パターンを確認し、descriptionを継続改善する。
  • Trace常時有効化 + S3 Glacierアーカイブ: 開発環境のみならず本番でもTraceを有効化し、CloudWatch Logsに保存する。幻覚・誤動作の根拠をTraceで示せない場合、原因特定が不可能になる。ストレージコストはS3 Glacierアーカイブ ($0.004/GB/月) で最小化する。
  • Prompt template段階的適用: 4フェーズを同時にカスタマイズせず、Pre-processingから順に1フェーズずつ検証してから適用する。複数変更を同時適用すると問題箇所の特定が困難になる。
  • Action Group IAM最小権限: BedrockエージェントのIAM Roleは bedrock:InvokeModel + 対象Lambdaのみに限定する。全Lambda関数への lambda:InvokeFunction 付与はセキュリティリスクになる。Terraformで対象Lambdaのarnを明示的にリソース指定すること。
  • Session IDとユーザーIDの分離: Session IDにユーザーIDを埋め込まない。UUID v4で独立したIDを発行し、DynamoDBでマッピングを管理する構造が安全かつスケールする。
Agents for Bedrock コスト試算目安

  • 基盤モデル呼び出し: claude-3-5-sonnet = $3/M input tokens + $15/M output tokens (1ターン ≒ 2,000 tokens = $0.006)
  • 1日1,000会話 × 3ターン = 3,000呼び出し → 月額 $180〜$540 (会話の複雑さに依存)
  • Action Group Lambda実行: 月100万回 × 平均500ms × 512MB = $10.4
  • DynamoDB sessions table: PAY_PER_REQUEST + TTL自動削除でコスト最小化 (月1,000万リクエスト ≒ $1.25)
  • CloudWatch Logs (Trace保存): 1KB/turn × 300万ターン/月 = 3GB → $4.5/月 + S3 Glacierアーカイブで長期保存

6. 詰まりポイント7選

6-1. Embedding cost試算不足でコスト爆発

なぜ詰まるか

初期の文書量が少ない段階では月数ドルで収まるため、コスト感覚が掴みにくい。
文書が10万→100万件に増えると、Embedding生成コスト + Vector Store容量 + 全件再Embedding費用が複利で膨らむ。
特に「スキーマ変更や次元数変更による全件再Embedding」の費用を初期設計で織り込まない事例が多い。

どう解くか

  • 月次Embedding試算表を最初から作成: 文書数 × 平均tokens × $0.0001/1K tokens
  • DynamoDB Embeddingキャッシュで同一文書の再生成コストを回避
  • Batch Embedding APIを活用し、リアルタイムAPIより最大50%コスト削減

6-2. Chunk size設計ミスで Retrieval精度低下

なぜ詰まるか

大きすぎるChunk(1500+ tokens)はLLMのcontextにノイズが混入し回答品質が低下する。
小さすぎるChunk(50 tokens以下)は文脈が失われてRetrievalが機能しない。
適切なサイズは文書の種類(FAQ / 技術マニュアル / コード)によって異なるため、一律設定が根本的な誤りとなる。

どう解くか

  • 本番デフォルト: 300-500 tokens + overlap 50 tokensをスタート点に
  • FAQ文書は150-200 tokens、技術マニュアルは500-700 tokensを試算点に設定
  • RAGAS評価でFaithfulness / Context Precisionを計測し、文書タイプごとに調整

6-3. Knowledge Bases Sync戦略誤りでデータ陳腐化

なぜ詰まるか

S3にファイルをアップロードしても、Knowledge Basesは自動同期しない仕様である。
手動でIngestion Job APIを呼ばない限り、古い情報のままRAGが回答し続ける。
「S3を更新したはずなのに最新情報で回答しない」というクレームの大半がこれに起因する。

どう解くか

  • EventBridge Scheduler + LambdaでIncremental Syncを日次自動化
  • 大規模ドキュメント更新時はFull Syncモードで実行し完了確認
  • Sync失敗をCloudWatch Alarmで検知し、Lambdaで自動リトライ

6-4. Agent幻覚抑制不足

なぜ詰まるか

Agents for BedrockのデフォルトPrompt templateは汎用設定で業務制約が存在しない。
「情報がない場合は答えない」という制約を明示しないと、自信のある口調で誤回答を生成する。
Orchestration phaseでFew-shotを注入しないと、Agentが業務ルールを学習できない。

どう解くか

  • Orchestration Promptに制約条件(「取得文書にない情報は回答しない」)とFew-shot追加
  • Pre-processing Promptでバリデーション(業務範囲外の質問を検出しリジェクト)
  • enableTrace=true + CloudWatch Logsで幻覚発生ステップを特定

6-5. Vector Store容量試算不足

なぜ詰まるか

1024次元ベクトル × float32(4バイト) × 100万ドキュメント = 約4GBのストレージを見落とす。
OpenSearch ServerlessはOCU(Index+Search合計最小2)で月額$700〜の固定コストが発生する。
pgvector(Aurora)の場合も、ベクトルインデックス構築後のストレージ増加率を試算しないと容量不足になる。

どう解くか

  • 事前試算: 次元数 × 4バイト × 文書数 = 必要ストレージ量を計算
  • OpenSearch Serverless: OCU最小2を試算点に(Indexing OCU + Search OCU)
  • スモールスタートにはpgvector(Aurora Serverless v2)が従量課金で適している

6-6. Re-ranking未導入で精度低下

なぜ詰まるか

ベクトル検索(ANN/HNSW)は近似探索のため、関連性スコアが高くない文書が結果に混入する。
top-5をそのままLLMに渡すと無関係な文書が回答品質を低下させる。
ベクトル距離と「クエリに対する文脈的関連性」は別物であることを見落としやすい。

どう解くか

  • top-20取得 → Cohere Rerank API → top-5 パイプラインを標準化
  • Cross-Encoderは精度は高いがLatency増加: SLAに応じてCohereとの使い分けを判断
  • RAGAS Context Precisionで改善効果を数値検証し導入の有無を判断

6-7. Trace可視化未設定で原因特定不能

なぜ詰まるか

Agents for Bedrockの推論ログはデフォルトで出力されない仕様である。
問題が起きても「どのKBから取得したか」「どの判断でActionを選んだか」が追跡できない。
本番で幻覚が多発してからTraceを有効化しようとすると、過去ログが存在せず原因特定が不可能になる。

どう解くか

  • InvokeAgent呼び出し時にenableTrace: Trueを明示(開発環境から必須)
  • CloudWatch Logs GroupをAgentに紐付けてTrace出力先を本番から設定
  • X-Ray統合でエンドツーエンドのLatency分解を可視化し、ボトルネック特定
詰まりポイント7選 — 根本原因の共通パターン

  • コスト詰まり(6-1/6-5): 初期段階の低コストが本番スケール後の試算ミスを引き起こす
  • 精度詰まり(6-2/6-6): ベクトル検索はチューニングなしでは「なんとなく動く」止まり
  • 鮮度詰まり(6-3): Knowledge BasesのSync仕様を正しく理解していないと必ず踏む
  • 品質詰まり(6-4/6-7): Prompt + Traceはセットで設定。片方だけでは原因特定もできない
本番デプロイ前 ML/AI チェックリスト

  • [ ] Embedding model選定 + 次元数試算 (1024次元スタート推奨)
  • [ ] Chunk size + overlap 設計レビュー (300-500 tokens + overlap 50)
  • [ ] Re-ranking導入確認 (Cohere Rerank top-20→top-5)
  • [ ] Knowledge Bases Sync頻度設計 (EventBridge日次自動化)
  • [ ] Agent Action Group OpenAPI Schemaレビュー
  • [ ] Trace可視化 (CloudWatch + X-Ray) 設定確認
  • [ ] Vector Store容量試算 + OCUコスト確認
  • [ ] RAGAS評価パイプライン構築 (Faithfulness / Context Precision)

7. アンチパターン → 正解パターン変換演習 5問

Q1. Embedding 256次元固定で本番運用

アンチパターン

# NG: 256次元は処理速度優先だが精度を犠牲にした設定
response = bedrock.invoke_model(
 modelId="amazon.titan-embed-text-v2:0",
 body=json.dumps({
  "inputText": text,
  "dimensions": 256,# NG: 低次元で検索精度が低下
  "normalize": False# NG: Cosine Similarity用正規化なし
 })
)

正解パターン

# OK: 1024次元 + normalize=True で Cosine Similarity安定化
response = bedrock.invoke_model(
 modelId="amazon.titan-embed-text-v2:0",
 body=json.dumps({
  "inputText": text,
  "dimensions": 1024,# 精度優先スタート
  "normalize": True  # Cosine Similarity用に正規化必須
 })
)
# 運用方針: RAGAS評価後に512/256への段階的削減を判断。
# 初回本番デプロイ時点での低次元化は禁止。

Q2. Chunk size を全文書 1500 tokens 固定

アンチパターン

# NG: 全文書1500tokensで分割
# 問題: LLMのcontextにノイズが混入し回答品質が低下する
def split_documents_bad(text):
 chunk_size = 1500
 return [text[i:i+chunk_size] for i in range(0, len(text), chunk_size)]

正解パターン

# OK: 文書タイプ別 Recursive Chunk + overlap 設定
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 技術マニュアル向け設定
manual_splitter = RecursiveCharacterTextSplitter(
 chunk_size=400,# 本番デフォルト: 300-500 tokens
 chunk_overlap=50, # 文脈維持のためoverlap必須
 length_function=len,
 separators=["\n\n", "\n", "。", ".", " ", ""],
)
chunks = manual_splitter.split_text(text)
# FAQ文書: chunk_size=200, overlap=30
# コード文書: chunk_size=500, overlap=100 (関数単位で区切れるよう調整)

Q3. Knowledge Bases Sync を月次バッチで運用

アンチパターン

# NG: 月次手動Syncでは最大31日間データが陳腐化する
# 「S3を更新したのになぜ古い情報で回答するのか」という問い合わせが多発
# → Syncは自動化されていないため、手動実行まで更新が反映されない

正解パターン

# OK: EventBridge Scheduler + Lambda でIncremental Sync 日次自動化
import boto3
import os

def handler(event, context):
 client = boto3.client("bedrock-agent")
 response = client.start_ingestion_job(
  knowledgeBaseId=os.environ["KB_ID"],
  dataSourceId=os.environ["DS_ID"],
  # デフォルトはIncremental (変更分のみ再Embedding)
 )
 job_id = response["ingestionJob"]["ingestionJobId"]
 print(f"Sync started: {job_id}")
 return {"jobId": job_id}

# EventBridge Scheduler設定: rate(1 day) / AM2:00 JST 実行
# 大規模更新(全件入れ替え)時: clientでforceFull=Trueのオプション設定

Q4. Agents Prompt template デフォルトのまま運用

アンチパターン

# NG: Prompt templateカスタマイズなし → 幻覚多発
# デフォルトtemplate は汎用設定で業務制約がない
response = bedrock_agent.invoke_agent(
 agentId=agent_id,
 agentAliasId="TSTALIASID",# NG: テスト用エイリアスを本番流用
 sessionId=session_id,
 inputText=user_input
 # enableTrace 未設定 → 幻覚原因が追跡不能
)

正解パターン

# OK: カスタムPrompt適用済エイリアス + Trace有効化
# Orchestration Promptに追加する制約例:
#"取得したドキュメントに基づいてのみ回答すること。
# ドキュメントにない情報の場合は『確認できませんでした』と応答せよ。"
# Few-shot例をOrchestration Promptに注入:
#User: 先月の売上は? → Agent: ドキュメントに該当情報がないため確認できません。

response = bedrock_agent.invoke_agent(
 agentId=agent_id,
 agentAliasId=os.environ["PROD_ALIAS_ID"],  # カスタムPrompt適用済エイリアス
 sessionId=session_id,
 inputText=user_input,
 enableTrace=True# 本番でもTrace必須 (幻覚ステップ特定のため)
)

Q5. Re-ranking なしで top-5 直接LLM入力

アンチパターン

# NG: ベクトル検索top-5をそのままLLMへ渡す
# 問題: ベクトル距離と「クエリへの文脈的関連性」は異なる。
# 関連性が低い文書がtop-5に混入してもフィルタリングされない。
response = bedrock_agent.retrieve(
 knowledgeBaseId=kb_id,
 retrievalQuery={"text": query},
 retrievalConfiguration={
  "vectorSearchConfiguration": {"numberOfResults": 5}
 }
)
documents = [r["content"]["text"] for r in response["retrievalResults"]]
# → top-5をそのままLLMへ渡す

正解パターン

# OK: top-20取得 → Cohere Rerank → top-5 パイプライン
import cohere
import boto3
import os

bedrock_agent = boto3.client("bedrock-agent-runtime")
co = cohere.Client(os.environ["COHERE_API_KEY"])

# Step1: ベクトル検索でtop-20取得
response = bedrock_agent.retrieve(
 knowledgeBaseId=os.environ["KB_ID"],
 retrievalQuery={"text": query},
 retrievalConfiguration={
  "vectorSearchConfiguration": {"numberOfResults": 20}
 }
)
documents = [r["content"]["text"] for r in response["retrievalResults"]]

# Step2: Cohere Rerankで関連性スコア再計算
rerank_response = co.rerank(
 query=query,
 documents=documents,
 top_n=5,
 model="rerank-multilingual-v3.0"  # 日本語対応モデル
)
top5 = [documents[r.index] for r in rerank_response.results]
# Cohere Rerankコスト: $0.001/1K queries → 精度向上に対して費用対効果が高い
演習5問 解法のポイント

  • Q1: 次元数削減はRAGAS評価後に決定。初回本番デプロイ時の低次元化は禁止
  • Q2: Chunk size は文書タイプ依存。RAGAS Faithfulness / Context Precisionで数値検証してから調整
  • Q3: Knowledge BasesはS3更新を自動検知しない。EventBridge日次Syncが最低ライン
  • Q4: Prompt templateカスタマイズはPre-processing(バリデーション) + Orchestration(Few-shot)のセット
  • Q5: Cohere Rerankは$0.001/1K queriesと低コスト。精度向上対比で必ず採用する設計が鉄則

8. まとめ — Vol3予告 + 落とし穴10選 + 全17軸ハブナビ

8-1. Vol2 まとめ

本記事はBedrock 4本柱(Embedding / RAG / Knowledge Bases / Agents for Bedrock)の本番運用知見を一気通貫で提示した。

本番設計の核心
Embedding1024次元スタート + normalize=True + Batch APIでコスト削減
RAG300-500 tokens Chunk + overlap 50 + Cohere Rerank (top-20→5)
Knowledge BasesEventBridge日次Incremental Sync + Metadata Filter活用
AgentsPrompt template両phase設定 + enableTrace + X-Ray統合

Terraform実装 + コスト試算 + RAGAS評価の3軸を組み合わせることで、本番品質のRAG/Agent基盤を構築できる。
Vol1(Agents統合入門)で基礎を固め、本記事Vol2で本番運用パターンを習得した上で、Vol3のマルチモーダル/Fine-tuningに進むのが推奨のラーニングパスである。

8-2. Vol3 予告

Vol3では以下のテーマを予定している:

  • Multi-modal RAG — 画像+テキストの複合知識ベース構築とRetrieval設計
  • Fine-tuning / Custom Model Import — 業務特化モデルの継続学習とデプロイ戦略
  • Bedrock Guardrailsによるコンテンツ制御 — フィルタリングポリシー + PIIマスキング本番運用
  • Bedrock + Step Functions 大規模ワークフロー — 並列RAGパイプライン + 分散Embedding処理

Vol3 (Multi-modal RAG × Custom Model × Guardrails) の続編を待つ

8-3. ML/AI本番運用 落とし穴10選

ML/AI本番運用 落とし穴10選 (§2〜§7 教訓凝縮)

  • ① Embeddingコスト試算なし → 月次試算表(文書数×tokens×単価)を必ず作成
  • ② Chunk size一律固定 → 文書タイプ別に300-500 tokens + overlap 50を設計
  • ③ normalize=False → Cosine Similarity用にnormalize=True必須
  • ④ Knowledge Bases手動Sync → EventBridge日次自動化(Incremental + 障害時Full)
  • ⑤ Re-ranking未導入 → Cohere Rerank top-20→5パイプラインを標準実装
  • ⑥ Prompt template未カスタマイズ → Pre-processing + Orchestration両方設定
  • ⑦ enableTrace未設定 → 開発から本番まで enableTrace=True + CloudWatch Logs
  • ⑧ Vector Store容量未試算 → 次元×4バイト×文書数を事前計算
  • ⑨ RAGAS評価なし → Faithfulness/Answer Relevance/Context Precisionで定期計測
  • ⑩ 次元数削減の時期を誤る → RAGAS評価後に段階的削減。初回デプロイ前の削減は禁止

8-4. AWS本番運用 全17軸 双方向ハブナビゲーション

関連: Analytics/Data Lake本番運用 Vol1 を読む