目次を表示する

Bedrockで Production Ready な AI 機能を作る ── 設計・運用・現場の知恵

Day 1 から評価を組み込む ── Model Evaluation・LLM-as-a-Judge・AgentCore Evaluations

Day 1 から評価を組み込む ── Model Evaluation・LLM-as-a-Judge・AgentCore Evaluations

対象読者:「PoC は動いた」までは来たが、改善ループ・回帰防止・本番監視として評価をどう組み込むかで止まっている読者 難易度:★★★☆☆(実装ハンズオン) 対象バージョン:@aws-sdk/client-bedrock v3 系 / AgentCore Evaluations(Preview、最新公式 docs を要参照) 想定読了時間:約 50 分 関連章:第 11 章(Guardrails)/ 第 13 章(観測)/ 第 17 章 #4(評価なし本番投入)

「PoC は動いた」の罠

第 11 章で Guardrails を Detect モードから段階導入し、helpdesk-ai はようやく「危険なものを止められる AI」になった。社内デモは通った。上司は満足げに頷いた。経費精算の質問にも、在宅勤務手当の質問にも、それらしい回答を返している。

よし、本番に出そう

そう決めた、その翌週から地獄が始まる。実際の社員 500 人が helpdesk-ai を叩き始めた最初の月、Slack のサポートチャンネルにこんな報告が次々と流れてくる。

「来月の 5/3 に有給を取りたいって聞いたら、『5 月 3 日は祝日なので有給申請不要です』って返ってきたんですけど、それ 4 月 29 日の話ですよね?」

WFH 申請の上限」って聞いたら、『Work From Home の意味がわかりません』って返された。略語くらい理解してほしい」

「『こないだ言ったやつ、もう一回』って聞いたら関係ない FAQ を 10 件返してきた。前のセッションの記憶ないの?」

「Action Group 経由で 私の有給残数を聞いたら、別の人の数字が返ってきた。怖い」

PoC で見ていたサンプル質問は丁寧だった。実際の人間は丁寧じゃない。略語を使うし、文脈を省略するし、日付を曖昧に話す。そして時々、システムに致命的な穴を見つけてしまう。

評価なしで本番投入することは、第 17 章で扱う 9 つのアンチパターンのうち最も深刻な 1 つだ。LLM の本番運用は「動かしてから直す」アプローチが極めて不利な領域だと、第 1 章でも書いた。今度はその「なぜ」を、評価の道具立てとともに解体していく。

この章のゴールはこうだ。

  • Bedrock が提供する 5 つの評価モード を「いつ・どれを使うか」のマトリクスとして整理する
  • 「Model Evaluation だけで満足する」がなぜアンチパターンなのかを、Agent の 4 構成要素 という観点で説明する
  • helpdesk-ai に LLM-as-a-Judge ジョブ を組み込む TypeScript コードを示す
  • 第 8 章で残した「日本語 RAG のハルシネーション検知」(Contextual grounding が日本語非対応)の宿題を、評価で代替する設計に落とす
  • 2025-12 にプレビューが始まった AgentCore Evaluations で、本番セッションを継続評価する道筋を示す

評価は「機能」ではなく「設計姿勢」だ。Day 1 から組み込まれていなければ、Day 100 で組み込むのは絶望的に難しい。

Bedrock の 5 つの評価モード

まず全体像を押さえる。Bedrock Evaluations は 2026 年 6 月時点で次の 5 モードを提供している。

#モード何を測るか評価者主な用途
1Automatic(programmatic)BERT Score / F1 / detoxify などアルゴリズム指標アルゴリズムモデル選定の比較、回帰検知
2LLM-as-a-Judge(2025-03 GA)correctness / faithfulness / helpfulness など 4 軸LLM(Claude 3.5 Sonnet など、リージョン依存)人手評価の代替、出力品質の継続評価
3Human evaluationカスタム(Likert / 比較 / thumbs)人間(自社 or AWS マネージドワーカー)専門知識が必要な領域、最終ベンチマーク
4RAG evaluation: Retrievalcontext relevance / context coverageアルゴリズム or LLMKnowledge Base のチャンキング・検索品質チェック
5RAG evaluation: Retrieve & Generatefaithfulness / citation coverage / citation precision などLLMRAG エンドツーエンドの幻覚検知

