AI Navigate

セマンティックキャッシュとは何か

Dev.to / 2026/3/17

💬 オピニオンDeveloper Stack & InfrastructureTools & Practical Usage

要点

  • 本記事は、意味に基づく類似性を用いて高価なLLM APIを繰り返し呼び出すことを避ける方法を説明します。
  • 従来のキャッシュは厳密な入力一致(LRUやLFU)に依存しており、非決定的な出力には対応できないため、意味理解がないままではRAG/生成系AIワークロードには適していません。
  • セマンティックキャッシングは、一般的なRAGパイプラインの中で意味的な類似性を測定(コサイン類似度などを用いる)してキャッシュヒットを判定することで、より効果的な結果の再利用を実現します。
  • Geminiの価格を用いた具体的なコスト例を提示し、キャッシュが中程度に活発なアプリケーションの月間API費用をどのように削減できるかを示します。

生成系AIのアプリケーションが増えるにつれて、その欠点がより明らかになります。LLMの大きな問題の一つは、各クエリのコストが高いことです。例えば Gemini — Gemini 2.5 Pro は入力トークン100万あたり1.25ドル、出力トークン100万あたり10ドルです。彼らの主力製品 Gemini 3.1 Pro はそれぞれ100万トークンあたり2ドルと12ドルに倍増します。アクティブなアプリでも月々数千ドルに達することがかなり早く起こりえます。日間500人のユーザーしかいない小さなカスタマーサポートボットを想像してみてください。2か月目には、API料金が静かに2,000ドルを超えています。これはエッジケースではなく、キャッシュを使っていないときに起こることです。ビジネス(または個人ユーザー)として、コストを削減し、運用を高速化することは、製品の成功を左右する非常に重要な要因です。コストを速く下げ、かつコストを抑える一つの方法として、シンプルな 'semantic cache' を使うことがあります。

それが何か

セマンティックキャッシュは従来のキャッシュと大差ありません。その背後にある考え方は同じです。通常、従来のキャッシュはLRU(Least Recently Used、最も最近使用されたデータ)またはLFU(Least Frequently Used、最も頻繁に使用されたデータ)を保存しており、同じクエリが来たときには検索せずに保存された結果を取得します。

ただし、RAGやgenAI製品に対して、出力が「決定論的」でない、つまり同じではないという理由だけで、全く同じパイプラインを適用することはできません。次の例を見てください:

職場でのAIの状況は何ですか?

AIツールは職場にどのような影響を及ぼしていますか?

意味的にはこれらは使用するには十分に似ており、ほぼ同じ意味を指すと理解できます。しかし、通常のキャッシュはそれを理解しません。どちらも「同じである」とは思わず、厳密には同じではないため異なるとみなします。

ここでセマンティックキャッシュが活用されます。直接比較するのではなく、それらの意味的な意味を比較し、それらはほぼ同じだと理解してキャッシュヒットを得ます。通常、2つの文書がどれだけ似ているかはコサイン類似度で判断します。

仕組み

これはセマンティックキャッシュを使用するRAGシステムの典型的なパイプラインです。

まず文書を分割して埋め込みベクトルに変換します。もちろんそれらを Chroma のようなベクトルデータベース、FAISS など、用途に適したものに格納します。ユーザーがクエリを送信した後、データベースには行きません。代わりにまずセマンティックキャッシュを確認します。キャッシュされたクエリに対してクエリが関連しているかを見ます。

この時点で次の2つのことが起こり得ます:

キャッシュヒット: クエリがキャッシュされたものに十分に似ている(閾値を上回る)場合 → キャッシュされたコンテキストが引き出され、LLMに渡されて応答が生成されます。高速で安価、データベースの検索は不要です。

キャッシュミス: キャッシュに類似したものが何もない場合 → 通常のベクトルDB取得が行われ → 関連するチャンクを取得し、応答を生成し、次回のために新しいクエリをキャッシュします。通常の速度ですが、キャッシュは温まっています。

語彙埋め込みはコサイン類似度を用いて比較されます:

cosine(θ) = (A · B) / (||A|| × ||B||)

これはベクトルの方向間の角度を非常に速く、シンプルに見る方法です。似ていれば、方向も似た方向を向くことになり、すなわち角度は小さく、その角度の cos は高くなります。出力は0から1の範囲で、0は全く似ていないことを意味し、1はもちろん全く同じであることを意味します。

例えば:

  • "AIが雇用に与える影響は?" vs "AIは雇用をどう変えているのか?" → スコア約0.91 → キャッシュヒット
  • "AIが雇用に与える影響は?" vs "サワードウブレッドの焼き方は?" → スコア約0.08 → キャッシュミス

最初の2つは精神的には明らかに同じ質問であり、スコアもそれを反映しています。

なぜ使うのか

  1. コストの大幅な削減。ベクトルDBへのクエリを削減することで、発生する料金の大半を削減できます。
  2. 応答時間の高速化。すでにキャッシュ済みのコンテンツを持っている場合、再取得は不要です。これにより、実運用でシステムをかなり高速化できます。
  3. リソースのより良い活用。似たようなクエリを再実行する必要がなくなるため、システムはより多くのタスクを実行でき、より良くスケールしたり、より複雑な機能に対応できます。

