Azure AI Foundry評価でGroundednessがnullになる原因と対処法|Completed with Errorsを解消

Azure AI Foundryの評価で、Relevance/Similarityは完走するのにGroundednessだけ「Completed with Errors」となり、一部スコアがnullになる——。本記事ではログの見方から、画像URLや欠損値が原因になるケースの切り分け、実際に完走した対処手順までを具体的に解説します。

目次

発生する症状:Groundednessだけ一部がnullになる

Azure AI Foundryの評価機能(Evaluation)で複数メトリクスを同じデータセットに対して実行したところ、次のような差が出ることがあります。

メトリクス実行結果見え方困るポイント
関連性(Relevance)全件正常94問すべてスコアあり特になし
類似度(Similarity)全件正常94問すべてスコアあり特になし
Groundedness(事実整合性)完了するがエラー混在「Completed with Errors」になり、一部行のスコアがnullどの質問が落ちたのか・原因が分からない

ログには completed_linesfailed_lines、さらに log_path が出ているのに、最終レポートでは「約10件がnull(結果なし)」のまま。こうなると、平均値や分布の解釈が歪むだけでなく、改善サイクルそのものが止まってしまいます。

nullを放置すると起こること(運用上の落とし穴)

  • A/B比較が崩れる:モデルAだけ特定の行が落ち、モデルBは落ちない…のような偏りが起きると、スコア差の原因が「モデル性能」ではなく「入力データ形式」になります。
  • 平均が“良く見える”:欠損を除外して平均する仕様だと、難しい(落ちやすい)行が抜けて平均が上がることがあります。
  • 改善の当たり所が分からない:どの質問が失敗したか追えないと、再発防止の前処理が作れません。

なぜRelevance/Similarityは通って、Groundednessだけ落ちるのか

ポイントは「評価処理が見ている入力の種類が違う」ことです。RelevanceやSimilarityは、主に“テキスト同士”の比較で成立します。一方、Groundednessは“回答がコンテキスト(根拠)に基づいているか”を判定するため、コンテキスト側の形式・品質に強く依存します。

特にRAG(検索拡張生成)の評価では、Groundednessは次のような列を参照しがちです。

  • 回答(モデルの出力)
  • 根拠コンテキスト(検索結果、チャンク、引用文)
  • 根拠のメタデータ(chunk_id、doc_id、出典など)

つまり「質問・回答テキストだけ見れば問題ない」状態でも、Groundednessが参照する列(コンテキスト、根拠、関連メタデータなど)に“地雷”があると、該当行だけ評価が失敗し、結果がnullとして落ちてしまいます。

原因の本命:画像URL(Markdown画像リンク)が混ざっている

サポート側で同一データセットを使って再現調査した結果、Groundednessで失敗していた行には共通点がありました。プロンプトやコンテキスト内に、画像URLを含むMarkdown画像リンクが混在していたのです。