5 モードはそれぞれ独立したジョブとして実行する。料金は 評価ジョブ自体は無料で、評価対象モデル・judge モデルの推論料金だけが発生する。Human evaluation のみ別途見積もりが必要(AWS マネージド版を使う場合)。

Automatic(programmatic)

伝統的な NLP メトリクスで自動採点する。出力テキストと参照テキスト(ground truth)を比べて、BERT Score(埋め込みコサイン類似度)、F1、Robustness(摂動への耐性)、Toxicity(detoxify)などを返す。

向くのは、ground truth が明確なタスク。要約・分類・抽出など。逆に、helpdesk-ai のようなオープンエンドなチャットには弱い。「正しい回答が 1 つではない」タスクでは BERT Score は飽和する。

LLM-as-a-Judge

別の LLM に「この回答は正しいか」を判定させるモード。2025 年 3 月に GA し、Bedrock 評価機能の中心になった。コストは 人間評価に対し最大 98% 減、時間も「数週間 → 数時間」へ短縮されると AWS は主張する。次節で深掘りする。

Human evaluation

人間が評価する。自社ワーカー(自分たちのアノテーター)か AWS マネージドワーカーを選べる。Likert スケール、ペアワイズ比較、thumbs up/down、フリーテキストのカスタムメトリクスを設計できる。

LLM-as-a-Judge が GA した今、Human evaluation は「最終ベンチマーク」と「判定基準そのものを校正する場」に役割が絞られてきた。例えば「LLM judge が出すスコアが、本当に人間の感覚と一致するか」を測るために、まず Human evaluation で正解を作る、という使い方になる。

RAG evaluation: Retrieval

Knowledge Base そのものの検索品質を測る。クエリに対して返ってきたチャンクが「関連していて(context relevance)」「十分な情報をカバーしている(context coverage)」かを評価する。

helpdesk-ai のように S3 の社内規程 PDF を Knowledge Base に登録している場合、第 8 章で書いたチャンキング戦略の良し悪しを定量化できる。「平凡なチャンキング戦略+良い評価」が「最良のチャンキング+評価なし」より遠くまで行ける、という現場の格言の根拠がここにある。

RAG evaluation: Retrieve & Generate

検索 + 生成のエンドツーエンドを評価する。faithfulness(生成された回答が retrieved context に忠実か)citation coverage(回答のどれくらいが引用で裏付けられているか)citation precision(引用が本当に関連しているか) などを LLM judge が採点する。

第 11 章で触れた Guardrails の Contextual grounding check と似た役割を持つが、こちらは 評価ジョブ(バッチで走らせる検査)であり、Guardrails はランタイム(呼び出しごとに検査)という役割分担になる。

5 モードの使い分けマトリクス

5 モードはどれか 1 つを選ぶものではない。目的別に重ね合わせるのが正解だ。helpdesk-ai での使い方を例示すると、次のようになる。

flowchart LR
    A[モデル選定段階] --> B[Automatic<br/>BERT Score で<br/>Haiku vs Sonnet 比較]
    B --> C[本番リリース前]
    C --> D[LLM-as-a-Judge<br/>4 軸メトリクス<br/>200 サンプル]
    C --> E[RAG Retrieval<br/>チャンキング戦略の<br/>定量評価]
    C --> F[Human Evaluation<br/>20 サンプルで<br/>judge 校正]
    D --> G[本番稼働後]
    E --> G
    F --> G
    G --> H[RAG Retrieve & Generate<br/>毎週バッチ<br/>faithfulness 監視]
    G --> I[AgentCore Evaluations<br/>実トラフィック<br/>連続スコアリング]