RAGにおける他のアプローチとの比較

アプローチ 意味的類似性の取り扱い コスト削減 速度向上 設定の複雑さ ユニークなクエリへの対応 適した用途
従来のキャッシュ いいえ(厳密一致のみ) 高い(ヒット時) 非常に高い 低い いいえ 繰り返しの厳密なクエリを扱う大規模アプリ
セマンティックキャッシュ はい 高い 高い いいえ 重複しつつも多様なクエリパターンを持つアプリ
クエリのリライト 部分的 低い 低い(ステップ追加) はい 曖昧または表現が不十分なクエリの検索を改善
再ランキング いいえ 低い いいえ(遅延を追加) はい 検索の取得が良好でも並び順が崩れている場合の関連性向上
ハイブリッド検索 部分的 低い 中程度 高い はい キーワード検索と意味検索の両方を必要とする複雑なドメイン
チャンク化最適化 いいえ 中程度 中程度 低〜中 はい ソースで検索品質を向上

ご覧のとおり、セマンティックキャッシュは万能の解決策ではありません。ユーザーが送るクエリの種類にある程度の重複がある場合に効果を発揮します。より多様でユニークなクエリパターンの場合、再ランキングやハイブリッド検索のようなアプローチの方が適しているかもしれません。

デメリット

  1. 従来のキャッシュシステムよりも構築が複雑です。
  2. 意味的に類似するチャンクを取得する可能性が高く、クエリへの回答に関連性がない、または有用でない場合があります。司書に「宇宙旅行についての本」と頼んで、以前の「宇宙探査についての本」というクエリからキャッシュされた推奨を受けるようなものです。表面上は近いですが、次に「宇宙旅行の健康リスクについての本」を尋ねると、クエリが似て見えるため同じ探検関係の本がキャッシュとして提供され続けることがあり、実際に求めているものはかなり異なることがあります。
  3. 閾値のバランスを取る必要があります。閾値が高すぎると有用なチャンクが得られず、低すぎると意味的に類似したチャンクが得られないことがあり、システムの性能を低下させます。適切なバランスを見つけることが重要です。
  4. 空のキャッシュは遅く、待機時間が長いです。
  5. すべてのユーザーのクエリがユニークな場合には適していません。

いつ使用しない方が良いか

セマンティックキャッシングは常に適したツールとは限りません。以下の場合は避けてください:

  • ユーザーが送るすべてのクエリがユニークです。コード生成、法的リサーチ、または非常に個別化されたものを想像してください。キャッシュはほとんどヒットせず、ただオーバーヘッドを増やすだけです。
  • アプリのトラフィックが少ない場合。1日あたり数件のクエリしか来ない場合、実質的な利点はありません。
  • 知識ベースが継続的に変更されている場合。文書が常に更新されている場合、キャッシュを無効化する時間の方が利益より長くなります。
  • 正確さは妥協できません。キャッシュされたコンテキストは少しずれていることがあります。少し間違っている方が遅いより悪いケースでは、キャッシュしないでください。

活用する最善の方法

  1. 閾値を慎重に調整します。良い開始点は0.85〜0.90のどこかです。そこから、特定の使用ケースに基づいて調整し、品質を監視します。ここに普遍的な正解はありません。
  2. TTL(Time To Live)の値を使用します。キャッシュされたエントリは期限切れにすべきです。特に基盤データが変更された場合やトピックが時機的である場合。陳腐化したキャッシュはキャッシュがない場合より悪いです。
  3. キャッシュを温めます。一般的または予想されるクエリで事前に格納しておくことで、実運用で完全にコールドスタートすることを避けられます。コールドキャッシュは恩恵を全くもたらしません。
  4. 知識ベースが更新されたときには無効化します。ベクトルDB内の文書が変更されると、古いチャンクに基づくキャッシュレスポンスは、あなたが気づかないうちに出力品質を静かに低下させることがあります。
  5. ヒット率を監視します。健全なセマンティックキャッシュは通常、約30〜60%のヒット率を示します。低すぎると閾値が厳しすぎる可能性があり、逆に高すぎて品質が低下している場合は、緩すぎることを意味します。
  • スコープについて考える — グローバルとユーザーレベルのキャッシュ。グローバルキャッシュは最も多くを保存しますが、非常に異なるユーザー文脈間で不一致のキャッシュ結果を提供することがあります。パーソナライズされたアプリケーションでは、効率が悪くても、ユーザー‑スコープのキャッシュの方が意味を成すことがあるかもしれません。
  • すでにこれを実装しているツール

    ゼロから作る必要はありません。いくつかのライブラリにはセマンティックキャッシュが組み込まれているか、容易にプラグインできます:

    • GPTCache — LLMのレスポンスをキャッシュするために特化して構築されたオープンソースライブラリ。かなり柔軟で、独自のパイプラインを自分で組んでいる場合は見る価値があります。
    • LangChain — 既存のチェーンにそれほど労力をかけずに組み込めるキャッシュ層を備えています。すでに使用している場合には良い出発点です。
    • Redis — ベクトル類似性拡張機能を備えた Redis は、高速なセマンティックキャッシュ層として機能します。特にスタックで既に使用している場合には有効です。

    これらが自作する前に存在することを知っておく価値があります。車輪を自分で作る前に。