0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【基礎編】RAGって実際どんな仕組み?

0
Last updated at Posted at 2026-07-30

この記事について

📚 RAG入門シリーズ

  • 【基礎編】 RAGって実際どんな仕組み? ← いまここ
  • 【詳細編】 RAGはモデルの"中"で何をしているのか ― hidden state と層の話

RAG(Retrieval-Augmented Generation)は「LLMに社内文書を読ませる仕組み」くらいの説明で済まされがちですが、実際に作ろうとすると急に分からなくなります。

  • 検索した文書は、モデルのどこに入るのか?
  • モデルの中身(重み)は変わっているのか?
  • 普通にプロンプトに貼り付けるのと何が違うのか?
  • ベクトルDBって必須なの?

この記事は、そのあたりを 「実際に何が動いているか」 のレベルで整理したものです。

対象読者:RAGという言葉は知っているが、中で何が起きているかは曖昧、という方。

このページの要点

長いので、要点を先に記します。

スクリーンショット 2026-07-31 3.30.58.png

1. RAGとは何か

定義

RAGは3つの単語でできています。

単語 意味 やっていること
Retrieval 検索 質問に関連する情報を外部の知識源から探す
Augmented 拡張 探した情報を質問と一緒にプロンプトへ連結する
Generation 生成 その合成プロンプトを使ってLLMが答えを作る

そして最も重要な性質がこれです。

RAGはモデルを再学習しない。重みは1バイトも変わらない。

IBM・AWS・Google Cloud などの公式解説でも、RAGは一貫して「再学習なしでLLMを外部の知識ベースに接続する仕組み」として説明されています。ファインチューニングとの決定的な違いがここです。

補足:よくある誤解①:「RAGはAIエージェントのToolUseの一種」

順序が逆です。

RAGは Lewis et al. (2020, Meta AI / University College London / New York University) の論文が起源で、エージェントやToolUseというパラダイムより前から存在します

古典的なRAGは「必ず検索してから生成する」固定パイプラインであって、LLMが呼ぶかどうかを判断するツールではありません。

古典的RAG(固定パイプライン):
  質問 → [必ず検索] → [必ず連結] → 生成

Agentic RAG(後発の派生形):
  質問 → LLMが「検索が必要か」を判断 → 必要なら検索ツールを呼ぶ → 生成

エージェントの文脈で検索をツールとして実装する形(Agentic RAG)は確かに一般的になっていますが、それはRAGの一形態であって、RAGの定義ではありません。

2. 【最重要】モデルはRAGと手打ちプロンプトを区別していない

ここが腑に落ちると、RAGの全体像が一気に見通せます。

LLMへの入力は、最終的にはただ1本のトークン列です。

そのトークン列が、

  • あなたが手でキーボードから打ったものなのか、
  • 検索システムが自動で取ってきた文書を連結したものなのか、

モデルには区別がつきませんし、区別する必要もありません。

手打ち:  [質問 + あなたが貼った資料] ──┐
                                      ├──► 同じトークン列としてモデルへ
RAG:    [質問 + 検索が取ってきた資料] ──┘

「RAG専用の特別な入力ポート」は存在しません。検索文書も、手で貼り付けたテキストとまったく同じ入り口から入っていきます。

つまり —

「RAGか手打ちか」はモデル内部の話ではなく、入力トークン列を誰がどう用意したかという“外側”の話。

補足:よくある誤解②:「Prompt BuilderはLLMの内部機構」

違います。プロンプトの組み立ては100%アプリケーション側の処理です。

LangChain / LlamaIndex / あるいは自前のコードが、検索結果とクエリを文字列連結しているだけです。実体はこんなものです。

# これがいわゆる "Prompt Builder" の正体
prompt = f"""以下のコンテキストに基づいて質問に答えてください。
コンテキストに答えがない場合は「わかりません」と答えてください。

# コンテキスト
{retrieved_chunks}

# 質問
{user_query}
"""

