Home AWS: Amazon Bedrock AgentsをRagasとLLM-as-a-Judgeで評価する
投稿
キャンセル

✍️ AWS: Amazon Bedrock AgentsをRagasとLLM-as-a-Judgeで評価する

ブログ概要(Summary)

AWSが公開したこのブログは、Amazon Bedrock Agentsの評価パイプラインをオープンソースのRagasライブラリとLLM-as-a-Judgeを組み合わせて構築する実践ガイドである。RAG評価(Faithfulness、Context Recall等)、Text-to-SQL評価(Answer Correctness、SQL Semantic Equivalence)、エージェント目標達成度評価(Helpfulness、Faithfulness、Instruction Following)の3カテゴリにわたる評価メトリクスを、Langfuseによるオブザーバビリティと統合した包括的な評価フレームワークを提供している。

この記事は Zenn記事: マルチエージェントRAGの応答品質をLLM-as-Judgeで分解評価する実践手法 の深掘りです。

情報源

技術的背景(Technical Background)

Amazon Bedrock Agentsは、LLMにツール呼び出し・Knowledge Base検索・コード実行などの能力を付与するマネージドサービスである。しかし、エージェントの応答品質を体系的に評価するための標準的なパイプラインは整備されていなかった。

著者らは、エージェント評価を2つのカテゴリに分割するアプローチを採用している。(1) すべての質問に対して実行されるエージェント目標達成度評価(Chain-of-Thought評価)、(2) 特定のツールが使用された場合にのみ実行されるタスク精度評価(RAG評価・Text-to-SQL評価)である。この2層構造により、エージェント全体の振る舞いと個別ツールの精度を同時に評価できる。

Ragasは、RAGパイプラインの品質を定量化するためのオープンソースライブラリであり、Faithfulness・Answer Relevancy・Context Recallなどのメトリクスを提供する。LangChainとの統合により、Bedrock上のモデルをエバリュエーターLLMとして直接使用できる。

実装アーキテクチャ(Architecture)

5ステップの評価パイプライン

著者らが提案する評価パイプラインは以下の5ステップで構成される。

graph TD
    A[1. ユーザー設定: agent_id, alias, model, QAデータセット] --> B[2. Bedrock Agent呼び出し: enableTrace=True]
    B --> C[3. トレース解析: convert_to_ragas_messages, extract_kb_trace]
    C --> D[4. 質問タイプ別評価ルーティング]
    D --> E1[RAG → Ragasメトリクス]
    D --> E2[Text2SQL → LLM-as-Judge]
    D --> E3[CoT → LLM-as-Judge]
    E1 --> F[5. Langfuseダッシュボード]
    E2 --> F
    E3 --> F

Bedrock Agent呼び出しとトレース取得

評価の起点は enableTrace=True によるBedrock Agentの呼び出しである。トレースにはエージェントの推論過程、ツール呼び出し、Knowledge Base検索結果がすべて記録される。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import boto3

def invokeAgent(query, session_id, session_state=dict()):
    agentResponse = bedrock_agent_runtime_client.invoke_agent(
        inputText=query,
        agentId=agent_id,
        agentAliasId=alias_id,
        sessionId=session_id,
        enableTrace=True,  # トレース有効化
        endSession=False,
        sessionState=session_state,
    )

    event_stream = agentResponse["completion"]
    traces = []
    for event in event_stream:
        if "chunk" in event:
            data = event["chunk"]["bytes"]
            agent_answer = data.decode("utf8")
            return agent_answer, traces
        elif "trace" in event:
            traces.append(event["trace"])
    return agent_answer, traces

トレースのRagasフォーマット変換

取得したトレースは convert_to_ragas_messagesextract_kb_trace でRagasが処理可能な形式に変換される。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from ragas.integrations.amazon_bedrock import (
    convert_to_ragas_messages, extract_kb_trace
)

# マルチターンサンプル(エージェント目標評価用)
ragas_messages = convert_to_ragas_messages(traces)
sample = MultiTurnSample(user_input=ragas_messages)

