{"notes":[{"id":"retrieval-augmented-generation/cag","category":"retrieval-augmented-generation","legacyPath":"","legacySlug":"","legacyPaths":[],"title":"CAG","subtitle":"覚えるほど得点アップ","author":"Z. Yuan","paperDate":"2024-12-20","publishedOn":"2026-07-19","paperTitle":"Don't Do RAG: When Cache-Augmented Generation is All You Need for Knowledge Tasks","paperUrl":"https://arxiv.org/abs/2412.15605","bibliography":{"title":"Don't Do RAG: When Cache-Augmented Generation is All You Need for Knowledge Tasks","authors":["Brian J Chan","Chao-Ting Chen","Jui-Hung Cheng","Hen-Hsen Huang"],"url":"https://arxiv.org/abs/2412.15605","pdfUrl":"https://arxiv.org/pdf/2412.15605v2","codeUrl":"https://github.com/hhhuang/CAG","preprint":{"id":"2412.15605","submittedOn":"2024-12-20","url":"https://arxiv.org/abs/2412.15605"},"publication":{"venue":"WWW Companion","year":2025,"kind":"conference","url":"https://doi.org/10.1145/3701716.3715490"},"citation":"@misc{retrievalaugmentedgenerationcag2024,\n  title = {{Don't Do RAG: When Cache-Augmented Generation is All You Need for Knowledge Tasks}},\n  author = {Brian J Chan and Chao-Ting Chen and Jui-Hung Cheng and Hen-Hsen Huang},\n  year = {2024},\n  eprint = {2412.15605},\n  archivePrefix = {arXiv},\n  url = {https://arxiv.org/abs/2412.15605}\n}\n","citationPath":"/content/papers/citations/retrieval-augmented-generation/cag.52cd8e1fb14c.bib"},"thumbnail":{"src":"/content/papers/assets/1ccb714a2b9e40fe07cb712c508763c4447c40b66c78a8c364a030957c65a789.png","width":694,"height":575},"locale":"ja","path":"/content/papers/ja/retrieval-augmented-generation/cag.5204febbb5e0.md","headings":[{"id":"問題の定義","text":"問題の定義","depth":2},{"id":"問題の解決","text":"問題の解決","depth":2},{"id":"読み終えた後に残るもの","text":"読み終えた後に残るもの","depth":3},{"id":"三つのステップ","text":"三つのステップ","depth":3},{"id":"実際のサイズを計算する","text":"実際のサイズを計算する","depth":3},{"id":"なぜ丸ごと入れるのか","text":"なぜ丸ごと入れるのか","depth":3},{"id":"考察","text":"考察","depth":2},{"id":"実験の比べ方","text":"実験の比べ方","depth":3},{"id":"精度多くで先行するが例外もある","text":"精度：多くで先行するが、例外もある","depth":3},{"id":"速度省けるのは読み直し","text":"速度：省けるのは読み直し","depth":3},{"id":"この論文が答えていないこと","text":"この論文が答えていないこと","depth":3},{"id":"結論","text":"結論","depth":2}],"anchorAliases":{},"guide":null,"relatedReading":[]}],"text":"「**RAGはやめよう**」。なかなか強気な題名です。\n\nRAG（Retrieval-Augmented Generation、検索拡張生成）は、言語モデルへ外部情報をつなぐ標準的な方法になりました。質問を受けたら知識ベースから関連する文章を探し、質問の前に貼り付けてモデルへ渡します。社内文書、製品FAQ、問い合わせ履歴などをチャットボットへ入れる際によく使われます。\n\n便利ですが、どの段階でも失敗する可能性があります。\n\n間違った段落を取れば、モデルは間違った材料で答えます。正しい資料でも半分しか取れなければ、答えも欠けます。文書分割、インデックス構築、top-kの調整、embeddingモデルの選択と、保守も楽ではありません。\n\n2024年にはオープンソースモデルでも十数万tokenを扱えるようになりました。そこで著者らは、直接的な問いを立てます。\n\n- **知識ベース全体が入るなら、なぜ先に検索する必要があるのか？**\n\n\n> [!TIP]\n> \n> **著者は誰？**\n> \n> 共同第一著者のBrian J Chan、Chao-Ting Chen、Jui-Hung Chengは国立政治大学資訊科学系、責任著者の黄瀚萱（Hen-Hsen Huang）は中央研究院資訊科学研究所の所属です。WWW Companion '25で発表され、本稿はarXiv v2に基づきます。コードは[**GitHub**](https://github.com/hhhuang/CAG)で公開されています。\n\n\n## 問題の定義\n\n知識ベースを丸ごとプロンプトへ入れる発想自体に、大きな新しさはありません。問題は費用です。\n\n85,000 tokenの知識ベースがあるとします。質問のたびに全文と質問を送り、モデルは最初のtokenから資料を読み終えて、ようやく回答を書き始めます。\n\n次の質問でも資料は変わっていないのに、最初から読み直します。\n\nこの「プロンプトを読む」処理が**prefill**です。長いほど時間がかかり、最初のtokenが表示されるまでの時間、TTFT（time to first token）も伸びます。\n\n著者らが本当に扱う問いは、次です。\n\n- **変わらない資料は、一度読むだけにできないか？**\n\n答えるには、読み終えたモデルの手元に何が残るかを知る必要があります。\n\n## 問題の解決\n\n### 読み終えた後に残るもの\n\n現在の言語モデルの多くはdecoder-only Transformerです。各層の自己注意は、各tokenをQuery、Key、Valueの三つのベクトルへ射影します。\n\n次のtokenを生成するとき、新しいQueryと**前の全tokenのKey**の類似度を計算し、**前の全tokenのValue**を重み付けします。\n\nここで重要なのは、前のtokenのKeyとValueは、一度計算したら変わらないことです。\n\nそのため保存して次のステップで再利用します。これが**KV cache**です。通常の逐次生成でも、再計算を避けるために使っています。\n\n\n> [!TIP]\n> \n> **なぜ前のKeyとValueは変わらないのか？**\n> \n> decoderは因果マスク（causal mask）を使い、各tokenは自分と前方しか見ません。後ろへ文章を加えても、前の計算結果は変わりません。\n> \n> 逆に、前方の文章を変えると、その後の全tokenのKeyとValueは再計算が必要です。後で制約を考える際に戻ります。\n\n\nCAG（Cache-Augmented Generation、キャッシュ拡張生成）は、この再利用を「一つの質問内」から「すべての質問間」へ広げます。\n\n- **知識ベース全体を一度読ませ、KV cacheを保存する。以後は質問だけを続けて読めばよい。**\n\n下図で、ピンクは推論のたびに動く部分です。\n\n[![RAGとCAGの流れ：RAGは推論時にIR Modelで検索し、CAGはオフラインでContext Cacheを作って推論時には質問だけを処理する](/content/papers/assets/1ccb714a2b9e40fe07cb712c508763c4447c40b66c78a8c364a030957c65a789.png)](/content/papers/assets/1ccb714a2b9e40fe07cb712c508763c4447c40b66c78a8c364a030957c65a789.png)\n\n*原論文Figure 1。[**v2 PDFの2ページ**](https://arxiv.org/pdf/2412.15605v2#page=2)から抜粋。上がRAG、下がCAGで、ピンクはオンライン推論時に動く要素です。*\n\n上のRAGは毎回IR Modelで知識ベースを検索し、Contextと質問$q$をLLMへ渡します。\n\n下のCAGはOffline Preloadingで知識ベースをContext Cacheへ変えます。推論時にピンクなのは質問$q$だけで、検索器が消えています。\n\n### 三つのステップ\n\n処理は三段階です。\n\n**第一段階：知識の事前読み込み**。文書集合$\\mathcal{D}=\\{d_1,d_2,\\dots\\}$を整理して一度読ませ、KV cacheを得ます。\n\n$$\nC_{KV}=\\text{KV-Encode}(\\mathcal{D})\n$$\n\nメモリに置いても、ディスクへ保存してもかまいません。その後に質問が何件来ても、文書を読む費用は一度だけです。\n\n**第二段階：推論**。$C_{KV}$を読み込み、質問$q$を接続します。\n\n$$\nr=\\mathcal{M}(\\mathcal{D}\\oplus q)=\\mathcal{M}(q\\mid C_{KV})\n$$\n\n左辺は文書と質問をつないで渡す計算、右辺はキャッシュを持ったモデルが質問だけを読む計算です。結果は同じで、文書部分を読み直す処理を省けます。\n\n**第三段階：キャッシュをリセット**。最初の質問に答えると、質問と回答のtokenもKV cacheへ追加されます。次の質問の前に追加分を切らないと、前の会話が見えてしまいます。\n\nKV cacheは後ろへ追加されるので、知識ベースを読み終えた長さを覚え、その後を切るだけです。ディスクからの再読み込みは不要です。\n\n### 実際のサイズを計算する\n\n「KV cacheを保存する」だけでは抽象的なので、論文で使ったLlama 3.1 8Bで計算します。\n\n[**Llama 3論文**](https://arxiv.org/abs/2407.21783)のTable 3によると、8Bモデルは32層、隠れ次元4096、attention headが32個です。grouped-query attentionによりKey/Valueのheadは8個だけで、各headは$4096/32=128$次元です。\n\n各層、各tokenで保存するKeyテンソルは、次の形です。\n\n$$\n[\\text{batch},\\ \\text{kv\\_heads},\\ \\text{seq\\_len},\\ \\text{head\\_dim}]=[1,\\ 8,\\ 1,\\ 128]\n$$\n\nValueも同じです。1 tokenを全層で保存する数値の個数は、\n\n$$\n2\\ (\\text{KとV})\\times 32\\ (\\text{層})\\times 8\\ (\\text{head})\\times 128\\ (\\text{次元})=65{,}536\n$$\n\n各値を16ビット（2 bytes）で持つと、1 tokenあたり128 KiBです。\n\n最大のHotPotQA条件は約85k tokenなので、\n\n$$\n85{,}000\\times 128\\ \\text{KiB}\\approx 11.1\\ \\text{GB}\n$$\n\nモデル重みは16ビットで約16 GBです。キャッシュだけで、その7割近くになります。\n\n\n> [!NOTE]\n> \n> モデル設定からの見積もりで、キャッシュを16ビットと仮定しています。論文には実測のキャッシュサイズやメモリ使用量の報告はありません。公式コードは既定で`torch.float16`を使い、4-bit量子化の選択肢もあります。\n\n\n形が分かると、公式コードのリセット処理も理解しやすくなります。\n\n```python\ndef clean_up(kv: DynamicCache, origin_len: int):\n    \"\"\"\n    Truncate the KV Cache to the original length.\n    \"\"\"\n    for i in range(len(kv.key_cache)):\n        kv.key_cache[i] = kv.key_cache[i][:, :, :origin_len, :]\n        kv.value_cache[i] = kv.value_cache[i][:, :, :origin_len, :]\n```\n\n*[**hhhuang/CAG**](https://github.com/hhhuang/CAG/blob/5c0d8ed6e28f34c72bb7208068f55cb3147d0c8c/kvcache.py#L120-L126)の`kvcache.py`より。MITライセンス。*\n\n`kv.key_cache[i]`は第$i$層のKeyで、形は`[1, 8, seq_len, 128]`です。`[:, :, :origin_len, :]`は第3次元、つまり系列長に沿って知識ベース部分だけを残します。\n\n事前読み込み完了時に`origin_len`を記録し、各質問の前にその長さへ戻します。リセット全体は、32層でそれぞれ2回スライスするだけです。\n\n### なぜ丸ごと入れるのか\n\nそれなら文書ごとに**別々に**キャッシュし、検索した分だけつなげればよいのでは、と思うかもしれません。\n\n先ほどの注意点が関係します。KeyとValueは、前の文章すべてと系列内の位置に依存します。文書Bを単独で計算すると、前にAがあることを知らず、位置番号も最初からです。単純に連結しても、A、Bを続けて読んだ結果とは異なります。\n\n前書きで触れられるTurboRAGは、各段落のKV cacheを事前計算するため、追加で位置IDを並べ直す必要があります。CAGはもっと単純です。**知識ベースが固定なら、全体を一緒に読む**。検索も、キャッシュを継ぎ合わせる問題もなくなります。\n\n代わりに、どこかの文章を変えると、その位置以後は再計算が必要です。\n\n\n> [!TIP]\n> \n> **これってprompt cachingでは？**\n> \n> GitHubでも[**似た質問**](https://github.com/hhhuang/CAG/issues/18)がありました。全文をsystem promptへ入れても結果は同じでは、というものです。\n> \n> 貢献者brian030128は、出力は同じだと答えています。違いは、プロンプトをKV cacheへ変換する処理が重いことです。CAGは先に計算してTTFTを節約します。その後には、API提供者のcontext cachingとの差を尋ねる質問もありました。\n> \n> 仕組みは非常に近いものです。[**Claudeのprompt caching**](https://platform.claude.com/docs/en/build-with-claude/prompt-caching)や[**Geminiのcontext caching**](https://ai.google.dev/gemini-api/docs/caching)も、繰り返す長いプロンプトを再利用できます。この論文の中心は、知識ベース全体の事前読み込みとキャッシュを、同じ質問応答でRAGと比較した点です。\n\n\n## 考察\n\n### 実験の比べ方\n\n二つのデータセットを使います。\n\n- **SQuAD 1.0**：答えが段落内の一節で、単一段落の位置特定が中心です。\n- **HotPotQA**：複数文書を組み合わせるマルチホップ推論です。\n\n知識ベースの拡大を見るため、各データを小・中・大へ分けます。HotPotQA smallなら16文書を抽出して一つの長い参考文へ連結し、その16文書に関係する問題だけを選びます。\n\n| データセット | 規模 | 文書数 | token数 | 問題数 |\n| --- | --- | ---: | ---: | ---: |\n| HotPotQA | Small | 16 | 21k | 1,392 |\n| HotPotQA | Medium | 32 | 43k | 1,056 |\n| HotPotQA | Large | 64 | 85k | 1,344 |\n| SQuAD | Small | 3 | 21k | 500 |\n| SQuAD | Medium | 4 | 32k | 500 |\n| SQuAD | Large | 7 | 50k | 500 |\n\n*原論文Table 1をもとに整理。*\n\nすべてLlama 3.1 8Bで回答し、Tesla V100 32GBを8枚使います。比較対象はLlamaIndexで作った二種類のRAGです。\n\n- **Sparse RAG**：BM25でキーワード一致と語の頻度から順位付けする。\n- **Dense RAG**：OpenAIのembeddingインデックスで意味の類似度から順位付けする。\n\n両方ともtop-1、3、5、10を試します。\n\n指標は**BERTScore**で、モデルの答えと参照解答の意味の近さを測ります。\n\n\n> [!TIP]\n> \n> **BERTScoreとは？**\n> \n> 二つの文章をBERT系モデルへ入れ、各tokenのベクトルを取り、対応させて類似度を計算します。「パリ」と「フランスの首都パリ」を、単純な文字列照合のように完全に別物としない利点があります。\n> \n> 測っているのは似ている度合いです。数字が一つ違うだけの誤答でも高くなる場合があります。以下の表で小数第3位の差を読むときは、慎重さが必要です。\n\n\n### 精度：多くで先行するが、例外もある\n\n[![原論文Table 2：Sparse・Dense RAGのtop-1、3、5、10とCAGについて、HotPotQAとSQuADの小・中・大でBERTScoreを比較](/content/papers/assets/13f5f729ad0290d70a0afcceaf6ec535895cb1017a05906e8dcfa0a9e91cae31.png)](/content/papers/assets/13f5f729ad0290d70a0afcceaf6ec535895cb1017a05906e8dcfa0a9e91cae31.png)\n\n*原論文Table 2。[**v2 PDFの4ページ**](https://arxiv.org/pdf/2412.15605v2#page=4)から抜粋。左がHotPotQA、右がSQuAD。太字が各条件の最高点です。*\n\n各群の最下行「CAG (Ours)」から見ます。\n\nSmallとMediumでは、両データセットともCAGが最高です。HotPotQA smallなら0.7951で、最良RAGのSparse top-5は0.7676でした。\n\nLargeでは、SQuADのCAGは0.7734で依然最高ですが、HotPotQAの太字はSparse RAG top-5の**0.7535**です。CAGは0.7407で、Sparse top-3の0.7463より低く、Dense top-3の0.7409とほぼ同じです。\n\n著者らも、データが大きくなると差が縮まると書き、長すぎる入力で長文モデルが劣化する研究を引用しています。\n\nもう一つ細部を見ます。前書きはRULERを引用し、Llama 3.1 8Bの**有効コンテキスト長を32K**としています。HotPotQA largeの約85kは、そこを大幅に超えています。CAGの敗因に関係する可能性がありますが、論文は分離して分析していません。ここは表を読む私たちの推測です。\n\n六条件のうち五つで、最良RAGはBM25でした。SQuAD mediumだけはDense top-10が0.0009上回ります。著者らは、この二つのデータセットは難しさが十分でなく、キーワード照合で必要な情報の大半を見つけられるのかもしれないと解釈しています。意味検索でなければ解けない難問が、比較対象にあまり含まれていないのです。\n\n### 速度：省けるのは読み直し\n\n時間も見ます。\n\n[![原論文Table 3：HotPotQAの三規模におけるRAG各設定、CAG、In-Context Learningの検索時間と生成時間](/content/papers/assets/fbdd7c78239b53372fb8d168a05e1cb9ffda9cfdc3c6301abb36dd2913389490.png)](/content/papers/assets/fbdd7c78239b53372fb8d168a05e1cb9ffda9cfdc3c6301abb36dd2913389490.png)\n\n*原論文Table 3。[**v2 PDFの4ページ**](https://arxiv.org/pdf/2412.15605v2#page=4)から抜粋。単位は秒で、Retrievalが検索、Generationが生成です。*\n\n**In-Context Learning**という比較が加わります。全文と質問を毎回送り、その都度KV cacheを計算します。問題の定義で見た、毎回最初から読む方法です。\n\nこの行とCAGの差が、事前キャッシュによる節約です。\n\n| HotPotQAの規模 | 毎回読み直す | CAG | 倍率 |\n| --- | ---: | ---: | ---: |\n| Small（21k） | 9.32秒 | 0.85秒 | 約11倍 |\n| Medium（43k） | 26.37秒 | 1.41秒 | 約19倍 |\n| Large（85k） | 92.08秒 | 2.26秒 | 約41倍 |\n\n知識ベースが大きいほど読み直しが高くつき、事前読み込みの利点も大きくなります。\n\nただしSparse RAGとの比較は違います。LargeのBM25 top-3は検索0.0008秒、生成0.67秒、合計1秒未満です。CAGは2.26秒です。BM25は短い3段落しか渡さず、処理するコンテキストが最初から短いのです。\n\nCAG自身も、知識ベースに応じて0.85、1.41、2.26秒へ増えます。読み直しを省いても、tokenを一つ生成するたび、注意は85k分のキャッシュ全体を参照します。\n\n表にない数字もあります。**キャッシュの構築時間**です。Table 3は事前読み込み後の1問あたり推論だけを測ります。公式コードは読み込み時間を記録しますが、論文に報告はありません。毎日更新する知識ベースなら重要な数字です。\n\n### この論文が答えていないこと\n\nLimitationsは明快です。関連文書すべてがコンテキストに入る必要があり、小規模企業の社内知識、FAQ、サポート窓口など、規模を管理できる場面に向きます。大きなデータセットでは現実的でなくなります。\n\n先ほどの導出から、実装上の問題も挙げられます。\n\n1. **文書が頻繁に更新される**。変更位置以後を再計算しますが、その更新コストは測定されていません。\n2. **利用者ごとに閲覧権限が違う**。一つのキャッシュが全知識を含むため、文書の組み合わせごとに別キャッシュが必要になる可能性があります。これは本稿の推測で、論文の議論にはありません。\n3. **キャッシュがモデルに依存する**。特定のモデル・版が計算した中間状態なので、モデルを替えると作り直しです。\n\n## 結論\n\nCAGの方法は一文にまとまります。**固定された知識ベースがコンテキストへ入るなら、一度読んでKV cacheを保存し、以後は質問だけを読む。**\n\n論文の設定では、次の結果でした。\n\n- **毎回読み直す場合より大幅に速い**。HotPotQA largeは92.08秒から2.26秒、約41倍です。\n- **精度は多くでRAG以上**。Small、MediumはCAGが最高、LargeのSQuADでも最高ですが、HotPotQAではBM25 top-5に負けます。\n- **軽量なBM25の方が速い場合もある**。短い3段落だけのRAGは合計1秒未満です。\n\n対象は一つの8Bモデル、最大約85k tokenの知識ベース、意味類似度指標BERTScoreです。\n\n題名は「RAGはやめよう」ですが、本文の答えは穏やかです。小さく安定し、繰り返し質問される知識ベースなら、検索を省いて先に覚える方法を検討できます。\n\n覚え切れないほど大きいなら、やはり調べに行きましょう。\n","imageSizes":{"/content/papers/assets/1ccb714a2b9e40fe07cb712c508763c4447c40b66c78a8c364a030957c65a789.png":{"width":694,"height":575},"/content/papers/assets/13f5f729ad0290d70a0afcceaf6ec535895cb1017a05906e8dcfa0a9e91cae31.png":{"width":694,"height":1028},"/content/papers/assets/fbdd7c78239b53372fb8d168a05e1cb9ffda9cfdc3c6301abb36dd2913389490.png":{"width":689,"height":717}}}
