

RAGシステムで検索結果を増やせば回答の網羅性は上がりますが、LLMのコンテキストウィンドウには上限があります。本記事では、実際にtop-kを倍増させた結果トークン上限エラーが発生した事例と、そこから得られた知見・対策案をまとめます。
top-k増加がトークン消費に与える影響(実測値付き)
検索漏れが発生する構造的な原因
トークン上限を超えずに検索網羅性を上げる対策案
RAGシステムの検索パラメータチューニングに取り組んでいる方
LLMのトークン上限に悩んでいる方
10以上のアプリケーションのドキュメントを1つのAzure AI Searchインデックスに格納し、ハイブリッド検索でtop=5のチャンクを取得していました。4つのカテゴリ(基礎知識・要件定義・仕様・詳細設計)それぞれで検索するため、合計で最大20チャンクがLLMに渡されます。
特定のアプリケーションについて質問しても、top=5では他のアプリのチャンクが上位を占めてしまい、「情報が含まれていません」という回答になるケースが発生。データはインデックスに確実に存在するにもかかわらず、です。
対策として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.
問題の根本原因は以下の構造にあります:
多数のアプリが1つのインデックスに混在 — 10以上のアプリのチャンクが同じインデックス内で競合するため、特定アプリのチャンクがtop-kに入りにくい
カテゴリ × top-kの掛け算 — 4カテゴリそれぞれでtop-k件取得するため、チャンク数がすぐに膨らむ
チャンクサイズが大きい — 自動生成されたドキュメントは1チャンクあたり数千トークンになることがある
top-kを増やしつつ、検索スコアが一定以下のチャンクを除外します。関連性の低いチャンクを排除することで、トークン消費を抑えながら網羅性を確保できます。
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']
各チャンクを一定文字数で切り詰めることで、top-kを増やしても総トークン数を制御できます。ただし情報の欠損リスクがあります。
Azure AI Searchのセマンティックランカーを使い、top=10で取得後に意味的関連性で再ランク付けし、上位5件のみをLLMに渡します。追加コストが発生しますが、精度と効率のバランスが良い方法です。
チャンクにアプリケーション名のメタデータを付与し、検索時にフィルタで絞り込みます。ユーザーの質問からアプリ名を抽出するステップが必要になります。
以下は実測に基づく目安です(4カテゴリ合計):
| top-k | 最大チャンク数 | 概算トークン数 |
|---|---|---|
| 5 | 20 | 〜80K(128K以内) |
| 10 | 40 | 〜190K(128K超過) |
| 7(スコア閾値付き) | 15〜28 | 〜100K(128K以内・推定) |
top-kを単純に増やすとトークン上限に達するリスクがある(実測: top=10で約190Kトークン)
多数のドキュメントが1インデックスに混在する場合、top-kの競合で特定ドキュメントが検索結果に含まれない問題が起きる
スコア閾値・セマンティックランカー・フィルタリングなど、top-k以外のアプローチで網羅性と効率を両立させることが重要