RAGの核心概念と必要性
大規模言語モデル(LLM)は特定の条件下で以下のような課題を示す:
- 事実に基づかない出力(ホールシネーション)の発生
- トレーニングデータ以降の最新情報への対応不能
- 専門分野や業界用語への理解不足
これらの原因として、モデルのトレーニングデータの制限、知識更新の遅延、特定分野への深さ不足が挙げられる。RAGはこれらの課題を解決するための主要な手法の一つであり、既存のLLMを改変することなく、外部知識をリアルタイムで参照可能にする仕組みである。
基本動作フロー
RAGシステムは以下の3段階で動作する:
- 前処理段階:ドキュメントを適切な単位で分割し、ベクトル化してデータベースに格納
- 検索段階:ユーザクエリをベクトル化し、類似度に基づいて関連ドキュメントを取得
- 生成段階:取得したドキュメントとクエリを組み合わせて、LLMに回答を生成させる
ベクトル化の実装例
テキストをベクトル空間にマッピングする処理を実装する。ここではスライディングウィンドウ方式によるテキスト分割と、埋め込みモデルの利用を示す:
import numpy as np
from typing import List, Generator
def sliding_text_chunker(text: str, chunk_size: int, stride: int) -> Generator[str, None, None]:
"""スライディングウィンドウによるテキスト分割"""
for start in range(0, len(text), stride):
yield text[start:start + chunk_size]
class VectorRepository:
"""ベクトルデータの永続化を管理するリポジトリ"""
def __init__(self, db_path: str = "./vector_store"):
self.vector_db = chromadb.PersistentClient(path=db_path)
self.collection = self.vector_db.get_or_create_collection("knowledge_base")
def generate_embeddings(self, texts: List[str]) -> List[List[float]]:
"""外部API経由で埋め込みベクトルを生成"""
# 実際の実装ではAPIクライアントを初期化
# ここではダミー実装
return [[0.1 * i for _ in range(1536)] for i in range(len(texts))]
def store_documents(self, raw_texts: List[str]):
"""ドキュメントのベクトル化と格納"""
all_chunks = []
for text in raw_texts:
all_chunks.extend(sliding_text_chunker(text, 200, 50))
embeddings = self.generate_embeddings(all_chunks)
self.collection.add(
documents=all_chunks,
embeddings=embeddings,
ids=[f"doc_{i}" for i in range(len(all_chunks))]
)
ベクトル類似度の評価手法
クエリとドキュメントの関連性を評価する主な指標:
| 評価指標 | 計算式 | 特徴 |
|---|---|---|
| コサイン類似度 | $\frac{A \cdot B}{\|A\| \|B\|}$ | 値が1に近いほど類似。方向性に注目 |
| ユークリッド距離 | $\sqrt{\sum (A_i - B_i)^2}$ | 値が小さいほど類似。絶対距離を評価 |
ベクトルデータベースの選択基準
用途に応じたデータベース選定のポイント:
- 開発検証段階:ChromaDB(軽量で組み込み可能)
- 大規模データ処理:Milvus(分散処理対応)
- ハイブリッド検索:Elasticsearch(キーワード+ベクトル検索)
- クラウドネイティブ環境:Qdrant(Rust実装で高効率)
医療FAQシステムの実装例
実際のRAGシステム構築フローを医療分野のFAQシステムで説明:
class MedicalRAGSystem:
def __init__(self):
self.vector_repo = VectorRepository()
self._load_medical_data()
def _load_medical_data(self):
"""医療ドキュメントの読み込みと格納"""
with open("medical_guidelines.jsonl", "r", encoding="utf-8") as f:
guidelines = [json.loads(line)["content"] for line in f]
self.vector_repo.store_documents(guidelines)
def respond_to_query(self, user_question: str) -> str:
"""クエリへの回答生成"""
# 類似ドキュメントの検索
results = self.vector_repo.collection.query(
query_texts=[user_question],
n_results=3
)
# コンテキストの構築
context = "\n".join(results["documents"][0])
prompt = f"医療専門家として以下の情報を基に回答してください:\n{context}\n\n質問: {user_question}"
# LLMによる回答生成
return llm_client.generate(prompt, temperature=0.3)
この実装では、医療ガイドラインを前処理し、ユーザの質問に対して関連情報を検索して回答を生成する。温度パラメータで出力の多様性を制御し、専門分野に適した精度を維持する。