# シングルターンサンプル(RAG評価用)
kb_trace = extract_kb_trace(traces)
single_turn_sample = SingleTurnSample(
    user_input=kb_trace[0].get("user_input"),
    retrieved_contexts=kb_trace[0].get("retrieved_contexts"),
    response=kb_trace[0].get("response"),
    reference="期待されるKB応答"
)

評価メトリクス詳細

カテゴリA: エージェント目標達成度(全質問に対して実行)

Chain-of-Thought評価として以下の3メトリクスが定義されている。

メトリクス説明評価スコア例
Helpfulness明示的・暗黙的な期待に対する充足度0.77
Faithfulness利用可能な情報への忠実性(ハルシネーション回避)0.87
Instruction Following明示的な指示・制約への遵守度0.69

カテゴリB: タスク精度(ツール使用時のみ実行)

RAG評価メトリクス(Ragasライブラリ):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from ragas.metrics import (
    Faithfulness, ContextRelevance, ResponseGroundedness
)

rag_metrics = [
    ContextRelevance(llm=evaluator_llm),   # 検索コンテキストの適合性
    Faithfulness(llm=evaluator_llm),        # コンテキストとの事実整合性
    ResponseGroundedness(llm=evaluator_llm)  # 応答の根拠付け度
]

kb_results = evaluate(
    dataset=EvaluationDataset(samples=[single_turn_sample]),
    metrics=rag_metrics
)

Text-to-SQL評価メトリクス(LLM-as-Judge):

メトリクス説明評価スコア例
Answer CorrectnessSQLクエリが正しい回答を返すか0.83
SQL Semantic Equivalence生成SQLがGround TruthのSQLと意味的に等価か0.81

エージェント固有のRagasメトリクス

AgentGoalAccuracy: エージェントの目標達成度を測定する。参照ありと参照なしの2バリアントがある。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from ragas.metrics import (
    AgentGoalAccuracyWithReference,
    AgentGoalAccuracyWithoutReference,
    ToolCallAccuracy
)

# 参照あり(より決定的)
scorer = AgentGoalAccuracyWithReference(llm=judge_llm)
result = await scorer.multi_turn_ascore(sample_with_ref)
# 返り値: 0.0 or 1.0(バイナリ)

# 参照なし(会話のみから推測)
scorer = AgentGoalAccuracyWithoutReference(llm=judge_llm)
result = await scorer.multi_turn_ascore(sample)

ToolCallAccuracy: LLMが必要なツールを正しく特定・呼び出したかを評価する。引数の比較にはLLMベースではなく文字列距離(Levenshtein, Hamming, Jaro)を使用することで、高速かつ決定的な評価を実現する。

1
2
3
4
5
6
from ragas.metrics._string import NonLLMStringSimilarity

scorer = ToolCallAccuracy()
scorer.arg_comparison_metric = NonLLMStringSimilarity()
result = await scorer.multi_turn_ascore(sample_with_tools)
# 返り値: 0.0 ~ 1.0

カスタムメトリクスの定義

著者らは、ビジネス固有の評価基準をAspectCriticとRubricsScoreで定義する方法を紹介している。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from ragas.metrics import AspectCritic, RubricsScore

# バイナリ評価(0 or 1)
request_completeness = AspectCritic(
    name="Request Completeness",
    llm=evaluator_llm,
    definition="すべてのリクエストを完全に満たしている場合は1、そうでなければ0を返す"
)

# ルーブリック評価(離散スコア)
rubrics = {
    "score-1_description": "メニューにない項目で、推薦もなし",
    "score0_description": "項目が利用可能、または非食品の問い合わせ",
    "score1_description": "項目が利用不可だが、代替品を推薦"
}
recommendations = RubricsScore(
    rubrics=rubrics, llm=evaluator_llm, name="Recommendations"
)

実践的なベンチマーク結果

著者らは製薬マルチエージェントシステム(がんバイオマーカー研究)のユースケースでフレームワークを実証している。Supervisorエージェントが4つの専門サブエージェント(バイオマーカーDB分析、臨床エビデンス研究、統計学者、医用画像専門家)を指揮する構成で、21トラジェクトリ・56質問のテストセットを使用した。