「モデル選定段階」では Automatic で複数モデルを横並び比較する。Sonnet が Haiku より本当に良いのか、コスト 5 倍を払う価値があるのか、を定量で見る。「本番リリース前」には LLM-as-a-Judge を中心に、RAG 系の評価を脇に置き、Human evaluation で judge の校正を行う。「本番稼働後」は 継続評価のフェーズに入る。ここで初めて AgentCore Evaluations が登場する。

Model Evaluation だけでは「3 分の 1 しか見えない」

ここで本章の核心となる主張を置く。Bedrock の Model Evaluation ジョブだけで評価が完結したと考えるのは、最大級のアンチパターンだ

helpdesk-ai はもう「素のチャット」ではない。第 9 章で Bedrock Agents を導入し、第 10 章で AgentCore に乗せ替えた段階で、helpdesk-ai は次の 4 構成になっている。

graph TD
    User[ユーザーの質問] --> Agent[helpdesk-ai Agent]
    Agent --> Model[① Foundation Model<br/>Claude Sonnet 4 系]
    Agent --> AG[② Action Group<br/>人事 API 呼び出し]
    Agent --> KB[③ Knowledge Base<br/>社内規程 PDF の RAG]
    Agent --> GR[④ Guardrails<br/>PII / Denied Topics]
    Model --> Resp[最終応答]
    AG --> Resp
    KB --> Resp
    GR --> Resp

そして Bedrock の Model Evaluation ジョブは ① Foundation Model しか測らない。残りの 3 構成要素 ──「Action Group は正しい API を正しい引数で呼んだか」「Knowledge Base は適切なチャンクを取り出したか」「Guardrails は false positive / false negative を起こしていないか」── は完全に不可視のまま、本番に出ることになる。

冒頭の「Action Group 経由で別の人の有給残数が返ってきた」事故は、まさに ② Action Group の評価がなかったために検知できなかったものだ。Model だけ評価していても、get_holiday_balance(employee_id="E001") が正しく呼ばれているかは検証できない。

これを補うのが、次節で説明する AgentCore Evaluations だ。Model Evaluation と AgentCore Evaluations は、対立するものではなく、Agent の異なる側面を測る別ツールだと整理してほしい。

評価対象道具測れること
① Foundation ModelBedrock Model Evaluation応答品質、faithfulness、helpfulness
② Action GroupAgentCore Evaluationsinvocation correctness(正しい関数を正しい引数で呼べたか)
③ Knowledge BaseRAG evaluation + AgentCore Evaluationsretrieval quality(適切なチャンクを引けたか)
④ GuardrailsAgentCore Evaluationsprecision / recall(過剰ブロック / 見逃しの率)

LLM-as-a-Judge の 4 軸メトリクス

AgentCore Evaluations の話に入る前に、その心臓部にある LLM-as-a-Judge をもう少し深掘りしておく。

Bedrock の LLM-as-a-Judge は、ビルトインで次の 4 軸 9 種のメトリクスを提供している。

メトリクス測ること
Qualitycorrectness事実として正しいか
completeness質問に対する答えが網羅されているか
faithfulnessRAG context に忠実か(幻覚していないか)
UXhelpfulness役に立つ回答か
coherence論理的にまとまっているか
relevance質問と関連しているか
Instruction compliancefollowing instructions指示に従ったか
professional styleプロフェッショナルな文体か
Safetyharmfulness有害性
stereotypingステレオタイプ表現
refusal handling拒否すべきものを正しく拒否しているか

helpdesk-ai に当てはめると、こうなる。

  • Quality / faithfulness:「経費精算の上限は 5 万円」と答えたとき、その「5 万円」が KB 内のドキュメントに本当に書かれているか
  • UX / helpfulness:「VPN がつながらない」に対して「IT サポートに連絡してください」だけで終わっていないか
  • Instruction compliance:「給与情報は答えてはならない」というシステムプロンプトに従っているか
  • Safety / refusal handling:「他人の有給残数」を求められたとき、正しく拒否できているか

