ベクトルデータベースとは?上位10件の壁に当たった話

ベクトルデータベースとは、文章を数値の並びに変えて、意味の近さで探せるようにした保存先です。日本語の全文検索が精度不足だったため、全文検索を避けてベクトル検索に寄せる判断をしました。ところが取得できるのは上位10件までで、そこに入らなかったものは存在しないのと同じになります。全体のナレッジグラフを作るのではなく、各チャンクに関係性のメタデータを持たせて局所的に推論させる方針まで書きます。
Total
0
Shares
ベクトルデータベースとは?全文検索をやめたら、上位10件の壁に当たった

図書館で調べ物をしたとき、司書の方に相談したことがあります。テーマを伝えると、10冊ほど持ってきてくれました。

的確でした。ただ、その10冊に答えは無かった。

もう一度相談すると、今度は別の10冊が来ます。また的確で、また答えが無い。

3回目のとき、司書の方が言いました。「この分野は、こっちの棚の続きにありますよ」

答えは、11冊目以降にありました。 選び方が悪かったのではありません。一度に10冊しか運べなかっただけです。

先に一行だけ置きます。ベクトルデータベースとは、文章を数値の並びに変換して保存し、意味の近さで検索できるようにした保存先のことです。

題材は、社内の文書をAIに参照させる仕組みです。議事録や案件記録を検索して、その内容を根拠に答えさせます。

前置きはさておき、本題に入ります。

今日は、この検索の精度を上げようとして、埋め込みではないところで詰まった話をしていこうと思います。

① 教科書どおりに、全文検索とベクトル検索を併用する

教科書どおりに、全文検索とベクトル検索を併用する

教科書どおりの構成から始めました。

→ 文書を断片(チャンク)に分ける
→ それぞれをベクトルに変換して保存
→ 質問もベクトルにして、近いものを取ってくる
→ あわせて全文検索もかけ、両方の結果を使う

併用が定石とされています。意味で引くベクトル検索と、語で引く全文検索。取りこぼしが減ります。

ここまでは、教科書どおりです。

② そのとおりに組んで、日本語の全文検索が効かない

そのとおりに組んで、日本語の全文検索が効かない

ところが、全文検索の精度が出ませんでした。

日本語には、語と語のあいだに空白がありません。どこで切るかを機械が決めることになります。切り方が想定と違うと、あるはずの文書が引けません。

使っていた基盤の日本語全文検索は、この精度が低いという課題を抱えていました。

そこで判断しました。全文検索を避けて、ベクトル検索の精度を根本的に上げるほうに寄せる。

片肺で飛ぶことになるので、ベクトル側を本気で改善する必要がありました。

③ そして、埋め込みではないところに天井があると気づく

そして、埋め込みではないところに天井があると気づく

改善の入口として、まず埋め込みの質を疑いました。モデルを変える、チャンクの分け方を変える、正規化を変える。

少しは良くなりました。ただ、頭打ちになります。

原因を探して、制約に行き当たりました。

検索結果は、上位K個しか指定できません。使っていた構成では、最大10個でした。

つまり、こうです。

→ 何万件あっても → 返るのは10件
→ 11番目に正解があっても → 存在しないのと同じ
→ 埋め込みを改善する → 10件の中身は良くなる
10件という数は、変わらない

司書の方と同じでした。 選び方は良くなっている。運べる冊数が変わっていない。

④ 原因は「取りに行く力」だけを上げようとしていたこと

原因は「取りに行く力」だけを上げようとしていたこと

原因を一つに絞ると、これでした。

私は、1回の検索で正解を引き当てようとしていました。

10件しか取れないなら、その10件に正解が入っている必要があります。 これを常に成立させるのは、無理です。質問の書き方ひとつで順位は動きます。

必要だったのは、10件の中身から周辺へ辿れることでした。

司書の方の「こっちの棚の続きにありますよ」が、まさにそれです。 持ってきた10冊が外れでも、そこから次の場所が分かればよかった。