LLMが受け取るのは、この組み上がった後の文字列(→トークン列)だけです。LLMは「どこまでが検索結果でどこからが質問か」を、テキストとして書かれている以上のことは知りません。

この誤解を持ったままだと、「RAGはLLMがやってくれる機能」と思ってしまい、RAGの本質(=外側の仕組み)が真逆に伝わります

実用上の違いはどこに出るか

入力が同じなら結果も同じ。では何のためのRAGなのか。

観点 手打ち RAG
スケール 人が探せる範囲だけ 数万〜数百万件のDBから、質問ごとに関連分だけ動的に選ぶ
鮮度 人が毎回探す必要 最新のDBから自動で取得
再現性 人によってブレる パイプラインとして固定できる
弱点 検索がズレると全部ズレる

最後の点が最重要です。RAGの失敗の多くは、生成側ではなく検索側で起きます。 見当違いの資料を渡せば、手打ちで間違った資料を貼ったのと同じで、答えも引きずられます。生成プロンプトをいくら磨いても、検索がズレていれば直りません。

3. RAGは2つのフェーズでできている

ここから実装の話です。RAGには時間軸の違う2つのフェーズがあります。ここを分けずに理解しようとすると混乱します。

【フェーズ①】インデックス構築(事前・バッチ)
  文書 → 前処理 → チャンク分割 → ベクトル化 → DBに格納

【フェーズ②】検索と生成(実行時・ユーザーのリクエストごと)
  質問 → ベクトル化 → 類似検索 → プロンプト組み立て → LLM生成

フェーズ①は「図書館に本を並べて索引を作る」作業、フェーズ②は「質問されたら索引を引いて本を持ってくる」作業です。①は事前に済ませておきます。

4. フェーズ①:インデックス構築

全体の流れ

スクリーンショット 2026-07-31 3.24.18.png

ステップ1:前処理とチャンク分割

あらゆる入力をテキスト文字列に正規化し、チャンクという単位に分割します。

チャンクサイズの例: 512 トークン / オーバーラップ 50 トークン

入力データ例

  • テキスト (Markdown/CSV)
  • PDF / Word / HTML
  • 画像 (PNG/JPEG)
  • RDBレコード
  • JSON

なぜ分割するのか:文書まるごとをベクトル化すると、意味が薄まって検索精度が落ちます。また、LLMのコンテキストに入る量にも限りがあります。

なぜオーバーラップするのか:切れ目で文脈が途切れるのを防ぐためです。「〜については、」で切れると、続きのチャンクだけ拾っても意味が通りません。

チャンク設計はRAGの精度を最も左右するパラメータの一つです。「とりあえず512」で始めて、あとで調整するのが実務的です。

ステップ2:Embedding Modelでベクトル化

分割したチャンクを、Embedding Model という深層学習モデルで固定長のベクトルに変換します。

補足:よくある誤解③:「Embedding ModelはLLMではない」

数年前までは概ね正しい説明でした(BERT系のエンコーダモデルが主流だったため)。しかし今は成り立ちません。

MTEBベンチマークの上位を占める NV-Embed-v2、E5-Mistral、SFR-Embedding、gte-Qwen2、Qwen3-Embedding などは、いずれも Mistral や Llama、Qwen といったデコーダ型LLMを出発点に、プーリング層を足して対照学習でファインチューニングしたものです。この手法が近年の性能向上を牽引しました。

正確な言い方はこうです。

Embedding Model は、回答生成用のLLMとは役割の異なる別モデル
BERT系エンコーダが伝統的だが、近年はLLMベースのものも主流。

「LLMではない」ではなく「生成用LLMとは別のモデル」と言えば、伝えたい趣旨を保ったまま正確になります。

Embedding Model の中身

中で何が起きているかを開けるとこうです。

テキスト
  │
  ▼
[Tokenize]           サブワードに分割 → トークンID列
  │
  ▼
[埋め込み + 位置エンコーディング]   ID → ベクトル(★ここで初めて数値になる)
  │
  ▼