メトリクスカテゴリ対象質問数平均スコア
HelpfulnessAgent Goal500.77
FaithfulnessAgent Goal500.87
Instruction FollowingAgent Goal500.69
Answer CorrectnessText-to-SQL260.83
SQL Semantic EquivalenceText-to-SQL260.81
Semantic SimilarityRAG200.66
Context RecallRAG200.53

Context Recall(0.53)の低さは、Knowledge Baseの検索精度に改善の余地があることを示唆している。一方、Faithfulness(0.87)は応答のハルシネーション率が低いことを示している。

Langfuseによるオブザーバビリティ

Langfuseは評価結果と実行トレースの可視化・分析プラットフォームとして統合されている。

提供する機能:

  • ダッシュボード: エージェントパフォーマンスの集約可視化
  • トレースパネル: 複数会話のフィルタリング分析
  • 個別トレース: ツール呼び出し・推論ステップの詳細分解
  • メトリクス: レイテンシ測定、モデルコスト(トークン数)の追跡
  • 評価スコア: ジャッジLLMのChain-of-Thought説明付きスコア

エンタープライズ環境では、LangfuseをAWS ECS/Fargate上にセルフホストすることでデータ主権を確保できる。

学術研究との関連

本ブログのフレームワークは、以下の学術研究を実務に適用している。

  • RAGAS (Shahul et al., 2023): RAG品質メトリクスの基盤。Faithfulness、Context Relevanceなどの概念を直接実装
  • LLM-as-a-Judge (Zheng et al., 2023): モデルベース評価の基本フレームワーク。Text-to-SQL評価やAgent Goal評価に応用
  • Agent-as-a-Judge (Zhuge et al., 2024): エージェントによるエージェント評価。本ブログのトレースベース評価アプローチはこの概念の実践的実装

Production Deployment Guide

AWS実装パターン(コスト最適化重視)

トラフィック量別の推奨構成:

Small(~100 req/日): Lambda + Bedrock構成

  • Lambda(評価パイプライン実行): $0.20/100万リクエスト
  • Bedrock Claude 3 Haiku(Judge LLM): 入力$0.00025/1K tokens, 出力$0.00125/1K tokens
  • DynamoDB(評価結果保存): オンデマンド$1.25/100万書き込み
  • 月額概算: $30-80

Medium(~1000 req/日): ECS Fargate + Bedrock構成

  • Fargate(0.25 vCPU, 0.5 GB): 約$9/月
  • Bedrock Claude 3.5 Sonnet(Judge LLM): 入力$0.003/1K tokens, 出力$0.015/1K tokens
  • Langfuse(セルフホスト、ECS): 約$30/月
  • 月額概算: $200-500

Large(10000+ req/日): EKS + Spot + Bedrock Batch構成

  • EKS + Spot Instances: 約$150/月(Spot活用で60%削減)
  • Bedrock Batch API: 通常の50%のコスト
  • ElastiCache(結果キャッシュ): 約$50/月
  • 月額概算: $800-2,000

コスト削減テクニック:

  • Bedrock Batch APIで50%削減(非同期評価ジョブに最適)
  • Prompt Caching有効化で30-90%削減
  • ToolCallAccuracyにNonLLMStringSimilarityを使用してLLM呼び出しを削減
  • 参照ありメトリクスを優先し、不確実な推論による再試行を削減

Terraformインフラコード

Small構成(Serverless):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# Lambda + Bedrock評価パイプライン
resource "aws_lambda_function" "eval_pipeline" {
  function_name = "bedrock-agent-eval"
  runtime       = "python3.12"
  handler       = "driver.handler"
  timeout       = 900
  memory_size   = 1024

  environment {
    variables = {
      BEDROCK_AGENT_ID    = var.agent_id
      BEDROCK_AGENT_ALIAS = var.agent_alias
      EVAL_MODEL_ID       = "anthropic.claude-3-haiku-20240307-v1:0"
      LANGFUSE_PUBLIC_KEY = var.langfuse_public_key
      LANGFUSE_HOST       = var.langfuse_host
    }
  }
}

