このような衝動に駆られたことはありませんか?ウェブサイトに数百件の記事がある場合、「サイト内AIアシスタント」を作りたくなり、すべての記事の本文をシステムのプロンプトに入力してから質問をするということです。
私も最初はそうしました。しかし、請求書が届いて、返答がばらばらになり始め、長い記事がコンテキストウィンドウをはみ出すようになるまで、それが間違ったアプローチだと気づきませんでした。
このチュートリアルでは、RAG(Retrieval-Augmented Generation)について詳しく説明しています。**なぜ「すべてをプロンプトに入力してはいけないのか」、正しい方法は何か、そして無料の2つのモデル(BAAI/bge-m3とBAAI/bge-reranker-v2-m3)を使って、正確でコスト効率の高いQ&Aシステムを構築する方法についてです。
これら2つの無料モデルを利用したい場合は、この登録リンクを使ってください:https://cloud.siliconflow.cn/i/MBwLMSrY
1. RAGとは何か(Why)
RAG = Retrieval-Augmented Generation(検索強化生成):大規模なモデルに答えを生成させる前に、まず外部の知識ベースから最も関連性の高い情報を検索し、その情報を問題と一緒にモデルに提供して、「情報を持って」答えを出させるというものです。
その核心的な考え方は一言で言えば、「モデルに事前に調べさせてから答えさせる」ことであり、世界中の知識をすべてパラメータに詰め込むのを期待するのではありません。
なぜRAGが必要なのか?
大規模なモデルにはいくつかの固有の限界があります:
- 知識には有効期限がある:モデルはトレーニングデータの時点までしか「理解」しておらず、それ以降のことは何も知りません。
- プライベートデータが見えない:会社の文書、あなたのブログ、顧客との会話記録など、モデルのトレーニング時には見ることができません。
- コンテキストウィンドウが限られている:たとえ20万件のコンテキストがあっても、知識ベース全体を収めることはできません。
- 誤った答えを出すことがある:情報にない内容でも、モデルはそれを信じられるように作り上げてしまうことがあります。
2020年にMetaなどが提案した「Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks」という論文は、モデルが答えを出す際に「まず情報を調べる」ようにすることで、上記の問題を解決するためのものです。その後、ベクトルデータベースやエンベディングモデルが成熟するにつれて、RAGはLLMアプリケーションで最も主流のアプローチとなりました。
RAGが解決する問題と従来の大規模モデルの答えとの違い
| 従来の大規模モデルの答え | RAGによる強化された答え | |
|---|---|---|
| 知識の出所 | トレーニング時に「覚え込んだ」パラメータ | リアルタイムで検索した外部の情報 |
| 時宜を得ているか | トレーニングの終了日で止まる | 情報が更新されるとすぐに反映 |
| プライベートデータ | 見ることができない | 検索できる |
| 出典の追跡ができる | 出典を示すことができない | 出典を示すことができる |
| 誤った答えを出すことがある | 言い訳がしやすい | 情報によって制限され、誤りが減る |
一言で言えば、「従来の大規模モデルは『暗記に頼った試験』であり、RAGは『開いた本での試験』です。」
2. なぜRAGが必要なのか(Why)
特に問題となる点を挙げます。これがRAGが存在する理由です:
- 大規模モデルの知識は時代遅れになる:2024年のモデルでは2026年の新しい政策や新しいAPIには答えられません。
- 誤った答えを出しやすい:情報がない場合、モデルは自信を持って事実やリンク、数字を作り上げてしまいます。
- 企業のプライベートデータに直接アクセスできない:内部のWikiや契約書、工事依頼など、モデルは知りません。
- プロンプトが長すぎてトークンコストが高くなる:サイト全体の記事をプロンプトに入力すると、質問するたびに数百万のトークンが消費されます。
- 情報の出典を示すことができない:ユーザーは「この言葉はどこから来たのか」を確認できず、信頼できません。
RAGは「記憶」と「情報の検索」を分離します。モデルは推論と表現を担当し、知識は外部に保存され、いつでも更新できます。
3. RAGのワークフロー
フローチャートで説明すると以下のようになります:
flowchart LR Q[ユーザーの質問] --> E[エンベディング(ベクトル化)] E --> V[ベクトルデータベース] V --> R[リトリーバー(Top-Kを検索)] R --> RR[リランカー(必要に応じて再ランク付け) RR --> L[LLM(答えを生成)]
文字で説明すると:
ユーザーの質問
↓
エンベディング(質問をベクトルに変換)
↓
ベクトルデータベース(すべての情報のベクトルが保存されている)
↓
リトリーバー(質問のベクトルと最も類似するいくつかのセグメントを検索)
↓
リランカー(必要に応じて、リコール結果を再ランク付け)
↓
LLM(最も関連性の高い情報を使って答えを生成)
各ステップの役割:
- エンベディング:自然言語を一連の数字(ベクトル)に変換し、「意味的な類似性」を「数学的な距離」で測定できるようにします。
- ベクトルデータベース:すべての情報のベクトルを保存し、「ベクトルで最も近いものを探す」効率的な検索をサポートします。
- リトリーバー:質問のベクトルを使ってデータベースで近傍検索を行い、候補となるいくつかのセグメントを絞り出します。
- リランカー:「質問」と各候補を組み合わせて再評価し、最も関連性の高いものを選び出します。これが品質を決定する重要なステップです。
- LLM:トップNの情報のみを使用して、出典を含んだ答えを生成します。
4. RAGのコアコンポーネント
1. エンベディング(Embeddingモデル)
テキストを固定の次元の密なベクトルに変換します。一般的な次元は768/1024/1536です。中国語の場合はBAAI/bge-m3(1024次元、中国語と英語の両方に強力)が推奨されます。SiliconFlowでは無料で利用できます。
では、「ベクトル」とは何でしょうか?
一言で言えば、「ベクトル」とは、「意味空間」におけるテキストの座標を表す一連の数字です。
地図に例えると、各記事や各文は点であり、意味的に近い文は地図上で近くにあります。しかし、テキスト自体には直接「距離」を計算することはできません。そのため、エンベディングモデルはそれを一連の固定長の数字に変換します。例えば1024次元の[0.12, -0.03, 0.87, …]のように。この数字の列がその文の「座標」です。
- 次元が高いほど、表現できる意味が細かくなります(1024次元の方が384次元よりも多くの微妙な違いを表現できます)。
- **意味的な類似性」≒「ベクトルの距離が近い」:余弦類似度(2つのベクトルの角度の余弦)を使って測定され、1に近いほど関連性が高く、0に近いほど関連性が低いです。
大規模モデルは「ベクトルを読む」のでしょうか?——これはよくある誤解です
いいえ、2つのことを区別する必要があります:
- ベクトルは「検索」にのみ使用されます:検索段階では、システムが質問もベクトルに変換し、ベクトルデータベースで「最も近い」いくつかのテキストセグメントを探します。この段階はすべて数学的な計算です(距離の計算や並べ替え)で、大規模モデルは関与しません。
- 大規模モデルに実際に渡されるのは「元のテキスト」です:検索が完了すると、システムは対応する元のテキストセグメント(数字の列ではなく)をプロンプトに組み込み、大規模モデルがそれに基づいて答えを生成します。
つまり、より正確に言えば、「ベクトルは正確な検索のためのものであり、大規模モデルはその結果を理解するためのものです。」この分業により、RAGは安価なモデルでも正確な答えを出すことができます。大規模モデルが見るのは厳選されたいくつかのテキストセグメントだけです。
2. チャンク(Chunk)の作成
長い文書を小さなセグメントに分割します。RAGの検索の粒度はチャンクであり、文書全体ではありません。分割が粗すぎるとリコールの精度が下がり、細かすぎると意味が壊れます。一般的には300〜500文字が適切です。
3. ベクトルデータベース(Vector Database)
ベクトルを保存し、ANN(近似最近傍)検索をサポートするデータベースです。Qdrant / Milvus / Chroma / FAISSなどが選択肢ですが、Postgresにpgvectorを拡張して使用することもできます(このブログではpgvectorを使用しています)。
4. リトリーバー(Retriever)
「質問のベクトル → 候補チャンク」の粗い選別を担当します。主な手法は余弦類似度/内積/ユークリッド距離です。
5. リランカー(Reranker)
Cross-Encoder構造を持ち、単純な余弦よりも精度が高いです。query + 候補を一緒に入力して評価します。BAAI/bge-reranker-v2-m3もSiliconFlowでは無料で利用できます。
6. LLM(生成モデル)
トップNの情報のみを使用して答えを生成します。セグメントが少ないため、より安価なモデルを使用することができ、コストが大幅に削減されます。
これらのコンポーネントがどのように連携するか:エンベディングモデルは「データの保存」と「検索時のベクトル化」を担当し、ベクトルデータベース+リトリーバーは粗い選別を行い、リランカーは精密なランク付けを行い、LLMは答えを生成します。最初の3つが「正確な検索」を決定し、最後のモデルが「良い答え」を決定します。
5. RAGの利点
- 答えがより正確:実際の情報をもとに答えるため、根拠のないものではありません。
- 誤った答えが減る:情報による制約があるため、作り話の余地が減ります。
- 出典を示すことができる:各答えには出典のセグメントが付き、ユーザーが確認できます。
- 知識がリアルタイムで更新される:新しい文書をデータベースに追加するだけで、モデルを再トレーニングする必要はありません。
- トークンの消費が減る:トップNのセグメントのみを送るため、トークンの消費が削減されます。
- より安価な生成モデルを使用できる:コンテキストが短くて精緻なため、安価なモデルでも十分な答えが得られます。
- 企業のデータを再トレーニングする必要がない:知識は外部にあるため、モデルのパラメータを変更する必要はありません。
六、RAGと微調整(Fine-tuning)の違い
これは頻繁に検索されるトピックです。以下の表で明確に説明します:
| 次元 | RAG | 微調整(Fine-tuning) |
|---|---|---|
| 知識の更新 | 速い(ドキュメントを追加するだけで効果が発揮される) | 遅い(再トレーニングが必要) |
| モデルの変更 | パラメータを変更しない | モデルの重みを変更する |
| 知識のリアルタイム更新 | サポートされる | サポートされない。再トレーニングが必要 |
| コスト | 低い(主に検索と少量の生成) | 高い(トレーニングの計算能力とデータのアノテーションが必要) |
| 適しているシナリオ | ナレッジベース/検索Q&A | モデルに特定の「能力」や「スタイル」を学ばせる |
重要なポイント:多くの企業が実際には 「RAG + 微調整」の組み合わせ を採用しています。RAGで最新で追跡可能な知識を提供し、微調整でモデルに特定の表現スタイルやタスクのフォーマットを習得させます。二者択一ではありません。
七、RAGの構築方法
完全なプロセス:
- ドキュメントの準備:ブログ記事、PDF、Wiki、チケットなどの原始資料を用意する。
- テキストの分割(Chunking):意味的に300〜500語の小さなセグメントに分割する。
- エンベディング(Embedding):bge-m3を使用して各セグメントをベクトルに変換する。
- ベクトルデータベースへの保存:
セグメント + ベクトル + メタデータをデータベースに保存する。 - ユーザーからの質問:質問が入力される。
- 検索:質問をベクトル化し、データベースからトップ15を取得する。
- 並べ替え(オプションだが推奨):bge-rerankerを使用して並べ替え、トップ5を選ぶ。
- LLMに回答を依頼:トップ5をプロンプトに組み込み、回答を生成し出典を付ける。
八、一般的なオープンソースソリューション(エコシステムの概要)
フレームワーク
- LangChain(最も人気があり、コンポーネントが豊富)
- LlamaIndex(データの接続とインデックス作成に強み)
- Haystack(企業向けのパイプライン)
エンベディングモデル
- BGE(
BAAI/bge-m3など、中国語と英語に強く、無料で利用可能) - Jina Embeddings
- Nomic Embed
ベクトルデータベース
- Qdrant(Go言語で書かれており、パフォーマンスが良く、導入が簡単)
- Milvus(大規模な分散型)
- Chroma(軽量で開発がしやすい)
- FAISS(Metaのベクトル検索ライブラリで、よく基盤エンジンとして使用される)
リランカー(Reranker)
- BGE Reranker(
BAAI/bge-reranker-v2-m3、無料) - Jina Reranker
九、RAGの最適化方法(見落とされがちだが最も価値がある)
- Chunkのサイズの選択:300〜500語が一般的な最適値です。長いドキュメントはタイトルや段落ごとに分割するとよい。
- Chunkのオーバーラップ(Overlap):10%〜20%のオーバーラップを設けることをお勧めします。これにより、文が途切れて意味が失われるのを防ぎます。
- ハイブリッド検索(Hybrid Search):ベクトル検索(意味的な情報)とキーワード検索(BM25/全文インデックス)を組み合わせると、リコール率が大幅に向上します。このブログのリソースサイトでは「ベクトル + ilikeの構文解析」を組み合わせたRRFを使用しています。
- メタデータのフィルタリング:まず時間、カテゴリ、著者でフィルタリングを行い、その後で検索するとノイズを大幅に減らすことができます(例:「2026年のチュートリアル」のみを検索)。
- Top-Kの設定:リコール数を15〜20に設定し、並べ替えた後にトップ5を選ぶのが安全な方法です。長すぎるとノイズが増える可能性があります。
- リランカーの使用:ほとんどの場合「必要」です。リランカーを使用すると、余弦関数による誤ったリコールを修正し、品質が大幅に向上します。
- トークンコストの削減:並べ替え後のトップ5のみを送信する。生成モデルは安価なものを使用し、エンベディングや並べ替えには無料のモデルを使用する。
- リコール率の向上:ハイブリッド検索 + メタデータのフィルタリング + 適切なChunkの分割 + オーバーラップ + 並べ替えを組み合わせる。
十、RAGの適用シナリオ
- 企業のナレッジベース:従業員が制度やプロセス、製品ドキュメントについて質問する。
- AIカスタマーサービス:過去のチケットやFAQを検索し、正確な回答を提供する。
- PDFに基づくQ&A:契約書や論文、マニュアルをアップロードし、内容について直接質問する。
- APIドキュメントアシスタント:開発者が「このAPIの使い方は?」と質問すると、対応するセクションを特定する。
- 法律相談:法律条文や判例を検索する(人の確認が必要で、直接信頼できない)。
- 医療知識アシスタント:ガイドラインや文献を検索する(同様に、専門的な判断の補助として使用)。
- 教育プラットフォーム:教科書の章ごとに質問に答えたり、練習問題を出したりする。
- ブログのQ&A:このブログの「この記事についてAIに質問する」のように、読者が特定の記事について質問すると、モデルがその記事に基づいて回答する。
十一、よくある誤解
- RAG = 微調整:RAGは知識を補うものであり、微調整はモデルの能力を学ばせるものです。混同しないでください。
- RAGではモデルが賢くならない:RAGは資料を提供するだけで、モデル自体の能力は変わりません。
- RAGはデータベースではない:RAGは「検索 + 生成」のパラダイムであり、基盤としてデータベースを使用しているだけです。
- RAGは検索エンジンではない:RAGは最終的に自然言語の回答を生成するため、リンクリストを返すわけではありません。
- RAGが必ずしもすべてのシナリオに微調整よりも適しているわけではない:モデルに新しい能力やスタイルを学ばせたい場合は、微調整が必要です。
- リランカーは必須ではない:しかし、使用すると通常は大幅に性能が向上するため、「オプションだが強く推奨される」。
- Chunkが大きいほど良いわけではない:過大なChunkは関連性を薄め、トークンを無駄にする可能性があります。
十二、実戦例:このブログの「この記事についてAIに質問する」機能
このブログを例にとると、実際のアーキテクチャは以下の通りです:
Markdown記事(ナレッジベース)
↓ 自動分割(段落や見出しごとに)
↓ bge-m3によるエンベディングの生成
↓ ベクトルデータベースへの保存(このブログではPostgres + pgvectorを使用;Qdrantも使用可能)
↓
ユーザーからの質問
↓ bge-m3による質問のベクトル化
↓ ベクトルデータベースから関連する記事の断片を取得(トップ15)
↓ bge-reranker-v2-m3によるランキング(トップ5)
↓ SiliconFlowのDeepSeek(またはより安価な無料モデル)を呼び出して回答を生成
一連の質問応答プロセス(擬似コード、直接実装可能):
// ① インデックス作成段階(記事を書く際に一度実行)
const chunks = splitByHeading(markdownText); // 自動分割
const vectors = await embed(chunks); // bge-m3によるエンベディングの生成
await db.insert(chunks.map((c, i) => ({ content: c, embedding: vectors[i] }));
// ② 質問段階
const queryVec = (await embed([question]))[0]; // 質問のベクトル化
const candidates = await db.search(queryVec, 15); // ベクトルデータベースからトップ15の候補を取得
const ranked = await rerank(query, candidates, 5); // bge-rerankerによるランキング(トップ5)
const context = ranked.map(r => candidates[r.index]).join('\n\n');
// ③ 回答生成段階(安価で正確な回答を5つだけ提供)
const answer = await chatWithLLM(`
以下の情報に基づいて回答し、出典を明記してください:
${context}
質問:${question}
`);
エンベディングとランキングにはSiliconFlowの無料モデルを使用します:
const SF_KEY = process.env.SILICONFLOW_API_KEY!;
// エンベディング:BAAI/bge-m3(無料、1024次元)
async function embed(texts: string[]): Promise<number[][]> {
const r = await fetch('https://api.siliconflow.cn/v1/embeddings', {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${SF_KEY}` },
body: JSON.stringify({ model: 'BAAI/bge-m3', input: texts, encoding_format: 'float' }),
});
const j = await r.json();
return j.data.map((d: any) => dembedding as number[]);
}
// ランキング:BAAI/bge-reranker-v2-m3(無料)
async function rerank(query: string, documents: string[], topN = 5) {
const r = await fetch('https://api.siliconflow.cn/v1/rerank', {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${SF_KEY}` },
body: JSON.stringify({ model: 'BAAI/bge-reranker-v2-m3', query, documents, top_n: topN }),
});
return (await r.json()).results; // [index, relevance_score]の形式で、スコア順に並んだ結果
}
完全なSQL(pgvectorのテーブル作成とコサイン類似度による検索)およびコストの比較については、別の記事を参照してください。結論としては、エンベディングとランキングはどちらも無料で、生成モデルにはトップ5のみを使用する(約3000トークン)ことで、「プロンプトをすべて使用する」方法よりも99%以上コストを削減できます。
まとめ
RAGの核心は一言で言えば:ナレッジをベクトルデータベースに保存し、質問時には最も関連性の高いいくつかの断片のみを大規模なAIモデルに送る。
- エンベディングには
BAAI/bge-m3を使用(無料) - ランキングには
BAAI/bge-reranker-v2-m3を使用(無料) - ベクトルデータベースにはpgvectorまたはQdrantを使用
- 生成モデルにはトップ5のみを使用する
この構造に従えば、AIによる質問応答の精度が向上し、処理速度も大幅に速くなり、コストも大幅に削減できます。これら2つの無料モデルを無料で利用したい場合は、以下のリンクから登録してください:https://cloud.siliconflow.cn/i/MBwLMSrY
この記事のコードはそのまま利用できます。実際、このブログの「この記事についてAIに質問する」機能も同じ処理フローを使用しています。