Case 10 / RAGの精度検証

全文が入るのに、RAGを使う理由

社内規程集(18,548文字)は、そのままでもLLMに渡せる大きさです。それでも渡す量を絞ったほうが、正確になり、作り話が減り、速くなりました。全文投入・RAG3方式・「検索が完璧な場合」の5条件を比べています。

  • 実施 2026年8月
  • 試行 各モデル3回(n=3)
  • 対象 ローカル8モデル + Sonnet 5
  • 出力 全1,350件

Conclusion

結論

社内規程集(18,548文字)は、そのままでもLLMに渡せる大きさです。それでも渡す量を絞ったほうが、正確になり、作り話が減り、速くなりました。全文投入・RAG3方式・「検索が完璧な場合」の5条件を比べています。

  1. 01

    社内規程を丸ごと渡したら、8B・14Bクラスが壊れた

    架空の社内規程集11本(18,548文字)を全文そのまま渡し、10問に答えさせました。phi4:14b は24問中6問、Swallow 8B は24問中1問しか正解できていません。この文字数はモデルが一度に読める上限(32,768トークン)に収まっており、入り切らないから失敗したのではなく、入っているのに読めていない状態です。

  2. 02

    壊れ方は2種類あり、危険度が違う

    ひとつは諦める型。Swallow 8B は答えが書いてある質問にも「提供された情報には記載がありません」と返しました。同じ設定で3回試すと、1回目だけ正解し2回目以降は諦めるという揺れも出ています。もうひとつは創作する型。phi4:14b は規程に存在しない退職金の計算式を作り、答えのない2問すべてで毎回でっち上げました。前者は答えが得られないだけですが、後者は誤った情報が社内に流れます。

  3. 03

    関連箇所だけを渡したら、同じモデルが満点になった

    文書を分割して検索し、質問に関係する部分だけを渡す方式(RAG)に変えると、崩れた3モデルはいずれも24問中24問に回復しました。全体では正答率が72%から100%へ、事実でない値を作る割合が28%から0%へ改善し、応答時間も2.8秒から1.3秒へ半分以下になっています。RAGは「入り切らない文書のための技術」と説明されがちですが、入る文書でも、渡す量を絞ったほうが良い結果になりました。

Input

入力:何を渡したか

実際に投入したデータを公開しています。

inputs/docs/01_就業規則.md入力:就業規則(試用期間・在宅勤務・フレックス・有給・副業・定年・懲戒)MARKDOWN · 10KB inputs/docs/02_経費精算規程.md入力:経費精算規程(精算期限・承認区分・出張旅費・宿泊費・日当)MARKDOWN · 5KB inputs/docs/03_交際費取扱規程.md入力:交際費取扱規程(事前承認・1人あたり上限・贈答)MARKDOWN · 2KB inputs/docs/04_情報セキュリティ規程.md入力:情報セキュリティ規程(情報資産の分類・パスワード・端末・インシデント)MARKDOWN · 6KB inputs/docs/05_生成AI利用ガイドライン.md入力:生成AI利用ガイドライン(入力禁止情報・出力の取扱い)MARKDOWN · 2KB inputs/docs/06_育児介護休業規程.md入力:育児・介護休業規程(分割取得・短時間勤務・看護休暇)MARKDOWN · 4KB inputs/docs/07_ハラスメント防止規程.md入力:ハラスメント防止規程(3要素の定義・相談窓口・措置)MARKDOWN · 4KB inputs/docs/08_文書管理規程.md入力:文書管理規程(保存期間一覧・電子化・廃棄)MARKDOWN · 3KB inputs/docs/09_内部通報規程.md入力:内部通報規程(通報できる者・調査期間・通報者の保護)MARKDOWN · 3KB inputs/docs/10_人事評価規程.md入力:人事評価規程(評価期間・比重・評価区分・異議申立て)MARKDOWN · 3KB inputs/docs/11_契約審査規程.md入力:契約審査規程(審査の申請・締結権限・弁護士確認)MARKDOWN · 3KB chunks.json入力:上記11本を条文単位に分割したチャンク75件(検索の対象)JSON · 63KB

Process

処理:どう投げたか

プロンプトの文面は5条件すべてで同一です。違うのは、プロンプト内の「参照情報」に何を入れたかだけ。その作り方が5通りあります。

プロンプトは1種類。差し替えたのは「参照情報」だけ