⑤ 直してみる — チャンクに関係性を持たせ、局所的にグラフを組ませる

直してみる — チャンクに関係性を持たせ、局所的にグラフを組ませる

最初に検討したのは、ナレッジグラフの全体構築でした。文書間の関係を全部つないだ地図を作る。

難しすぎました。 文書は増え続け、関係の定義も揺れます。地図を作り終える前に、地図が古くなります。

そこで、方針を変えました。

各チャンクに「関係性」のメタデータを持たせ、AIが内部で局所的なナレッジグラフを推論・構築する。

全体像は作りません。取ってきた断片が持っている関係の情報から、その場で必要な範囲だけを組み立てさせます。

→ 上位10件のうち、1件でも当たればいい
→ そこに「これは何に属し、何の前後にあり、何を参照しているか」が書いてある
→ AIがその関係を辿って、周辺を引き直す

件数の天井は動かせないので、1件あたりの情報量を増やしました。

そして、この方針には現実的な利点がありました。実装のハードルが低い。 全体のグラフを構築する仕組みは要らず、チャンクにメタデータを足して、ベクトルデータベースと連携させれば動きます。

⑥ あとで知った — これは検索の古典的なトレードオフだった

あとで知った — これは検索の古典的なトレードオフだった

素朴な対処のつもりでしたが、やっていたことを言い換えると、情報検索の基本的なトレードオフでした。

検索の評価には、対になる2つの見方があります。

適合率 — 取ってきたものが、どれだけ正しいか
再現率 — 正しいもののうち、どれだけ取れたか

そして、上位K件で切るという設計は、再現率に天井を置く行為です。

正解が20件あるのに10件しか返さないなら、再現率は最初から50%が上限です。埋め込みをどれだけ改善しても、この上限は動きません。改善されるのは適合率のほうだけです。

私は、再現率の問題を、適合率の道具で解こうとしていました。

そして、関係性のメタデータを足すという対処は、1回の検索で完結させるのをやめて、辿れるようにすることでした。天井を上げるのではなく、天井の下で往復できるようにしたわけです。

図書館で司書の方がやっていたのも、同じです。10冊という制約は変えず、次の棚を教えてくれました。

新しい理屈は、ひとつも要りませんでした。
改善の記録にはこう残っています。「RAG検索において全文検索を避け、ベクトル検索の精度を根本的に向上させる」。ところで、その手段として書かれていたのが「各チャンクに関係性のメタデータを付与し、AIエージェントが内部でローカルなナレッジグラフを推論・構築する」でした。ただし「ナレッジグラフの全体構築の難しさを踏まえ」という前置きが付いています。実は、この前置きが判断の本体です。全体を作らないと決めたから、実装のハードルが下がりました。

あとで知った — 天井の正体は、件数ではなく次元だった

上位K件で切っているから届かない、という理解で運用していました。ところが、もう一段下に原因があります。

Google DeepMind が公開した LIMIT というデータセットの研究が、そこを扱っています。1本のベクトルで表現できる上位K件の組み合わせの数は、埋め込みの次元数によって上限が決まるという指摘です。

文書が増えるほど、「関連あり」と言えるまとまり方の数は急速に増えます。一方で、表現できる側の容量は次元数のまま動きません。 つまり天井は、検索の設定ではなく、表現の幾何そのものから来ています。

これはモデルを大きくしても、学習データを増やしても消えません。

数字が厳しいところです。Google や Snowflake の最新の埋め込みモデルでも、この課題では再現率が20%を割ったと報告されています。そして同じ課題で、数十年前からある BM25 という語の一致で探す方式が、非常に良い成績を出しました。

意味で探すほうが賢い、という前提が崩れる結果です。

だから現在の実務では、ベクトル検索と語の一致検索を併用する多段構成が推奨されています。1段目の再現率が0.85なら、どれだけ優れた並べ替えを後段に置いても0.85を超えません。 後段は、前段が取れなかったものを取り戻せないからです。