┌─────────────────────────────┐
│ [Self-Attention]  文脈を加味  │
│         ↓                    │  × N層(12層 / 24層 など)  ★繰り返す
│ [FFN 層]          意味変換    │
└─────────────────────────────┘
  │
  ▼
[Pooling]            トークン数ぶんのベクトル → 1本に圧縮
  │                  (mean pooling / CLSトークン / last-token pooling)
  ▼
[正規化]             長さ1のベクトルに揃える
  │
  ▼
1024次元などのベクトル(モデルにより 768〜4096 が一般的)

押さえるべき2点:

  1. Self-Attention → FFN のブロックは1回ではなく、N層繰り返されます。 図解でここが1回に描かれていることが多いのですが、実際は12層・24層と積み重なっています。
  2. 層を流れているのはトークンではなく、hidden state(連続値のベクトル)です。 トークンIDが使われるのは一番下の埋め込み層で1回だけ。以降は「1トークン位置につきベクトル1本」が、層を上がるたびに中身を変えながら流れていきます。

Pooling が必要な理由:Transformerは「トークンごとに1本」のベクトルを出しますが、チャンク全体を1本のベクトルとしてDBに入れたい。そこで平均を取るなどして1本に潰します。

正規化が効いてくる理由:長さ1に揃えておくと、コサイン類似度と内積が同じ順位を返すようになり、計算が効率化できます(後述)。

画像はどう扱うか(3つの選択肢)

「画像 → OCR → テキスト」だけが選択肢ではありません。実際は3系統あります。

方式 やり方 特徴
(a) テキスト化 OCR や VLM でキャプション生成してから埋め込み 既存のテキストRAG基盤をそのまま使える。最も枯れている。OCR品質に依存
(b) マルチモーダル埋め込み CLIP / SigLIP などで画像とテキストを同じベクトル空間 「売上推移グラフ」という語の埋め込みが、実際のグラフ画像の埋め込みと近くなる。図表の判別は苦手な面も
(c) ページ画像を直接 ColPali などでページを視覚的な実体として処理 OCR不要でレイアウトを保持。図表の多い文書に強い。GPUと実装コストが必要

最初は (a) で十分です。「画像はOCRしてテキスト化し、元画像へのリンクをメタデータに持たせる」構成が実装しやすくおすすめです。

ステップ3:Vector DBに格納

CREATE TABLE documents (
  id       uuid PRIMARY KEY,
  vector   vector(1024),   -- ベクトル本体
  content  text,           -- ★元のチャンクテキスト(必須!)
  meta     jsonb           -- 出典URL、ページ番号、更新日など
);

content を保存し忘れないでください。 ベクトルだけ持っていても、検索でヒットした後にLLMへ渡すテキストがありません。ベクトルは「検索するための鍵」であって、「渡す中身」ではありません。ここを落とす設計図は意外と多いです。

meta も重要で、出典表示(引用元リンク)やフィルタリング(部署別、期間別)に使います。

補足:よくある誤解④:「RAG=ベクトルDB」

ベクトル検索はRAGの一形態にすぎません。

  • キーワード検索(BM25):固有名詞や型番の完全一致に強い。ベクトル検索が苦手な領域
  • ハイブリッド検索:ベクトル + BM25 を組み合わせる。実務ではこれが定番になりつつある
  • ナレッジグラフ(GraphRAG):エンティティ間の関係をたどる

「意味が近い」を拾うのがベクトル検索、「その語がある」を拾うのがキーワード検索です。型番や社内用語が飛び交う現場では、ベクトル検索だけだと驚くほど拾えません。 ハイブリッドを検討してください。

5. フェーズ②:検索と生成

スクリーンショット 2026-07-31 3.21.57.png

[1] ユーザーの質問
      │
      ▼
[2] Embedding Model でベクトル化   ← ★フェーズ①と「同じモデル」を使う
      │
      ▼
