はじめに
こんにちは、いのせです。
最近、RAGという単語をあまり耳にしなくなった気がしませんか?Claude Codeの進化だったり、MCPだったり、次から次へと新しい技術が登場していて、僕自身もそういった最新技術ばかり追いかけていました。
ところがある日、寝る前に弊社の先輩方が出版している本を読んでいたら、RAGのハンズオンが載っていて、「そういえば久々にRAGって単語を目にしたな」とふと思ったんですよね。
今回は、「RAGって結局なんなの?」という方や、RAGについて知りたい初学者の方に向けて、コードを使わずにできるだけわかりやすく解説していきます。
この記事を書くきっかけになった本はこちらです。わかりやすくて面白い書籍なので、興味がある方はぜひ読んでみてください!!
そもそもRAGって何?
RAGを一言で表すと、LLMに「外部のデータ」を検索・参照させて、回答の質を上げる技術のことです。
RAGはRetrieval(検索)・Augmented(拡張)・Generation(生成)の略で、この3つの単語がそのまま処理の流れを表しています。
- ユーザーの質問に関連する情報を検索する
- 見つけた情報でLLMへの入力を拡張する
- LLMが拡張された情報をもとに回答を生成する
...とはいえ、「なにそれ、なんか美味しくなさそう」という方もいると思うので、もっとわかりやすく説明します。
学校のテストで考えてみよう
通常のLLM=クローズドブック試験
- 自分の記憶(学習データ)だけで解答をする
- 知らないことは推測するしかない→もっともらしい嘘が起きる(ハルシネーション)
- 授業で習っていない範囲や最新の出題傾向には対応できない
RAG付きのLLM=オープンブック試験
- 教科書やノート(外部のデータ)を見ながら解答して良い
- 質問に関するページを探して、そこに書いてある内容をもとに解答する
- 根拠のある回答ができるため、正確性が大幅に向上する
この記事を読んでいる間は、この「学校のテストの例え」をぜひ覚えておいてください。RAGの仕組みを理解するというのは、ここでは「どうやってオープンブック試験を成功させるか」を理解することと同じです。
RAGは特定のライブラリやサービスの名前ではなく、設計パターンです。MastraやLangChainなどのフレームワークは、このパターンを実装しやすくするためのツールだと考えてください。
なぜRAGが必要なのか
LLMはとても優秀ですが、大きな弱点が3つあります。
【弱点1】 ハルシネーション
LLMは、知らないことでももっともらしく答えてしまう性質があります。テストで言うと、答えがわからない問題でも空欄にせず、自信満々でそれらしい答えを書いてしまう生徒です。しかも本人は嘘をついている自覚がゼロなんですよね。
【弱点2】 知識の鮮度が古い
LLMの知識は学習した時点で止まっています。これを学習データのカットオフと呼びます。テストで言うと、2年前の教科書で勉強した生徒に、今年の法改正について出題するようなものです。教科書に載っていないんだから、もちろん答えられません。
【弱点3】 プライベートデータへの無知
LLMはインターネット上の公開情報を学習していますが、あなたの会社や学校の内部資料は一切知りません。そりゃそうですよね、極秘情報を知っていたら怖いですし。
【コラム】ファインチューニングじゃダメですか?
※某アイドルが歌っている歌に似ているような...というのは一旦置いといて(笑)
ここで、「LLMに自社データを直接覚えさせれば良いのでは?」と思う方もいるかもしれません。これをファインチューニングと言います。
| ファインチューニング | RAG | |
|---|---|---|
| 例え | 教科書を全部書き直す | 参考資料を持ち込む |
| コスト | 高い(再学習に大量の計算資源) | 低い(検索の仕組みを作るだけ) |
| 更新の手軽さ | 大変(データが変わるたびに再学習) | 簡単(参考資料を差し替えるだけ) |
| 向いている場面 | 口調やスタイルを変えたいとき | 事実に基づいた回答をさせたいとき |
表を見てわかるかと思いますが、多くの場合はRAGの方が手軽で実用的です。
RAGの仕組みをざっくり理解する
RAGには大きく2つのフェーズがあります。
フェーズ1「準備フェーズ」(試験前の準備)
オープンブック試験に参考資料を持ち込むには、まず資料を準備する必要がありますよね。仮に300ページの教科書をそのまま持ち込んでも、試験中に解答が載っているページを見つけるのは至難の業です。
そこで、事前にこんな準備をします。
ドキュメント → チャンク分割 → ベクトル化 → ベクトルDB保存
- チャンク分割 → 教科書の重要ポイントを要点カードに書き出す(大きなドキュメントを小さな断片に分割する)
- ベクトル化 → 各カードに「このカードはどんなテーマか」を示すタグをつける(テキストを数値に変換する)
- ベクトルDB保存 → タグ付きカードをファイルボックスに整理して保管する
フェーズ2「質問フェーズ」(試験本番)
試験本番では、問題を読んで関連するカードを探し、答えを書きます。
ユーザーの質問 → ベクトル化 → 類似検索 → 関連チャンクをLLMに渡す → 回答生成
- 質問のベクトル化 → 問題文を読んで、どんなテーマの問題かを把握する
- 類似検索 → ファイルボックスから関連するカードを探す
- 回答生成 → 見つけたカードの内容をもとに答えを書く
この2つのフェーズのうち、準備フェーズは一度やればOKです。質問フェーズはユーザーが質問するたびに繰り返されます。
Embedding(埋め込み)とは
そもそもの問題
コンピュータは文字の「意味」を理解できません。人間なら「猫」と「ネコ」が同じ意味だとすぐにわかりますが、コンピュータにとってこれらはただの異なる文字の並びでしかないんです。
では、どうすれば「意味が近い」ことをコンピュータに判断させられるのでしょうか?
料理の味プロフィールで考えてみよう
料理を数値で表現してみましょう。すべての料理は「甘さ」「塩味」「酸味」「辛さ」「旨味」といった軸で数値化できますよね。
| 料理 | 甘さ | 塩味 | 酸味 | 辛さ | 旨味 |
|---|---|---|---|---|---|
| 味噌ラーメン | 2 | 8 | 1 | 3 | 9 |
| 塩ラーメン | 1 | 9 | 1 | 1 | 7 |
| チョコレートケーキ | 9 | 1 | 0 | 0 | 2 |
| レモンサワー | 3 | 0 | 8 | 0 | 1 |
この数値を見ると、味噌ラーメンと塩ラーメンは数値のパターンが似ていますよね。どちらも塩味が高くて旨味が強いという共通点があります。一方、チョコレートケーキはパターンが全く異なります。
Embeddingはこれと同じことをテキストに対して行います。テキストを数値の配列(ベクトル)に変換することで、意味の近さを数値で比較できるようにするんです。
"猫は可愛いペットです" → [0.023, -0.451, 0.118, ...]
"ネコはかわいい動物だ" → [0.025, -0.448, 0.121, ...] ← 近い値!
"日本経済の成長率" → [-0.312, 0.087, -0.445, ...] ← 全然違う値
料理の味プロフィールが5〜6個の軸だったのに対し、テキストのEmbeddingは1536個もの軸(次元)で表現されます。人間には到底想像できない空間ですが、コンピュータなら計算で距離を測れるわけです。
意味が近い = ベクトルが近い
Embeddingの最も重要なポイントは、意味が似ているテキスト同士は似たベクトルになるということです。
「子犬」と「パピー」は近いベクトル(同じ意味)になり、「子犬」と「ペット」はやや近いベクトル(関連するテーマ)になります。一方で、「子犬」と「確定申告」は遠いベクトル(無関係)になります。
これにより、ユーザーが「犬の飼い方」と質問したとき、ドキュメント内に「犬の飼い方」という完全一致の文がなくても、「わんこのお世話方法」や「ペットの育て方ガイド」といった意味的に近いチャンクを見つけることができます。これがキーワード検索との大きな違いです。
ベクトルの近さはどう測るのか
2つのベクトルがどれくらい似ているかは、コサイン類似度という指標で測ります。簡単に言えば「2つの矢印がどれくらい同じ方向を向いているか」を数値にしたものです。
1.0なら完全に同じ方向(=同じ意味)、0.5前後ならある程度関連がある、0.0付近ならほとんど無関係、という感じです。ベクトルDBはこのコサイン類似度を計算して「質問に最も意味が近いチャンク」を探してくれます。
ベクトルデータベースとは
通常のデータベースは、キーワードの完全一致や部分一致で検索します。例えば WHERE title = '猫の飼い方' と書けば、タイトルが「猫の飼い方」と完全に一致するものだけがヒットします。
一方、ベクトルデータベースは意味の近さで検索します。「ペットのお世話について」と検索すれば、「猫の飼い方」「犬のしつけガイド」「小動物の健康管理」など、意味的に関連するものがまとめてヒットするわけです。
音楽のレコメンドで考えてみよう
音楽アプリを想像してみてください。
通常のDB的な検索は、曲名やアーティスト名で検索するイメージです。「Official髭男dism」と入力すればそのアーティストの曲だけがヒットしますが、曲名を正確に知っていないと目当ての曲は見つかりません。
一方、ベクトルDB的な検索は「この曲に似た曲」を探すイメージです。ある曲の雰囲気(テンポ、ジャンル、ムード)を数値化して、似た雰囲気の曲をレコメンドしてくれます。曲名を知らなくても「こんな感じの曲」で見つかるんです。
Spotifyの「あなたへのおすすめ」は、まさにベクトル検索に近い仕組みで動いています。
テストの例えに戻すと
ベクトルDBは、オープンブック試験に持ち込むファイルボックスです。要点カード(チャンク)がベクトル付きで整理されていて、問題文のテーマに近いカードを瞬時に探せます。カードの枚数が何万枚になっても高速に検索できるのが、ベクトルDBの強みです。
RAGの処理の流れを追ってみる
ここまでの知識を全部つなげて、RAGの処理を最初から最後まで具体例で追ってみましょう。
シナリオ
ある会社に100ページの社員ハンドブックがあります。新入社員が「リモートワークのルールを教えてください」と質問する場面を考えます。
【ステップ1】 チャンキング(ドキュメントを分割する)
まず、100ページのハンドブックをそのままLLMに渡すことはできません。LLMが一度に読める量には限りがあり、これをコンテキストウィンドウと呼びます。
そこで、ハンドブックを小さな断片(チャンク)に分割します。1ページ分くらいの適度な長さで区切り、チャンク同士が少し重なるようにします。重なりを持たせるのは、境目で文脈が途切れるのを防ぐためです。こうして100ページのハンドブックが約200個のチャンクに分割されます。
テストの例えで言うと、教科書の重要ポイントを一枚ずつカードに書き出す作業にあたります。
【ステップ2】 ベクトル化(各チャンクをEmbeddingする)
分割した各チャンクをEmbeddingモデルに渡して、数値ベクトルに変換します。
「リモートワーク規定: 週3日まで在宅勤務が可能…」 → [0.15, -0.32, ...]
「有給休暇: 入社6ヶ月後に10日付与…」 → [-0.08, 0.44, ...]
「服装規定: ビジネスカジュアルを推奨…」 → [0.21, -0.11, ...]
リモートワーク規定と在宅勤務のチャンクは近いベクトルに、服装規定のチャンクは離れたベクトルになります。
テストの例えだと、各カードに「このカードはなんのテーマか」を示すタグをつける作業です。
【ステップ3】 ベクトルDBに保存する
ベクトル化した各チャンクを、元のテキスト情報(メタデータ)と一緒にベクトルDBに保存します。保存されるのは、ベクトル(数値の配列)、元のテキスト、出典(「社員ハンドブック 第5章」など)の3つです。
ここまでが準備フェーズ。一度やれば、質問を何回受けても再利用できます。
【ステップ4】 質問を受けて検索する
新入社員が「リモートワークのルールを教えてください」と質問します。
この質問文もEmbeddingモデルでベクトル化し、ベクトルDBの中から質問のベクトルに最も近いチャンクを探します。すると、上位3〜5件のチャンクが見つかります。
「リモートワーク規定: 週3日まで在宅勤務が可能…」(類似度: 0.92)
「テレワーク時のセキュリティ注意事項…」 (類似度: 0.85)
「在宅勤務の申請手続き…」 (類似度: 0.81)
「リモートワーク」という単語が含まれていなくても、「テレワーク」「在宅勤務」といった意味的に近いチャンクがちゃんと見つかるのがポイントです。
テストの例えだと、問題文を読んでファイルボックスから関連するカードを引っ張り出す段階にあたります。
【ステップ5】 LLMが回答を生成する
最後に、見つけたチャンクをLLMに渡して回答を生成してもらいます。LLMに渡される情報はこんなイメージです。
以下の参考資料に基づいて、ユーザーの質問に回答してください。
参考資料に書かれていない情報は「わかりません」と回答してください。
【参考資料】
- リモートワーク規定: 週3日まで在宅勤務が可能。事前に上長の承認が必要...
- テレワーク時のセキュリティ注意事項: VPN接続必須。公共Wi-Fi禁止...
- 在宅勤務の申請手続き: 勤怠システムから「在宅勤務申請」を選択...
【ユーザーの質問】
リモートワークのルールを教えてください
LLMはこの参考資料を読んで回答を生成します。
ポイントは、LLMが自分の学習データからではなく、検索で見つけた参考資料に基づいて回答しているということです。だからこそ、社内ルールのような非公開情報にも正確に答えられるんですね。
テストの例えでは、見つけたカードの内容を読んで問題の答えを書く段階です。
RAGを使うと何が嬉しいのか
RAGを導入することで得られるメリットは大きく6つあります。
【1】 ハルシネーションの大幅な削減
LLMが参考資料に基づいて答えるようになるため、根拠のない推測が大きく減ります。さらに「参考資料に書いてなければわかりませんと答えてね」という指示を加えることで、嘘をつくリスクをさらに下げることができます。
【2】 最新情報への素早い対応
LLMを再学習させる必要がなく、ドキュメントをベクトルDBに追加するだけで、すぐに最新の情報を参照できるようになります。新しい社内規定が発表されたら、ベクトルDBに追加して即座に回答に反映させる、といったイメージです。
【3】 プライベートデータの活用
社内ドキュメントや議事録など、LLMが学習していない非公開データを活用できます。LLMにデータを覚えさせるのではなく、質問のたびに「見せる」だけなので、データの管理もシンプルです。
【4】 コスト効率
ファインチューニングには大量の計算資源とお金がかかりますが、RAGはEmbeddingの生成と検索の仕組みを構築するだけで済みます。データの追加・更新もベクトルDBに保存し直すだけです。
【5】 透明性(出典を示せる)
RAGでは「どのドキュメントのどの部分を参考にしたか」がわかります。回答と一緒に出典を表示すれば、ユーザーは自分で正しさを確認できます。例えば「この回答は社員ハンドブック第5章3節に基づいています」のような形です。
【6】 データのコントロール
あなたのデータはベクトルDBに保存されるだけで、LLMの学習データには組み込まれません。いつでもデータを追加・更新・削除でき、不要になった情報は削除すれば検索されなくなります。ユーザーごとにアクセスできるデータを分けることも可能です。
RAGの限界と注意点
RAGは強力な技術ですが、万能ではありません。ここでは知っておくべき限界と注意点を整理します。
【1】 検索の品質が全てを左右する
RAGの回答品質は、検索で見つかったチャンクの質に直結します。関係ないチャンクが見つかってしまったら、LLMはその情報をもとに回答を生成してしまいます。テストの例えで言うと、ファイルボックスから間違ったカードを引いたら、当然間違った答えを書いてしまうということです。検索品質を高めるには、チャンキングの方法やEmbeddingモデルの選択が重要になります。
【2】 チャンキングは試行錯誤が必要
ドキュメントをどう分割するかは、RAGの性能に大きく影響します。大きすぎるチャンクだと関係ない情報が混ざってLLMの注意が散漫になるし、小さすぎるチャンクだと文脈が失われて断片的な情報しか得られません。最適なサイズはドキュメントの種類によって異なるため、実際に試しながら調整する必要があります。
【3】 推論や計算は苦手
RAGが得意なのは事実の検索と参照です。複数のデータを組み合わせた推論や計算は、RAGだけでは難しい場合があります。例えば「過去3年間の売上成長率を計算して」という質問には、各年の売上データを見つけることはできても、成長率の計算はLLM自身の能力に頼ることになります。
【4】 コンテキストウィンドウの制約
LLMが一度に読める量には上限があります。質問に答えるために50ページ分の情報が必要でも、そのすべてをLLMに渡すことはできません。だからこそ「関連度の高いチャンクだけを厳選して渡す」ことが重要になるわけです。
【5】 Embeddingモデルの限界
「これ何?」のような短すぎるクエリだとEmbeddingの精度が低くなりがちです。また、非常にニッチな業界用語は汎用モデルではうまくベクトル化できないこともあります。さらに、日本語で質問して英語のドキュメントを検索するなど、言語をまたぐ検索では精度が落ちる場合もあります。
【6】 インデックス更新のタイムラグ
ドキュメントが更新されても、チャンキング → ベクトル化 → DB保存が完了するまでは検索に反映されません。リアルタイム性が求められる場面では、更新パイプラインの設計が重要になります。
おわりに
今回は、RAGの基本概念から仕組み、メリット、そして限界まで、できるだけわかりやすく解説してみました。
RAGは「LLMに参考資料を持ち込ませる」というシンプルなアイデアですが、Embedding、ベクトルDB、チャンキングなど、裏側ではいろいろな技術が組み合わさって動いています。概念を理解した上で実際に手を動かしてみると、
「あ、あの話はこういうことなのか〜!なるほどねぇ〜」と腑に落ちる瞬間があるはずです。
僕自身はMastraを使ってRAGの勉強をしていて、次回以降の記事では実際にコードを書きながら実装についても解説していけたらと思っています。
最後まで読んでいただきありがとうございました!