RAGってそもそも何?

スポンサーリンク

最近「RAG」という言葉をよく耳にするようになりました。ChatGPTやClaudeといったAIツールを使っていると、「社内ドキュメントを読み込んだAI」や「自分のメモを元に答えてくれるチャットボット」の話が出てきます。その多くが RAG という仕組みを使っています。

RAG とは Retrieval-Augmented Generation(検索拡張生成)の略です。日本語にすると「情報を検索してから文章を生成する」という意味で、LLM(大規模言語モデル)が知らない情報を外部から補ってあげる仕組みです。

一言でいえば、「AIの脳みそ(LLM)」と「自分の本棚(ナレッジベース)」を合体させる技術です。

LLMだけでは何が足りないのか

ChatGPTやClaudeは非常に賢いですが、いくつかの根本的な制限があります。

限界 具体例
学習データに含まれない情報は知らない 自社のマニュアル・社内規定・個人メモなど
学習カットオフ以降の情報は知らない 最新ニュース・最近リリースされたライブラリなど
「もっともらしい嘘」をつく(ハルシネーション) 存在しないURLや書籍を自信を持って答える

RAG はこれらの問題を「正しい情報を先に引っ張ってきて、それをもとに回答させる」という方法で解消します。人間でいえば、試験会場に参考書を持ち込んで答える「持ち込みOK方式」のようなものです😊

RAGの仕組みをざっくり理解する

RAG には大きく2つのフェーズがあります。

フェーズ①:事前準備(Indexing)

ドキュメント(テキスト)をベクトル化して保存します。ベクトル化とは、文章の意味を数値の配列(ベクトル)に変換することです。意味が似ている文章ほど近い数値になります。

フェーズ②:検索&生成(Retrieval + Generation)

ユーザーの質問をベクトル化し、事前に保存したドキュメントと照合して「一番意味が近いもの」を取り出します。その取り出した文章を LLM に渡し、「この情報を使って回答して」と指示することで、正確な回答を生成させます。

RAGの処理フロー図
RAGの処理フロー。ドキュメントを事前にベクトル化しておき、質問時に類似ドキュメントを検索してLLMに渡す

ベクトル同士の類似度を測る方法として コサイン類似度 がよく使われます。2つのベクトルが向いている方向が近いほど「意味が似ている」と判断します。値は -1 〜 1 で、1 に近いほど類似しています。

OllamaのローカルLLMでRAGを実装してみた

理屈がわかったところで、実際に手元の Mac で動かしてみました。外部 API は一切使わず、ローカルで動く LLM(Ollama)だけで完結させています。

使用した環境

項目 内容
LLM llama3.2:3b(Meta製、3B パラメータ、2.0GB)
実行環境 Ollama(Mac ローカル)
Embedding Ollama の /api/embed(llama3.2:3b で 3072 次元)
ベクトルDB numpy だけで自作(外部ライブラリ不要)
Python パッケージ requests、numpy のみ

Ollama は以前 Mac に導入済みの環境を使っています(Ollama 公式サイト)。ollama run llama3.2:3b でモデルをダウンロードするだけで、API サーバーが localhost:11434 で起動します。

コードの全体像

外部のベクトル DB(Chroma や FAISS など)は使わず、numpy のコサイン類似度だけで検索を実装しました。これが最小構成の RAG です。

rag_demo.pyのコード
rag_demo.py — requests と numpy だけで動く最小 RAG 実装
import requests
import numpy as np

OLLAMA_URL = "http://localhost:11434"
MODEL      = "llama3.2:3b"

# ナレッジベース(自前のドキュメント)
docs = [
    {"title": "NavMeshとは",  "text": "NavMeshはUnityのAI経路探索..."},
    {"title": "MCPとは",      "text": "MCPはAnthropicの通信規格..."},
    # ...
]

# ① 事前に全ドキュメントをembedding
def embed(text):
    """Ollama embedding API でテキストをベクトル化(3072次元)"""
    resp = requests.post(OLLAMA_URL + "/api/embed",
                         json={"model": MODEL, "input": text}, timeout=30)
    return np.array(resp.json()["embeddings"][0])

for doc in docs:
    doc["embedding"] = embed(doc["text"])

# ② 質問をembeddingして類似ドキュメントを検索
def cosine_similarity(a, b):
    return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))

query_emb = embed(query)
ranked = sorted(
    [(d, cosine_similarity(query_emb, d["embedding"])) for d in docs],
    key=lambda x: x[1], reverse=True
)

# ③ 上位2件をコンテキストとしてLLMに渡す
context = "\n".join(d["text"] for d, _ in ranked[:2])
prompt  = f"【情報】\n{context}\n【質問】\n{query}\n"
resp    = requests.post(OLLAMA_URL + "/api/generate",
                        json={"model": MODEL, "prompt": prompt, "stream": False})
print(resp.json()["response"])

コードのポイントは3ステップだけです。①ドキュメントをベクトル化→②質問と類似したものを取り出す→③LLMに渡して回答させる。この流れがRAGのすべてです。

実行結果

実際に2つの質問で試してみました。

rag_demo.pyの実行結果
実行結果。各ドキュメントとのコサイン類似度が表示され、最も近いドキュメントが上位に来る

「NavMeshを使って敵がプレイヤーを追いかける仕組みは?」という質問に対して、NavMesh のドキュメントが類似度 0.6760 で1位になり、正しく回答が生成されました。

「PythonでBlenderの3Dモデルを自動生成するには?」という質問では、Blender のドキュメントが 0.7147 で1位になり、bpy モジュールの説明を正確に使って回答してくれました。

ナレッジベースに存在しない情報については「情報がありません」と正直に答えてくれるのも大きなポイントです。プロンプトで「情報に書かれていないことは答えないでください」と指示することで、ハルシネーションを抑制できます。

RAGを使うときの注意点

RAGは便利ですが、いくつか注意点もあります。

① 検索で取れなかったら回答も悪くなる

RAG の品質はベクトル検索の精度に依存します。質問の言い回しとドキュメントの表現が大きくずれると、関係のないドキュメントが上位に来てしまいます。今回の実験でも、「UnityでAIを使って敵を動かすには?」という少し遠回りな質問では NavMesh が1位にならないケースがありました。質問の言い回しを工夫するか、ドキュメントを細かく分割(チャンキング)することが対策になります。

② ドキュメントが多いほど重くなる

今回は4件だけでしたが、ドキュメントが数百件になると embedding の事前計算とメモリ使用量が増えます。本格的に使うなら ChromaFAISS などの専用ベクトル DB を使うのがおすすめです。

③ embedding モデルの品質が重要

llama3.2:3b の embedding は 3072 次元と十分な精度ですが、日本語テキストの場合は日本語に特化した embedding モデル(例:multilingual-e5 など)を使うとさらに精度が上がります。

まとめ

RAG を一言でまとめると、「LLMの脳みそに、自分の本棚の情報を追加して使う仕組み」です。今回のポイントをまとめます。

  • LLM は学習データ外の知識を持たない → RAG で補える
  • ドキュメントをベクトル化(embedding)して保存するのが事前準備
  • 質問をベクトル化してコサイン類似度で検索するのが Retrieval
  • 取り出した情報をプロンプトに入れて回答させるのが Generation
  • Ollama + numpy だけで最小構成の RAG を実装できる

今回作ったコードはほんの50行ほどです。「社内マニュアルを読み込んだAI」や「自分のメモを参照してくれるアシスタント」を作りたい方は、ぜひ試してみてください😊

それでは、今回はここまで。ありがとうございました😊