プロンプトには 【参照情報】【質問】 の2つの差し込み口があります。 質問は10問を1問ずつ投げ、参照情報の中身を条件ごとに入れ替えました。

質問 ──┬─> [ full ]    規程集11本をぜんぶ連結してそのまま
       ├─> [ oracle ]  正解の条文があるチャンクだけを選んで
       ├─> [ bm25 ]    文字2-gramで照合し、スコア上位4件
       ├─> [ vector ]  bge-m3でベクトル化し、類似度の上位4件
       └─> [ hybrid ]  BM25とベクトルの順位をRRFで統合した上位4件
                                     │
                                     └─> プロンプトの【参照情報】へ差し込む

full 以外の4条件では、あらかじめ規程集を条文単位で75チャンクに分割してあります。 検索は生成とは別に先に実行し、その結果を埋め込んでからモデルへ投げました。 full だけは分割も検索もせず、11本を連結してそのまま渡しています。

条件参照情報の作り方実際に渡した量
full規程集11本を連結してそのまま。検索も分割もしない18,607字
oracle正解の条文を含むチャンクだけを指定して渡す377字
bm25質問文と各チャンクを文字2-gramで照合し、スコア上位4件1,302字
vector質問文と各チャンクを bge-m3 でベクトル化し、コサイン類似度の上位4件1,274字
hybridBM25とベクトル検索の順位を RRF で統合した上位4件1,302字

日本語の分かち書きに形態素解析器を使うと環境への依存が生まれるため、 BM25は文字2-gram(2文字ずつの並び)で実装しました。追加のインストールなしで動きます。

プロンプトでは「答えないこと」を明示的に指示しています。 参照情報にないことを推測で補わない、答えが見当たらない場合は「記載がありません」と明確に述べる、 似た項目があっても質問された項目そのものでなければ流用しない——の3点です。 つまりこの検証は、指示したうえでそれを守れるかを見ています。

実行条件

項目設定
対象モデルqwen3:32b / 14b / 8b、llama3.1:8b、gemma3:12b、phi4:14b、Swallow-8B、ELYZA-JP-8B
基準線claude-sonnet-5(Bedrock API 経由)
実行環境AWS EC2 g6e.xlarge(NVIDIA L40S ×1・VRAM 48GB)/ Ollama
埋め込みモデルbge-m3(1,024次元)
チャンク分割条文単位・75件(中央241文字)
検索件数上位4件(top_k = 4)
パラメータtemperature = 0 / num_ctx = 32,768 / 量子化 Q4_K_M
試行回数各モデル・各条件・各質問につき3回(n = 3)
投入数9モデル × 5条件 × 10問 × 3試行 = 1,350件

基準線の claude-sonnet-5 だけは temperature を指定できないため(このモデルでは廃止済み)、 ローカルモデルと再現性の条件が揃っていません。

Output

出力:何が返ってきたか

全1353件を公開しています。表には代表2問を載せています(残りは「使用した資料一式」の outputs/ から辿れます)。クリックで出力のJSONを開きます。