[3] Retriever:類似度で Top-k 件を検索
      │
      ▼
[4] (任意)Reranker で並べ替え
      │
      ▼
[5] Prompt Builder:質問 + チャンクを連結  ← ★アプリ側の処理
      │
      ▼
[6] LLM が回答を生成

ステップ1:ユーザーの質問を受け取る

ここがフェーズ②の起点です。フェーズ①(インデックス構築)は事前にバッチで済ませてありますが、フェーズ②はユーザーのリクエストが来るたびに毎回走ります。

この違いは実装上けっこう重要で、フェーズ②の各ステップはすべて応答レイテンシに直接乗ります。リランクを入れるかどうかの判断も、「精度は上がるが、その分ユーザーを待たせる」というトレードオフとして考えることになります。

なお、実務では質問をそのままベクトル化せず、前処理を挟むこともあります。

  • クエリ書き換え:曖昧な質問を検索向きに整形する
  • 会話履歴の統合:「それって何年から?」のような指示語を、履歴から具体化する(マルチターンでは必須)
  • クエリ拡張:複数の言い換えを生成して、それぞれで検索する

最初は素通しで構いませんが、チャットボット形式にする場合は会話履歴の統合だけは避けて通れません。 「それ」「その件」が何を指すか分からないままベクトル化しても、検索は当たりません。

ステップ2:クエリも同じEmbedding Modelで

これは絶対条件です。 インデックス構築に使ったのと同じEmbedding Model でクエリをベクトル化しなければなりません。

モデルが違えばベクトル空間が違い、距離の比較そのものが無意味になります。「埋め込みモデルを新しいものに差し替えたい」場合は、**既存インデックスを全件作り直す(re-embedding)**必要があります。運用設計時に必ず意識してください。

ステップ3:類似度検索

補足:よくある誤解⑤:「コサイン類似度で比較する」

コサインは代表的ですが、唯一ではありません。pgvector は3種類の距離をサポートしています。

演算子 距離 特徴
<=> コサイン距離 ベクトルの「向き」だけを見る。大きさを無視
<-> L2距離(ユークリッド) 直線距離
<#> 内積(負の値を返す) 正規化済みベクトルなら最も効率的

正規化済みの埋め込み(OpenAIのモデルなど)では、内積とコサインは同じ順位を返します。 その場合、内積のほうが計算が軽くて有利です。

⚠️ 実務でハマる罠:演算子とインデックスの不一致

これは知らないと2週間溶かします。

vector_cosine_ops でHNSWインデックスを作りながら、クエリで <->(L2)を使うと、PostgreSQLは黙ってインデックスを無視してシーケンシャルスキャンに落ちます。 エラーも警告も出ません。ただ遅くて、期待と違う順序の結果が返ってくるだけです。

-- インデックスとクエリの演算子は必ず揃える
CREATE INDEX ON documents USING hnsw (vector vector_cosine_ops);
SELECT content FROM documents ORDER BY vector <=> $1 LIMIT 5;  -- ← <=> で揃える

EXPLAIN ANALYZEIndex Scan になっていれば正しく、Seq Scan なら不一致です。

ANNインデックス(HNSW / IVFFlat)

全件と総当たりで比較すると遅いので、近似最近傍探索(ANN)を使います。pgvector は HNSWIVFFlat を提供しています。多少の再現率を犠牲に、大幅な高速化と引き換えます。

HNSWでは ef_search パラメータが精度に効きます。デフォルトから引き上げると、距離関数を変えるより検索品質が改善することもあります(当然、レイテンシとのトレードオフです)。

ステップ4:リランク(推奨)

Top-k で取ってきた候補を、より高精度なモデル(Cross-Encoder)で並べ替える工程です。

  • ベクトル検索:速いが粗い → 広めに30件取る
  • リランカー:遅いが精密 → その中から本当に良い5件を選ぶ

精度改善のコスパが非常に良いので、精度に困ったらチャンクサイズの調整より先に試す価値があります。