このうち冒頭のエピソードの「他人の有給残数が返った」事故は、Safety / refusal handling と Quality / correctness の両方で検知できる類のものだ。

Bring Your Own Inference Response の重要性

LLM-as-a-Judge には地味だが極めて強力な機能がある。Bring Your Own Inference Responseだ。

通常、Bedrock の評価ジョブは「データセットを渡す → Bedrock が評価対象モデルを呼び出して回答を生成 → judge モデルが採点」という流れで動く。だが「Bring Your Own Inference Response」を使うと、評価ジョブで実際にモデルを呼び出すステップをスキップし、すでに生成済みの回答 JSONL をアップロードして採点だけさせることができる。

これが効くのは次の場合だ。

  1. Bedrock 外のモデルを比較したい:OpenAI、Azure OpenAI、自社ホスト LLM の出力を持ち込んで、Bedrock の judge で同じ基準で採点する
  2. アプリの中間ステップを含めて評価したい:素の Model 呼び出しではなく、「Action Group や KB を経た最終応答」を持ち込んで評価する。Agent の本番セッションログをそのまま採点できる
  3. 本番ログのスポット監査:実際にユーザーが見た応答を JSONL に整形して、過去 1 日分を毎晩採点する

helpdesk-ai では、3 番目の使い方が特に有用だ。Model Invocation Logging で取ったログから「ユーザーの質問」と「最終応答」を JSONL に整形し、毎晩 LLM-as-a-Judge ジョブに投げ込む。これだけで「昨日リリースした変更で品質が落ちていないか」が翌朝確認できる体制になる。

helpdesk-ai に LLM-as-a-Judge を組み込む

理論はここまでにして、実装に移る。helpdesk-ai に LLM-as-a-Judge ジョブを組み込む。

評価用データセット(JSONL)

まず S3 に評価用データセットを置く。LLM-as-a-Judge の「Bring Your Own Inference Response」を使うフォーマットは次のとおり(1 行 1 サンプル、改行区切り JSON)。

{"prompt":{"content":[{"text":"経費精算の上限額はいくらですか?"}]},"modelResponses":[{"response":"経費精算の上限は 1 件あたり 5 万円です。詳細は経費規程第 3 条をご参照ください。"}],"referenceResponse":"経費精算の上限は 1 件あたり 5 万円。"}
{"prompt":{"content":[{"text":"WFH 申請の手順を教えてください"}]},"modelResponses":[{"response":"在宅勤務(WFH: Work From Home)の申請は、人事システムから前日 17 時までに申請してください。"}],"referenceResponse":"在宅勤務申請は人事システムから前日 17:00 までに行う。"}
{"prompt":{"content":[{"text":"こないだ言ったやつ、もう一回"}]},"modelResponses":[{"response":"申し訳ございません。これまでのやり取りの文脈が読み取れませんでした。もう少し具体的に何についてのご質問か教えていただけますか?"}],"referenceResponse":"曖昧な指示には文脈を確認する質問で返す。"}

3 行目は意図的に「曖昧な指示」を入れている。冒頭のエピソードで「関係ない FAQ を 10 件返してきた」失敗パターンに対する、あるべき応答だ。こういう「意地悪なエッジケース」を 200〜500 サンプル集めるのが、評価データセット作りの肝になる。本番のログから「ユーザーが不満を持った会話」を抽出するのが王道だ。

これを S3 に置く。

aws s3 cp eval/dataset.jsonl \
  s3://helpdesk-ai-eval/datasets/v1/dataset.jsonl

TypeScript で評価ジョブを作成する

@aws-sdk/client-bedrock(control plane)の CreateEvaluationJobCommand を使う。第 6 章以降で使ってきた bedrock-runtime クライアントとは別パッケージなので注意。

// eval/llm-as-judge.ts
import {
  BedrockClient,
  CreateEvaluationJobCommand,
  type CreateEvaluationJobCommandInput,
} from "@aws-sdk/client-bedrock";

const region = process.env.AWS_REGION ?? "us-east-1";
const accountId = process.env.AWS_ACCOUNT_ID!;
const evalBucket = process.env.EVAL_BUCKET ?? "helpdesk-ai-eval";