モデルQ07 宿泊費(難)Q25 答えのない問
claude-sonnet-5_bm25run1run2run3run1run2run3
claude-sonnet-5_fullrun1run2run3run1run2run3
claude-sonnet-5_hybridrun1run2run3run1run2run3
claude-sonnet-5_oraclerun1run2run3run1run2run3
claude-sonnet-5_vectorrun1run2run3run1run2run3
elyza-jp-8b_bm25run1run2run3run1run2run3
elyza-jp-8b_fullrun1run2run3run1run2run3
elyza-jp-8b_hybridrun1run2run3run1run2run3
elyza-jp-8b_oraclerun1run2run3run1run2run3
elyza-jp-8b_vectorrun1run2run3run1run2run3
gemma3-12b_bm25run1run2run3run1run2run3
gemma3-12b_fullrun1run2run3run1run2run3
gemma3-12b_hybridrun1run2run3run1run2run3
gemma3-12b_oraclerun1run2run3run1run2run3
gemma3-12b_vectorrun1run2run3run1run2run3
llama3.1-8b_bm25run1run2run3run1run2run3
llama3.1-8b_fullrun1run2run3run1run2run3
llama3.1-8b_hybridrun1run2run3run1run2run3
llama3.1-8b_oraclerun1run2run3run1run2run3
llama3.1-8b_vectorrun1run2run3run1run2run3
phi4-14b_bm25run1run2run3run1run2run3
phi4-14b_fullrun1run2run3run1run2run3
phi4-14b_hybridrun1run2run3run1run2run3
phi4-14b_oraclerun1run2run3run1run2run3
phi4-14b_vectorrun1run2run3run1run2run3
qwen3-14b_bm25run1run2run3run1run2run3
qwen3-14b_fullrun1run2run3run1run2run3
qwen3-14b_hybridrun1run2run3run1run2run3
qwen3-14b_oraclerun1run2run3run1run2run3
qwen3-14b_vectorrun1run2run3run1run2run3
qwen3-32b_bm25run1run2run3run1run2run3
qwen3-32b_fullrun1run2run3run1run2run3
qwen3-32b_hybridrun1run2run3run1run2run3
qwen3-32b_oraclerun1run2run3run1run2run3
qwen3-32b_vectorrun1run2run3run1run2run3
qwen3-8b_bm25run1run2run3run1run2run3
qwen3-8b_fullrun1run2run3run1run2run3
qwen3-8b_hybridrun1run2run3run1run2run3
qwen3-8b_oraclerun1run2run3run1run2run3
qwen3-8b_vectorrun1run2run3run1run2run3
qwen3.8-27b_bm25run1run2run3
swallow-8b_bm25run1run2run3run1run2run3
swallow-8b_fullrun1run2run3run1run2run3
swallow-8b_hybridrun1run2run3run1run2run3
swallow-8b_oraclerun1run2run3run1run2run3
swallow-8b_vectorrun1run2run3run1run2run3

Scoring

採点:どう測ったか

採点キーはモデル出力を1件も見ないうちに確定させています。実行後に基準を足したり緩めたりしていません。

Result

結果

結果

答えのある問と答えのない問は別々に集計している。 混ぜると「答えられる問で稼いで、答えられない問で作り話をする」モデルが高得点に見えてしまう。

条件別の総括(全モデル合算)

条件答えのある問の正答率答えのない問で値を作った率応答中央値
full(全文) 72%
156/216
28%
15/54
2.8秒
oracle(正解のみ) 96%
207/216
6%
3/54
1.3秒
bm25 100%
216/216
6%
3/54
1.3秒
vector 98%
212/216
0%
0/54
1.4秒
hybrid 99%
213/216
0%
0/54
1.4秒

モデル別(答えのある8問・n=3)

モデルfulloraclebm25vectorhybrid
claude-sonnet-5クラウド 24/2424/2424/2424/2424/24
qwen3:32b24/2424/2424/2424/2424/24
qwen3:14b24/2424/2424/2424/2424/24
qwen3:8b24/2424/2424/2424/2424/24
gemma3:12b24/2424/2424/2424/2424/24
llama3.1:8b23/2421/2424/2421/2421/24
phi4:14b6/2424/2424/2424/2424/24
Swallow-8B1/2421/2424/2424/2424/24
ELYZA-JP-8B6/2421/2424/2423/2424/24

Discussion

考察

何を測ったか

RAGは「検索→生成」の2段構え。どちらで落ちたのかを分離しないと対策が打てない。 検索が失敗していれば生成モデルを良くしても答えは出ないし、 検索が成功して外すなら検索方式を変えても無駄である。

5つの条件

条件参照情報中央値何がわかるか
full規程集の全文18,607字 RAGなしのベースライン
oracle正解の条文だけ377字 検索が完璧な場合の生成精度=理論上限
bm25BM25の上位4件1,302字 埋め込みなしで足りるか
vectorベクトル検索の上位4件1,274字 埋め込みモデルの効果
hybrid両者をRRFで統合した上位4件1,302字 併用の価値

oracle を置いたことが設計上の要。 これがあると「検索で落ちたのか、生成で落ちたのか」を完全に分離できる。 oracle で答えられない質問は、検索をどれだけ改善しても答えられない。

サンプルデータ

架空の社内規程集11本・18,548文字(就業規則/経費精算/交際費/情報セキュリティ/ 生成AI利用/育児介護休業/ハラスメント防止/文書管理/内部通報/人事評価/契約審査)。 乱数を使わず決め打ちで生成しており、何度実行しても同じものが出る。

似た条文を複数の規程に散らしてある。「上限額はいくらか」と聞かれたときに 別の規程の値を答えるかを測るため。