ステップ5:プロンプト組み立て(アプリ側)

前述のとおり、ここはアプリケーションの仕事です。定番のテンプレートはこんな形です。

以下のコンテキストのみに基づいて質問に答えてください。
コンテキストに答えが含まれていない場合は「わかりません」と答えてください。

# コンテキスト
{取得したチャンク(出典付き)}

# 質問
{ユーザーの質問}

「コンテキストにない場合はわからないと答えろ」という指示が重要です。これがないと、モデルは自分の内部知識で埋めにいきます(次章)。

ステップ6:LLMが生成する

注:コピーではない

最後に、よくある誤解をもう一つ。

通常のRAGは、検索したトークンを出力にコピーしているわけではありません。

LLMは検索文書を「情報源」として読み、その意味・事実を根拠に、語彙全体から新しくトークンを生成します。

方式 動作
通常のRAG 資料を読んで、自分の言葉で答えを書く
抽出型QA / コピー機構 資料の該当箇所をそのまま抜き出す(別設計)

固有名詞や数値が文書と一致することはよくありますが、それは「コピーした」のではなく「読んで、それに基づいて書いた」結果です。

6. 【重要】入力に入れても、必ず従うとは限らない

RAGを実運用すると必ずぶつかる問題です。

モデルが検索文書を無視して、自分の内部知識(=重みが覚えている知識)で答えてしまうことがあります。 これは 知識衝突(knowledge conflict) あるいは context-unfaithfulness として研究されている課題です。

サーベイ論文の結論は端的です。

文脈と重み、どちらを優先するかに決定的なルールは存在しない。
ただしモデルは、意味的に整合していて論理的で説得力のある情報を選ぶ傾向がある。

さらに厄介なことに、強力なモデルほど、正しい外部証拠が与えられていても自分の内部記憶に固執する場合があるという報告もあります。

実務での対処

  • プロンプトで「コンテキストのみに基づいて答えよ」と明示する
  • 「コンテキストにない場合はわからないと答えよ」を入れる
  • 出典を必ず併記させ、人間が検証できるようにする
  • 評価データセットを作り、context-faithfulness を継続的に測る

「検索文書を渡したのだから、必ずそれに従うはずだ」という前提を置かないこと。 これが実務でいちばん大事な心構えかもしれません。

7. 【発展】検索を「モデルの中」に入れる方式もある

ここまでの話は「プロンプトに連結する」標準的なRAGでした。実は、検索結果をモデルの中間層に直接注入するアーキテクチャも存在します。興味があれば、という位置づけで紹介します。

手法 統合する場所
RETRO(DeepMind, 2022) 特定の中間層に chunked cross-attention を挿入し、検索チャンクを直接参照させる
kNN-LM(2020) 出力確率分布のレベルで、最近傍検索の結果と補間する
FiD(Fusion-in-Decoder, 2021) パッセージを個別にエンコードし、デコーダで融合する

RETROが注目されたのは、事前学習済みモデルの重みを凍結したまま cross-attention 層を追加する(論文では6層目から3層ごと)方式で、GPT-3級の性能を約25分の1のパラメータ数で達成したためです。

ただし実務でRAGと言えばほぼ100%、標準的な「プロンプトに連結する」方式です。まずはそちらを固めてください。

なお:標準RAGでも「中の層」には影響している

余談ですが、標準的なRAGでも検索文書は全層のhidden stateに影響しています。Transformerの self-attention が系列内の全トークンを混ぜ合わせるため、質問・回答位置のベクトルは各層で検索文書を参照するからです。

ただしこれは重みが変わったからではなく、入力が変わったことがattention経由で全層に波及しているだけです。RETRO系のような構造的な介入とは、原理が別物です。

📖 このあたり(hidden state、層をまたぐ流れ、RETRO系との違い)は、詳細編でじっくり扱っています。

8. まとめ

