Azure AI SearchでマルチモーダルRAG(テキスト+画像のベクトル検索)を作ると、画像単体がベクトル化されない、Blobに追加した新規ファイルがインデクサーに拾われない…といった壁に当たりがちです。本記事では原因と設定・運用の解決策を具体例付きで整理します。
まず押さえるべき全体像
Azure AI Searchで「テキストと画像の両方をベクトル検索したい」と考えたとき、つまずきやすいのは大きく2つです。ひとつは画像ファイルをスキルセットに正しく渡せていない(入口の問題)。もうひとつはインデクサーの増分更新が期待通りに進まず、新規Blobが取り込まれない(増分の問題)です。
結論から言うと、単体画像(.jpg / .png)をRAGの検索対象にするには、画像→(説明文・OCRなどのテキスト)→テキスト埋め込みという2段構成で考えるのが現実的です。そして増分インデックスは、Blob側の更新時刻とインデクサーの状態管理(チェックポイント)を前提に運用する必要があります。
なぜ「PDF内の画像」は通るのに「単体画像」は通らないように見えるのか
PDFやOffice文書では、インデクサーが最初に文書クラッキング(コンテンツ抽出)を実行し、本文テキストに加えて「埋め込み画像」を正規化した形で取り出せることがあります。するとスキルセット側では、その正規化画像を入力として画像解析スキルが動き、キャプションやOCR結果が生成されます。
一方で単体画像ファイルは、アップロード方法や設定によっては「画像をスキルが読める形で渡す」段階が抜け落ちやすく、スキルセットが実行されていても画像入力が空になってしまいます。結果として「ベクトル化されない」「パイプラインを通っていない」ように見えます。
| ケース | インデクサーが得意な取り出し | スキルセットに渡しやすい入力 | 起こりがちな誤解 |
|---|---|---|---|
| PDF内の画像 | 本文+埋め込み画像を同時に抽出しやすい | 正規化画像(複数枚)をコンテキストにできる | 「画像も直接ベクトル化できている」と思い込みやすい |
| 単体画像(jpg/png) | メタデータは取れるが、画像入力の渡し方で詰まりやすい | 画像バイト列 or 参照URLなど、スキルが読める形にする必要 | スキルセット設定だけで自動的に画像が流れると考えがち |
画像ベクトル化の前提:Azure AI Searchは「画像→ベクトル」を直接やらない
マルチモーダルRAGという言葉から「画像そのものをエンベディングモデルに渡して画像ベクトルを作る」と想像しがちですが、Azure AI Searchの一般的な構成では、画像からまずテキスト特徴を作り、そのテキストを埋め込みにかける設計になります。
ここでいうテキスト特徴とは、たとえば次のような情報です。
- キャプション:画像の説明文(例:『会議室でホワイトボードを指す人物』)
- タグ:画像の主要要素(例:person, whiteboard, meeting)
- OCRテキスト:画像内に写っている文字列
これらを結合したテキストをEmbeddingにかけ、生成されたベクトルを検索インデックスのベクトル型フィールドに保存します。検索時は、質問文(テキスト)を同じEmbeddingモデルでベクトル化し、画像由来ベクトルと近いものを引き当てます。
おすすめのパイプライン(最小構成)
Blobの画像 → Image Analysis(キャプション/OCR) → Text Embedding → imageVector に格納
スキルセット設計のコツ:入出力マッピングで「どこに何があるか」を固定する
設定で迷うポイントは、ほとんどがinputのsourceパスとoutputのtargetNameの対応です。特に、画像解析スキルの入力が空だと、後段のEmbeddingが空振りしてベクトルフィールドも埋まりません。
設計の考え方
- 画像解析スキルの出力(キャプション/OCR)を、Embeddingに渡す「中間テキスト」として明示的に作る
- Embeddingの出力(ベクトル)を、インデックスのベクトルフィールドに確実にマッピングする
- PDF向け(正規化画像)と単体画像向け(画像そのもの)で、入力の取り方が違うならインデクサーを分けると運用が安定しやすい
| 工程 | 入力 | 出力 | 目的 |
|---|---|---|---|
| 画像解析 | 画像(バイト列または参照) | caption / ocrText / tags | 画像を検索可能なテキストへ変換 |
| 整形 | caption+ocrText+tags | embeddingInput | ノイズを減らしEmbeddingに最適化 |
| 埋め込み | embeddingInput | imageVector | ベクトル検索用の特徴量を生成 |
サンプル:単体画像向けスキルセット(概念例)
実際のプロパティ名やスキルの種類は環境(Azure AI Vision / Azure OpenAI / プレビュー機能)によって異なるため、ここでは「流れが分かる」ことを優先した概念例を示します。重要なのは画像→テキスト→ベクトルのデータの受け渡しが途切れないことです。
{
"name": "mmrag-image-skillset",
"skills": [
{
"type": "imageAnalysis",
"context": "/document",
"inputs": [{ "name": "image", "source": "/document/content" }],
"outputs": [
{ "name": "caption", "targetName": "imageCaption" },
{ "name": "ocrText", "targetName": "imageOcrText" },
{ "name": "tags", "targetName": "imageTags" }
]
},
{
"type": "shaper",
"context": "/document",
"inputs": [
{ "name": "caption", "source": "/document/imageCaption" },
{ "name": "ocr", "source": "/document/imageOcrText" },
{ "name": "tags", "source": "/document/imageTags" }
],
"outputs": [
{ "name": "output", "targetName": "embeddingInput" }
]
},
{
"type": "textEmbedding",
"context": "/document",
"inputs": [{ "name": "text", "source": "/document/embeddingInput" }],
"outputs": [{ "name": "vector", "targetName": "imageVector" }]
}
]
}
インデクサー設定で差が出るポイント
単体画像の取り込みで見落としやすいのが、インデクサー側の「抽出・正規化の設定」です。PDFで動いていた構成をそのまま流用すると、画像ファイルでは入力パスが想定とズレてしまうことがあります。まずは、インデクサーが画像をスキルが読める形にしているかを確認してください。
| 設定項目(例) | 狙い | 単体画像での注意点 |
|---|---|---|
| imageAction | 画像を正規化してスキルに渡しやすくする | PDF向けに有効化していても、単体画像側の入力sourceが別になることがある |
| dataToExtract | 本文/メタデータのどちらを抽出するか | 画像は本文テキストが無いので、メタデータとスキル出力が主役になる |
| parsingMode | 解析方法の選択 | 画像はファイル種別判定に依存するため、contentTypeが正しいかも併せて確認する |
| フィールド/出力マッピング | スキル出力をインデックスへ書き込む | ベクトルフィールドの型・次元数が一致していないと格納できない |
サンプル:Blobインデクサー(概念例)
下記は「データソース→スキルセット→インデックス」に流す際の形が分かるように簡略化した例です。実際は認証情報やリソース名を環境に合わせて置き換えてください。
{
"name": "mmrag-image-indexer",
"dataSourceName": "blob-datasource-images",
"targetIndexName": "mmrag-index",
"skillsetName": "mmrag-image-skillset",
"parameters": {
"configuration": {
"dataToExtract": "contentAndMetadata",
"imageAction": "generateNormalizedImages"
}
},
"fieldMappings": [
{ "sourceFieldName": "metadata_storage_path", "targetFieldName": "sourcePath" },
{ "sourceFieldName": "metadata_storage_last_modified", "targetFieldName": "lastModified" }
],
"outputFieldMappings": [
{ "sourceFieldName": "/document/imageCaption", "targetFieldName": "imageCaption" },
{ "sourceFieldName": "/document/imageOcrText", "targetFieldName": "imageOcrText" },
{ "sourceFieldName": "/document/imageVector", "targetFieldName": "imageVector" }
]
}
インデクサーの状態確認と切り戻し
増分が効いているか、どこで失敗しているかは「推測」ではなく、インデクサーの実行履歴で判断します。特に次の3点は、トラブルシュートで必ず使う基本操作です。
- ステータス確認(直近の成功/失敗、処理件数、エラー詳細)
- 再実行(増分での再クロール)
- リセット(チェックポイントを破棄してフルクロール)
リセットは強力ですが、フルクロールになるため影響も大きくなります。検証環境では有効な一方、本番では「なぜ拾えていないのか」をログで突き止めた上で使うのがおすすめです。
インデックス設計:画像もテキストも「同じ検索体験」に寄せる
マルチモーダルRAGを安定させるには、インデックス側に「後から困らないフィールド」を用意しておくことが重要です。特に、画像由来のキャプション/OCR/タグは、ベクトル検索だけでなくキーワード検索やデバッグにも役立ちます。
| フィールド名例 | 型 | 用途 | ポイント |
|---|---|---|---|
| id | Edm.String(key) | 一意キー | BlobのパスやURLをベースにすると衝突しにくい |
| content | Edm.String | 文書本文(PDF等) | RAGの根拠引用に使うならchunk化設計も検討 |
| imageCaption | Edm.String | 画像説明文 | 画像検索の説明としてUIにも出せる |
| imageOcrText | Edm.String | 画像内テキスト | 日本語OCRは誤認識もあるため、後段でクリーニングすると精度が上がる |
| imageVector | Collection(Edm.Single) | 画像由来テキストのベクトル | Embeddingモデルの次元数に合わせる(変更すると再作成が必要) |
| sourcePath | Edm.String | Blobの参照 | 同名ファイルの上書き運用ならバージョン情報も持たせる |
| lastModified | Edm.DateTimeOffset | 更新判定の目安 | 増分インデックスが効いているかの監視に使える |
検索品質を上げる小技(画像向け)
- captionだけに依存しない:captionは短くなりがちなので、OCRやタグも混ぜて情報量を確保する
- OCRノイズを抑える:改行や不要記号を軽く整形するだけでもEmbeddingが安定する
- 日本語の表記ゆれ対策:全角半角、英数字混在、型番のハイフンなどを整えるとヒット率が上がる
単体画像がベクトル化されないときの実践チェックリスト
「PDFは動くのにjpg/pngだけが入らない」場合、原因はスキルよりもインデクサーが画像をスキル入力に渡せていないケースが多いです。次の順番で切り分けると、最短距離で原因に到達できます。
| 確認ポイント | 見る場所 | OKの目安 | NGのときの対処 |
|---|---|---|---|
| インデクサーが対象Blobを列挙できているか | インデクサー実行履歴 | 新規画像ファイル名が処理対象に出る | データソースのコンテナー/プレフィックス/フィルタを見直す |
| 画像がスキル入力に渡っているか | デバッグセッション/エンリッチ出力 | imageCaptionやOCRが空でない | 画像入力source(/document/content等)を再確認する |
| Embeddingが実行されているか | 実行履歴のスキルログ | ベクトル生成が成功している | Embedding入力が空でないか、モデル/認証/制限を確認 |
| インデックスに正しくマッピングされているか | フィールドマッピング/出力マッピング | imageVectorに値が入る | outputFieldMappingsを見直し、フィールド型と次元数を揃える |
単体画像は「専用インデクサー」に分けると安定する
PDF向けの設定(正規化画像、チャンク分割など)と、単体画像向けの設定(画像そのものを入力にする)を1つのインデクサーで両立させようとすると、コンテキストパスの違いが原因で不安定になりがちです。運用をシンプルにするなら、次の分割が有効です。
- インデクサーA:PDF/Office文書用(本文抽出+埋め込み画像の解析)
- インデクサーB:jpg/png用(画像解析→テキスト化→埋め込み)
インデックスは共通にしておけば、検索側(RAGアプリ)は統一できます。取り込み経路だけ分ける発想です。
それでも「画像→ベクトル化」が難しいときの現実解
どうしてもインデクサー経由で単体画像が安定しない場合、取り込みをイベント駆動にして外部で前処理するのが確実です。たとえば次のような流れです。
- Blobに画像が追加されたらイベントで検知(Event Gridなど)
- Azure AI Visionでキャプション/OCRを生成
- Azure OpenAIでEmbeddingを生成
- Searchの「ドキュメント追加/更新API」でインデックスに投入
この方式のメリットは、画像の読み込み方法・再実行・リトライをアプリ側で制御できることです。インデクサーに依存する部分が減るため、障害時の復旧もやりやすくなります。
増分インデックスが不安定に見える理由
Blobインデクサーの増分更新は、基本的に「前回処理した時点」以降に更新されたBlobだけを対象にします。つまり、インデクサー側でチェックポイント(どこまで見たか)を保持しており、Blob側では更新時刻(LastModified)が更新検知のトリガーになります。
ここが噛み合わないと、次のような現象が起こります。
| 症状 | よくある原因 | 対処の方向性 |
|---|---|---|
| 新規ファイルを追加したのに拾われない | 対象範囲フィルタ外/実行が途中で止まっている/スケジュール未設定 | 対象コンテナー/プレフィックス、実行履歴、スケジュールを確認 |
| 上書き更新したのに再処理されない | アップロード方法がLastModifiedを変えない(タイムスタンプ保持など) | 更新時刻が変わる方法で再アップロードするか、リセットで強制再クロール |
| 一部だけ取り込まれて止まる | 失敗アイテムが多い/スキルがタイムアウト/実行時間制限 | 失敗ログの原因を潰し、バッチ/並列/タイムアウト設定を調整 |
| リセットしても改善しない | データソースの条件が間違い/同名衝突で更新が見えない | 最小構成で新規インデクサーを作り、切り分ける |
Blobに追加した新規ファイルを「確実に」再インデックスさせる手順
「毎回リセットが必要なのか?」という疑問に対しては、開発段階と本番段階で答えが変わります。検証中はリセットが手っ取り早い一方、本番で毎回リセットするとコストも時間も増え、失敗リスクも上がります。ここでは、現場で再現性が高い手順を紹介します。
開発・検証で確実に反映させたいとき
- まずインデクサー実行履歴で、追加したBlobが「列挙されているか」を確認する
- 列挙されていない場合は、データソースのコンテナー/プレフィックス/フィルタ条件を見直す
- 列挙されているのに処理されない場合は、スキル実行ログ(失敗・スキップ)を確認する
- 状態が怪しいときは、インデクサーのリセットでチェックポイントをクリアし、フルクロールで整合性を取り直す
本番運用で安定させたいとき
- スケジュール実行を設定し、手動実行に依存しない
- アップロード手段(CI/CDやバッチ)で更新時刻が正しく変わることを保証する
- インデクサーの実行履歴を監視し、失敗が続く場合は早期に検知する
- 大容量コンテナーは、プレフィックス単位に分割してインデクサーを複数にする(1回あたりの処理を小さくする)
| 運用の狙い | おすすめ設定 | 理由 |
|---|---|---|
| 新規取り込みの遅延を減らす | 短い間隔でスケジュール実行 | 手動実行の漏れを防ぎ、増分前提の運用に寄せられる |
| 処理失敗で止まるのを防ぐ | 失敗ログの監視+原因除去 | 増分更新は「最後に成功した地点」からズレると体感が不安定になる |
| 再処理を簡単にする | インデクサーを種類別に分割 | PDFと画像で失敗原因が違うため、切り分けが早い |
よくある質問(現場で困りがちなポイント)
画像を「本当の意味で」画像埋め込みしたい
本格的に画像特徴(ピクセル由来の特徴量)で近傍検索をしたい場合、テキスト経由のEmbeddingだけでは限界があります。その場合は、画像埋め込みモデル(CLIP系など)で画像ベクトルを外部で生成し、Searchインデックスへ投入する構成が現実的です。検索UIは同じでも、取り込みパイプラインだけ別レイヤーで実装します。
PDFの本文と画像の両方を同時に検索したい
おすすめは、本文は本文でchunk化してtextVectorへ、画像はcaption/OCRでimageVectorへ、と役割を分けて2種類のベクトルフィールドを持つ設計です。検索時はハイブリッド(キーワード+ベクトル)にし、必要に応じて両方のスコアを合成すると、質問タイプに強い検索が作りやすくなります。
インデクサーのリセットはどのタイミングで使うべき?
リセットは「チェックポイントを捨ててフルクロールする」強い手段です。検証環境での構成変更後(スキルセット/マッピング/フィールド設計の変更)や、取り込み漏れが発生して原因が追えないときの最終手段として使うのが現実的です。本番で頻繁に行う前提にすると、コスト・処理時間・失敗時の影響が大きくなります。
まとめ
- 単体画像(.jpg / .png)は、画像→テキスト化(キャプション/OCR)→テキスト埋め込みの2段構成で考えると設計がブレません。
- 「ベクトル化されない」場合は、Embedding以前に画像入力がスキルに渡っていない可能性が高いので、実行履歴とデバッグ出力で入口から確認します。
- 増分インデックスは、Blobの更新時刻とインデクサーのチェックポイントが前提です。新規ファイルが拾われないときは、対象範囲・アップロード方法・実行履歴(失敗/途中終了)を順に潰します。
- PDF向けと画像向けはインデクサーを分けると、設定の衝突が減って運用が安定します。

コメント