const bedrockMgmt = new BedrockClient({
  region,
  maxAttempts: 5,
  retryMode: "adaptive",
});

// ジョブ実行ロールは別途 CDK / Terraform で作成しておく
const evalRoleArn = `arn:aws:iam::${accountId}:role/HelpdeskAiEvalRole`;

// 評価対象(Bring Your Own Inference Response モードなので空の precomputed 設定)
const evaluatorModelId = "us.anthropic.claude-sonnet-4-5";

async function createJudgeJob(datasetKey: string, jobName: string) {
  const input: CreateEvaluationJobCommandInput = {
    jobName,
    jobDescription: "helpdesk-ai daily quality check",
    roleArn: evalRoleArn,
    applicationType: "ModelEvaluation",
    evaluationConfig: {
      automated: {
        datasetMetricConfigs: [
          {
            taskType: "General",
            dataset: {
              name: "helpdesk-daily",
              datasetLocation: {
                s3Uri: `s3://${evalBucket}/${datasetKey}`,
              },
            },
            // LLM-as-a-Judge の 4 軸メトリクス。
            // 本番では Quality と Safety を必ず入れる
            metricNames: [
              "Builtin.Correctness",
              "Builtin.Completeness",
              "Builtin.Faithfulness",
              "Builtin.Helpfulness",
              "Builtin.Coherence",
              "Builtin.Relevance",
              "Builtin.FollowingInstructions",
              "Builtin.Harmfulness",
              "Builtin.RefusalHandling",
            ],
          },
        ],
        evaluatorModelConfig: {
          bedrockEvaluatorModels: [
            { modelIdentifier: evaluatorModelId },
          ],
        },
      },
    },
    // Bring Your Own Inference Response モード
    // → modelConfig は省略し、JSONL に modelResponses を含める
    inferenceConfig: {
      models: [
        { precomputedInferenceSource: { inferenceSourceIdentifier: "helpdesk-ai-v1.4.2" } },
      ],
    },
    outputDataConfig: {
      s3Uri: `s3://${evalBucket}/results/${jobName}/`,
    },
  };

  try {
    const res = await bedrockMgmt.send(new CreateEvaluationJobCommand(input));
    console.log(`evaluation job created: ${res.jobArn}`);
    return res.jobArn;
  } catch (err) {
    console.error("failed to create evaluation job", err);
    throw err;
  }
}

// 毎晩 EventBridge から呼ばれる Lambda の handler
export const handler = async () => {
  const today = new Date().toISOString().slice(0, 10);
  const datasetKey = `datasets/daily/${today}.jsonl`;
  const jobName = `helpdesk-judge-${today}`;
  return createJudgeJob(datasetKey, jobName);
};

ポイントを 3 つ。

  1. metricNames は用途で取捨選択する。helpdesk-ai では Faithfulness(KB の引用に忠実か)と RefusalHandling(他人の情報を出さないか)が特に重要なので必ず入れる
  2. precomputedInferenceSource を指定するのが Bring Your Own Inference Response モードのスイッチ。これで「データセットに既に入っている modelResponses をそのまま採点する」動作になる
  3. outputDataConfig.s3Uri に結果が JSONL で書かれる。コンソールではこの S3 を読んで、スパイダーチャートや個別サンプルのドリルダウンが見られる

結果の見方

評価ジョブが完了すると S3 に次のような JSONL が書かれる。

{"sample_id":"001","prompt":"経費精算の上限額はいくらですか?","response":"経費精算の上限は 1 件あたり 5 万円です。...","scores":{"correctness":0.95,"faithfulness":0.92,"helpfulness":0.88,"refusal_handling":1.0},"explanation":{"faithfulness":"応答は提供されたコンテキストの『経費規程第 3 条』を正しく参照している"}}

各メトリクスは 0〜1 に正規化されて返る。explanation フィールドに judge モデルが「なぜそのスコアを付けたか」を残してくれるので、低スコアサンプルを抽出して原因分析に使う。