【設計として押さえること】
  □ RAGは重みを変えない。ファインチューニングとは別物
  □ プロンプト組み立てはアプリ側の仕事。LLMの機能ではない
  □ モデルはRAGか手打ちかを区別していない
  □ フェーズ①(事前のインデックス構築)と②(実行時の検索・生成)を分けて考える

【実装で落とさないこと】
  □ Vector DB に content(元テキスト)を保存する
  □ クエリはインデックスと同じ Embedding Model でベクトル化する
  □ インデックスの演算子クラスとクエリの演算子を揃える
  □ ベクトル検索一択にせず、ハイブリッド検索を検討する

【運用で意識すること】
  □ 精度が出ないときは、まず検索側を疑う(生成側ではない)
  □ リランクはコスパの良い改善策
  □ 文脈を渡しても必ず従うとは限らない(知識衝突)
  □ 出典を併記させ、人間が検証できる状態にする

ひとことで言えば、RAGとは —

LLMという「読んで書ける有能なエンジン」に、検索してきた文書という「事実の素材」を、プロンプト経由で手渡す仕組み。

エンジン(重み)は変わらない。素材は外から来る。手渡す作業をするのは、あなたが書くアプリケーションコードです。

次に読む:詳細編

この記事では、RAGを外側の仕組みとして組み立てました。インデックスの作り方、ベクトル検索、プロンプトの組み立て — どれもモデルの外側の話です。

詳細編では、そこからモデルの内側に降ります。

  • 検索文書は途中の層や hidden state にどう影響するのか
  • 層を流れているのはトークンではなく何なのか
  • 検索したトークンをコピーしているわけではない、とはどういうことか
  • RETRO / kNN-LM / FiD が標準RAGと何が違うのか
  • なぜ「文脈に入れても必ず従うとは限らない」ことが起きるのか
[基礎編:あなたが作る領域]              [詳細編:モデルがやる領域]
文書 → チャンク → ベクトル化 → 検索  →  トークン列 → 全層に波及 → 生成
                                    ↑
                              ここが境界線

【詳細編】 RAGはモデルの"中"で何をしているのか ― hidden state と層の話

実装だけなら基礎編で足ります。ただ、この境界線がどこにあるかを掴んでおくと、精度が出ないときにどちら側を疑えばいいかの判断が速くなります。

参考・出典

本記事の技術的主張は以下で確認しました(2026年7月時点)。

RAGの基礎・定義

Embedding Model

  • NV-Embed: Improved Techniques for Training LLMs as Generalist Embedding Models(arXiv:2405.17428)— デコーダLLMベースの埋め込みモデル
  • Alibaba-NLP/gte-Qwen2-7B-instruct(Hugging Face)— encoder-only系とdecoder-only系の2系統
  • MTEB(Massive Text Embedding Benchmark)

ベクトル検索・pgvector

マルチモーダル

知識衝突(context-faithfulness)

  • Knowledge Conflicts for LLMs: A Survey(arXiv:2403.08319)— 優先順位に決定的ルールは存在しない
  • FaithfulRAG: Fact-Level Conflict Modeling for Context-Faithful RAG(arXiv:2506.08938)
  • ConflictBank / FaithEval — 知識衝突のベンチマーク

検索をモデル内部に統合する方式

  • Borgeaud et al. (2022)「Improving language models by retrieving from trillions of tokens」(RETRO, PMLR)— https://proceedings.mlr.press/v162/borgeaud22a/borgeaud22a.pdf
  • Khandelwal et al. (2020)「Generalization through Memorization: Nearest Neighbor Language Models」(kNN-LM)
  • Izacard & Grave (2021)「Leveraging Passage Retrieval with Generative Models for Open Domain Question Answering」(FiD)

図の元ネタ

  • LangChain|RAG from scratch: Overview

※ 本記事は入門者向けに図・比喩を優先しており、細部(KVキャッシュ、causal attention の厳密な依存関係、リランカーの内部構造など)は簡略化しています。正確な実装や最新の研究動向は、上記の一次資料をあたってください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?