導入
「とりあえず、ベクトル検索でヒットした上位10件のドキュメントを全部コンテキストに入れておこう」
RAG(検索拡張生成)システムのPoC(概念実証)段階では、こうしたアプローチがよく取られます。開発初期はそれでうまくいくかもしれません。しかし、本番運用が近づき、ユーザー数が増え、扱うドキュメントの量が増大するにつれて、プロジェクトマネージャーの顔色は曇り始めるはずです。
毎月のOpenAI APIの請求額が、想定していた予算曲線を大きく上回る。
回答生成までの待ち時間(レイテンシ)が長くなり、UXが悪化する。
そして何より、ドキュメントをたくさん渡しているはずなのに、LLMが重要な情報を見落とすようになる。
実務の現場では、「情報は多ければ多いほど良い」という誤解がしばしば見受けられます。LLMは魔法の箱ではありません。入力データの質と量に対して、極めてシビアな特性を持つシステムです。
特にRAGにおいては、検索結果(Retrieved Documents)をいかに「圧縮」してLLMに渡すかが、プロジェクトのROI(投資対効果)を決定づける重要なファクターになります。LangChainには便利な圧縮機能(Contextual Compression)が備わっていますが、これらを無思考に導入すればよいというものでもありません。場合によっては、圧縮処理そのものが新たなコストと遅延を生む「本末転倒」な事態さえ招きます。
この記事では、RAG開発における「プロンプト圧縮」の選択肢を、技術的な仕組みだけでなく、ビジネスインパクト(コストと時間)の観点から批判的に比較・分析します。プロジェクトにとって、本当に必要な「賢い圧縮」とは何か。論理的かつ体系的に紐解いていきましょう。
なぜ「全部入りプロンプト」がプロジェクトを失敗させるのか
まずは現状認識から始めましょう。なぜ、検索したドキュメントをそのままプロンプトに詰め込む「全部入り」アプローチが危険なのか。理由は大きく分けて「コスト」と「精度」の2点に集約されます。
塵も積もれば山となるトークン課金の罠
LLMのAPI課金モデルは、基本的にトークン従量制です。入力(Prompt)トークンと出力(Completion)トークンの両方に費用が発生します。ChatGPTやClaudeの最新モデルなど、近年の高性能モデルは以前に比べて安価になりましたが、それでもRAGのコンテキストサイズは侮れません。
例えば、1回の検索で平均2,000トークン分のドキュメントを取得し、それをそのままLLMに投げるとします。1日1,000回の利用があれば、月間で約6,000万トークン。これに入力プロンプトや出力トークンを加算すると、APIコストだけで数万円から数十万円規模になることも珍しくありません。
「その程度のコストなら許容範囲だ」と思われるかもしれません。しかし、もし検索精度を上げるために取得ドキュメント数を増やしたり、ドキュメントのチャンクサイズ(切り出し単位)を大きくしたりすれば、コストは掛け算で膨れ上がります。無駄な情報(ノイズ)にお金を払い続けるのは、プロジェクトマネージャーとして健全な予算管理とは言えません。
Lost in the Middle現象による精度低下リスク
さらに深刻なのが「精度」の問題です。「コンテキストウィンドウが数十万トークンあれば、本を丸ごと一冊入れても大丈夫」というのは、スペック上の話であって、性能上の真実ではありません。
多くの研究や実務の現場から指摘されているのが、「Lost in the Middle(中間の消失)」という現象です。LLMは、プロンプトの「冒頭」と「末尾」にある情報はよく認識しますが、中間部分に埋もれた情報は無視しがちであるという特性があります。
検索結果の上位10件をそのまま渡したとき、本当に重要な情報が5番目や6番目にあったとしたらどうなるでしょうか? 前後の大量のノイズ情報に埋もれ、LLMはその情報を「見なかったこと」にするかもしれません。つまり、情報を詰め込めば詰め込むほど、重要なシグナルがノイズに埋没し、回答精度(特にRecall:再現率)が低下するリスクが高まるのです。
最新の評価フレームワーク(Ragasなど)を用いた検証においても、コンテキスト内の関連情報の密度(Information Density)が下がると、LLMの推論能力が低下する傾向が報告されています。したがって、プロンプト圧縮は単なる「節約術」ではありません。LLMに正しく情報を認識させるための「品質向上施策」でもあるのです。
比較対象となるLangChainの主要圧縮テクニック
LangChainには、検索結果(Documents)をLLMに渡す前に処理するための ContextualCompressionRetriever というクラスが用意されています。これは「Base Retriever(元の検索機)」と「Document Compressor(圧縮機)」を組み合わせるラッパーです。
ここでは、実務でよく検討の遡上に上がる3つの主要な圧縮アプローチについて、その仕組みを解説します。
【速度重視】EmbeddingsFilter(類似度フィルタ)
最もシンプルで高速なアプローチです。これはLLMを使わずに、Embedding(ベクトル埋め込み)モデルを利用します。
- ユーザーのクエリと、検索された各ドキュメントの類似度スコアを計算する。
- あらかじめ設定した「閾値(Threshold)」以下のドキュメントを切り捨てる。
これだけです。非常に高速で、追加のLLM呼び出しコストもかかりません。いわば「検索後の再フィルタリング」に近いイメージです。明らかに無関係なドキュメントを弾くのに適しています。
【精度重視】LLMChainExtractor(要約抽出)
こちらは「圧縮」の名にふさわしい、より高度な処理を行います。取得した各ドキュメントに対してLLMを呼び出し、次のような指示を与えます。
「以下のドキュメントから、ユーザーの質問『〜』に関連する部分だけを抜き出してください。関連する部分がなければ何も返さないでください」
ドキュメント全体を渡すのではなく、本当に必要な「センテンス」や「段落」だけを抽出して再構築します。情報の密度は劇的に高まりますが、その分、処理は重くなります。
【バランス型】LLMChainFilter(適合判定)
Extractorと似ていますが、こちらは「抽出」ではなく「判定」を行います。LLMに対して、そのドキュメントが質問に答えるのに役立つかどうかをYes/Noで判定させます。
「このドキュメントは質問『〜』に関連していますか? YES/NOで答えてください」
関連すると判断されたドキュメントはそのまま(丸ごと)残し、関連しないものは捨てます。情報の書き換えを行わないため、文脈が分断されるリスクはExtractorより低いですが、圧縮率はドキュメント単位になります。
徹底比較:コスト・速度・精度のトレードオフ
さて、ここからが本題です。これら3つの手法、どれを選ぶべきでしょうか? 多くのエンジニアが「精度が高そうだから」という理由で安易に LLMChainExtractor を選びがちですが、この選択には注意が必要です。
それぞれの特性を、コスト、速度(レイテンシ)、精度の観点から比較分析してみましょう。
トークン削減率とAPIコストの試算
まず理解しておくべきは、「圧縮処理自体にコストがかかる」というパラドックスです。
EmbeddingsFilter:
- 追加コスト:ほぼゼロ(Embedding計算のみ)。
- 最終プロンプトコスト:削減率 中(ドキュメント単位で削除)。
LLMChainExtractor:
- 追加コスト:特大。検索されたドキュメントの数だけLLMを呼び出します。例えば10件のドキュメントがあれば、回答生成の前に10回のLLM呼び出し(入力トークン課金)が発生します。
- 最終プロンプトコスト:削減率 大(必要な文のみ残るため、最終的な回答生成時の入力は減る)。
ここで注意が必要です。Extractorを使うと、最終的な回答生成(Generation)の入力トークンは激減しますが、その前段階の圧縮フェーズで大量の入力トークンを消費しています。トータルで見ると、コスト削減どころか増大するケースが多々あります。「トークン削減」を謳っていますが、それはあくまで「最終プロンプトのトークン削減」であって、「総コストの削減」とは限らないのです。
レイテンシへの影響(LLM呼び出し回数の差)
ビジネスアプリケーションにおいて、さらに致命的なのがレイテンシ(待ち時間)です。
- EmbeddingsFilter: 追加遅延は数ミリ〜数十ミリ秒。無視できるレベルです。
- LLMChainExtractor / Filter: 検索結果が10件あれば、単純計算で10回のLLM推論が走ります。並列処理(非同期実行)させたとしても、最も遅いレスポンスに引きずられます。ユーザーが質問してから回答が返ってくるまでに、数秒〜十数秒の追加タイムラグが発生する可能性があります。
「待たされるチャットボット」ほど、ユーザー体験を損なうものはありません。
文脈保持能力と回答精度の変化
精度面でも、Extractorには落とし穴があります。LLMに「関連部分だけ抜き出せ」と指示すると、文脈(Context)が失われることがあるのです。
例えば、「A製品の価格は100円ですが、条件によっては異なります」という文から、「A製品の価格は100円」だけが抽出され、「条件によっては〜」という重要な制約事項がカットされるリスクがあります。これを防ぐためのプロンプト調整は非常に難易度が高い作業です。
一方、EmbeddingsFilterはドキュメントを丸ごと残すか捨てるかなので、ドキュメント内部の文脈は保たれます。ただし、Embeddingの類似度だけで判断するため、「キーワードは含んでいるが、意味が全く違う」ドキュメントを残してしまう(あるいは重要なのに捨ててしまう)リスクはあります。
| 手法 | 実装クラス | コスト効率 | レイテンシ | 文脈保持 | 向いている用途 |
|---|---|---|---|---|---|
| 類似度フィルタ | EmbeddingsFilter | ◎ (最高) | ◎ (高速) | 〇 | コスト・速度最優先、一般的なRAG |
| 要約抽出 | LLMChainExtractor | △ (悪化リスク) | △ (遅い) | △ (分断リスク) | 非常に長い文書からのピンポイント抽出 |
| 適合判定 | LLMChainFilter | △ (悪化リスク) | △ (遅い) | 〇 | 誤情報の混入を厳密に防ぎたい場合 |
ケーススタディ:あなたのプロジェクトに最適な手法は?
では、具体的なシナリオに基づいて、どの手法(あるいはその組み合わせ)を採用すべきか、推奨されるパターンをご紹介します。
ケース1:社内FAQボット(コスト最優先)
推奨:EmbeddingsFilter のみ
社内利用のボットであれば、多少のノイズが含まれていても、コストを抑えて高速にレスポンスを返すことが求められます。まずは EmbeddingsFilter で類似度の低い(例えば0.7以下など)ドキュメントをバッサリ切り捨てましょう。これだけで、無関係なドキュメントによるトークン浪費を大幅に防げます。
ケース2:契約書・法務ドキュメント分析(精度・詳細重視)
推奨:DocumentCompressorPipeline(EmbeddingsFilter + LLMChainExtractor)
契約書のように「一言一句が重要」で、かつ「文書が非常に長い」場合、単純な切り捨ては危険ですが、全部入れるとコンテキストが溢れます。
ここではパイプライン処理(Chain)が有効です。
- まず
EmbeddingsFilterで明らかに無関係な条項を捨てる(候補を絞る)。 - 残った候補に対してのみ
LLMChainExtractorを適用し、該当箇所を抽出する。
こうすることで、ExtractorにかけるLLMコストと時間を最小限に抑えつつ、精度の高い抽出が可能になります。いきなり全件Extractorにかけるのは避けましょう。
ケース3:リアルタイム接客AI(速度重視)
推奨:圧縮なし、または厳しめのEmbeddingsFilter + リランキング
ECサイトの接客など、リアルタイム性が命の場面では、LLMを使った圧縮は避けるべきです。代わりに、Cohere Rerank などの専用リランキングモデル(Reranker)の導入を検討してください。LangChainではこれも圧縮機の一種として扱えます。
リランキングモデルはLLMよりはるかに高速かつ安価で、Embedding(ベクトル検索)よりも高精度にドキュメントの関連順位を並べ替えてくれます。上位3件だけに絞ってLLMに渡す、といった戦略が最もバランスが良いでしょう。
導入前に確認すべきチェックリスト
技術選定の方向性が見えてきたところで、実際に実装を進める前に、以下のポイントを確認してください。これらを無視して導入すると、後で「なぜか精度が下がったが、原因がわからない」という泥沼にハマります。
1. 現在の平均トークン消費量の計測
「なんとなく高そう」ではなく、現状のRAGが1クエリあたり平均何トークン消費しているか、ログを取っていますか? LangSmithなどのトレーシングツールを使えば簡単に可視化できます。削減目標(例:1クエリあたり5円以内に抑える)を立ててから手法を選びましょう。
2. 許容可能なレイテンシの定義
ユーザーが待てるのは3秒ですか? 10秒ですか?
もし「3秒以内」が要件なら、LLMChainExtractorの導入は不可能です。ストリーミング表示をするとしても、最初のトークンが出るまでの待機時間(TTFT)は圧縮処理が終わるまで発生します。この「体感速度」への影響を甘く見てはいけません。
3. 実装の複雑さとメンテナンス性
圧縮プロセスを入れるということは、パイプラインが複雑になることを意味します。「回答がおかしい」というバグ報告があったとき、原因が「検索(Retriever)」にあるのか、「圧縮(Compressor)」で必要な情報を消してしまったのか、「生成(Generator)」の問題なのか、切り分けが難しくなります。
デバッグ時には、圧縮前と圧縮後のドキュメントをログに出力し、比較できるようにしておくことが必須です。LangChainの set_verbose(True) やコールバック機能を活用して、ブラックボックス化を防ぎましょう。
まとめ
RAGにおけるプロンプト圧縮は、コスト削減の特効薬にもなれば、パフォーマンスを悪化させる毒にもなり得ます。
重要なのは、「圧縮率」という単一の指標に惑わされず、プロジェクトの目的(コスト重視か、精度重視か、速度重視か)に合わせて適切な手法を組み合わせることです。
- まずは EmbeddingsFilter で安価にノイズをカットする。
- より高度な抽出が必要な場合のみ、対象を絞って LLMChainExtractor を使う。
- 速度と精度のバランスが必要なら Reranking を検討する。
これらを適切に使い分けることで、RAGシステムは「ただ動くもの」から「ビジネス価値を生み出す、持続可能なシステム」へと進化します。
もし、これらの設定によるコスト変動や応答速度の違いを実際のデータで検証したい場合は、専用の検証ツールやデモ環境を活用することをおすすめします。異なる圧縮パイプラインを切り替え、リアルタイムにトークン消費量と回答品質を比較検証できる環境を構築することで、プロジェクトに最適な構成解を見つけることができるでしょう。
コメント