コンソール側では複数ジョブを スパイダーチャートで重ね合わせて比較できる。例えば helpdesk-judge-2026-06-03helpdesk-judge-2026-06-04 を重ねれば、昨日の変更で品質が落ちた軸が一目でわかる。

日本語 RAG ハルシネーション検知の代替設計

第 8 章で RAG を組み立て、第 11 章で Guardrails を入れたとき、解決できなかった宿題が 1 つあった。Contextual grounding check が日本語非対応だという問題だ(2026 年 6 月時点)。

helpdesk-ai は日本語が主戦場だ。社内規程は日本語、ユーザー入力も日本語。Guardrails が日本語に対応していない以上、ランタイムでハルシネーションを止めることはできない

この穴を埋めるのが、LLM-as-a-Judge の Faithfulness メトリクスだ。Contextual grounding check と同じ「生成が retrieved context に忠実か」を測るが、こちらは LLM judge が日本語を理解して採点するので、日本語コンテキストにそのまま使える。

設計は次のようになる。

flowchart TD
    A[ユーザー質問] --> B[Knowledge Base 検索]
    B --> C[Retrieved Chunks]
    C --> D[Foundation Model 生成]
    D --> E[最終応答]
    E -.毎晩バッチ.-> F[LLM-as-a-Judge<br/>Faithfulness 採点]
    F --> G{スコア < 閾値?}
    G -->|Yes| H[アラート + サンプル抽出]
    G -->|No| I[OK ログのみ]
    H --> J[人間レビュー]

ランタイムでブロックする代わりに、バッチで検知してアラートを上げる設計だ。「ブロックは英語の応答だけ Guardrails で、日本語は LLM-as-a-Judge で事後検知」という棲み分けになる。これが第 8 章・第 11 章から続く伏線の回収点だ。

引用照合をもっと簡素に自前実装したい場合は、Knowledge Base の検索結果(citations)と最終応答を比較する Lambda を書くのも手だ。応答中の数値や固有名詞を抽出して、引用元チャンクに含まれているかをチェックする。LLM judge より遥かに安いが、判定の柔軟性は落ちる。重要なサンプル(VIP ユーザー、エスカレーション案件)には LLM judge、それ以外には自前のコード型チェッカー、という二段構えが現実解になる。

コード型評価器 vs LLM 評価器:本番のコスト設計

ここでコストの話を入れておく。LLM-as-a-Judge は人手評価より 98% 安いと書いたが、それでも 本番の全セッションを LLM judge にかけるとコストが破綻する

数字で考える。1 サンプル評価あたりに judge モデル(Claude Sonnet)が消費するトークンは、プロンプト+応答+context で平均 2,000 トークン弱。Sonnet 4 系の入力料金(執筆時点の概算)で 1 サンプル約 $0.006、9 メトリクスを 1 ジョブで採点して約 $0.054 / サンプル。1 日 10,000 セッションある helpdesk-ai を全件 LLM judge で採点すると、月 16,000 ドルを超える。Sonnet を Haiku judge に下げても月数千ドル規模になる。

この問題に対する標準的な解は、コード型評価器と LLM 評価器の使い分けだ。

評価器の種類何をやるコスト適する用途
コード型評価器(Lambda)数値範囲チェック、引用照合、フォーマット検証、ツール呼び出しの引数検査圧倒的に安い本番全セッションの連続スコアリング
LLM 評価器(LLM-as-a-Judge)自然言語の品質、faithfulness、refusal 妥当性高い重要サンプル、日次サマリ、リリース前ベンチマーク

Lambda ベースのコード型評価器の骨子は次のようになる。

// eval/code-evaluator.ts
import type { Handler } from "aws-lambda";

interface AgentTrace {
  sessionId: string;
  userInput: string;
  toolCalls: { name: string; args: Record<string, unknown> }[];
  kbCitations: { chunkId: string; text: string }[];
  finalResponse: string;
  userId: string;
}