隣を辿らせるという対処は、この幾何の制約を、件数ではなく経路で迂回するやり方だったことになります。理屈は後から追いつきました。

⑦ 何が変わったか

何が変わったか

改善の入口が変わりました。

“`
前:精度が出ない → 埋め込みを変える → 頭打ち
後:精度が出ない → 何件返しているかを見る → 天井を確認してから手を打つ
“`

最初に見るのが、モデルではなく件数になりました。
順番を変えてからの実測です。埋め込みを触らずに、関係性のメタデータを足しただけで、必要な文書に辿り着いた割合が上がりました。 取得件数の上限は10件のままです。

施策上位10件の質辿り着けた割合
埋め込みの変更上がる変わらず
チャンクの分割変更少し上がる少し上がる
関係性メタデータ変わらず上がる

⑧ 現場で使うなら、この2枚

現場で使うなら、この2枚

表1:精度が出ないときに見る順番

見るもの天井かどうか
1返している件数の上限天井。ここが効く
2チャンクの分け方効くが上限内
3関係性のメタデータの有無天井の下で往復できるか
4埋め込みモデル適合率だけが動く
5全文検索の併用可否言語による

1番を確認せずに4番から始めると、必ず頭打ちになります。

表2:チャンクに持たせるメタデータ

種類何に効くか
所属どの文書・どの章の一部か周辺を引き直せる
前後直前・直後の断片文脈が切れない
参照言及している別文書関係を辿れる
日付・作成者絞り込みに使える

全体のグラフは作らないでください。 作り終える前に古くなります。

表3:件数の上限を確かめる手順

手順見るもの
1検索の設定で返す件数の上限を読む
2上限を10件から20件へ上げられるか試す
3上げられないなら、辿れる設計へ切り替える
4上げられても、応答時間が2倍になっていないか見る

2行目で上げられる環境なら、そもそもこの記事の問題は起きません。 上げられない前提の構成を使っているかどうかを、最初に確かめてください。
設定を読むときの合言葉も置いておきます。「返す件数の上限はいくつか」。ただ、この一言を先に聞くだけで、埋め込みの入れ替え2回とチャンク分割の変更3回が省けました。

運用では、次の5件に名前を付けて配りました。名前が付くと、会議の中で指差せるようになります。

1件目、「上位K件に入らなかった文書は、無いのと同じ」。 存在していても、後段には届きません。ここは精度の問題ではなく、件数の問題です。

2件目、「再現率には、構造的な天井がある」。 上位10件で切る限り、11件目以降がどれだけ良くても届きません。埋め込みを磨いても、この天井は動きません。

3件目、「埋め込みを直す前に、件数の上限を疑う」。 改善の順番を逆にすると、数か月かけて天井の下を磨くことになります。

4件目、「チャンクに、隣を指す情報を持たせる」。 前後や参照先を持たせておくと、10件しか取れなくても周辺へ辿れます。取得件数を増やさずに届く範囲が広がります。

5件目、「全体を作らず、局所を辿らせる」。 全体の関係図を作る労力と、隣を1件持たせる労力では、桁が違います。

この5件は、1枚に印刷して配れる分量に収めています。増やすと、誰も覚えません。

⑨ この考え方が効き続ける理由

この考え方が効き続ける理由

埋め込みは、これからも良くなります。それでも、この制約は残ります。 件数で切るという設計は、費用と応答速度の都合から来ているからです。

→ 全部を渡せない → 上位K件で切る
→ 切る → 再現率に天井ができる
→ 天井 → 埋め込みでは越えられない

渡せる量に上限がある限り、辿れる設計が要ります。

そして、この考え方は検索に限りません。一度に全部を運べない場面すべてに当てはまります。 運ぶ量を増やすか、運んだものから次を辿れるようにするか。たいてい、後者のほうが安い。

