RAG作成コンペに出て学んだことの備忘録
はじめに
本記事はRAG(Retrieval-Augmented Generation)構築のコンペティションについて参加をし、そこで得た学び、気づきをまとめた備忘録となります。
実際の業務でRAGを使うとしたらどのような構成が扱いやすいのかを技術情報をもとに改めて整理しました。
特に気になったのが、社内の共有フォルダのように、PDF、Word、Excel、PowerPointなどが混在する環境です。こうした資料群に対して、
- 自然言語で資料を探したい
- 複数の文書を横断して質問したい
- 回答だけでなく、その根拠となった資料も確認したい
- 文書の更新や版管理にも対応したい
という要求を満たすには、単に「文書をEmbeddingしてVector Searchする」だけでは十分ではありません。
そこでこの記事では、特定のデータセットや課題には依存せず、一般的な業務文書を対象としたRAGをどう設計するかについて整理します。
結論から言うと、業務RAGでは次の構成が扱いやすいと考えています。
Hybrid Search + Metadata + 必要に応じたTool実行 + Evidence管理
以降では、なぜこの構成にするのか、またRAGを使いやすくするために資料そのものをどう管理するとよいのかを順に説明します。
EmbeddingとVector Searchについて補足
RAGでは、文書をそのまま検索するのではなく、文書の内容を数値として表現して検索する方法がよく使われます。
そのときに使われるのが Embedding(埋め込み) です。
Embeddingとは、文章や単語などの意味的な特徴を、数値の並びである「ベクトル」に変換する処理です。
例えば、
- 文章A→「自動車の故障について調べる」
- 文章B→「車両の不具合原因を確認する」
という2つの文章は、使われている単語は完全には一致しませんが、意味としては近い内容です。
Embeddingモデルに文章を入力すると、概念的には次のような数値列へ変換されます。
文章A → [0.12, -0.35, 0.81, ...]
文章B → [0.10, -0.31, 0.79, ...]
実際には数百~数千次元程度のベクトルとして表現されます。
意味が近い文章ほどベクトル空間上でも近い位置になるように表現し、この「ベクトル同士の近さ」を使って検索するのが Vector Search(ベクトル検索) です。
RAGでは一般的に、文書をある程度の大きさに分割(Chunk化)し、それぞれをEmbeddingして保存しておきます。
質問を受け取った際にも質問文をEmbeddingし、そのベクトルに近いChunkを検索します。
文書
↓
Chunkに分割
↓
Embedding
↓
ベクトルとして保存
ユーザーの質問
↓
Embedding
↓
Vector Search
↓
意味の近いChunkを取得
↓
LLMに渡して回答生成
例えば通常のキーワード検索では、
検索語:設備停止
に対して「設備停止」という文字列を含む文書を探すことが基本となります。
一方Vector Searchでは、
質問:装置が止まったときの対応方法
に対して、
設備停止時の復旧手順
のように、同じ単語を使っていなくても意味的に近い文章を検索できる可能性があります。
このため、自然言語で質問するRAGと相性のよい検索方法となっています。
(EmbeddingとVector Searchについて補足終了)
背景:社内資料を生成AIから使いたい。でも、ベクトル検索だけでは足りない
社内の共有ドライブやSharePointには、次のような資料が蓄積されていきます。
- 手順書・規程・仕様書のPDF / Word
- 試験結果や管理台帳のExcel
- 会議資料や報告資料のPowerPoint
- 議事録、過去トラブル、FAQ
- 図表や画像の中にしか残っていない情報
人が必要な情報を探す場合でも、資料名、版、部署、日付、表の列名などを手掛かりに何度もフォルダを行き来します。
ここに生成AIを組み合わせて、RAGを用いて次のような問い合わせへ答えられる仕組みを作りたくなります。
「この設備の異常時に確認すべき項目は?」
「型式AB-1234の最新版の変更点は?」
「2026年に承認された試験結果だけを対象に平均値を計算して」
最初の案としては、全文書をChunkへ分けてEmbeddingし、Vector Searchで近い文章を取得してLLMへ渡すRAGが考えられます。
しかし業務文書を対象にすると、次のような問題があります。
- 型式・文書番号・版番号は意味検索より完全一致検索の方が強い
- 旧版と最新版を区別しないと誤参照する
- Excelの表構造を文章化すると行列関係が失われる
- PDFやPowerPointの図表、画像内文字は通常のテキスト抽出だけでは取れない
- 「平均を計算する」「2版を比較する」は検索ではなく処理が必要
- 答えだけ出ても、どの資料のどこを根拠にしたか分からないと業務では使いにくい
つまり、業務RAGで本当に難しいのは「高性能なEmbeddingモデルを選ぶこと」だけではありません。
どの資料を検索対象にするか、資料をどう構造化するか、検索後に何をコードで処理するか、根拠をどう残すかまで含めて設計する必要があります。
目的:回答生成ではなく「正しい資料へ到達し、根拠付きで処理できる仕組み」を作る
この記事で考えるRAGの目的は、単にLLMへ社内資料を読ませることではありません。
次の状態を目指します。
- 正しい資料・版を探せる
- 質問に関係する箇所まで絞り込める
- 表計算や版差分などは必要に応じて処理ができる
- 回答と一緒にEvidenceを残せる
- 資料が更新されても古い情報を参照し続けない
この目的で考えると、RAGは「LLMの前に付ける検索機能」ではなく、文書管理と検索と処理をつなぐ業務基盤として設計した方が分かりやすくなります。
結論:実務では「Hybrid RAG + Metadata + Tool + Evidence」が扱いやすい
先に結論を書くと、私なら業務RAGを次の構成にします。
原本文書
↓
文書管理
Document ID / Version / Status / ACL
↓
形式別Parser
PDF / DOCX / PPTX / XLSX / Image
↓
構造を残したChunk + Metadata
↓
Metadata Filter
↓
Keyword Search + Vector Search
↓
Rank Fusion / Reranking
↓
Question Router
├─ 文書QA → LLM
├─ 表計算 → pandas等
├─ 版差分 → Diff処理
└─ 画像・図表 → OCR / Vision
↓
Evidence付き回答
ポイントは5つです。
- Vector Searchだけにしない:型式・文書番号などに強いKeyword Searchを併用する
- Metadataで先に絞る:最新版、Approved、対象部署などを検索条件にする
- Chunkに文書構造を残す:見出し、ページ、スライド、シート、表名を保持する
- 計算や差分はToolへ分ける:LLMに暗算・比較を任せすぎない
- Evidenceを出力の一部にする:回答だけでなく、資料名・ページ・セル範囲まで追えるようにする
そして、この構成を安定させるために最も効くのが、検索エンジン側の工夫だけではなく、RAG活用をしやすい資料管理へ寄せていくことです。
この記事で扱うこと
以降では、上の結論を具体化するために次の順で説明します。
- Vector Searchだけで困りやすい理由
- 業務RAGの全体アーキテクチャ
- RAG活用がしやすい資料管理
- ChunkとMetadataの設計
- Excel・表の扱い
- RAGとTool / Executorの分離
- Evidenceと「分からない」の設計
- 文書更新とIndex同期
- 評価方法と導入ステップ
1. 「Vector Searchだけ」で困りやすい理由
Embeddingを作り、質問と近いChunkを検索するVector SearchはRAGの中心技術です。
一方、業務文書にはVector Searchが苦手な情報も多く含まれます。
例えば次のような情報です。
-
AB-1234のような型式 -
REV-Cのような版番号 -
T-042のような管理番号 - 日付、金額、部署名、規格番号
- 資料タイトルそのもの
このような文字列は「意味が近い文章」を探すより、文字列として一致する文書を探す方が強いケースがあります。
そのため、実務では次のようなHybrid Searchが扱いやすい構成です。
Hybrid Searchは特殊な考え方ではなく、実際の検索サービスでも採用されています。
例えばMicrosoft Azure AI Searchでは、フルテキスト検索とVector Searchを並列に実行し、それぞれの検索結果をRRF(Reciprocal Rank Fusion)によって統合するHybrid Searchが提供されています。
ここで重要なのはAzure AI Searchそのものではなく、「キーワードによる一致」と「意味による類似検索」を組み合わせるという考え方です。
RRFの補足
RRF(Reciprocal Rank Fusion)は、**複数の検索結果ランキングを1つに統合する方法**です。Hybrid Searchでは、例えば
- キーワード検索
- Vector Search
をそれぞれ実行すると、別々の検索順位が得られます。
例えば、次のような結果になったとします。
キーワード検索
1位:資料A
2位:資料C
3位:資料B
Vector Search
1位:資料B
2位:資料A
3位:資料D
ここで、それぞれの検索スコアを単純に足し合わせるのは難しい場合があります。
例えば、
キーワード検索のスコア:12.4
Vector Searchのスコア :0.83
のように、検索方式ごとにスコアの意味や尺度が異なるためです。
そこでRRFでは、検索スコアそのものではなく、それぞれの検索結果で何位だったかを使って評価します。
基本的な考え方は次の式で表されます。
$$
\mathrm{RRF}(d)=\sum_i \frac{1}{k+\mathrm{rank}_i(d)}
$$
- $d$:対象となる文書
- $\mathrm{rank}_i(d)$:検索方式 $i$ における文書 $d$ の順位
- $k$:順位によるスコア差を調整する定数
つまり、
キーワード検索でも上位
+
Vector Searchでも上位
↓
最終ランキングでも上位になりやすい
という仕組みです。
RRFの利点は、異なる検索方式のスコアを無理に同じ尺度へ変換せず、「順位」という共通の尺度を使って統合できることです。
今回のHybrid Searchでは、
「文字列として一致する文書」と「意味的に近い文書」の両方を検索し、それぞれの検索順位をまとめて最終的な検索結果を決める仕組み」
と考えると分かりやすいです。
Keyword Searchが強い例
「AB-1234の改訂履歴を教えて」
Vector Searchが強い例
「異常振動が出たときに確認すべき項目は?」
両方を使うことで、固有文字列への強さと意味検索への強さを両立できます。
2. 業務RAGの全体構成
今回のRAGの全体構造は、検索部分だけではなく前後も含めた次の5層の構成です。
① 文書管理:Document Management
原本管理です。例えば下記のようなものたちです。中には過不足ある事もあると思いますので、状況によって使い分ける必要はあります。
- 文書ID
- 版
- ステータス
- 所有部署
- 作成日・更新日
- 機密区分
- アクセス権
② 解析・正規化:Parsing / Normalization
PDF、Word、Excel、PowerPointなどは、それぞれファイル内部の構造が異なります。
例えば、Wordでは「見出し」「段落」「表」という構造を持っていますが、PowerPointでは「スライド」「テキストボックス」「図表」、Excelでは「シート」「セル」「表」といった構造になります。
そのため、RAGで検索する前に、まず各ファイルから必要な情報を取り出します。この処理をここでは Parsing(解析) と呼びます。
PDF → ページごとの文章・表・画像を取得
DOCX → 見出し・段落・表を取得
PPTX → スライドごとのテキスト・図表を取得
XLSX → シート・表・セル範囲を取得
CSV → 行・列からなるテーブルとして取得
画像 → OCRやVisionによって文字・内容を取得
ただし、このままではファイル形式によってデータの持ち方がバラバラです。
そこで次に、取り出した情報をRAG側で共通して扱える形に変換します。これが Normalization(正規化) です。
例えば、ファイル形式にかかわらず、
本文 : text
資料名 : source
ページ : page
見出し : section
シート名 : sheet
スライド番号: slide
といった共通の項目を持たせます。
イメージとしては、
PDF ────┐
DOCX ────┤
PPTX ────┤
XLSX ────┼─→ Parsing ─→ Normalization ─→ RAGで扱える共通形式
CSV ────┤
Image ────┘
という流れです。
この処理を行っておくことで、後段の検索処理では「これはWordだから」「これはPowerPointだから」と毎回意識する必要がなくなり、異なる形式の資料を横断して検索しやすくなります。
③ 索引構築:Indexing
Parsing / Normalizationによって文書の内容を取り出したら、次はそれを検索できる状態に変換します。
これが Indexing(索引構築) です。
例えば、長い文書をそのまま1つの検索対象にすると、
- 文書のどの部分が質問と関係しているのか分かりにくい
- Embeddingしたときに複数の話題が混ざる
- LLMへ渡す情報量が大きくなりすぎる
といった問題が起きます。
そのため、一般的には文書をある程度の大きさに分割します。この分割単位を Chunk(チャンク) と呼びます。
1つの文書
↓
見出しA
├─ Chunk 1
├─ Chunk 2
└─ Chunk 3
見出しB
├─ Chunk 4
└─ Chunk 5
そして、それぞれのChunkに対して検索に必要な情報を持たせます。
例えば次のようなものです。
-
Chunk text
検索対象となる本文そのもの -
Embedding
Vector Searchに使用する、Chunkの意味を数値化したベクトル -
Keyword index
キーワード検索で文字列を高速に探すための索引 -
Metadata
ファイル名、文書種別、作成日、部署、版番号、ページ番号などの付加情報 -
Parent document reference
そのChunkが元々どの文書に属していたかを示す情報
例えば、概念的には次のようなデータを検索用に保持します。
{
"chunk_id": "DOC001_CHUNK_003",
"text": "設備停止時は、まず主電源を確認する。",
"source": "設備保全マニュアル.pdf",
"page": 12,
"section": "異常時の対応",
"revision": "REV-C",
"embedding": "[...]"
}
このように、本文だけではなく「その情報がどこから来たのか」も一緒に保持しておくことが、後述するEvidence管理にもつながります。
④ 検索・回答:Retrieval / Generation
ここまで準備すると、ユーザーからの質問に対して実際に検索できるようになります。
まず行うのが Retrieval(検索) です。
例えば、
「設備が停止した場合、最初に何を確認すればよいですか?」
という質問を受け取った場合、登録されているChunkの中から質問に関係しそうなものを検索します。
この記事で想定しているHybrid Searchでは、例えば次のような処理を行います。
ユーザーの質問
↓
┌─────────────────┐
│ Keyword Search │
│ Vector Search │
└─────────────────┘
↓
検索結果を統合
↓
Metadataで絞り込み
↓
関連度の高いChunkを取得
Metadataを利用すれば、
部署 = 製造部
版 = 最新版
文書種別 = マニュアル
のように、検索対象そのものを限定することもできます。
さらに必要であれば、検索結果を Reranking(再順位付け) し、質問との関連性がより高いChunkを上位に並べ直します。
そして、取得した情報とユーザーの質問をLLMへ渡して回答を生成します。
質問+検索で取得したChunk
↓
LLM
↓
回答
このように、RAGではLLMにすべてを覚えさせるのではなく、質問されたタイミングで必要な情報を外部文書から取得し、その情報を使って回答させることが基本となります。
また、質問によっては検索だけでは回答できない場合があります。
例えば、
- Excelの複数セルから合計値を計算する
- 2つの資料の差分を比較する
- 表を条件で絞り込む
- 画像やグラフを読み取る
といった処理です。
このような場合は、検索結果をそのままLLMへ渡すだけではなく、必要に応じて計算処理や画像解析などの Tool を実行する構成にします。
質問
↓
Retrieval
↓
必要な情報を取得
↓
┌──────────────────┐
│ そのまま回答可能か │
└──────────────────┘
↓Yes ↓No
LLM Tool実行
↓
LLM
つまり、ここでのLLMの役割は単なる「検索結果の要約」だけではなく、取得した情報と必要な処理を組み合わせて最終回答を作ることになります。
⑤ 根拠・評価:Evidence / Evaluation
最後に重要になるのが Evidence(根拠) の管理です。
例えばRAGが、
設備停止時は、最初に主電源を確認します。
と回答したとしても、
「その情報はどの資料のどこに書いてあるのか?」
が分からなければ、業務ではその回答をそのまま信用することが難しくなります。
そこで、回答と一緒に、
回答:
設備停止時は、最初に主電源を確認します。
Evidence:
設備保全マニュアル.pdf
12ページ
「異常時の対応」
のように、回答の根拠となった資料・ページ・Chunkなどを保存します。
Evidenceとして保持する情報は、例えば次のようなものです。
- 元ファイル名
- ページ番号
- スライド番号
- シート名 / セル範囲
- 見出し
- 使用したChunk
- Toolによる計算結果
- 検索方法や検索スコア
これにより、
回答
↓
その回答に使った情報
↓
元資料
を後から辿れるようになります。
特に業務利用では、単に「それらしい回答が返ってくる」だけではなく、人が元資料まで戻って確認できることが重要だと考えています。
また、RAGを改善していくためには Evaluation(評価) も必要です。
評価するときは、最終回答だけを見るのではなく、
正しい資料を検索できたか
↓
正しい箇所を取得できたか
↓
必要な処理を正しく実行できたか
↓
根拠に沿った回答を生成できたか
と工程を分けて確認します。
例えば回答が間違っていたとしても、
- 検索した資料が間違っていた
- 正しい資料だがChunkが違った
- 検索は正しかったが計算を間違えた
- 根拠は正しいがLLMが回答を誤った
では、直すべき場所が全く異なります。
そのため、回答だけでなく「検索 → 処理 → 根拠 → 回答」の過程を記録しておくことが、RAGの品質改善にもつながります。
3. RAGを活用しやすい資料管理とは何か
RAGの精度を上げる方法として、EmbeddingモデルやRerankerに目が行きがちです。
しかし、実際には 検索される側の資料管理 が大きく効きます。
3.1 文書に一意なIDを持たせる
例えば、次のようなファイル名だけの管理は避けたいです。
報告書_最終.xlsx
報告書_最終2.xlsx
報告書_最新版.xlsx
報告書_修正版.xlsx
人間でもどれが正本か分かりません。
RAGならなおさらです。
おすすめは、ファイル名とは別に文書メタデータを持たせることです。
document_id: DOC-000123
title: 設備評価レポート
version: "3.1"
status: approved
owner: engineering
created_at: 2026-04-10
updated_at: 2026-06-21
confidentiality: internal
supersedes: DOC-000123@3.0
3.2 Draft / Approved / Obsoleteを分ける
最低限でも、
Draft(下書き)
Approved(新版)
Obsolete(旧版)
などを区別できるようにします。
検索時には原則として、
status = approved
だけを対象にします。
「古い版も検索したい」という質問のときだけObsoleteを含めます。
3.3 フォルダ階層を意味情報として使う
例えば、
knowledge/
├── standards/
├── manuals/
├── test_reports/
├── meeting_notes/
└── specifications/
という分類があるなら、これを単なるパスとして捨てず、
{
"document_type": "test_report"
}
のようなMetadataへ変換しておきます。
3.4 「最新版」はファイル名で判断しない
最新版
最新
final
final2
FIX
のような文字列に頼ると破綻します。
最新版判定は、
document_id + version + status
で機械的に行えるようにします。
4. Chunkは「文字数」だけで切らない
RAGでは長い文書をChunkへ分割します。
単純な固定長分割は実装しやすい一方、業務文書では意味の境界を壊すことがあります。
例えば、
3. 試験条件
3.1 温度条件
3.2 測定条件
3.3 判定基準
という章があるとき、文字数だけで分割すると、見出しと本文が別Chunkになる可能性があります。
そこで、できるだけ次の単位を優先します。
- 見出し単位
- 段落単位
- 表単位
- スライド単位
- Excelの表・領域単位
親子構造を残す
Chunkだけを保存するのではなく、親情報を残します。
{
"chunk_id": "DOC-000123#sec-3-2",
"document_id": "DOC-000123",
"section": "3.2 測定条件",
"page": 8,
"text": "..."
}
検索はChunk単位、回答時には親文書や前後Chunkまで広げる構成にすると、文脈を失いにくくなります。
5. Metadata Filterを積極的に使う
Vector Searchに全部任せるのではなく、検索前に候補を減らします。
例えば質問が、
「2026年に承認された手順書から、安全確認の項目を探して」
なら、まず、
year = 2026
status = approved
document_type = procedure
で絞り、その後にVector Searchをかけます。
Metadataとして有用なのは例えば次です。
| Metadata | 用途 |
|---|---|
| document_id | 文書識別 |
| document_type | 種類絞り込み |
| title | タイトル検索 |
| version | 版管理 |
| status | Approvedのみ検索 |
| created_at | 時期絞り込み |
| owner | 部署・担当 |
| confidentiality | 権限制御 |
| page / slide / sheet | 根拠位置 |
| tags | 業務カテゴリ |
6. Excel・表は「文章」に潰しすぎない
ExcelをRAGへ入れるとき、全セルを単純に文字列化すると、表構造が失われます。
例えば、
| 条件 | 温度 | 結果 |
|---|---|---|
| A | 20 | 12.5 |
| B | 40 | 18.1 |
を、
A 20 12.5 B 40 18.1
としてEmbeddingすると、行・列の関係が弱くなります。
最低限、
条件=A / 温度=20 / 結果=12.5
条件=B / 温度=40 / 結果=18.1
のように構造を残します。
さらに実務では、
- sheet_name
- table_name
- row_key
- column_name
- cell_range
をMetadataに持たせると扱いやすくなります。
RAGで検索、計算はコードで行う
例えば、
「温度40℃以上の平均値を求めて」
という質問に対して、LLMへ表を渡して暗算させるより、
- RAGで対象表を特定
- pandas等でフィルタ
- 平均値を計算
- 計算条件と結果をLLMへ渡す
という構成の方が安定します。
7. RAGとTool / Executorを分ける
すべてをRAGで解こうとすると不安定になります。
質問タイプを大きく2つに分けます。
Retrieval向き
「この仕様書では安全率をどう定義している?」
→ 文書検索+生成
Tool向き
「この表で条件Aだけの平均を計算して」
→ 表検索+Python処理
「版Aと版Bで変更された項目を一覧化して」
→ 文書取得+差分処理
おすすめは次の形です。
Question Router
├─ Document QA → Hybrid RAG
├─ Table Calc → pandas(python)
├─ Diff → structured diff
└─ Image/Table → OCR / Vision Transformer
RAGは資料を見つける役割、Toolは確定的な処理をする役割と考えると整理しやすいです。
8. Evidenceを回答と同じくらい重要にする
業務利用では「答えが合っている」だけでは足りません。
例えば回答を次の構造で保存します。
{
"answer": "推奨値は15 mmです",
"evidence": [
{
"document_id": "DOC-000123",
"section": "4.2 設計条件",
"page": 12
}
],
"route": "hybrid_rag",
"confidence": 0.91
}
これにより、
- 根拠確認
- 誤答分析
- 文書更新時の影響調査
- システム改善
が行いやすくなります。
「分からない」を設計する
根拠が弱いときに無理に回答しないことも重要です。
誤った回答を自信満々に返すより安全ですし、ユーザのシステムへの信頼度も高まります。
十分な根拠が取得できない
↓
回答を保留
↓
検索条件を変える / 人に確認
9. 文書更新をどう反映するか
RAGを運用していると、元文書は少しずつ更新されていきます。
例えば、
- Wordの手順書が改訂された
- Excelの数値が更新された
- PowerPointにスライドが追加された
- 古い資料が廃止された
- 新しい資料が追加された
といった変更です。
このとき、文書が1件更新されるたびに、すべての文書を再Parsing(解析) / Embeddingしていては処理コストが大きくなります。
そこで、変更された文書だけを検出して再処理する差分更新を行います。
これにより、文書数が増えても更新処理を抑えることができます。
いつ文書の更新を確認するか
最もシンプルなのは、一定間隔・決まった時間でで文書一覧を確認する方法です。しかし、必ずしもリアルタイム更新が必要とは限りません。更新頻度が低い文書まで数分おきに確認すると、不要な処理が増えてしまいます。
もし利用しているストレージや文書管理システム側でファイル更新イベントを取得できる場合は、
ファイル更新
↓
更新イベントを検知
↓
対象文書だけ差分確認
↓
Index更新
というイベント駆動型にすることも可能なため、実務では、**「定期巡回を基本とし、即時性が必要な場所だけイベント駆動にする」**といった構成も考えられます。
更新された文書をどう判定するか
更新確認のタイミングが決まったら、次に「実際に内容が変わったか」を判定します。
単純にはファイルの更新日時を見る方法があります。
last_modified
ただし、更新日時だけでは、
- ファイルを開いて保存しただけ
- 内容は変わっていないがMetadataだけ変化した
といったケースでも「更新あり」と判定される可能性があるため、文書内容からHashを計算して比較します。
文書ごとにHashを持つ
各文書をParsing / Normalizationしたあと、その内容からHashを計算して保存しておきます。
例えばSHA-256を使う場合、下記のような形です。
Hashは、文書内容から作られる「内容の指紋」のようなものです。
content_hash = SHA256(normalized_content)
例えば、次のようにHashが変わっていれば、その文書の内容が変更されたと判断できます。
前回:content_hash = abc123...
今回:content_hash = def456...
新規追加・更新・削除を分けて考える
実際の運用では、文書を次のように分類すると分かりやすくなります。
| 状態 | 処理 |
|---|---|
| 新規文書 | Parsing → Chunk → Embedding → Index追加 |
| 内容変更あり | 既存Chunkを削除 → 再Chunk → 再Embedding → Index更新 |
| 内容変更なし | 何もしない |
| 削除された文書 | Indexから削除 |
| 廃止文書 | 検索対象から除外 |
例えば、
共有フォルダ
├─ A.pdf ← 変更なし
├─ B.docx ← 更新あり
├─ C.xlsx ← 新規追加
└─ D.pdf ← 削除済み
だった場合、
A.pdf → 何もしない
B.docx → 再処理
C.xlsx → 新規Index登録
D.pdf → Indexから削除
という処理になります。
なぜ元ファイルそのものではなく normalized_content のHashを見るのか
単純にファイルそのもののHashを計算する方法もあります。
ただし、Officeファイルなどでは、本文を変更していなくても内部Metadataや保存情報が変化し、ファイルのバイナリが変わる場合があります。
そのため、RAGで実際に検索対象として使用する
normalized_content
を基準にHashを計算すると、
「検索対象となる内容が実際に変わったか」
を判定しやすくなります。
一方で、
- 更新日時
- 文書ステータス
- アクセス権
- 版番号
などのMetadataも検索条件に使っている場合は、本文とは別にMetadataの変更も監視する必要があります。
例えば、
content_hash
metadata_hash
を分けて持たせる方法もあります。
本文変更あり
↓
Chunk / Embeddingを再生成
Metadataだけ変更
↓
Embeddingはそのまま
↓
Metadataのみ更新
とすれば、不要なEmbedding生成をさらに減らせます。
さらに細かく更新することもできる
文書単位ではなく、Chunk単位でHashを持たせることもできます。
例えば100ページある文書のうち、1ページだけ変更された場合、
文書単位Hash
→ 文書全体を再Embedding
するのではなく、
Chunk単位Hash
→ 変更されたChunkだけ再Embedding
することも可能です。
Document
├─ Chunk 1 → Hash同じ → 再利用
├─ Chunk 2 → Hash同じ → 再利用
├─ Chunk 3 → Hash変更 → 再Embedding
├─ Chunk 4 → Hash同じ → 再利用
└─ Chunk 5 → 新規 → Embedding作成
大量の長文資料を扱う場合は、このようなChunk単位の差分更新も有効です。
ただし、Chunk分割方法を変更するとChunk IDや境界自体が変わるため、その場合は文書全体を再Indexした方が安全です。
更新フロー全体
最終的には、例えば次のような流れになります。
文書一覧取得
↓
新規 / 更新 / 削除を判定
↓
Hash比較
↓
┌──────────────────────────────────────────────────┐
│ 変更なし → そのまま │
│ Metadataのみ変更 → Metadata更新 │
│ 本文変更 → 再Chunk / 再Embedding │
│ 新規 → 新規Index登録 │
│ 削除・廃止 → Indexから除外 │
└──────────────────────────────────────────────────┘
このように、RAGでは一度Indexを作って終わりではなく、
「元文書の状態と検索Indexを継続的に同期する仕組み」
まで含めて設計しておくことが重要です。
10. 評価データは公開・合成データで作る
RAGを改善するには評価セットが必要です。
ただし、公開記事やサンプルコードでは、機密文書そのものを評価データに使う必要はありません。
例えば架空の文書を作り、次のタイプを含む質問セットを用意できます。
単純検索
複数文書参照
版管理
数値抽出
表計算
該当なし
根拠不足
そして、検索と回答を別々に評価します。
Retrieval評価
- Recall@K
- MRR
- nDCG
Answer評価
- Exact Match
- LLM Judge
- Citation accuracy
- Abstention accuracy
重要なのは、""回答が違った""だけで終わらず、どこで間違えたかを層別することです。
資料を見つけられなかった
↓
Retriever問題
資料は正しかったが回答を間違えた
↓
Generator / Tool問題
11. 最初から大規模に作らない
業務RAGを作るなら、次の順で育てるのがおすすめです。
Phase 1:検索だけ
質問 → 関連資料Top5
まずRetrieverを評価します。
Phase 2:回答生成
質問 → Retrieval → LLM → Evidence付き回答
Phase 3:Hybrid化
BM25 + Vector + Metadata
Phase 4:Tool追加
頻出する処理だけTool化します。
Excel集計
版差分
単位変換
決まった計算
Phase 5:運用設計
- ACL
- 最新版管理
- 更新検知
- 監査ログ
- 評価セット
を追加します。
12. RAG Readyな資料のチェックリスト
資料を作る側にも少し工夫してもらえると、RAGはかなり使いやすくなります。
文書
- タイトルが明確
- 見出し階層が使われている
- 文書IDがある
- 版番号がある
- Draft / Approved / Obsoleteが区別できる
- 作成日・更新日がある
- 略語が初出時に定義されている
表
- 列名が一意
- 単位が列名またはMetadataにある
- 結合セルに意味を依存しすぎない
- 色だけで意味を表さない
- 表タイトルがある
図・画像
- キャプションがある
- 図番号がある
- 本文から参照されている
- 重要な数値が画像だけに閉じていない
運用
- 正本が一意に決まる
- 古い版を判別できる
- アクセス権をMetadataへ反映できる
- 削除・更新をIndexへ同期できる
まとめ
業務RAGを安定させるには、Embeddingモデルだけではなく、検索される資料の設計まで含めて考える必要があります。
特に重要だと考えているのは次の5点です。
- Vector SearchだけでなくKeyword Searchも併用する
- Metadataで検索対象を絞る
- Chunkへ文書構造を残す
- 計算・差分などはToolへ分離する
- Evidenceと文書ライフサイクルを最初から設計する
RAGは「LLMに文書を読ませる仕組み」というより、
組織の文書を、機械が検索・検証しやすい状態に整える仕組み
と考えると、設計方針がかなり明確になります。






