Amazon Bedrock Knowledge BasesをRAGASで評価する:RAGの品質を数値で測る実践ガイド
はじめに
RAGシステムを構築した後、「なんとなく動いている」状態で本番投入していないでしょうか。
「回答が正確かどうか」「検索が適切に機能しているか」を感覚ではなく数値で把握することが、
RAGシステムの品質向上には不可欠だと考えます。
本記事では、Amazon Bedrock Knowledge BasesとRAGAS(Retrieval-Augmented Generation Assessment)を組み合わせて、
RAGの品質を定量的に評価する方法を実装・計測・考察します。
また、ベクターストアにAmazon S3 Vectorsを採用することで、
従来のOpenSearch Serverless比で最大90%のコスト削減を実現する構成も紹介します。
RAGASとは
RAGASはRAGシステムの品質を自動評価するOSSフレームワークです。
LLM-as-a-Judgeのアプローチを採用しており、人手によるアノテーションなしに
以下の4つの指標を0〜1のスコアで算出できます。
| 指標 | 評価対象 | 何を測るか |
|---|---|---|
| Faithfulness | 生成 | 回答が検索結果に忠実か(ハルシネーション検出) |
| Answer Relevancy | 生成 | 回答が質問に対して関連しているか |
| Context Precision | 検索 | 検索されたコンテキストの精度(ノイズが少ないか) |
| Context Recall | 検索 | 必要なコンテキストが検索できているか |
これらの指標を組み合わせることで、RAGパイプラインのどこに問題があるかを特定できます。
RAGASのスコア算出ロジック
RAGASの全指標に共通するのが「LLM-as-a-Judge」のアプローチです。
人手でアノテーションするのではなく、LLMが採点役を担います。
Faithfulnessは「回答の各主張がコンテキストから導けるか」を評価します。
LLMが回答を複数のステートメントに分解し、各ステートメントがcontextsに支持されているか判定します。
回答: "DynamoDBのアイテムの最大サイズは400KBです。"
contexts: ["アイテムの最大サイズは400KBです。"]
→ LLMが回答をステートメントに分解
→ 各ステートメントがcontextsから導けるか判定
→ スコア = 支持されたステートメント数 / 全ステートメント数
Answer Relevancyは「回答が質問に対してどれだけ関連しているか」を評価します。
LLMが回答から逆算して複数の質問を生成し、元の質問とのコサイン類似度を計算します。
回答: "400KBです。"
→ LLMが回答から質問を逆生成
例: "DynamoDBのアイテムサイズ制限は?"
→ 生成した質問と元の質問のコサイン類似度を計算
→ スコア = 類似度の平均
Context Precisionは「検索されたコンテキストのうち回答に必要なものの割合」を評価します。
LLMが各チャンクを「回答に必要か/不要か」判定し、上位に必要なチャンクが来ているほど高スコアになります。
Context Recallは「ground_truthを生成するために必要な情報がcontextsに含まれているか」を評価します。
LLMがground_truthを複数のステートメントに分解し、各ステートメントがcontextsに含まれているか判定します。
ground_truth: "Auroraは標準MySQLの最大5倍のパフォーマンスを発揮します。"
→ LLMがground_truthをステートメントに分解
→ 各ステートメントがcontextsに含まれているか判定
→ スコア = contextsに含まれたステートメント数 / 全ステートメント数
この指標のみground_truthが必要で、他の3指標はground_truthなしで評価できます。
アーキテクチャ
[ドキュメント(Markdown)]
↓ S3にアップロード
[Amazon S3(ドキュメントバケット)]
↓ Ingestion Job
[Amazon Bedrock Knowledge Bases]
├── Embedding: Amazon Titan Embed Text v2
└── Vector Store: Amazon S3 Vectors ← コスト最適化のポイント
↓ RetrieveAndGenerate API
[評価スクリプト(RAGAS)]
├── 質問 → Retrieve → コンテキスト取得
├── コンテキスト → Generate → 回答生成
└── RAGAS評価(faithfulness / answer_relevancy / context_precision / context_recall)
S3 Vectorsを選んだ理由
従来、Bedrock Knowledge BasesのベクターストアはAmazon OpenSearch Serverlessが主流でしたが、
最低でも2 OCU(約$170/月)の固定コストが発生します。
一方でAmazon S3 Vectorsは、ベクターの保存・クエリをS3ネイティブで実現し、
コストをOpenSearch Serverless比で最大90%削減できます。
東京リージョン(ap-northeast-1)でも利用可能です。
計測環境
- リージョン: ap-northeast-1(東京)
- 生成モデル: Amazon Nova Lite (
amazon.nova-lite-v1:0) - Embeddingモデル: Amazon Titan Embed Text v2 (
amazon.titan-embed-text-v2:0) - ベクターストア: Amazon S3 Vectors
- チャンク設定: 固定サイズ(MaxTokens: 300, Overlap: 20%)
- ナレッジドキュメント: AWSサービス概要・セキュリティ・コスト最適化の3ファイル(Markdown)
- 評価データセット: 10問(question / ground_truthのペア)
- RAGASバージョン: 0.4.3
ナレッジドキュメント
S3にアップロードしたドキュメントは以下の3ファイル(Markdown形式)です。
| ファイル | 内容 |
|---|---|
| aws_services_overview.md | S3・EC2・RDS・Lambda・DynamoDBの概要(約200行) |
| aws_security_best_practices.md | IAM・VPC・暗号化・CloudTrail・AWS Configのベストプラクティス(約150行) |
| aws_cost_optimization.md | EC2/S3のコスト最適化・Savings Plans・Cost Explorer(約130行) |
いずれも箇条書きと見出しで構造化したMarkdownで、
各サービスの仕様値(数値・パーセンテージ等)を明示的に記載しています。
これにより「答えがドキュメントに明示されているか」を評価しやすい構成にしました。
評価データセットの設計
評価データセットは以下の観点で10問を設計しました(dataset/eval_dataset.json)。
{
"question": "Amazon S3のストレージクラスにはどのような種類がありますか?",
"ground_truth": "Amazon S3のストレージクラスには、S3 Standard(頻繁にアクセスするデータ向け)、
S3 Standard-IA(アクセス頻度が低いデータ向け)、S3 Glacier(長期アーカイブ向け)、
S3 Intelligent-Tiering(自動階層化)があります。"
}
| 観点 | 問数 | 例 |
|---|---|---|
| 数値・仕様値を問う問い | 4問 | Lambdaの同時実行数上限、DynamoDBアイテムの最大サイズ、S3耐久性、スポットインスタンス割引率 |
| 概念・違いを問う問い | 3問 | セキュリティグループとネットワークACLの違い、IAMのベストプラクティス、CloudTrailの役割 |
| 複数情報を統合する問い | 3問 | S3ストレージクラス一覧、Auroraのパフォーマンス、AWS Budgetsの監視対象 |
ground_truthはドキュメントの記述を元に作成しており、
Context Recallの測定(「必要な情報が検索できているか」)に使用されます。
実装
依存ライブラリ
pip install ragas==0.4.3 boto3 langchain-aws datasets
RAGASは0.4.x系でAPIが大幅に変わっています。
0.2.x系では Faithfulness() のようにインスタンス化できましたが、
0.4.x系では llm 引数が必須になり、LangchainLLMWrapper でラップしたLLMを渡す必要があります。
Bedrock Knowledge BasesへのRetrieveAndGenerate
def retrieve_and_generate(kb_id: str, question: str) -> dict:
client = boto3.client("bedrock-agent-runtime", region_name="ap-northeast-1")
response = client.retrieve_and_generate(
input={"text": question},
retrieveAndGenerateConfiguration={
"type": "KNOWLEDGE_BASE",
"knowledgeBaseConfiguration": {
"knowledgeBaseId": kb_id,
"modelArn": f"arn:aws:bedrock:ap-northeast-1::foundation-model/amazon.nova-lite-v1:0",
"retrievalConfiguration": {
"vectorSearchConfiguration": {"numberOfResults": 5}
}
}
}
)
answer = response["output"]["text"]
contexts = [
c["content"]["text"]
for c in response.get("citations", [{}])[0].get("retrievedReferences", [])
]
return {"answer": answer, "contexts": contexts}
retrieve_and_generate APIは1回の呼び出しで検索と生成を行います。
コンテキストは citations[].retrievedReferences[].content.text から取得します。
RAGASの評価実行
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy, context_precision, context_recall
from ragas.llms import LangchainLLMWrapper
from ragas.embeddings import LangchainEmbeddingsWrapper
from langchain_aws import ChatBedrockConverse, BedrockEmbeddings
llm = LangchainLLMWrapper(
ChatBedrockConverse(model="amazon.nova-lite-v1:0", region_name="ap-northeast-1")
)
embeddings = LangchainEmbeddingsWrapper(
BedrockEmbeddings(model_id="amazon.titan-embed-text-v2:0", region_name="ap-northeast-1")
)
result = evaluate(
dataset=dataset, # datasets.Dataset形式
metrics=[faithfulness, answer_relevancy, context_precision, context_recall],
llm=llm,
embeddings=embeddings,
)
dataset は datasets.Dataset 形式で、question / answer / contexts / ground_truth の4カラムが必要です。
contexts は List[str] 型(検索されたチャンクのリスト)である点に注意してください。
計測結果
チャンクサイズ 300トークン(デフォルト)
| 指標 | スコア |
|---|---|
| Faithfulness | 0.8067 |
| Answer Relevancy | 0.9981 |
| Context Precision | 0.9000 |
| Context Recall | 0.9000 |
※ Answer Relevancyの一部サンプルでNova LiteのJSON出力パースエラーが発生しましたが、有効サンプルの平均値として算出しています。
チャンクサイズ 150トークン(小さめ)・500トークン(大きめ)との比較
チャンクサイズ変更による比較は今後の検証課題とします。
300トークンの結果ではContext Precision・Recallともに0.90と高水準で、
今回のドキュメント構成(構造化Markdown)との相性が良いことが確認できました。
考察
チャンクサイズとContext Precision / Recallのトレードオフ
今回の計測(300トークン)ではContext Precision・Recallともに0.90と高水準でした。
これは今回のナレッジドキュメントが構造化されたMarkdownで、
1チャンクあたりの情報密度が適切だったためと考えられます。
チャンクサイズを小さくするとContext Precisionは上がりますが、Context Recallが下がる傾向があります。
「1チャンクあたりの情報密度が上がる一方、1つの回答に必要な情報が複数チャンクに分散する」ためです。
逆にチャンクサイズを大きくするとContext Recallは上がりますが、Context Precisionが下がります。
このトレードオフはドキュメントの性質に応じて最適なチャンクサイズを選ぶ必要があります。
Faithfulnessが0.81にとどまった理由
Faithfulnessは0.8067と他の指標より低い結果になりました。
エラーログを見るとNova LiteがJSON形式ではなく自然文で回答を返すケースがあり、
RAGASの内部パーサーが解析に失敗したサンプルが複数ありました。
これはモデルの出力形式の問題であり、Claude等の構造化出力が安定したモデルを使うと改善が期待できます。
Answer Relevancyが0.9981と高い理由
Answer Relevancyはほぼ満点に近い値を示しました。
これはナレッジドキュメントが質問に対して直接的な回答を含む構造になっており、
RAGが質問に関連した回答を生成できていることを示しています。
RAGASの評価自体にもLLMを使う点に注意
RAGASはLLM-as-a-Judgeのアプローチを採用しているため、
評価に使うLLMの性能がスコアに影響します。
今回はNova Liteを使用しましたが、JSON出力の安定性の問題でパースエラーが発生しました。
より高性能なモデル(Nova Pro等)を使うと評価の精度が上がる可能性があります。
RAGASのスコアは絶対値ではなく相対比較に使うことが重要です。
構築時のハマりポイント
1. S3 Vector BucketとIndexはCFnデプロイ前にコンソールで手動作成が必要
AWS::S3Vectors::VectorBucket というCFnリソースは存在しますが、
実際にはS3 Vector BucketとIndexをCFnで作成しようとしても動作しませんでした。
CLIでも aws s3vectors create-vector-bucket はプロキシ環境等でSSLエラーになるケースがあります。
結局、AWSコンソールから手動でVector BucketとIndexを作成しました。
AWS::Bedrock::KnowledgeBase の S3VectorsConfiguration には
IndexArn が必要なため、先にコンソールでBucketとIndexを作成し、
そのARNをCFnパラメータとして渡す構成にする必要があります。
StorageConfiguration:
Type: S3_VECTORS
S3VectorsConfiguration:
IndexArn: arn:aws:s3vectors:REGION:ACCOUNT:bucket/BUCKET/index/INDEX
2. Knowledge BaseのIAMロールにS3 Vectorsの権限が必要
Bedrock Knowledge BaseのIAMロールには、S3 Vectorsに対する以下の権限が必要です。
{
"Effect": "Allow",
"Action": [
"s3vectors:PutVectors",
"s3vectors:QueryVectors",
"s3vectors:GetVectors",
"s3vectors:DeleteVectors"
],
"Resource": "arn:aws:s3vectors:REGION:ACCOUNT:bucket/BUCKET_NAME/*"
}
この権限が不足していると、Ingestion Jobが失敗します。
3. Ingestion Jobは非同期
start-ingestion-job APIは非同期で実行されます。
ジョブが完了するまでRetrieveAndGenerateを呼んでも結果が返りません。
完了確認は list-ingestion-jobs APIで行います。
aws bedrock-agent list-ingestion-jobs \
--knowledge-base-id <KB_ID> \
--data-source-id <DS_ID>
4. RAGASのground_truthはContext Recallにのみ必要
RAGASの4指標のうち、ground_truthが必要なのはContext Recallのみです。
Faithfulness・Answer Relevancy・Context Precisionはground_truthなしで評価できます。
評価データセットの準備コストを下げたい場合は、まずground_truthなしの3指標から始めるのも有効です。
5. RetrieveAndGenerateのコンテキスト取得
retrieve_and_generate APIのレスポンスからコンテキストを取得するには
citations[].retrievedReferences[].content.text を参照する必要があります。
retrieve APIを別途呼ぶ方法もありますが、retrieve_and_generate の方が1回のAPI呼び出しで済みます。
6. RAGAS 0.4.xの新APIはOpenAI専用でBedrockでは使えない
RAGAS 0.4.x系には ragas.dataset_schema や ragas.metrics.collections を使う新しいAPIが存在しますが、
これらはOpenAIを前提とした実装になっているように見えており、今回の環境では動作しませんでした。
Bedrockで使う場合は旧来の ragas.metrics からメトリクスをインポートする方式を使いました。
# NG: 新API(OpenAI専用)
from ragas.metrics.collections import RAGASMetrics
# OK: 旧来のAPI(Bedrockでも動作)
from ragas.metrics import faithfulness, answer_relevancy, context_precision, context_recall
また、0.4.x系では Faithfulness() のようにメトリクスをインスタンス化する際に llm 引数が必須になっています。
evaluate() 関数に llm= と embeddings= を渡すことで、各メトリクスにLLMが自動的に注入されます。
まとめ
| 観点 | 内容 |
|---|---|
| コスト | S3 Vectors採用でOpenSearch Serverless比最大90%削減 |
| Faithfulness | 0.8067(Nova LiteのJSON出力不安定が影響) |
| Answer Relevancy | 0.9981(質問への関連性は高水準) |
| Context Precision | 0.9000(検索精度良好) |
| Context Recall | 0.9000(必要情報の網羅性良好) |
| 改善の優先度 | Faithfulness向上にはより安定したモデル(Nova Pro等)の使用が有効 |
RAGASを使うことで「なんとなく動いている」RAGを数値で評価できるようになります。
スコアの絶対値よりも、パラメータ変更前後の相対比較に使うことが重要です。
チャンクサイズ・Overlap率・検索件数(numberOfResults)を変えながら
スコアの変化を追うことで、自分のユースケースに最適なRAG設定を見つけられます。
おわりに
本記事の内容は執筆時点の情報に基づいており、正確性・完全性を保証するものではありません。実際の構築・運用はご自身の責任のもとで行ってください。最後まで読んでいただきありがとうございました。