resource "aws_iam_role_policy" "bedrock_access" {
  name = "bedrock-agent-eval-policy"
  role = aws_iam_role.lambda_role.id
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "bedrock:InvokeAgent",
          "bedrock:InvokeModel"
        ]
        Resource = "*"
      }
    ]
  })
}

resource "aws_dynamodb_table" "eval_results" {
  name         = "bedrock-eval-results"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "eval_id"
  range_key    = "timestamp"

  attribute {
    name = "eval_id"
    type = "S"
  }
  attribute {
    name = "timestamp"
    type = "N"
  }
}

resource "aws_cloudwatch_event_rule" "eval_schedule" {
  name                = "daily-eval-run"
  schedule_expression = "cron(0 14 * * ? *)"
}

Large構成(Container):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# EKS + Spot構成
module "eks" {
  source          = "terraform-aws-modules/eks/aws"
  cluster_name    = "bedrock-eval-cluster"
  cluster_version = "1.29"

  vpc_id     = module.vpc.vpc_id
  subnet_ids = module.vpc.private_subnets

  eks_managed_node_groups = {
    eval_workers = {
      instance_types = ["m6i.large", "m5.large"]
      capacity_type  = "SPOT"
      min_size       = 1
      max_size       = 10
      desired_size   = 2
    }
  }
}

resource "aws_budgets_budget" "eval_budget" {
  name         = "bedrock-eval-monthly"
  budget_type  = "COST"
  limit_amount = "2000"
  limit_unit   = "USD"
  time_unit    = "MONTHLY"

  notification {
    comparison_operator       = "GREATER_THAN"
    threshold                 = 80
    threshold_type            = "PERCENTAGE"
    notification_type         = "ACTUAL"
    subscriber_sns_topic_arns = [aws_sns_topic.budget_alerts.arn]
  }
}

運用・監視設定

CloudWatch Logs Insightsクエリ(コスト異常検知):

1
2
3
4
fields @timestamp, @message
| filter @message like /bedrock/
| stats sum(input_tokens) as total_input, sum(output_tokens) as total_output by bin(1h)
| sort @timestamp desc

CloudWatchアラーム(Bedrock使用量):

1
2
3
4
5
6
7
8
9
10
11
resource "aws_cloudwatch_metric_alarm" "bedrock_throttle" {
  alarm_name          = "bedrock-eval-throttle"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 2
  metric_name         = "ThrottledCount"
  namespace           = "AWS/Bedrock"
  period              = 300
  statistic           = "Sum"
  threshold           = 10
  alarm_actions       = [aws_sns_topic.alerts.arn]
}

コスト最適化チェックリスト

  • アーキテクチャ選択: 日次評価ジョブならServerless(Lambda)、常時評価ならContainer(EKS)
  • LLMコスト削減: Judge LLMにはHaiku(低コスト)を使用し、高精度が必要な場面のみSonnetに切り替え
  • Batch API活用: 非同期で良い評価ジョブはBedrock Batch APIで50%削減
  • キャッシュ戦略: 同一クエリの再評価をElastiCacheでキャッシュし、LLM呼び出しを削減
  • 監視: AWS Budgets + Cost Anomaly Detection + CloudWatchアラームの3層監視

まとめ

本ブログは、Amazon Bedrock AgentsをRagasとLLM-as-a-Judgeで評価するための実践的なフレームワークを提供している。2層評価構造(Agent Goal + Task Accuracy)、トレースベースの自動メトリクス計算、Langfuseによる可視化の組み合わせは、マルチエージェントRAGの品質評価パイプラインの構築に直接応用できる。特に、ToolCallAccuracyでの文字列距離活用やAspectCriticによるカスタムメトリクスは、ビジネス固有の評価基準を効率的に定義する方法として実用的である。

参考文献

この投稿は CC BY 4.0 でライセンスされています。