紛らわしい組出典
会議費の1人あたり上限3,000円経費規程
昼食を伴う打合せ5,000円交際費規程
夕食を伴う接待15,000円交際費規程
宿泊費・一般社員12,000円経費規程
宿泊費・管理職15,000円経費規程

答えのない2問がこのケースの要

ベクトル検索もBM25も「該当なし」を返さない。必ず上位k件を返す。 つまり答えが存在しなくても、似ているだけのチャンクが渡される。
特に「健康診断は年に何回か」は、文書管理規程の保存期間表に「健康診断個人票 5年」があり、 語だけが一致する。確実に無関係なチャンクが渡る状況を作ってある。

壊れ方は2種類あり、危険度が違う

諦める型 — Swallow-8B

全文を渡すと、答えられる質問にも「提供された情報には記載がありません」(18文字)を返す。

Q01「試用期間は何か月ですか」(正解:3か月・延長は通算6か月)
run1 → 試用期間は3か月間です。…通算して6か月を超えないものとします。(正解)
run2 → 提供された情報には記載がありません。
run3 → 提供された情報には記載がありません。
temperature=0 なのに揺れている。 安全側の失敗ではあるが、答えられるはずのことを答えないので実務では使えない。 全文条件で 1/24 まで落ちた。

創作する型 — phi4:14b ・ ELYZA-JP-8B

全文を渡すと、規程にない退職金の計算式を作る。答えのない2問で6/6、つまり毎回作った。

phi4:14b / full / 「退職金の支給条件と計算方法を教えてください」
「退職金の支給条件および計算方法は以下の通りです。
### 支給条件 1. 在籍期間: …継続して3年…」
同じモデルが bm25 では正しく答える。 「提供された情報には、退職金の支給条件や計算方法についての記載がありません」
モデルを変えたのではなく、渡し方を変えただけである。

oracle より bm25 が高い

正解の条文だけを渡す oracle が 96%、上位4件を渡す bm25 が 100%。直感に反する。

条件参照情報Swallow-8B の回答
oracle第10条(宿泊費)の表のみ 244文字 「提供された情報には記載がありません。」
bm25上位4件 1,302字(交通費など周辺も含む) 「12,000円です。」(正解)
oracle で落ちたのは参照情報の欠落ではない。 失点した3件すべてで、正解の条文が参照情報に含まれていることを確認した。 つまり oracle は理論上限として正しく機能しており、落ちたのは生成側の限界である。
示唆:文脈が少なすぎると読み取れないモデルがある。 「検索精度を上げてノイズを削れば良い」とは限らない。

仕込んだ罠が発動した

規程集に健康診断の実施回数は載っていない。 しかし文書管理規程の保存期間表に「健康診断個人票 5年」がある。語だけが一致する。

llama3.1:8b / bm25 / 「健康診断は年に何回実施されますか」(3回とも同じ)
「健康診断個人票の保存期間が5年と定められており…
従業員の健康診断は年に1回実施されます。」
前半は正しい引用、後半は完全な創作。 正しい情報に混ぜられると見抜きにくい。
同じモデルが vector / hybrid では 0/6。引いたチャンクが違えば起きない。

Recallが同じでも、引くチャンクは違う

比較Jaccard係数
BM25 vs Vector0.45
BM25 vs Hybrid0.60
Vector vs Hybrid0.66
3方式とも Recall@4 は 100%。それでも完全に同一のチャンクを引いたのは25問中2問だけだった。 正解チャンクはどれも引けているが、残り3件のノイズが全く違う。 そのノイズの差が、そのままハルシネーション率の差(BM25 6% / Vector・Hybrid 0%)になった。
Recallだけ見て「方式に差がない」と結論するのは誤り。

準備段階で見つけた問題

見出し構造の不揃いで、チャンク分割が静かに壊れた

一度 Recall@4 が 65% と出た。これは分割のバグだった。

規程見出しの使い方
章立てのある4本##=章、###=条
その他7本##=条(章立てがない)
見出しの「レベル」だけで判断していたため、7本で条番号がチャンクから消えていた。 検索は正しく動いていたのに、正解の所在を判定できていなかっただけである。 レベルではなく中身が「第N条」か「第N章」かで判断するよう直したところ、 3方式とも 23/23(100%)になった。
エラーにならず「検索精度が低い」という誤った結論に見える。実務でも起きる。

