言語モデルにナレッジベースへのアクセスを与える2つの方法を、実際に手を動かして比較しました。1つは埋め込み(embedding)とベクトル検索を使う従来型の RAG(Retrieval-Augmented Generation)、もう1つは2026年半ばにGoogleが公開した Markdown ベースのナレッジ表現形式 OKF(Open Knowledge Format) です。
目的は「どちらが勝ちか」を決めることではありません。同じソースデータの上に両方の仕組みを構築し、同じ質問セットで動かして、それぞれがどこで壊れるのかを見ることが狙いです。面白いのは成功例ではなく失敗例のほうでした。簡単な質問はどちらもきちんと答えますが、測定してみて初めて分かる形で両者は分岐します。
すべてローカルで動作し、有料APIは一切使いません。埋め込みも生成も Ollama を使っています。
結論(先に要点だけ)
18問(単純な検索、マルチホップ質問、意図的に答えられない質問)で検証しました。
- RAG は18問中14問が完全正解、OKF は13問。全体では僅差ですが、両者は異なる質問で失敗しました。ここが本質です。
- OKF は依存関係・関連性を問う質問で明確に優れていました。明示的なリンクをたどるほうが、top-k に必要なチャンクが入ることを祈るより確実です。
- RAG は「答えられない質問」で明確に優れていました。直感に反しますが、RAG は常に何らかの検索結果をプロンプトに注入するため、モデルがナレッジベース外の質問に対して「答えない」判断を保てました。一方 OKF は該当する概念が見つからなかったとき、モデルが自身の学習知識にフォールバックして答えてしまいました。クローズドなナレッジシステムとしては誤った挙動です。
- 「OKF はただのファイル参照だから速い」という利点は、マルチホップ質問では消えました。リンクをたどって複数のドキュメント全体を読み込むと、プロンプトが大きくなり生成が遅くなります。時には RAG より遅くなりました。
どちらの手法も万能ではありません。適切な選択は、ユーザーが「システム内の概念の正式名称」で質問するかどうか、そして「誤った回答」と「回答漏れ」のどちらがユースケースにとって高コストかによって変わります。
構成
ナレッジベース
架空の「AIプラットフォームチーム」向けの、合成でありながら現実的な社内ナレッジベースです。相互リンクされた20個の Markdown ドキュメントで構成されています。
- サービス(5件): auth-service、orders-api、agent-orchestrator、embedding-service、notification-service
- メトリクス(5件): p99レイテンシ、エージェント成功率、スループット、認証失敗率、日次アクティブエージェント数
- Runbook(5件): デプロイ、ロールバック、インシデント対応、キーローテーション、スケーリング
- アーキテクチャ/プロセス文書(5件): システム概要、エージェントライフサイクル、オンコールプロセス、注文データフロー
これらのドキュメントは、実際の社内文書のように相互参照し合っています(Runbook は監視対象のメトリクスにリンクし、サービスは自身の依存先にリンクする、など)。この相互リンクこそが、マルチホップ質問を意味あるものにしています。
Path A — RAG
標準的な検索パイプラインです。
- 各ドキュメントをセクション見出しごとにチャンクへ分割
- 各チャンクを
nomic-embed-text(Ollama経由)で埋め込み - ベクトルを FAISS インデックスに格納(
IndexFlatIP+ L2正規化により、内積 = コサイン類似度) - クエリ時:質問を埋め込み、上位3チャンクを取得し、
phi3に渡して生成
Path B — OKF
同じ20ドキュメントを OKF 形式に変換しました(各ファイルの先頭に YAML フロントマターを追加 — type、title、description、tags)。検索は埋め込み類似度ではなく、構造的に行います。
- 質問を概念の名前やタイトルと直接マッチング
- 失敗した場合、各概念の title + description + tags に対するキーワード重複でフォールバック
- マッチした概念から Markdown リンクを1ホップたどり、関連概念を取り込む
- マッチした概念を
phi3に渡して生成
埋め込みもベクトルストアも使いません。ファイルのパース、文字列マッチング、リンクの走査だけです。
結果
正解(✓)、部分正解(〜)、不正解(✗)で採点しました。部分正解とは、正しい答えは含まれているものの、不完全・埋もれている・曖昧にぼかされている場合を指します。
| # | 質問タイプ | RAG | OKF |
|---|---|---|---|
| 1 | 現在のエージェント成功率 | ✓ | ✓ |
| 2 | 現在のp99レイテンシ | ✓ | ✓ |
| 3 | 現在の認証失敗率 | ✓ | ✓ |
| 4 | 日次アクティブエージェント数 | ✓ | ✓ |
| 5 | 埋め込みスループットの目標値 | 〜 | ✓ |
| 6 | auth-service の所有者 | ✓ | ✓ |
| 7 | orders-api のエンドポイント | ✓ | ✓ |
| 8 | orders-api の依存先とレイテンシ(マルチホップ) | ✗ | ✓ |
| 9 | embedding-service 障害時の影響(マルチホップ) | 〜 | ✓ |
| 10 | 高レイテンシ・インシデント対応手順(マルチホップ) | ✓ | ✓ |
| 11 | キーローテーション後の監視対象(マルチホップ) | ✓ | 〜 |
| 12 | インシデントエスカレーションのプロセス | ✓ | ✓ |
| 13 | 注文作成後のライフサイクル(マルチホップ) | ✓ | ✗ |
| 14 | ロールバック手順と対象サービス | ✓ | ✓ |
| 15 | 「AIとは何か?」(答えられない質問) | ✓ | ✗ |
| 16 | 「今日の天気は?」(答えられない質問) | ✓ | ✗ |
| 17 | 「今四半期の売上は?」(答えられない質問) | ✓ | ✓ |
| 18 | 埋め込みスループットの現在値 | ✓ | ✓ |
合計:RAG 14✓ / 2〜 / 2✗ — OKF 13✓ / 1〜 / 4✗
失敗が実際に教えてくれること
1. OKF は関連性を問う質問で勝つ(Q8、Q9)
質問8 —「orders-api はどのサービスに依存していて、それらの現在のp99レイテンシはいくつか?」— は、このベンチマークで最も明快な単一の結果です。
RAG は完全に失敗しました。上位3チャンクがすべてレイテンシメトリクスのファイルから来てしまい、orders-api の依存関係セクションがコンテキストに入らず、モデルは正しく「依存先が見つからない」と答えました。これは top-k 検索の根本的な限界です。複数のドキュメントに散らばった関連事実は、それらがすべて独立して十分上位にランクされて初めて回答に入りますが、そうなる保証はありません。
OKF は設計上、正しく答えました。orders-api の概念にマッチし、その外向きリンクをたどり、依存構造全体を手元に持っていたからです。明示的な関連をたどるほうが、類似検索が必要な要素をすべて拾ってくれることを祈るより確実です。
2. RAG は答えられない質問で勝つ(Q15、Q16)— そしてその理由は直感に反する
ナレッジベースには人工知能や天気に関する情報は一切ありません。正しい挙動は「答えないこと」です。
RAG は正しく答えを拒否しました。常に何らかの結果を取得してプロンプトに入れるため、モデルは「提供されたコンテキストのみから答える」という枠に留まり、コンテキストが無関係なときは回答を拒否しました。
OKF は答えてしまいました。AIの定義や天気の一般知識を、モデル自身の学習データから引っ張り出したのです。OKF の検索が該当概念を見つけられなかったとき、プロンプトにはモデルを引き留めるグラウンディング用のテキストが何もなく、モデルは通常の汎用チャットボットに戻ってしまいました。
これはよく噛みしめる価値があります。RAG の最も洗練されていない性質 —「大したマッチがなくても何かしらのチャンクをモデルに押し込む」— が、偶然にも安全装置として機能したのです。「自信を持って間違える」ことが「分かりません」より悪いクローズドなシステムでは、これは非常に重要です。OKF 側でも「概念が取得できなかったら答えを拒否せよ」と明示的に指示すれば修正できますが、そのガードがないデフォルトの挙動は安全ではありません。
3. OKF のキーワードマッチングは、RAG の意味検索が強い領域で脆い(Q13)
「注文が作成された後、何が起こるか?」は data-flow-orders ドキュメントが答えを持っています。しかしこの質問は、そのドキュメントのタイトル・説明・タグとほとんど字面が一致しません。「order(注文)」はありふれすぎて識別性がなく、「data flow」は質問に一度も出てきません。OKF の2つのマッチング戦略はどちらも外し、何も返しませんでした。
RAG はこれを難なく処理しました。埋め込みは共通単語ではなく意味でマッチするからです。「注文に何が起こるか」は、注文がシステム内をたどる流れを説明したドキュメントと、語彙の重複がなくても意味的に近いのです。
これは発見1の裏返しです。OKF の構造的検索は、質問が概念にきれいに対応するときは正確で、対応しないときは何も見えません。RAG の意味的検索はより曖昧ですが、より緩やかに劣化します。
4. 速度の話は予想より複雑だった
単一概念の検索では、OKF の検索は実質的にコストゼロです(埋め込み呼び出しがなく、ファイル読み込みと文字列操作だけ)。一方 RAG はクエリごとに埋め込み呼び出しの費用を払います。
しかしこの優位性はマルチホップ質問で逆転します。OKF がリンクをたどって4〜5個のドキュメント全体を取り込むと、RAG の3つの小さなチャンクよりはるかに大きなプロンプトを生成モデルに渡すことになります。生成時間は入力サイズに比例して増えるため、最も重いマルチホップの OKF 回答は、実行全体の中でも最も遅い部類に入りました。対応する RAG 回答より遅かったのです。「ファイルを読むだけだから速い」は、たくさんのファイルを読んでそれを全部モデルに渡すまでの話でしかありません。
記録に値する検索バグ
RAG パイプラインの初期バージョンは、メトリクスの値を検索するのに一貫して失敗していました。あるメトリクスの現在値を尋ねると、その数値以外のすべてが返ってくるのです。
原因は、メトリクスの値がほとんどテキストのない独立したチャンクとして格納されていたことでした。チャンクの中身が 94.2% だけ、ということもあります。裸の数値は埋め込みモデルにとって意味的なシグナルをほとんど持たないため、それがまさに答えであっても、自然言語の質問に対して上位にランクされることがありませんでした。
修正は、埋め込みを行う前に、各チャンクにドキュメントのタイトルとセクション名を先頭付与することでした。94.2% は埋め込み用には agent-success-rate - Current Value: 94.2% になり、表示やモデルに渡すのはクリーンな版のまま、という具合です。これで数値ファクトの検索が「失敗」から「安定」へ変わりました。これは汎用的に使えるテクニックです。疎なチャンクや数値チャンクは、周囲の文脈を埋め込みテキストに焼き込まないと、検索から見えなくなってしまいます。
正直な限界
- 小規模である。20ドキュメント・18問はデモであって、本番のベンチマークではありません。実際のナレッジベースは桁違いに大きく、いくつかの発見(特に速度と top-k のカバレッジ周り)は規模が変われば変化するでしょう。
-
小さなローカルモデルを使っている。生成モデルは
phi3(3.8B)で、すべてを無料かつローカルに保つために選びました。時々、複数項目のリストを途中で切ってしまったり、答えが目の前にあるのに「私の学習データは〜まで」という余計な但し書きを出したりしました。より大きなモデルなら「部分正解」のいくつかは解消されるでしょうが、RAG と OKF の検索の違いは残ります。 - 採点は手動である。回答は既知の正解リファレンスに対して手作業で採点しました。ハーネスは、後から自動ジャッジ(より強力なモデルが各回答を採点)を差し込めるよう構造化してあり、他の部分を変更せずに置き換えられます。
- OKF のマッチングは意図的にシンプルにしてある。本番の OKF システムなら、概念の選択専用に埋め込みステップを1つ足すでしょう。意味検索でどの概念を開くかを選び、その後 OKF の構造を使って読み進め、たどる、というハイブリッドです。これなら Q13 型の失敗を修正しつつ、マルチホップの優位性を保てるはずです。
まとめ
ユーザーがシステム内の物事の名前を使って質問し、かつ「答えを取りこぼす」ほうが「答えをでっち上げる」より安全なら、OKF の構造的アプローチは正確で、透明性が高く、低コストです。ユーザーが自分の言葉で質問し、曖昧・的外れな質問への優雅な対応が必要なら、RAG の意味的検索のほうが寛容です。最も強いシステムはおそらくハイブリッドでしょう。意味検索で正しい概念を見つけ、構造的走査でそれと近傍を読む、という形です。
両方を作ってみて得られたより大きな教訓は、ほとんどの結果を決めたのはモデルの質ではなく検索の質だったということです。不正解のほぼすべては、モデルが正しいドキュメントを扱い損ねたのではなく、間違ったドキュメントが検索されたことに起因していました。
実行方法
必要環境: Python 3.10以上、Ollama(インストール・起動済み)
# モデルの取得
ollama pull nomic-embed-text
ollama pull phi3
# 依存パッケージのインストール
pip install -r requirements.txt
# RAGインデックスの構築
python rag_pipeline/build_index.py
# いずれかのパイプラインで単一の質問を実行
python rag_pipeline/generate.py
python okf_pipeline/generate.py
# 両パイプラインで全質問の評価を実行
python eval/run_eval.py