第8部 外部知識とツールでLLMを拡張する

第31章検索の落とし穴とクエリ設計

ベクトル検索は便利ですが、質問のしかたによっては、意図とずれた結果を返すことがあります。とくに つまずきやすいのが、否定を含む質問です。この章では、なぜそうしたずれが起きるのかを、しくみに 立ち返って理解し、実際の対処を見ていきます。RAGを実務で使ううえで、知っておきたい落とし穴です。

31.1質問文をそのまま埋め込んだ場合の挙動

ベクトル検索は、質問文を埋め込んでできたベクトルと近いものを探すだけです。質問の意図を論理的に解釈しているわけではありません。

ここで、前章で見た検索のしくみを、もう一度冷静に確認しておきます。ベクトル検索がしているのは、 質問文をそのまま埋め込んでベクトルに変換し、そのベクトルと近いものをコサイン類似度で探し出す、という ことだけです。人間のように、質問の意図を読み取ったり、条件を論理的に解釈したりしているわけではありません。 あくまで、埋め込まれたベクトルの近さで機械的に探しているのです。この点を押さえておくことが、次に見る 落とし穴の理解につながります。

「検索」と聞くと、人が図書館の司書に相談するように、意図をくんで探してくれる姿を思い浮かべがちです。 しかしベクトル検索は、そうではありません。渡されたベクトルに数値的に近いものを返すだけの、機械的な 処理です。この「賢く解釈してくれるわけではない」という前提を持っておくことが、次に見る失敗を避ける 出発点になります。

31.2見た目の近さと意味の近さのずれ

埋め込みは、文章に使われている言葉に強く影響されます。そのため、意味は反対でも、使われている言葉が似ている文章どうしは、近いベクトルになりがちです。

埋め込みは意味を反映するとはいえ、その意味は、文章に含まれる言葉から作られます。そのため、使われている 言葉がほとんど同じで、違いが「ない」などの否定語だけ、という二つの文章は、ベクトルとしては非常に近く なりがちです。人にとっては正反対の意味でも、機械にとっては「ほとんど同じ言葉でできた、よく似た文章」に 見えてしまうのです。ここに、見た目の近さと、人が意図する意味の近さとの間の、ずれが生まれます。

31.3否定形の質問でつまずく例

「〜できない場合」と尋ねると、言葉のよく似た「〜できる場合」の文章が上位に来てしまう、といったことが起こります。否定が検索に効きにくいのです。

具体例で見てみます。ある製品の資料に、「エラーが出る場合の設定」と「エラーが出ない場合の設定」という 二つの文章があったとします。ここで利用者が「エラーが出ない場合の設定」を知りたくて、その言葉で検索した としても、二つの文章は「ない」以外がほぼ同じ言葉でできているため、どちらも高い類似度を示します。その 結果、意図とは反対の「エラーが出る場合」の文章が上位に来てしまうことがあります。否定は、こうした ベクトル検索では、うまく効いてくれないことが多いのです。

質問:「エラーが出ない場合の設定」をそのまま検索 エラーが出る場合の設定 …(意図と反対) 類似度 0.94 エラーが出ない場合の設定 …(求める内容) 類似度 0.93 言葉がほとんど同じため、反対の意味の文章もほぼ同じ類似度になる(数値はイメージ)
図31-1 否定を含む質問では、反対の意味の文章も高い類似度を示し、上位に紛れ込むことがあります。

同じことは、否定だけでなく、細かい条件の違いでも起こります。「初心者向け」と「上級者向け」、「無料の プラン」と「有料のプラン」のように、言葉の大半が共通していて一部だけが反対、という組は、ベクトルが 近づきやすく、取り違えのもとになります。数値の一致で探すという性質上、こうした「わずかな違いが決定的」 なケースは、ベクトル検索の苦手分野なのです。

31.4クエリの書き換えや前処理という対処

対処としては、質問をそのまま埋め込むのではなく、検索に適した形に書き換える、要点となる言葉を抜き出す、他の検索方法と組み合わせる、といった工夫があります。

こうしたずれには、いくつかの対処があります。一つは、質問をそのまま埋め込むのではなく、検索に適した形へ 書き換えてから使う方法です。たとえば、LLM自身に質問を検索用の言い回しへ整理させたり、要点となる言葉を 抜き出したりします。次のコードは、その考え方のイメージです。

Python(イメージ)
# 質問をそのまま埋め込むのではなく、検索用に整えてから使う
search_query = rewrite_for_search(user_question)  # 要点の抽出や言い換え
query_vec = embed(search_query)
results = vector_db.search(query_vec)

そのほか、言葉そのものの一致も加味する検索方法と組み合わせる、条件で絞り込む情報をあらかじめ分けて 持っておく、といった対処もあります。大切なのは、「質問文をそのまま投げれば意図どおりに検索される」と 思い込まないことです。検索のしくみを理解したうえで、質問を扱いやすい形に整える、という発想が役立ちます。

実務メモ:まず「取り出せているか」を疑う

RAGを組んだのに答えがおかしい、というとき、いきなりプロンプトやモデルを疑う前に、検索が正しい文章を 取り出せているかを確かめると、原因にたどり着きやすくなります。実際に取り出された文章を目で見て、 意図した内容か、反対の意味のものが混じっていないかを確認します。否定や細かい条件が絡む質問で 取り違えが起きていたら、クエリの書き換えや、資料の区切り方(第30章)の見直しが効きます。検索と生成を 分けて点検する習慣が、遠回りを防ぎます。

31.5検索精度を確かめる

RAGでは、LLMの応答だけでなく、その手前の検索が適切な情報を取り出せているかを確かめることが重要です。検索がずれれば、応答もずれます。

RAGでうまく答えられないとき、原因はLLMの側だけでなく、その手前の検索にあることが少なくありません。 検索の段階で見当違いの文章を取り出していれば、いくらLLMが優秀でも、正しい応答は返せません。そこで、 応答の良し悪しを見るだけでなく、「どの文章が検索されたのか」を実際に確認することが役立ちます。意図した 文章が取り出せているか、反対の意味のものが紛れていないかを見て、必要ならクエリの整え方を調整します。

検索と生成の二段構えであるRAGでは、それぞれの段階を分けて確かめる姿勢が、精度を高める近道になります。 次の章では、LLMに外部の機能を使わせる、ファンクションコーリングを見ていきます。

この章の要点

  • ベクトル検索は、質問文を埋め込んで近いものを探すだけで、意図を論理的に解釈してはいません。
  • 埋め込みは使われる言葉に強く影響され、意味が反対でも言葉が似た文章は近いベクトルになりがちです。
  • そのため、否定や「わずかな違いが決定的」な質問では、反対の意味の文章が上位に紛れ込むことがあります。
  • 質問を検索用に書き換える、他の検索方法と組み合わせる、といった対処が有効です。
  • 答えがおかしいときは、まず検索が正しい文章を取り出せているかを確認します。