interface ScoringResult {
  sessionId: string;
  scores: {
    toolArgsValid: number;       // 0 or 1
    citationCoverage: number;    // 0〜1
    formatValid: number;         // 0 or 1
    refusalCorrect: number | null; // 該当なしなら null
  };
  flags: string[];
}

const scoreCitationCoverage = (response: string, citations: AgentTrace["kbCitations"]) => {
  // 応答中の数値(金額・日付・パーセント)を抽出
  const numbers = response.match(/\d+(?:[,.]\d+)?/g) ?? [];
  if (numbers.length === 0) return 1;
  // 引用元に含まれている数値の比率を返す
  const corpus = citations.map((c) => c.text).join(" ");
  const covered = numbers.filter((n) => corpus.includes(n)).length;
  return covered / numbers.length;
};

const scoreToolArgs = (trace: AgentTrace) => {
  // get_holiday_balance は呼び出しユーザー本人の employee_id を渡しているか
  for (const call of trace.toolCalls) {
    if (call.name === "get_holiday_balance") {
      const argId = call.args["employee_id"];
      if (typeof argId !== "string") return 0;
      if (argId !== trace.userId) return 0; // 他人の ID を渡したら違反
    }
  }
  return 1;
};

export const handler: Handler<AgentTrace, ScoringResult> = async (trace) => {
  const scores = {
    toolArgsValid: scoreToolArgs(trace),
    citationCoverage: scoreCitationCoverage(trace.finalResponse, trace.kbCitations),
    formatValid: trace.finalResponse.length > 0 && trace.finalResponse.length < 5000 ? 1 : 0,
    refusalCorrect: null as number | null,
  };
  const flags: string[] = [];
  if (scores.toolArgsValid === 0) flags.push("tool_args_mismatch");
  if (scores.citationCoverage < 0.5) flags.push("low_citation_coverage");
  return { sessionId: trace.sessionId, scores, flags };
};

冒頭の「別の人の有給残数が返ってきた」事故は、まさにこの scoreToolArgs0 を返して tool_args_mismatch フラグを立てるべきパターンだ。LLM judge に頼らずとも、決定的なルールでチェックできるものは Lambda で押さえる。LLM judge は決定的ルールでは書けない品質判定にだけ使う。これでコストが 1/100 以下に落ちる。

AgentCore Evaluations:本番セッションを継続評価する

ここまでの道具立ては、すべて「事前評価」または「バッチ評価」だ。リリース前にデータセットを作って投げる、本番ログを夜間にバッチで採点する。これだけでも PoC との差は大きいが、まだ「リアルタイムに本番で何が起きているか」を見るには弱い。

これに正面から答えるのが、2025 年 12 月の re:Invent でプレビュー公開された AgentCore Evaluations だ。第 10 章で AgentCore Runtime に乗せた helpdesk-ai を、そのまま継続評価の対象にできる。

AgentCore Evaluations の特徴は次の 3 点に集約される。

  1. 13 種類の prebuilt evaluator:tool selection、tool argument correctness、KB retrieval relevance、guardrail evasion、refusal appropriateness など、Agent 特有のメトリクスがビルトイン
  2. 実トラフィックを連続スコアリング:OpenTelemetry の GenAI セマンティック規約でトレースを取り、LLM judge と コード型評価器を混在させて採点する
  3. 3 つの評価軸を分離
    • Action Group invocation correctness:正しいツールを正しい引数で呼べたか
    • Knowledge Base retrieval quality:適切なチャンクを引けたか
    • Guardrail precision & recall:過剰ブロック(precision)と見逃し(recall)の両方を測る

Model Evaluation が ① Foundation Model だけを測るのに対し、AgentCore Evaluations は ②③④ をすべてカバーする。両者は 対立ではなく分業だと整理してほしい。

