「生成モデルを大きくすれば答えが良くなる」
モデルが動かすのは忠実度だけで、到達率の天井は変わりません。到達率を測る前にモデルを替えると、料金が上がって正答率は天井に張り付いたままです。まず 2 つの数字を測り、天井が低いほうから直してください。
DBC Tech Academy / AI / 上級
答えが「もっともらしいのに違う」「聞き方を変えると変わる」「記載があるのに無いと言う」。こういうとき、多くの現場は生成モデルを大きなものに替えます。しかし答えが悪い原因の大半は、正しい根拠が LLM に届いていないことにあります。ここでは、答えの悪さを検索の到達率と生成の忠実度に分解し、どちらを直すべきかを数字で決める手順を扱います。
正答率 64% の内訳は、到達率 68% × 忠実度 94% です。生成モデルを替えて忠実度を 100% にしても、正答率は 68% を超えられません。
The ceiling
RAG の回答は、検索が渡した根拠の範囲でしか正しくなれません。正しい根拠が届いていなければ、どんなモデルでも正しく答えられず、答えたように見えるならそれは推測です。
到達率が 70% なら、正答率の天井は 70% です。残りの 30% では、LLM の手元に正解が存在しません。この天井の下で、届いた根拠を正しく使えたかを表すのが忠実度です。
直す順番は、天井が低いほうからです。到達率が 85% を下回っていれば、生成には手を付けず検索を直します。到達率が高いのに正答率が低いときだけ、生成側の根拠の渡し方と指示を直します。
生成モデルの変更は、この両方を測った後の最後の選択肢です。モデルが動かすのは忠実度だけで、天井そのものは 1 ポイントも動きません。
検索が正しいチャンクを上位に置けた割合。ここが正答率の天井を決めます。上げる手は分割・配分・リランク・絞り込みで、モデルの変更では上がりません。
届いた根拠を正しく使えた割合。根拠の並べ方・件数・指示で決まり、最後の手段としてモデルでも動きます。
この 2 つを測らずにモデルを替えると、費用が上がって正答率は天井のまま張り付きます。まず測ることが、最初の打ち手です。
Where it breaks
質問から回答までを 7 段階に分けます。前半 3 つは取り込み時、後半 4 つは質問時に動きます。1 から 5 の失敗は到達率に、6 と 7 の失敗は忠実度に現れます。
Two numbers
到達率は対の質問を検索に流して測り、忠実度は正解のチャンクを手で渡して生成させて測ります。検索を通さないので、生成だけの実力が出ます。 値を動かして、モデルの変更がどこに効くのかを確かめてみましょう。
k を上げると到達率は上がりますが、根拠が増えるぶん中ほどが読まれにくくなり、忠実度はわずかに下がります。生成モデルを替えると忠実度だけが動きます。
この図が再現しているのは、正答率が到達率を超えられないことと、モデルの変更が動かすのは忠実度だけだということです。到達率と忠実度の実際の値、k を上げたときの忠実度の下がり方は、文書と質問で変わります。自分の環境では、次の節のログと質問・正解の対から 2 つの数字を実測し、その値で判断してください。
| 到達率 | 忠実度 | 直す場所 | 理由 |
|---|---|---|---|
| 85% 未満 | 問わず | 検索 | 天井が低いので、生成をいくら直しても正答率は上がりません |
| 85% 以上 | 85% 未満 | 生成 | 正解は届いています。使い方の問題です |
| 85% 以上 | 85% 以上 | 並べ替え、または対 | 正答率が積より低いなら並べ替え。積と一致するなら、対が現場の質問を代表していません |
3 行目が起きたときの見分け方。到達率と忠実度の積が実測の正答率と一致していれば、測定は正しく、モデルは想定どおり動いています。それでも現場から苦情が出るなら、対の質問が現場の実際の質問と違うのです。次の節のログから feedback = 0 の質問を拾い、対を作り直してください。
Three experiments
答えが悪い質問を 1 件取り、次の順に試します。各実験は 1 分で終わり、結果で次が決まります。全体を測る前に、まず 1 件で当たりを付けるほうが速いことが多くあります。
検索を通さず、人が正解のチャンクを根拠としてプロンプトに入れ、生成させます。これで生成側の実力だけが分かります。
同じ質問で検索を流し、上位 k 件に正解のチャンクがあるかを chunk_id で確かめます。ログがあれば、過去の質問についても同じことができます。
SELECT heading_path, content FROM chunks WHERE chunk_id = :chunk_id を実行し、そのチャンクに何が書かれているかを目で見ます。
この 3 つで、直す段階は 1 つに決まります。ここを飛ばして複数の打ち手を同時に入れると、効いたものと効かなかったものが区別できなくなり、次に同じ症状が出たときに何も学べません。
Instrumentation
質問 1 件につき 1 行を、検索と生成の両方について残します。これがないと、切り分けのたびに現象を再現するところから始めることになります。
retrieved_ids と cited_ids を分けて持つのは、「渡したが使われなかった」を見つけるためです。正解が渡されているのに引用されていなければ、原因は段階 05 か 06 で、検索ではありません。この 1 列があるかどうかで、切り分けの速さが変わります。
各行について、設計書のどのチャンクを読めば答えられるかを人が付け、質問・正解の chunk_id・質問の種類(識別子入り/言い換え/複数文書)を書き出します。
役に立たなかった質問こそ、正解を付ける価値があります。うまくいっている質問を集めても、改善の材料にはなりません。
&& は配列が重なるかどうかを見る演算子です。対ができれば、過去のログにさかのぼって到達率が出せます。設定を変える前と後を、同じ質問で比べられます。
実験 1 の「正解のチャンクを手で渡す」は、対に answer_chunk_ids があれば手作業ではなくなります。SELECT content FROM chunks WHERE chunk_id = ANY(:ids) で本文を引き、検索を通さずにそのまま根拠として生成に渡すだけです。
この 1 本を diagnose.py に入れておくと、到達率と忠実度が同じコマンドで出ます。ヒーローの表がその出力です。切り分けのたびに人が根拠を貼り付けている現場は、この自動化がないだけで測る回数が減り、判断が遅れます。
「正しく答えたか」の判定は、最初の 50 件は必ず人が行います。そのうえで、人の判定を例として渡した別の LLM に同じ 50 件を判定させ、人との一致率が 9 割を超えてから、以後の判定を任せます。
一致率を確かめずに任せると、判定そのものがずれていても気付けず、正しさの数字が改善の根拠として使えなくなります。一致率は四半期ごとに、新しい 20 件で測り直してください。
このテーブルには、利用者が入力した文と文書の内容が入ります。閲覧範囲のある文書を扱っているなら、rag_queries の閲覧権限も文書と同じ範囲にそろえてください。切り分けのために作ったログが、権限の抜け道になります。
Fixing retrieval
実験 3 で決まった段階ごとに、打ち手は 1 つずつ試します。1 つ変えるたびに到達率を測り直してください。2 つ同時に変えると、どちらが効いたか分かりません。
分割し直すと chunk_id が変わります。対の正解を付け直してから到達率を測ってください。付け直しを忘れると、到達率が 0 に近い値になって別の原因を探し始めることになります。
どちらも検索の前に生成が 1 回増え、レイテンシが 1〜2 秒延びます。到達率の伸びと引き換えにできるかは、対で測ってから決めてください。
種類別の到達率を見れば、どちらへ寄せるかが決まります。
| 落ちている種類 | 打ち手 | 合わせて確かめる |
|---|---|---|
| 識別子入り | 全文検索側の配分を上げる | 本文に識別子がそのままの表記で入っているか |
| 言い換え | ベクトル側の配分を上げる | 埋め込みモデルが多言語に対応した版か |
| 2 文書にまたがる | 質問を 2 回の検索に分ける | 両方の正解が k 件に入る必要がある。片方ずつ検索して合わせる |
| 両方 | k を上げる | hnsw.ef_search が k より小さくないか |
正解が k 件に入っているのに末尾にいるなら、検索で 20〜30 件取り、リランクモデルで並べ直して上位 6 件を渡します。リランクは質問とチャンクを 1 対ずつ読んで関連度を出すので、埋め込みの近さより精度が高く、その分 1 回の呼び出しに数百ミリ秒かかります。
リランクのモデルは、Bedrock なら Cohere Rerank、手元なら日本語に対応した交差符号化のモデルを使います。どれを選んでも、組み込む位置と、渡す件数を 6 に保つことは変わりません。広く取って狭く渡す、という形が要点です。
確かめ方は、到達率ではなく平均順位です。リランクは k 件に入っているものを並べ替えるだけなので、到達率の数字は変わらないことがあります。効いているかどうかは、正解の平均順位が下がったか(前に来たか)で見ます。
「バッチ設計書の中で」のように範囲が分かる質問は、文書の種別で WHERE を絞ってから検索します。候補が減るぶん、正解が上位に来ます。質問から範囲を取り出すのは正規表現で足りることが多く、足りなければ LLM に種別を 1 語で出させます。
SET hnsw.ef_search をセッションで上げると、その接続のすべての検索が遅くなります。検索の直前に SET LOCAL でトランザクション内に閉じてください。接続プールを使う構成では、閉じないと前のリクエストの値が次の検索に効き、到達率が測るたびに違うという現象になります。
Fixing generation
実験 1 で「正解を手で渡しても答えない」と分かったときの打ち手です。ここでも 1 つずつ変え、そのたびに忠実度を測り直します。
[n] 文書名 > 見出し を付けます。LLM が「どの根拠を使ったか」を書ける形にしておくと、cited_ids が取れるようになり、次の切り分けが速くなります。指示の変更は、対の質問には効いても現場の別の質問で副作用が出ます。「記載なし」を減らす指示は推測を増やし、増やす指示は答えられる質問を減らします。変更後 1 週間は、feedback = 0 の件数を変更前と比べてください。
モデルを替えるのは最後です。到達率が 85% 以上あり、根拠の並べ方と指示を直しても忠実度が 85% に届かないとき、初めてモデルを 1 段上げます。上げた後、同じ対で忠実度を測り、差を記録してください。差がなければ戻します。測らずに上げたままにすると、効果のない費用が毎月続きます。
Read the symptom
運用中の RAG でよく持ち込まれる 3 つの症状です。それぞれ、どの段階に原因があるのかを考えてみましょう。
Myth vs. reality
どれも一見もっともらしく、実際には正答率を動かしません。
モデルが動かすのは忠実度だけで、到達率の天井は変わりません。到達率を測る前にモデルを替えると、料金が上がって正答率は天井に張り付いたままです。まず 2 つの数字を測り、天井が低いほうから直してください。
k 件に入っても末尾は読まれず、根拠が増えるほど中ほどが見落とされます。到達率は上がっても正答率が下がる領域があります。k を上げるならリランクと組み合わせ、実際に渡す件数は 6 前後に保ってください。
文書が増え、利用者の質問の傾向が変わると、到達率は下がります。しかもエラーは出ず、答えの質だけが静かに落ちます。ログから四半期ごとに feedback = 0 の質問で対を足し、測り直してください。
Knowledge check
答えを当てるだけでなく、なぜそう判断できるのかを説明できれば合格です。
選択肢を1つ選んでください。
Where to look first
目次