例(コンテキスト中に紛れ込む典型)

  • ![](https://example.com/image.png)
  • ![](data:image/svg+xml,%3Csvg%20xmlns=...)(data URI形式の埋め込み)
  • <img src="https://example.com/image.png">(HTMLタグ)

Groundednessの評価処理は、単なる「画像へのURLリンク」を前提にしていない(もしくは処理できない)ことがあります。特に、評価入力として画像を扱う場合は「画像そのもの(Base64などでエンコードしたデータ)」を期待する設計になっているケースがあり、URLだけが混ざると、該当行で例外が起きやすくなります。

結果として、評価ジョブ全体は走り切るものの、特定行が失敗 → その行のスコアがnull → 最終レポートで欠損、という流れになります。

「問題の行」をイメージできるBefore/After

状態context(例)Groundedness評価への影響
Before(失敗しやすい)…手順は以下。![](data:image/svg+xml,%3Csvg%20xmlns=...) 次に、設定画面で…画像リンクの解釈で例外 → 対象行がnull
After(安定)…手順は以下。次に、設定画面で…テキスト根拠のみになり、評価が完走しやすい

まず見るべき場所:Evaluation画面とlog_pathのログ

「何が悪いのか分からない」状態を抜け出すには、失敗した行番号(インデックス)を最短で特定するのが重要です。確認ポイントは大きく3つあります。

評価ジョブの詳細画面で“失敗行数”を把握する

Azure AI FoundryのEvaluationで対象のGroundedness評価ジョブを開き、次の項目を確認します。

  • ステータスが「Completed with Errors」になっていないか
  • completed_linesfailed_lines の値
  • ログの保存先として log_path が表示されているか

log_path配下で“落ちた行のindexes”を探す

ログには、どの行で失敗したのかが indexes: [6, 3, 0] のように出ることがあります(表記は環境により異なります)。この配列が見つかったら、次のように解釈すると切り分けが進みます。

ログの情報意味次にやること
failed_lines失敗した行数最終レポートのnull件数と概ね一致するか確認
indexes: [...]失敗した行のインデックス(0始まりのことが多い)データセットの該当行を抽出して中身を点検
例外メッセージ(例:Invalid image URL等)失敗理由の直接ヒント画像URL・壊れたMarkdown・欠損値を重点的に確認

もしログに例外メッセージが出ているなら、そこが最短ルートです。特に「Invalid image URL」「cannot parse image」など、画像周りの文言があれば原因はほぼ確定と考えてよいでしょう。

失敗行をすぐ抜き出す(pandasの小技)

ログで得たインデックス(例:[6, 3, 0])があるなら、まずその行だけを抜き出して“現物”を見ます。

failed_idx = [6, 3, 0]  # ログのindexesを転記
df_failed = df.iloc[failed_idx].copy()

# 画像リンクっぽいものを目視しやすい列だけ残す

df_failed[["eval_id", "question", "context"]] 

「ログは見られるが、どの質問か分からない」問題は、eval_id のような一意キーを最初から持たせるだけで劇的に改善します。

原因の典型パターン:画像URL/壊れたMarkdown/欠損値

Groundednessのnullは、実務上よく次の3パターンに集約されます。まとめて潰すと再発が減ります。

原因ありがちな入力例何が起きるか対処
画像URL(Markdown/HTML/data URI)![](https://...) / <img ...>画像として解釈→失敗→該当行nullURL削除、画像はBase64で別フィールド
壊れたMarkdown![]( で終わる、括弧が閉じないパースできず例外記法を除去してプレーンテキスト化
null/NA(欠損)chunk_id がnull、文字列列にNA集計/型変換で失敗、結果欠損空文字・ダミー値で補完、型固定

解決策:URL除去・画像はBase64・欠損値は補完

最終的に有効だった対処はシンプルです。Groundednessに渡す入力(特にコンテキスト)を「評価が扱える形」に整えるだけで、全行がパスするようになります。

対処1:データセットから画像URL(および不要なリンク)を取り除く

Groundedness評価において、リンクそのものが評価に必須でないなら、まずはURLを削除するのが最も堅い対策です。特に次のパターンは優先的に除去します。

  • Markdown画像:![](...)
  • HTML画像:<img ...>
  • data URI(Base64ではない、またはSVG等で崩れやすい形式)
  • HTTP/HTTPSの生URL(引用元として不要なら削除)

実務では「表示用のリンク」と「評価用テキスト」を同じ列に混ぜないのがコツです。表示用リンクが必要なら、別列(例:source_url)に分離し、Groundednessに渡す列からは外します。

Python(pandas)での簡易クリーニング例

import re

def clean_for_groundedness(text: str) -> str:
if text is None:
return ""
# 1) Markdown画像 ![alt](...)
text = re.sub(r'![[^\]]*\]\([^\)]+\)', '', text)
# 2) HTML imgタグ
text = re.sub(r']*>', '', text, flags=re.IGNORECASE)
# 3) 生URL(必要なら残す方針にしてもOK)
text = re.sub(r'https?://\S+', '', text)
# 4) 連続空白の整理
text = re.sub(r'\s{2,}', ' ', text).strip()
return text 

URLを完全に消したくない場合は、妥協案として「ドメイン名だけ残す」「(参照:社内ポータル)のようにテキスト化する」方法もあります。要点は、評価入力に“解釈が割れる形式”を混ぜないことです。

対処2:画像を使いたいなら「URLではなくBase64」で渡す

評価の設計上、画像を扱えるケースでも「リンク(URL)をテキストとして混ぜる」やり方だと失敗しやすくなります。画像が評価に必要なら、次の方針に切り替えます。

  • コンテキスト本文から画像URLを削除する
  • 画像はBase64エンコードしたデータとして、評価が期待する形式で別フィールドに渡す

Base64変換(ローカル画像ファイルを想定)

import base64
from pathlib import Path

img_bytes = Path("sample.png").read_bytes()
img_b64 = base64.b64encode(img_bytes).decode("utf-8")

# 例:data URI形式にする場合(評価入力仕様に合わせて調整)

data_uri = f"data:image/png;base64,{img_b64}" 

注意点として、data:image/svg+xml,... のようなSVG埋め込みは、Base64ではなくURLエンコードで入っていることがあり、評価処理が「画像データ」として解釈できず失敗することがあります。画像を使うなら、PNG/JPEGに寄せてBase64に統一するほうがトラブルが減ります。

対処3:Null/NAなどの欠損値を前処理で潰す

Groundedness評価の落とし穴は、画像URLだけではありません。chunk_id などの列に null / NA があると、集計(平均値など)や内部処理でエラーになり、結果がnullとして扱われるケースがあります。

列の種類よくある欠損おすすめの補完方法理由
ID系(chunk_id / doc_id)null, NA, 空空文字にする、または一律のダミー(例:Result 1)結合・集計で型が揺れるのを防ぐ
テキスト系(question/context/answer)null空文字にする評価器が文字列前提のため
数値系(既存スコア等)null0または平均で補完(目的次第)計算時の例外を防ぐ

pandasでの欠損潰し例

text_cols = ["question", "context", "answer"]
for c in text_cols:
    df[c] = df[c].fillna("").astype(str)

df["chunk_id"] = df["chunk_id"].fillna("").astype(str)

# 文字列として "NA" や "null" が入っている場合の掃除

df["chunk_id"] = df["chunk_id"].replace({"NA": "", "null": "", "None": ""}) 

最短で直すための実践手順(切り分け→修正→再実行)

「なんとなく全部の列を掃除する」よりも、ログで落ちた行を狙い撃ちして原因を確定させるほうが早く、再発防止にもつながります。おすすめの流れは次の通りです。

  1. EvaluationのGroundednessジョブで failed_lines を確認(まず件数を把握)
  2. log_pathから失敗 indexes を拾う(どの行が落ちたか特定)
  3. 該当行だけを抽出して目視(画像URL、壊れたMarkdown、null/NAを探す)
  4. 原因がURLなら、まず削除して同じ行だけで再評価(最小単位で再現性を潰す)
  5. うまくいった前処理を全データに適用して再評価(failed_lines=0を目標)

この手順にしておくと、「何を変えたら直ったのか」が明確になり、次回の評価でも同じ落とし穴にハマりにくくなります。

再発防止の設計:評価用データセットを“別物”として扱う

Groundedness評価で安定してスコアを返すには、RAGの実運用データ(リンクや装飾が混在)をそのまま流し込むより、評価に最適化したデータセットを別途用意するほうが結果的に楽です。

おすすめの列設計(例)

列名内容ポイント
eval_id一意のIDログのインデックスがズレても追える
questionユーザー質問null禁止、型は文字列固定
answerモデル回答評価対象。リンク装飾は最小限
context根拠(検索結果/引用)画像URLは除去。テキスト根拠に寄せる
chunk_id根拠の識別子nullにしない(空文字でも可)

評価前の“プレフライトチェック”を自動化する

評価を回してからエラーに気づくと、1回の実行が無駄になりがちです。そこで、実行前にデータセットを検査する簡単なチェックを入れておくと、運用が安定します。

チェック項目判定例NGのときの対応
画像Markdownの混入![] を含む画像リンクを削除、または画像列へ退避
生URLの混入http:// or https:// を含む評価に不要なら削除、必要なら別列へ
壊れたMarkdown![]( だけある等記法を除去してプレーンテキスト化
null/NA欠損がある空文字・ダミー値で補完
過度に長いcontext一定文字数/トークンを超える要約・上位Nチャンクに制限(再現性を保つ)

特に最後の「contextが長すぎる」は見落とされがちです。Groundednessはコンテキストを根拠として参照するため、長文化しやすいRAG構成だと入力上限に当たり、例外や切り捨ての副作用が出ることがあります。画像URL問題を潰した後でもスコアが欠ける場合は、長さ制限もあわせて疑うと切り分けが前に進みます。

よくある質問(現場で詰まりやすいポイント)

Completed with Errorsでも全体の平均スコアは信用していい?

おすすめしません。null行が混ざると、平均の算出方法によっては「欠損を除外して平均」になったり、「欠損があるため集計自体が落ちる」ことがあります。改善判断に使うなら、まずfailed_lines=0にしてからスコアを比較するのが安全です。

ログにindexesが出てこない/どの行か分からない

この場合は、評価用データセットに eval_id を入れておき、出力側(評価結果)にも eval_id が残る形にすると追跡が容易です。運用として「評価は必ずeval_id付きのデータで回す」をルール化するのが効果的です。

URLを全部消すと、引用元が分からなくならない?

評価に必要なのは「根拠の内容(テキスト)」です。引用元のURL自体は、評価用列からは外しても問題ありません。どうしても保持したい場合は、source_url のような別列に保存し、Groundednessに渡すテキスト列には含めない運用にすると、評価の安定性とトレーサビリティを両立できます。

画像を使う評価をしたいときはどうする?

画像を評価に含める場合は、URLではなくBase64など“画像データ”として渡す設計に寄せます。ただし、入力スキーマは環境やワークフロー(PromptFlow/評価テンプレート)によって異なるため、AI Foundryの評価が受け付ける形式に合わせてください。迷う場合は、まず画像を評価から外してGroundednessを安定させ、その後に画像入力を段階的に戻すと安全です。

まとめ:Groundednessのnullは「入力データの地雷」を潰せば直る

Groundedness評価だけ一部がnullになるとき、原因は評価器そのものよりも、入力データ(特にコンテキスト)の形式にあることが多いです。実際に効果があった対策は次の3点でした。

  • プロンプト/コンテキストから画像URL(Markdown画像リンク等)を除去する
  • 画像が必要なら、URLではなくBase64でエンコードした画像データとして渡す
  • null/NAなどの欠損値を事前に補完し、型を揃える

ログ(log_path)で失敗行を特定し、前処理を最小単位で検証してから全体に展開する。この流れを作っておけば、Azure AI FoundryでのGroundedness評価は安定して完走し、改善の比較ができる状態に戻せます。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次