メインコンテンツへスキップ
kt-tech.blog

【トラブルシューティング】RAGのtop-k設定とトークン上限のトレードオフ

トラブルシューティング5分で読めます

この記事でわかること

  • top-k を増やすとチャンク数がカテゴリ数との掛け算で膞らむという構造
  • top=5 → 10 で約 19 万トークンに達した実測値
  • 1 つのインデックスに多数のドキュメントを混ぜると検索漏れが起きる理由
  • トークンを増やさずに網羅性を上げる 4 つの手(スコア閾値・切り詰め・セマンティックランカー・フィルタ)

RAG を実装したことがあり、ベクトル検索と top-k の意味を知っていること。例は Azure AI Search + Azure OpenAI ですが、考え方は他の構成でも同じです。

メモ
RAGでtop-kを増やしたら検索精度が上がるはず…が、LLMのトークン上限を超過してエラーに。検索漏れ vs トークン上限のジレンマと対策案を解説します。

はじめに

RAGシステムで検索結果を増やせば回答の網羅性は上がりますが、LLMのコンテキストウィンドウには上限があります。本記事では、実際にtop-kを倍増させた結果トークン上限エラーが発生した事例と、そこから得られた知見・対策案をまとめます。


発生した問題

状況

10以上のアプリケーションのドキュメントを1つのAzure AI Searchインデックスに格納し、ハイブリッド検索でtop=5のチャンクを取得していました。4つのカテゴリ(基礎知識・要件定義・仕様・詳細設計)それぞれで検索するため、合計で最大20チャンクがLLMに渡されます。

問題1: 検索漏れ

特定のアプリケーションについて質問しても、top=5では他のアプリのチャンクが上位を占めてしまい、「情報が含まれていません」という回答になるケースが発生。データはインデックスに確実に存在するにもかかわらず、です。

問題2: top-k増加 → トークン上限超過

対策としてtop=5 → top=10に変更したところ、4カテゴリ × top=10 = 最大40チャンクとなり、合計約19万トークンに達してLLMの128Kトークン上限を大幅に超過しました。

openai.BadRequestError: This model's maximum context length is 128000 tokens.
However, your messages resulted in 190779 tokens.

原因分析

チャンク数がどこで膞らむのかを図にするとこうです。

flowchart LR
    Q[質問 1 件] --> C1[基礎知識]
    Q --> C2[要件定義]
    Q --> C3[仕様]
    Q --> C4[詳細設計]
    C1 -->|top-k 件| R[LLM に渡すチャンク]
    C2 -->|top-k 件| R
    C3 -->|top-k 件| R
    C4 -->|top-k 件| R
    R --> T[合計 = 4 × top-k × 平均チャンクサイズ]

つまり top-k を 5 から 10 にすると、チャンク数は 5 件増えるのではなく 20 件増えます。この掛け算を見落とすと上限に当たります。

根本原因は以下の構造にあります:

  1. 多数のアプリが1つのインデックスに混在 — 10以上のアプリのチャンクが同じインデックス内で競合するため、特定アプリのチャンクがtop-kに入りにくい
  2. カテゴリ × top-kの掛け算 — 4カテゴリそれぞれでtop-k件取得するため、チャンク数がすぐに膨らむ
  3. チャンクサイズが大きい — 自動生成されたドキュメントは1チャンクあたり数千トークンになることがある

対策案

案1: スコアしきい値で絞り込み

top-kを増やしつつ、検索スコアが一定以下のチャンクを除外します。関連性の低いチャンクを排除することで、トークン消費を抑えながら網羅性を確保できます。

Python
results = search_client.search(
    search_text=query,
    vector_queries=[vector_query],
    top=10,  # 多めに取得
)
for doc in results:
    if doc['@search.score'] > score_threshold:  # スコアで絞り込み
        content += doc['content']

案2: チャンクの文字数を制限

各チャンクを一定文字数で切り詰めることで、top-kを増やしても総トークン数を制御できます。ただし情報の欠損リスクがあります。

案3: セマンティックランカーの活用

Azure AI Searchのセマンティックランカーを使い、top=10で取得後に意味的関連性で再ランク付けし、上位5件のみをLLMに渡します。追加コストが発生しますが、精度と効率のバランスが良い方法です。

案4: インデックスのフィルタリング

チャンクにアプリケーション名のメタデータを付与し、検索時にフィルタで絞り込みます。ユーザーの質問からアプリ名を抽出するステップが必要になります。

トークン消費の目安表

以下は実測に基づく目安です(4カテゴリ合計):

top-k 最大チャンク数 概算トークン数
5 20 〜80K(128K以内)
10 40 〜190K(128K超過)
7(スコア閾値付き) 15〜28 〜100K(128K以内・推定)

Tips

メモ
top-kを変更する前に、現在の総トークン数を概算で把握しておく。4カテゴリ × top-k × 平均チャンクサイズで見積もれる
メモ
複数ドメイン横断モード(AI自動選択)では、存在しないインデックスへの検索でも空ヘッダーが追加され、無駄なトークンを消費する場合がある
メモ
最適なtop-kは「インデックス内のアプリ数」「チャンクサイズ」「LLMのコンテキスト上限」の3要素で決まる

参考リンク

まとめ

  • top-kを単純に増やすとトークン上限に達するリスクがある(実測: top=10で約190Kトークン)
  • 多数のドキュメントが1インデックスに混在する場合、top-kの競合で特定ドキュメントが検索結果に含まれない問題が起きる
  • スコア閾値・セマンティックランカー・フィルタリングなど、top-k以外のアプローチで網羅性と効率を両立させることが重要

更新履歴

  1. チャンク数がカテゴリ数との掛け算で膞らむ構造を mermaid の図で追加。コードブロックの言語指定を plain text から text に修正。関連する他記事(E2Eテスト、ベクトルインデックス作成)へのリンクを追加。「この記事でわかること」「対象読者」を概要プロパティへ移動。