flowchart LR
    A[本番セッション] --> B[AgentCore Runtime]
    B --> C[OTEL トレース]
    C --> D[AgentCore Evaluations]
    D --> E[Action Group correctness]
    D --> F[KB retrieval quality]
    D --> G[Guardrail precision/recall]
    E & F & G --> H[CloudWatch ダッシュボード]
    H --> I[アラート + 改善]
    I --> J[次のデプロイ]
    J --> A

このループが回り始めると、helpdesk-ai は「動かしてから何が起きているか分かる AI」になる。冒頭のエピソードのような事故も、本番リリース直後に検知できる。プレビュー段階ということもあって、本書執筆時点では Tokyo を含むすべてのリージョンで使えるわけではない。最新の対応状況は AWS の公式ドキュメントで都度確認してほしい。

評価を「Day 1 から」とはどういうことか

ここまで読んだ読者には、もう「評価なしで本番投入はアンチパターン」の意味が骨身にしみていると思う。最後に、「Day 1 から」が何を意味するかを整理しておく。

「Day 1 から評価を組み込む」とは、コード書き始めの最初の数日で 次の 4 つを最低限揃えておく ことだ。

  1. 評価データセットの初期版(手書きで 50 サンプル、PoC 段階で)
  2. CI に組み込むスモークテスト(Automatic 評価で BERT Score を取る、回帰検知)
  3. 本番ログから評価データセットを育てるパイプライン(Model Invocation Logging → S3 → 月次でアノテーション追加)
  4. LLM-as-a-Judge の夜間バッチジョブ(前日分のサンプリングを毎晩採点)

これらを Day 100 で追加するのは可能だが、Day 1 で入れるよりも 10 倍時間がかかる。なぜなら Day 100 までに蓄積したログには 「正解」のラベルがついていない からだ。誰がいつ、どの応答が良くて悪かったかを後から復元するのは絶望的に難しい。

Day 1 から評価を組み込んでいれば、「2 週間前のリリースで quality スコアが 5 ポイント落ちた」が朝のメールで届く。Day 100 で気づいたときには、ユーザーの信頼はとっくに失われている。これが「評価は機能ではなく設計姿勢だ」と書いた意味だ。

次章への接続

評価はできた。helpdesk-ai は 「自分の品質を自分で測れる AI」 になった。だが、まだ足りないものがある。「本番で今、何が起きているか」を見る目 だ。

評価ジョブは昨日のスナップショットを採点する。リアルタイムの異常 ── スロットリングが急増している、特定の API キーが大量にコストを焼いている、特定のセッションで Guardrails が連続発火している ── を捉えるには、観測(Observability)が要る。

次章では CloudWatch メトリクスと Model Invocation Logging を中心に、helpdesk-ai に 観測を設計する 道筋を示す。第 11 章で予告した「Mask モードでも Model Invocation Logs に原文が残る」という落とし穴も、観測のレイヤーで初めて顔を出すことになる。


章末まとめ

  • Bedrock Evaluations は 5 モード:Automatic / LLM-as-a-Judge / Human / RAG Retrieval / RAG Retrieve & Generate。目的別に重ね合わせて使う
  • Model Evaluation だけでは Agent の 1/4 しか測れない。残り 3/4(Action Group / KB / Guardrails)は AgentCore Evaluations で測る
  • LLM-as-a-Judge は 4 軸 9〜11 種メトリクス(Quality / UX / Instruction compliance / Safety)。人手評価比で最大 98% コスト削減
  • Bring Your Own Inference Response で Bedrock 外のモデル出力や本番ログをそのまま採点できる
  • 日本語 RAG は Contextual grounding 非対応 → LLM-as-a-Judge の Faithfulness で代替する
  • 本番セッションの連続スコアリングは Lambda ベースのコード型評価器が主役、LLM judge は重要サンプルに絞る
  • 「Day 1 から評価を組み込む」とは、評価データセット・CI スモークテスト・ログパイプライン・夜間ジョブの 4 点セットを最初に整えること
  • 評価なしで本番投入することは、第 17 章で扱う 9 つのアンチパターンのうち最も深刻な 1 つ
  • 次章では本番で「今」何が起きているかを見る、観測の設計に進む