ただし、正直に書いておきます。この関係性メタデータを、誰がどう付けるかは解けていません。 人が付けると続きません。AIに付けさせると、付け方が揺れます。 そして揺れた関係を辿ると、辿った先も揺れます。

いまは、AIに付けさせて、目視で直しています。 きれいな解ではありません。

順番を変えてからの実測です。埋め込みには手を触れず、関係性のメタデータを足しただけで、必要な文書に辿り着いた割合が上がりました。取得件数の上限は10件のままです。上位10件のうち、関係を辿る手がかりを持つ断片は平均で3件ありました。

もう1件、運用で効いたことを書いておきます。取得件数の上限は、設定画面に書いてありました。 誰も読んでいなかっただけです。埋め込みを2回入れ替えて、チャンクの分割を3回変えたあとで、ようやく設定を開きました。手を動かす前に、制約を読む。 当たり前のことが、いちばん後回しになります。

当時の判断を、もう1件だけ引いておきます。「実装のハードルが低い」。ところで、これは性能ではなく着手のしやすさの話です。ただし、着手できない改善は存在しないのと同じでした。

関連して、MCPで社内の文書をAIから引けるようにする話と、LLMに自分の出力を検証させてはいけない話を別に書いています。社内RAGが現場で止まる場所話はすでに公開しています。あわせて読むと、同じ判断が別の場所でも効いていることが分かると思います。

この判断を実際の案件で使う場面については、社内GPT・RAG構築のページに整理しています。


上位10件といえば、検索結果の2ページ目を見た記憶がほとんどありません。

無かったのではなく、見ていなかっただけでした。だいたいいつも、そういうことになっています。

以上です。

You May Also Like

LLMとは?検算させたら拾い漏れと「拾いました」が並んだ

LLMとは大規模言語モデルの略で、大量の文章から言葉の続き方を学習したモデルです。指摘を全部反映したかの確認まで任せたところ、拾い漏れと「全部拾いました」という報告が同時に成立しました。判定をモデルに渡す限りこれは消えません。判定を一切LLMに渡さず、退屈な文字列一致で機械が検査する設計に変えました。全角空白の除去まで含めた実装と、判定に使ってよい出力・いけない出力の切り分けを書きます。
View Post

MCPとは?社内ナレッジ検索の画面を作るのをやめて、MCP接続にした話

MCPとはModel Context Protocolの略で、AIと社内の道具やデータを繋ぐための共通の作法です。社内ナレッジを全社に開くために専用のフロントエンドを作ろうとしていましたが、初期フェーズではそれを見送り、MCP接続を主軸にする設計変更をしました。理由は画面が要らないからではありません。画面を作ると、そこに認証も権限も検索体験も全部くっついてくるからです。残したのはAPIキーの発行とフォルダごとの権限管理だけでした。何を作らないと決めたのかを書きます。
View Post

プロンプトインジェクションとは?禁止をプロンプトに書かない

プロンプトインジェクションとは、AIへの指示文に別の指示を紛れ込ませて、本来の制約を外させる攻撃です。対策としてまず思いつくのは、システムプロンプトに禁止事項を書き足すことです。私たちもそうしました。そして守られませんでした。プロンプトによる禁止は確率的で、ツール層のフックは決定的です。44行のフックで守る側へ移した実装と、パスの正規化や失敗時に閉じる設計まで、そのまま書きます。
View Post

プロンプトエンジニアリングとは?AIに組み立てさせるのをやめた

プロンプトエンジニアリングとは、AIから望む出力を得るために指示文を設計する技術です。ところが指示文そのものをAIに組み立てさせると、同じ入力でも毎回わずかに違う文面が生成され、結果が揺れます。原因を追うと、揺れているのはモデルではなくプロンプトのほうでした。CLIが変数を埋め込んだ全文を出力する方式に変えると、プロンプトはバージョン管理できる成果物になります。再現性をどこから取り戻すかを書きます。
View Post