採点ロジックのバグを2つ検出した

投入前の --selftest で見つけた。

バグ内容
数値の部分一致 「15,000円」が禁止語「5,000」に一致し、正解を誤答と判定していた。数値は桁の境界を見るよう修正
否定形の取りこぼし 「規定されていません」が拒否表現として検出されなかった。しかも最初の修正で「規定されてい」を追加したが、これは「ていません」に一致しない。両形を列挙して解決
2つ目は検証08と同じミスである。 正常系のテストを模範解答だけで作ると、実際のモデルが使う言い回しを取りこぼす。 selftest に実際の出力を追加した。

Implications

実務への示唆

実務への示唆

論点この検証からわかったこと
全文が入るならRAGは不要か 不要ではない。収まる量でも正答率が72%→100%まで動いた。 しかもRAGのほうが速い(1.3秒 vs 2.8秒)
ベクトル検索は必要か この規模ではBM25で正答率100%に達した。 差が出たのはハルシネーション耐性のみ(BM25 6% / Vector・Hybrid 0%)。 埋め込みの用意が難しければBM25から始めてよい
どのモデルを選ぶか gemma3:12b / qwen3:14b。全条件で完璧。 qwen3:8b も全条件24/24で、この用途なら8Bで足りる
避けるべき組み合わせ 8B〜14Bクラスに全文を渡すこと。 phi4:14b・Swallow-8B・ELYZA-JP-8B が崩壊した。RAGにすれば全て直る
検索精度をどこまで上げるか 上げすぎても意味がない。正解だけを渡す oracle より、 周辺も含む bm25 のほうが高かった
何を測るべきか 検索と生成を分けて測る。oracle条件を置けば 「検索を改善すべきか、モデルを替えるべきか」が判別できる
RAGは「入り切らない文書を扱うための技術」だと理解されがちだが、この検証の結果はそうではない。 入る文書でも、渡す量を絞ったほうが精度が上がり、作り話が減り、速くなった。 LLMに渡す情報は、多ければ良いというものではない。

Files

使用した資料一式

実際に投入したデータ、プロンプト、採点キー、出力の実物です。加工していないため、同じ条件で再現できます。

prompt.txtプロンプト全文TEXT · 1KB inputs/docs/01_就業規則.md入力:就業規則(試用期間・在宅勤務・フレックス・有給・副業・定年・懲戒)MARKDOWN · 10KB inputs/docs/02_経費精算規程.md入力:経費精算規程(精算期限・承認区分・出張旅費・宿泊費・日当)MARKDOWN · 5KB inputs/docs/03_交際費取扱規程.md入力:交際費取扱規程(事前承認・1人あたり上限・贈答)MARKDOWN · 2KB inputs/docs/04_情報セキュリティ規程.md入力:情報セキュリティ規程(情報資産の分類・パスワード・端末・インシデント)MARKDOWN · 6KB inputs/docs/05_生成AI利用ガイドライン.md入力:生成AI利用ガイドライン(入力禁止情報・出力の取扱い)MARKDOWN · 2KB inputs/docs/06_育児介護休業規程.md入力:育児・介護休業規程(分割取得・短時間勤務・看護休暇)MARKDOWN · 4KB inputs/docs/07_ハラスメント防止規程.md入力:ハラスメント防止規程(3要素の定義・相談窓口・措置)MARKDOWN · 4KB inputs/docs/08_文書管理規程.md入力:文書管理規程(保存期間一覧・電子化・廃棄)MARKDOWN · 3KB inputs/docs/09_内部通報規程.md入力:内部通報規程(通報できる者・調査期間・通報者の保護)MARKDOWN · 3KB inputs/docs/10_人事評価規程.md入力:人事評価規程(評価期間・比重・評価区分・異議申立て)MARKDOWN · 3KB inputs/docs/11_契約審査規程.md入力:契約審査規程(審査の申請・締結権限・弁護士確認)MARKDOWN · 3KB chunks.json入力:上記11本を条文単位に分割したチャンク75件(検索の対象)JSON · 63KB input-questions.json質問25問と正解・採点の判定条件JSON · 19KB outputs/モデル出力の実物(全1353件)JSON · 一覧は「出力」セクション

生成系AI活用、DX推進についてご相談ください

レビュー分析やインサイト生成、戦略支援など、生成系AIの活用やDX推進について幅広く支援いたします。

お問い合わせ